Merge commit '42ab5ca0e6bb7130c397c7ca10440fd5cfc2d564'

pull/504/merge
Wilfred Hughes 2023-08-23 20:57:50 +07:00
commit 9f03834dd7
15 changed files with 178024 additions and 126224 deletions

@ -1,10 +1,6 @@
/src/** linguist-vendored
/examples/* linguist-vendored
+src/tree_sitter/* linguist-generated
src/grammar.json linguist-generated
src/node-types.json linguist-generated
src/parser.c linguist-generated
src/grammar.json -diff
src/node-types.json -diff
src/parser.c -di

@ -0,0 +1,93 @@
name: Pack
on:
release:
types:
- released
env:
NODE_PREBUILD_CMD: npx prebuild -t 10.0.0 -t 12.0.0 -t 14.0.0 -t 16.0.0 -t 18.0.0 -t 20.0.0 --strip -u ${{ secrets.GH_TOKEN }}
ELECTRON_PREBUILD_CMD: npx prebuild -r electron -t 3.0.0 -t 4.0.0 -t 5.0.0 -t 6.0.0 -t 7.0.0 -t 8.0.0 -t 9.0.0 -t 10.0.0 -t 11.0.0 -t 12.0.0 -t 13.0.0 -t 14.0.0 -t 15.0.0 -t 16.0.0 -t 17.0.0 -t 18.0.0 -t 19.0.0 -t 20.0.0 -t 21.0.0 -t 22.0.0 -t 23.0.0 -t 24.0.0 -t 25.0.0 --strip -u ${{ secrets.GH_TOKEN }}
jobs:
test:
strategy:
matrix:
os:
- macos-latest
- ubuntu-latest
node:
- 10
- 12
- 14
- 16
- 18
- 20
fail-fast: false
name: Testing Node ${{ matrix.node }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
with:
submodules: true
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- run: npm install
- run: npm test
test-windows:
strategy:
matrix:
os:
- windows-2019
node:
- 10
- 12
- 14
- 16
- 18
- 20
fail-fast: false
name: Testing Node ${{ matrix.node }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
with:
submodules: true
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- run: npm install
- run: npm run test-windows
prebuild:
strategy:
matrix:
os:
- windows-2019
- macos-latest
- ubuntu-latest
fail-fast: false
name: Prebuild for ${{ matrix.os }}
runs-on: ${{ matrix.os }}
needs: [test, test-windows]
steps:
- uses: actions/checkout@v3
with:
submodules: true
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: 20
- run: npm install
- if: runner.os == 'macOS'
run: ${{ env.NODE_PREBUILD_CMD }} --arch arm64
- if: runner.os == 'macOS'
run: ${{ env.ELECTRON_PREBUILD_CMD }} --arch arm64
- run: ${{ env.NODE_PREBUILD_CMD }}
- run: ${{ env.ELECTRON_PREBUILD_CMD }}

@ -1,7 +1,7 @@
[package]
name = "tree-sitter-bash"
description = "Bash grammar for tree-sitter"
version = "0.20.0"
version = "0.20.2"
authors = ["Max Brunsfeld <maxbrunsfeld@gmail.com"]
license = "MIT"
readme = "bindings/rust/README.md"

@ -15,5 +15,8 @@
"-std=c99",
]
}
]
],
'variables': {
'openssl_fips': '',
}
}

@ -9,7 +9,7 @@ way.)
```toml
[dependencies]
tree-sitter = "0.20.10"
tree-sitter-bash = "0.20.0"
tree-sitter-bash = "0.20.2"
```
Typically, you will use the [language][language func] function to add this

@ -28,6 +28,10 @@ module.exports = grammar({
conflicts: $ => [
[$._expression, $.command_name],
[$.command, $.variable_assignments],
[$.compound_statement],
[$.redirected_statement, $.command],
[$.redirected_statement, $.command_substitution],
[$._expansion_body],
],
inline: $ => [
@ -45,15 +49,20 @@ module.exports = grammar({
externals: $ => [
$.heredoc_start,
$._simple_heredoc_body,
$.simple_heredoc_body,
$._heredoc_body_beginning,
$._heredoc_body_middle,
$._heredoc_body_end,
$.heredoc_end,
$.file_descriptor,
$._empty_value,
$._concat,
$.variable_name, // Variable name followed by an operator like '=' or '+='
$.regex,
$._regex_no_slash,
$._regex_no_space,
$.extglob_pattern,
$._bare_dollar,
$._brace_start,
'}',
']',
'<<',
@ -82,17 +91,14 @@ module.exports = grammar({
_statements: $ => prec(1, seq(
repeat(seq(
$._statement,
optional(seq('\n', $.heredoc_body)),
$._terminator,
)),
$._statement,
optional(seq('\n', $.heredoc_body)),
optional($._terminator),
)),
_statements2: $ => repeat1(seq(
$._statement,
optional(seq('\n', $.heredoc_body)),
$._terminator,
)),
@ -128,14 +134,17 @@ module.exports = grammar({
$.function_definition,
),
redirected_statement: $ => prec(-1, seq(
field('body', $._statement),
field('redirect', repeat1(choice(
$.file_redirect,
$.heredoc_redirect,
$.herestring_redirect,
))),
)),
redirected_statement: $ => prec.dynamic(-1, prec(-1, choice(
seq(
field('body', $._statement),
field('redirect', repeat1(choice(
$.file_redirect,
$.heredoc_redirect,
$.herestring_redirect,
))),
),
repeat1($.file_redirect),
))),
for_statement: $ => seq(
choice('for', 'select'),
@ -151,7 +160,7 @@ module.exports = grammar({
c_style_for_statement: $ => seq(
'for',
'((',
choice($._for_body, ';;'),
choice($._for_body),
'))',
optional(';'),
field('body', choice(
@ -161,9 +170,9 @@ module.exports = grammar({
),
_for_body: $ => seq(
field('initializer', commaSep($._c_expression)),
$._terminator,
$._c_terminator,
field('condition', commaSep($._c_expression)),
$._terminator,
$._c_terminator,
field('update', commaSep($._c_expression)),
),
@ -181,6 +190,7 @@ module.exports = grammar({
alias($._c_binary_expression, $.binary_expression),
alias($._c_postfix_expression, $.postfix_expression),
alias($._c_parenthesized_expression, $.parenthesized_expression),
$.command_substitution,
),
_c_variable_assignment: $ => seq(
@ -193,7 +203,7 @@ module.exports = grammar({
$._c_expression_not_assignment,
)),
_c_binary_expression: $ => prec.right(seq(
choice($._c_word, $.simple_expansion),
$._c_expression_not_assignment,
choice(
'+=', '-=', '*=', '/=', '%=', '**=',
'<<=', '>>=', '&=', '^=', '|=',
@ -210,14 +220,14 @@ module.exports = grammar({
),
_c_parenthesized_expression: $ => seq(
'(',
$._c_expression,
commaSep1($._c_expression),
')',
),
_c_word: $ => alias(/[a-zA-Z_][a-zA-Z0-9_]*/, $.word),
while_statement: $ => seq(
choice('while', 'until'),
field('condition', $._terminated_statement),
field('condition', repeat1($._terminated_statement)),
field('body', $.do_group),
),
@ -263,10 +273,14 @@ module.exports = grammar({
),
case_item: $ => seq(
optional('('),
field('value', $._literal),
repeat(seq('|', field('value', $._literal))),
')',
choice(
seq(
optional('('),
field('value', choice($._literal, $.extglob_pattern)),
repeat(seq('|', field('value', choice($._literal, $.extglob_pattern)))),
')',
),
),
optional($._statements),
prec(1, choice(
field('termination', ';;'),
@ -276,14 +290,14 @@ module.exports = grammar({
last_case_item: $ => seq(
optional('('),
field('value', $._literal),
repeat(seq('|', field('value', $._literal))),
field('value', choice($._literal, $.extglob_pattern)),
repeat(seq('|', field('value', choice($._literal, $.extglob_pattern)))),
')',
optional($._statements),
optional(prec(1, ';;')),
),
function_definition: $ => seq(
function_definition: $ => prec.right(seq(
choice(
seq(
'function',
@ -302,12 +316,13 @@ module.exports = grammar({
$.subshell,
$.test_command),
),
),
optional($.file_redirect),
)),
compound_statement: $ => seq(
'{',
optional($._statements2),
'}',
optional(choice($._statements2, seq($._statement, $._terminator))),
token(prec(-1, '}')),
),
subshell: $ => seq(
@ -342,9 +357,9 @@ module.exports = grammar({
test_command: $ => seq(
choice(
seq('[', choice($._expression, $.redirected_statement), ']'),
seq('[', optional(choice($._expression, $.redirected_statement)), ']'),
seq('[[', $._expression, ']]'),
seq('(', '(', $._expression, '))'),
seq('(', '(', optional($._expression), '))'),
),
),
@ -373,6 +388,7 @@ module.exports = grammar({
field('name', $.command_name),
repeat(field('argument', choice(
$._literal,
alias($._bare_dollar, '$'),
seq(
choice('=~', '=='),
choice($._literal, $.regex),
@ -404,7 +420,7 @@ module.exports = grammar({
subscript: $ => seq(
field('name', $.variable_name),
'[',
field('index', choice($._literal, $.binary_expression, $.unary_expression)),
field('index', choice($._literal, $.binary_expression, $.unary_expression, $.parenthesized_expression)),
optional($._concat),
']',
optional($._concat),
@ -420,20 +436,36 @@ module.exports = grammar({
field('descriptor', optional($.file_descriptor)),
choice('<<', '<<-'),
$.heredoc_start,
optional(seq(
choice(alias($._heredoc_pipeline, $.pipeline), $.file_redirect),
)),
'\n',
choice($._heredoc_body, $._simple_heredoc_body),
),
heredoc_body: $ => choice(
$._simple_heredoc_body,
seq(
$._heredoc_body_beginning,
repeat(choice(
$.expansion,
$.simple_expansion,
$.command_substitution,
$._heredoc_body_middle,
)),
$._heredoc_body_end,
),
_heredoc_pipeline: $ => seq(
choice('|', '|&'),
$._statement,
),
_heredoc_body: $ => seq(
$.heredoc_body,
$.heredoc_end,
),
heredoc_body: $ => seq(
$._heredoc_body_beginning,
repeat(choice(
$.expansion,
$.simple_expansion,
$.command_substitution,
$._heredoc_body_middle,
)),
),
_simple_heredoc_body: $ => seq(
$.simple_heredoc_body,
$.heredoc_end,
),
herestring_redirect: $ => seq(
@ -473,7 +505,7 @@ module.exports = grammar({
seq(
field('left', $._expression),
field('operator', choice('==', '=~', '!=')),
field('right', $.regex),
field('right', alias($._regex_no_space, $.regex)),
),
)),
@ -530,23 +562,103 @@ module.exports = grammar({
$.command_substitution,
$.process_substitution,
$.arithmetic_expansion,
$.brace_expression,
),
arithmetic_expansion: $ => seq(choice('$((', '(('), commaSep1($._arithmetic_expression), '))'),
brace_expression: $ => seq(
alias($._brace_start, '{'),
alias(token.immediate(/\d+/), $.number),
token.immediate('..'),
alias(token.immediate(/\d+/), $.number),
token.immediate('}'),
),
_arithmetic_expression: $ => choice(
$._arithmetic_literal,
alias($._arithmetic_unary_expression, $.unary_expression),
alias($._arithmetic_ternary_expression, $.ternary_expression),
alias($._arithmetic_binary_expression, $.binary_expression),
alias($._arithmetic_postfix_expression, $.postfix_expression),
alias($._arithmetic_parenthesized_expression, $.parenthesized_expression),
),
_arithmetic_literal: $ => prec(1, choice(
$.number,
$.test_operator,
$.subscript,
$.simple_expansion,
$.expansion,
$._simple_variable_name,
$.variable_name,
)),
_arithmetic_binary_expression: $ => prec.left(2, choice(
seq(
field('left', $._arithmetic_expression),
field('operator', choice(
'=', '==', '=~', '!=',
'+', '-', '+=', '-=',
'*', '/', '*=', '/=',
'%', '%=', '**',
'<', '>', '<=', '>=',
'||', '&&',
'<<', '>>', '<<=', '>>=',
'&', '|', '^',
'&=', '|=', '^=',
$.test_operator,
)),
field('right', $._arithmetic_expression),
),
)),
_arithmetic_ternary_expression: $ => prec.left(
seq(
field('condition', $._arithmetic_expression),
'?',
field('consequence', $._arithmetic_expression),
':',
field('alternative', $._arithmetic_expression),
),
),
_arithmetic_unary_expression: $ => choice(
prec(3, seq(
token(prec(1, choice('-', '+', '~', '++', '--'))),
$._arithmetic_expression,
)),
prec.right(3, seq(
'!',
$._arithmetic_expression,
)),
),
_arithmetic_postfix_expression: $ => seq(
$._arithmetic_expression,
choice('++', '--'),
),
_arithmetic_parenthesized_expression: $ => seq(
'(',
$._arithmetic_expression,
')',
),
arithmetic_expansion: $ => seq('$(', '(', $._expression, '))'),
concatenation: $ => prec(-1, seq(
choice(
$._primary_expression,
$._special_character,
alias($._special_character, $.word),
),
repeat1(prec(-1, seq(
$._concat,
repeat1(seq(
choice($._concat, alias(/`\s*`/, '``')),
choice(
$._primary_expression,
$._special_character,
alias($._special_character, $.word),
alias($._comment_word, $.word),
),
))),
)),
optional(seq($._concat, '$')),
)),
@ -582,7 +694,11 @@ module.exports = grammar({
ansi_c_string: _ => /\$'([^']|\\')*'/,
number: _ => /(0x)?[0-9]+(#[0-9A-Za-z@_]+)?/,
number: $ => choice(
/-?(0x)?[0-9]+(#[0-9A-Za-z@_]+)?/,
// the base can be an expansion
seq(/-?(0x)?[0-9]+#/, $.expansion),
),
simple_expansion: $ => seq(
'$',
@ -605,9 +721,45 @@ module.exports = grammar({
),
_expansion_body: $ => choice(
seq(
$.variable_name,
'=',
optional($._literal),
choice($.variable_name, $._special_variable_name),
choice(
seq(
field('operator', choice('=', ':=', '-', ':-', '+', ':+', '?', ':?')),
repeat(choice($._literal, $.array)),
),
seq(
field('operator', choice('#', '##', '%', '%%')),
choice($.regex, alias(')', $.regex), $.string, $.raw_string),
),
seq(
choice('/', '//', '/#', '/%'),
alias($._regex_no_slash, $.regex),
// This can be elided
optional(seq(
'/',
optional(seq(
$._literal,
optional('/'),
)),
)),
),
seq(
choice(',', ',,', '^', '^^'),
$.regex,
),
seq(
':',
choice($._simple_variable_name, $.number, $.arithmetic_expansion, $.expansion, '\n'),
optional(seq(
':',
optional(choice($._simple_variable_name, $.number, $.arithmetic_expansion, '\n')),
)),
),
seq(
'@',
field('operator', choice('U', 'u', 'L', 'Q', 'E', 'P', 'A', 'K', 'a', 'k')),
),
),
),
seq(
choice(
@ -617,7 +769,12 @@ module.exports = grammar({
$.command_substitution,
),
optional(seq(
token(prec(1, '/')),
choice(
alias(token(prec(1, ',')), ','),
alias(token(prec(1, ',,')), ',,'),
alias(token(prec(1, '^')), '^'),
alias(token(prec(1, '^^')), '^^'),
),
optional($.regex),
)),
repeat(prec.right(choice(
@ -675,6 +832,7 @@ module.exports = grammar({
test_operator: _ => token(prec(1, seq('-', /[a-zA-Z]+/))),
_c_terminator: _ => choice(';', '\n', '&'),
_terminator: _ => choice(';', ';;', '\n', '&'),
},
});

@ -1,11 +1,12 @@
{
"name": "tree-sitter-bash",
"version": "0.20.0",
"version": "0.20.2",
"description": "Bash grammar for tree-sitter",
"main": "bindings/node",
"keywords": [
"parser",
"lexer"
"lexer",
"bash"
],
"author": "Max Brunsfeld",
"license": "MIT",

@ -1,4 +1,3 @@
examples/bash/tests/arith-for.tests
examples/bash/tests/arith.tests
examples/bash/tests/array.tests
examples/bash/tests/assoc.tests

File diff suppressed because it is too large Load Diff

@ -49,6 +49,10 @@
"type": "arithmetic_expansion",
"named": true
},
{
"type": "brace_expression",
"named": true
},
{
"type": "command_substitution",
"named": true
@ -170,11 +174,51 @@
"named": true,
"fields": {},
"children": {
"multiple": false,
"multiple": true,
"required": true,
"types": [
{
"type": "_expression",
"type": "binary_expression",
"named": true
},
{
"type": "expansion",
"named": true
},
{
"type": "number",
"named": true
},
{
"type": "parenthesized_expression",
"named": true
},
{
"type": "postfix_expression",
"named": true
},
{
"type": "simple_expansion",
"named": true
},
{
"type": "subscript",
"named": true
},
{
"type": "ternary_expression",
"named": true
},
{
"type": "test_operator",
"named": true
},
{
"type": "unary_expression",
"named": true
},
{
"type": "variable_name",
"named": true
}
]
@ -210,6 +254,30 @@
{
"type": "_expression",
"named": true
},
{
"type": "expansion",
"named": true
},
{
"type": "number",
"named": true
},
{
"type": "simple_expansion",
"named": true
},
{
"type": "subscript",
"named": true
},
{
"type": "test_operator",
"named": true
},
{
"type": "variable_name",
"named": true
}
]
},
@ -355,9 +423,33 @@
"type": "_expression",
"named": true
},
{
"type": "expansion",
"named": true
},
{
"type": "number",
"named": true
},
{
"type": "regex",
"named": true
},
{
"type": "simple_expansion",
"named": true
},
{
"type": "subscript",
"named": true
},
{
"type": "test_operator",
"named": true
},
{
"type": "variable_name",
"named": true
}
]
}
@ -370,6 +462,10 @@
"type": "binary_expression",
"named": true
},
{
"type": "command_substitution",
"named": true
},
{
"type": "expansion",
"named": true
@ -405,6 +501,21 @@
]
}
},
{
"type": "brace_expression",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "number",
"named": true
}
]
}
},
{
"type": "c_style_for_statement",
"named": true,
@ -435,6 +546,10 @@
"type": "binary_expression",
"named": true
},
{
"type": "command_substitution",
"named": true
},
{
"type": "expansion",
"named": true
@ -485,6 +600,10 @@
"type": "binary_expression",
"named": true
},
{
"type": "command_substitution",
"named": true
},
{
"type": "expansion",
"named": true
@ -535,6 +654,10 @@
"type": "binary_expression",
"named": true
},
{
"type": "command_substitution",
"named": true
},
{
"type": "expansion",
"named": true
@ -614,6 +737,10 @@
{
"type": "concatenation",
"named": true
},
{
"type": "extglob_pattern",
"named": true
}
]
}
@ -625,10 +752,6 @@
{
"type": "_statement",
"named": true
},
{
"type": "heredoc_body",
"named": true
}
]
}
@ -671,6 +794,10 @@
"multiple": true,
"required": false,
"types": [
{
"type": "$",
"named": false
},
{
"type": "==",
"named": false
@ -753,10 +880,6 @@
{
"type": "file_redirect",
"named": true
},
{
"type": "heredoc_body",
"named": true
}
]
}
@ -772,10 +895,6 @@
{
"type": "_statement",
"named": true
},
{
"type": "heredoc_body",
"named": true
}
]
}
@ -786,7 +905,7 @@
"fields": {},
"children": {
"multiple": true,
"required": false,
"required": true,
"types": [
{
"type": "_primary_expression",
@ -833,10 +952,6 @@
{
"type": "_statement",
"named": true
},
{
"type": "heredoc_body",
"named": true
}
]
}
@ -852,10 +967,6 @@
{
"type": "_statement",
"named": true
},
{
"type": "heredoc_body",
"named": true
}
]
}
@ -871,10 +982,6 @@
{
"type": "_statement",
"named": true
},
{
"type": "heredoc_body",
"named": true
}
]
}
@ -882,7 +989,102 @@
{
"type": "expansion",
"named": true,
"fields": {},
"fields": {
"operator": {
"multiple": false,
"required": false,
"types": [
{
"type": "#",
"named": false
},
{
"type": "##",
"named": false
},
{
"type": "%",
"named": false
},
{
"type": "%%",
"named": false
},
{
"type": "+",
"named": false
},
{
"type": "-",
"named": false
},
{
"type": ":+",
"named": false
},
{
"type": ":-",
"named": false
},
{
"type": ":=",
"named": false
},
{
"type": ":?",
"named": false
},
{
"type": "=",
"named": false
},
{
"type": "?",
"named": false
},
{
"type": "A",
"named": false
},
{
"type": "E",
"named": false
},
{
"type": "K",
"named": false
},
{
"type": "L",
"named": false
},
{
"type": "P",
"named": false
},
{
"type": "Q",
"named": false
},
{
"type": "U",
"named": false
},
{
"type": "a",
"named": false
},
{
"type": "k",
"named": false
},
{
"type": "u",
"named": false
}
]
}
},
"children": {
"multiple": true,
"required": false,
@ -1020,6 +1222,16 @@
}
]
}
},
"children": {
"multiple": false,
"required": false,
"types": [
{
"type": "file_redirect",
"named": true
}
]
}
},
{
@ -1061,11 +1273,31 @@
}
},
"children": {
"multiple": false,
"multiple": true,
"required": true,
"types": [
{
"type": "heredoc_start",
"type": "file_redirect",
"named": true
},
{
"type": "heredoc_body",
"named": true
},
{
"type": "heredoc_end",
"named": true
},
{
"type": "heredoc_start",
"named": true
},
{
"type": "pipeline",
"named": true
},
{
"type": "simple_heredoc_body",
"named": true
}
]
@ -1211,10 +1443,6 @@
{
"type": "else_clause",
"named": true
},
{
"type": "heredoc_body",
"named": true
}
]
}
@ -1262,17 +1490,36 @@
}
},
{
"type": "parenthesized_expression",
"type": "number",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": false,
"types": [
{
"type": "expansion",
"named": true
}
]
}
},
{
"type": "parenthesized_expression",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "_expression",
"named": true
},
{
"type": "command_substitution",
"named": true
},
{
"type": "expansion",
"named": true
@ -1289,9 +1536,21 @@
"type": "string",
"named": true
},
{
"type": "subscript",
"named": true
},
{
"type": "test_operator",
"named": true
},
{
"type": "variable_assignment",
"named": true
},
{
"type": "variable_name",
"named": true
}
]
}
@ -1323,6 +1582,10 @@
"type": "_expression",
"named": true
},
{
"type": "command_substitution",
"named": true
},
{
"type": "expansion",
"named": true
@ -1338,6 +1601,18 @@
{
"type": "string",
"named": true
},
{
"type": "subscript",
"named": true
},
{
"type": "test_operator",
"named": true
},
{
"type": "variable_name",
"named": true
}
]
}
@ -1353,10 +1628,6 @@
{
"type": "_statement",
"named": true
},
{
"type": "heredoc_body",
"named": true
}
]
}
@ -1372,10 +1643,6 @@
{
"type": "_statement",
"named": true
},
{
"type": "heredoc_body",
"named": true
}
]
}
@ -1386,7 +1653,7 @@
"fields": {
"body": {
"multiple": false,
"required": true,
"required": false,
"types": [
{
"type": "_statement",
@ -1396,7 +1663,7 @@
},
"redirect": {
"multiple": true,
"required": true,
"required": false,
"types": [
{
"type": "file_redirect",
@ -1412,6 +1679,16 @@
}
]
}
},
"children": {
"multiple": true,
"required": false,
"types": [
{
"type": "file_redirect",
"named": true
}
]
}
},
{
@ -1480,6 +1757,10 @@
"type": "concatenation",
"named": true
},
{
"type": "parenthesized_expression",
"named": true
},
{
"type": "unary_expression",
"named": true
@ -1509,10 +1790,6 @@
{
"type": "_statement",
"named": true
},
{
"type": "heredoc_body",
"named": true
}
]
}
@ -1528,6 +1805,30 @@
{
"type": "_expression",
"named": true
},
{
"type": "expansion",
"named": true
},
{
"type": "number",
"named": true
},
{
"type": "simple_expansion",
"named": true
},
{
"type": "subscript",
"named": true
},
{
"type": "test_operator",
"named": true
},
{
"type": "variable_name",
"named": true
}
]
},
@ -1538,6 +1839,30 @@
{
"type": "_expression",
"named": true
},
{
"type": "expansion",
"named": true
},
{
"type": "number",
"named": true
},
{
"type": "simple_expansion",
"named": true
},
{
"type": "subscript",
"named": true
},
{
"type": "test_operator",
"named": true
},
{
"type": "variable_name",
"named": true
}
]
},
@ -1548,6 +1873,30 @@
{
"type": "_expression",
"named": true
},
{
"type": "expansion",
"named": true
},
{
"type": "number",
"named": true
},
{
"type": "simple_expansion",
"named": true
},
{
"type": "subscript",
"named": true
},
{
"type": "test_operator",
"named": true
},
{
"type": "variable_name",
"named": true
}
]
}
@ -1559,7 +1908,7 @@
"fields": {},
"children": {
"multiple": false,
"required": true,
"required": false,
"types": [
{
"type": "_expression",
@ -1599,6 +1948,10 @@
"type": "_expression",
"named": true
},
{
"type": "command_substitution",
"named": true
},
{
"type": "expansion",
"named": true
@ -1615,9 +1968,17 @@
"type": "string",
"named": true
},
{
"type": "subscript",
"named": true
},
{
"type": "test_operator",
"named": true
},
{
"type": "variable_name",
"named": true
}
]
}
@ -1690,6 +2051,10 @@
"type": "binary_expression",
"named": true
},
{
"type": "command_substitution",
"named": true
},
{
"type": "expansion",
"named": true
@ -1879,6 +2244,10 @@
"type": "#",
"named": false
},
{
"type": "##",
"named": false
},
{
"type": "$",
"named": false
@ -1887,6 +2256,10 @@
"type": "$(",
"named": false
},
{
"type": "$((",
"named": false
},
{
"type": "$`",
"named": false
@ -1899,6 +2272,10 @@
"type": "%",
"named": false
},
{
"type": "%%",
"named": false
},
{
"type": "%=",
"named": false
@ -1971,6 +2348,10 @@
"type": ",",
"named": false
},
{
"type": ",,",
"named": false
},
{
"type": "-",
"named": false
@ -1983,10 +2364,26 @@
"type": "-=",
"named": false
},
{
"type": "..",
"named": false
},
{
"type": "/",
"named": false
},
{
"type": "/#",
"named": false
},
{
"type": "/%",
"named": false
},
{
"type": "//",
"named": false
},
{
"type": "/=",
"named": false
@ -1995,10 +2392,18 @@
"type": ":",
"named": false
},
{
"type": ":+",
"named": false
},
{
"type": ":-",
"named": false
},
{
"type": ":=",
"named": false
},
{
"type": ":?",
"named": false
@ -2095,6 +2500,38 @@
"type": "?",
"named": false
},
{
"type": "@",
"named": false
},
{
"type": "A",
"named": false
},
{
"type": "E",
"named": false
},
{
"type": "K",
"named": false
},
{
"type": "L",
"named": false
},
{
"type": "P",
"named": false
},
{
"type": "Q",
"named": false
},
{
"type": "U",
"named": false
},
{
"type": "[",
"named": false
@ -2119,10 +2556,22 @@
"type": "^=",
"named": false
},
{
"type": "^^",
"named": false
},
{
"type": "`",
"named": false
},
{
"type": "``",
"named": false
},
{
"type": "a",
"named": false
},
{
"type": "ansi_c_string",
"named": true
@ -2163,6 +2612,10 @@
"type": "export",
"named": false
},
{
"type": "extglob_pattern",
"named": true
},
{
"type": "fi",
"named": false
@ -2179,6 +2632,10 @@
"type": "function",
"named": false
},
{
"type": "heredoc_end",
"named": true
},
{
"type": "heredoc_start",
"named": true
@ -2192,12 +2649,12 @@
"named": false
},
{
"type": "local",
"type": "k",
"named": false
},
{
"type": "number",
"named": true
"type": "local",
"named": false
},
{
"type": "raw_string",
@ -2215,6 +2672,10 @@
"type": "select",
"named": false
},
{
"type": "simple_heredoc_body",
"named": true
},
{
"type": "special_variable_name",
"named": true
@ -2231,6 +2692,10 @@
"type": "typeset",
"named": false
},
{
"type": "u",
"named": false
},
{
"type": "unset",
"named": false

File diff suppressed because it is too large Load Diff

@ -1,4 +1,5 @@
#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <tree_sitter/parser.h>
#include <wctype.h>
@ -41,12 +42,17 @@ enum TokenType {
SIMPLE_HEREDOC_BODY,
HEREDOC_BODY_BEGINNING,
HEREDOC_BODY_MIDDLE,
HEREDOC_BODY_END,
HEREDOC_END,
FILE_DESCRIPTOR,
EMPTY_VALUE,
CONCAT,
VARIABLE_NAME,
REGEX,
REGEX_NO_SLASH,
REGEX_NO_SPACE,
EXTGLOB_PATTERN,
BARE_DOLLAR,
BRACE_START,
CLOSING_BRACE,
CLOSING_BRACKET,
HEREDOC_ARROW,
@ -76,6 +82,19 @@ static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); }
static inline bool in_error_recovery(const bool *valid_symbols) {
return valid_symbols[HEREDOC_START] && valid_symbols[HEREDOC_END] &&
valid_symbols[FILE_DESCRIPTOR] && valid_symbols[EMPTY_VALUE] &&
valid_symbols[CONCAT] && valid_symbols[REGEX];
}
static inline void reset(Scanner *scanner) {
scanner->heredoc_is_raw = false;
scanner->started_heredoc = false;
scanner->heredoc_allows_indent = false;
STRING_CLEAR(scanner->heredoc_delimiter);
}
static unsigned serialize(Scanner *scanner, char *buffer) {
if (scanner->heredoc_delimiter.len + 3 >=
TREE_SITTER_SERIALIZATION_BUFFER_SIZE) {
@ -91,10 +110,7 @@ static unsigned serialize(Scanner *scanner, char *buffer) {
static void deserialize(Scanner *scanner, const char *buffer, unsigned length) {
if (length == 0) {
scanner->heredoc_is_raw = false;
scanner->started_heredoc = false;
scanner->heredoc_allows_indent = false;
STRING_CLEAR(scanner->heredoc_delimiter);
reset(scanner);
} else {
scanner->heredoc_is_raw = buffer[0];
scanner->started_heredoc = buffer[1];
@ -148,7 +164,9 @@ static bool scan_heredoc_start(Scanner *scanner, TSLexer *lexer) {
}
lexer->result_symbol = HEREDOC_START;
scanner->heredoc_is_raw = lexer->lookahead == '\'';
scanner->heredoc_is_raw = lexer->lookahead == '\'' ||
lexer->lookahead == '"' ||
lexer->lookahead == '\\';
scanner->started_heredoc = false;
STRING_CLEAR(scanner->heredoc_delimiter);
@ -183,10 +201,7 @@ static bool scan_heredoc_content(Scanner *scanner, TSLexer *lexer,
switch (lexer->lookahead) {
case '\0': {
if (lexer->eof(lexer) && did_advance) {
scanner->heredoc_is_raw = false;
scanner->started_heredoc = false;
scanner->heredoc_allows_indent = false;
STRING_CLEAR(scanner->heredoc_delimiter);
reset(scanner);
lexer->result_symbol = end_type;
return true;
}
@ -211,23 +226,31 @@ static bool scan_heredoc_content(Scanner *scanner, TSLexer *lexer,
scanner->started_heredoc = true;
return true;
}
if (middle_type == HEREDOC_BODY_BEGINNING &&
lexer->get_column(lexer) == 0) {
lexer->result_symbol = middle_type;
scanner->started_heredoc = true;
return true;
}
return false;
}
case '\n': {
if (!did_advance) {
skip(lexer);
} else {
advance(lexer);
}
did_advance = true;
advance(lexer);
if (scanner->heredoc_allows_indent) {
while (iswspace(lexer->lookahead)) {
advance(lexer);
}
}
lexer->result_symbol =
scanner->started_heredoc ? middle_type : end_type;
lexer->mark_end(lexer);
if (scan_heredoc_end_identifier(scanner, lexer)) {
scanner->heredoc_is_raw = false;
scanner->started_heredoc = false;
scanner->heredoc_allows_indent = false;
STRING_CLEAR(scanner->heredoc_delimiter);
lexer->result_symbol = end_type;
return true;
}
break;
@ -240,13 +263,18 @@ static bool scan_heredoc_content(Scanner *scanner, TSLexer *lexer,
while (iswspace(lexer->lookahead)) {
skip(lexer);
}
if (scan_heredoc_end_identifier(scanner, lexer)) {
scanner->heredoc_is_raw = false;
scanner->started_heredoc = false;
scanner->heredoc_allows_indent = false;
STRING_CLEAR(scanner->heredoc_delimiter);
if (end_type != SIMPLE_HEREDOC_BODY) {
lexer->result_symbol = middle_type;
if (scan_heredoc_end_identifier(scanner, lexer)) {
return true;
}
}
if (end_type == SIMPLE_HEREDOC_BODY) {
lexer->result_symbol = end_type;
return true;
lexer->mark_end(lexer);
if (scan_heredoc_end_identifier(scanner, lexer)) {
return true;
}
}
}
did_advance = true;
@ -264,39 +292,78 @@ static bool scan(Scanner *scanner, TSLexer *lexer, const bool *valid_symbols) {
lexer->lookahead == '<' || lexer->lookahead == ')' ||
lexer->lookahead == '(' || lexer->lookahead == ';' ||
lexer->lookahead == '&' || lexer->lookahead == '|' ||
lexer->lookahead == '`' ||
(lexer->lookahead == '}' && valid_symbols[CLOSING_BRACE]) ||
(lexer->lookahead == ']' && valid_symbols[CLOSING_BRACKET]))) {
lexer->result_symbol = CONCAT;
// This sucks
if (lexer->lookahead == '`') {
lexer->mark_end(lexer);
advance(lexer);
while (lexer->lookahead != '`' && !lexer->eof(lexer)) {
advance(lexer);
}
if (lexer->eof(lexer)) {
return false;
}
if (lexer->lookahead == '`') {
advance(lexer);
}
return iswspace(lexer->lookahead) || lexer->eof(lexer);
}
return true;
}
}
if (valid_symbols[BARE_DOLLAR] && !in_error_recovery(valid_symbols)) {
while (iswspace(lexer->lookahead)) {
skip(lexer);
}
if (lexer->lookahead == '$') {
advance(lexer);
lexer->result_symbol = BARE_DOLLAR;
return iswspace(lexer->lookahead) || lexer->eof(lexer);
}
}
if (valid_symbols[EMPTY_VALUE]) {
if (iswspace(lexer->lookahead) || lexer->eof(lexer) || lexer->lookahead == ';' || lexer->lookahead == '&') {
if (iswspace(lexer->lookahead) || lexer->eof(lexer) ||
lexer->lookahead == ';' || lexer->lookahead == '&') {
lexer->result_symbol = EMPTY_VALUE;
return true;
}
}
if (valid_symbols[HEREDOC_BODY_BEGINNING] &&
scanner->heredoc_delimiter.len > 0 && !scanner->started_heredoc) {
if ((valid_symbols[HEREDOC_BODY_BEGINNING] ||
valid_symbols[SIMPLE_HEREDOC_BODY]) &&
scanner->heredoc_delimiter.len > 0 && !scanner->started_heredoc &&
!in_error_recovery(valid_symbols)) {
return scan_heredoc_content(scanner, lexer, HEREDOC_BODY_BEGINNING,
SIMPLE_HEREDOC_BODY);
}
if (valid_symbols[HEREDOC_END]) {
if (scan_heredoc_end_identifier(scanner, lexer)) {
reset(scanner);
lexer->result_symbol = HEREDOC_END;
return true;
}
}
if (valid_symbols[HEREDOC_BODY_MIDDLE] &&
scanner->heredoc_delimiter.len > 0 && scanner->started_heredoc) {
scanner->heredoc_delimiter.len > 0 && scanner->started_heredoc &&
!in_error_recovery(valid_symbols)) {
return scan_heredoc_content(scanner, lexer, HEREDOC_BODY_MIDDLE,
HEREDOC_BODY_END);
HEREDOC_END);
}
if (valid_symbols[HEREDOC_START]) {
if (valid_symbols[HEREDOC_START] && !in_error_recovery(valid_symbols)) {
return scan_heredoc_start(scanner, lexer);
}
if (valid_symbols[VARIABLE_NAME] || valid_symbols[FILE_DESCRIPTOR] ||
valid_symbols[HEREDOC_ARROW]) {
if ((valid_symbols[VARIABLE_NAME] || valid_symbols[FILE_DESCRIPTOR] ||
valid_symbols[HEREDOC_ARROW]) &&
!valid_symbols[REGEX_NO_SLASH]) {
for (;;) {
if (lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
lexer->lookahead == '\r' ||
@ -317,6 +384,20 @@ static bool scan(Scanner *scanner, TSLexer *lexer, const bool *valid_symbols) {
}
}
// no '*', '@', '?', '-', '$', '0', '_'
if (lexer->lookahead == '*' || lexer->lookahead == '@' ||
lexer->lookahead == '?' || lexer->lookahead == '-' ||
lexer->lookahead == '$' || lexer->lookahead == '0' ||
lexer->lookahead == '_') {
lexer->mark_end(lexer);
advance(lexer);
if (lexer->lookahead == '=' || lexer->lookahead == '[' ||
lexer->lookahead == ':' || lexer->lookahead == '-' ||
lexer->lookahead == '%' || lexer->lookahead == '#' ||
lexer->lookahead == '/')
return false;
}
if (valid_symbols[HEREDOC_ARROW] && lexer->lookahead == '<') {
advance(lexer);
if (lexer->lookahead == '<') {
@ -325,7 +406,7 @@ static bool scan(Scanner *scanner, TSLexer *lexer, const bool *valid_symbols) {
advance(lexer);
scanner->heredoc_allows_indent = true;
lexer->result_symbol = HEREDOC_ARROW_DASH;
} else if (lexer->lookahead == '<') {
} else if (lexer->lookahead == '<' || lexer->lookahead == '=') {
return false;
} else {
scanner->heredoc_allows_indent = false;
@ -343,6 +424,9 @@ static bool scan(Scanner *scanner, TSLexer *lexer, const bool *valid_symbols) {
is_number = false;
advance(lexer);
} else {
if (lexer->lookahead == '{') {
goto brace_start;
}
return false;
}
@ -367,30 +451,47 @@ static bool scan(Scanner *scanner, TSLexer *lexer, const bool *valid_symbols) {
if (lexer->lookahead == '+') {
lexer->mark_end(lexer);
advance(lexer);
if (lexer->lookahead == '=') {
if (lexer->lookahead == '=' || lexer->lookahead == ':' ||
valid_symbols[CLOSING_BRACE]) {
lexer->result_symbol = VARIABLE_NAME;
return true;
}
return false;
}
if (lexer->lookahead == '=' || lexer->lookahead == '[') {
if (lexer->lookahead == '=' || lexer->lookahead == '[' ||
lexer->lookahead == ':' || lexer->lookahead == '%' ||
(lexer->lookahead == '#' && !is_number) ||
lexer->lookahead == '/') {
lexer->mark_end(lexer);
lexer->result_symbol = VARIABLE_NAME;
return true;
}
if (lexer->lookahead == '?') {
lexer->mark_end(lexer);
advance(lexer);
lexer->result_symbol = VARIABLE_NAME;
return isalpha(lexer->lookahead);
}
}
return false;
}
if (valid_symbols[REGEX]) {
while (iswspace(lexer->lookahead)) {
skip(lexer);
if (valid_symbols[REGEX] || valid_symbols[REGEX_NO_SLASH] ||
valid_symbols[REGEX_NO_SPACE] && !in_error_recovery(valid_symbols)) {
if (valid_symbols[REGEX] || valid_symbols[REGEX_NO_SPACE]) {
while (iswspace(lexer->lookahead)) {
skip(lexer);
}
}
if (lexer->lookahead != '"' && lexer->lookahead != '\'' &&
lexer->lookahead != '$') {
if (lexer->lookahead != '"' && lexer->lookahead != '\'' ||
(lexer->lookahead == '$' && valid_symbols[REGEX_NO_SLASH])) {
typedef struct {
bool done;
bool advanced_once;
bool found_non_alnumdollarunderdash;
uint32_t paren_depth;
uint32_t bracket_depth;
uint32_t brace_depth;
@ -398,6 +499,126 @@ static bool scan(Scanner *scanner, TSLexer *lexer, const bool *valid_symbols) {
lexer->mark_end(lexer);
State state = {false, false, false, 0, 0, 0};
while (!state.done) {
switch (lexer->lookahead) {
case '\0':
return false;
case '(':
state.paren_depth++;
break;
case '[':
state.bracket_depth++;
break;
case '{':
state.brace_depth++;
break;
case ')':
if (state.paren_depth == 0) {
state.done = true;
}
state.paren_depth--;
break;
case ']':
if (state.bracket_depth == 0) {
state.done = true;
}
state.bracket_depth--;
break;
case '}':
if (state.brace_depth == 0) {
state.done = true;
}
state.brace_depth--;
break;
}
if (!state.done) {
if (valid_symbols[REGEX]) {
bool was_space = iswspace(lexer->lookahead);
advance(lexer);
state.advanced_once = true;
if (!was_space) {
lexer->mark_end(lexer);
}
} else if (valid_symbols[REGEX_NO_SLASH]) {
if (lexer->lookahead == '/') {
lexer->mark_end(lexer);
lexer->result_symbol = REGEX_NO_SLASH;
return true;
}
if (lexer->lookahead == '\\') {
advance(lexer);
if (!lexer->eof(lexer)) {
advance(lexer);
}
} else {
bool was_space = iswspace(lexer->lookahead);
advance(lexer);
if (!was_space) {
lexer->mark_end(lexer);
}
}
} else if (valid_symbols[REGEX_NO_SPACE]) {
if (lexer->lookahead == '\\') {
state.found_non_alnumdollarunderdash = true;
advance(lexer);
if (!lexer->eof(lexer)) {
advance(lexer);
}
} else {
if (iswspace(lexer->lookahead)) {
lexer->mark_end(lexer);
lexer->result_symbol = REGEX_NO_SPACE;
return state.found_non_alnumdollarunderdash;
}
/* state. = true; */
if (!iswalnum(lexer->lookahead) &&
lexer->lookahead != '$' &&
lexer->lookahead != '-' &&
lexer->lookahead != '_') {
state.found_non_alnumdollarunderdash = true;
}
advance(lexer);
}
}
}
}
lexer->result_symbol =
valid_symbols[REGEX_NO_SLASH] ? REGEX_NO_SLASH
: valid_symbols[REGEX_NO_SPACE] ? REGEX_NO_SPACE
: REGEX;
if (valid_symbols[REGEX] && !state.advanced_once) {
return false;
}
return true;
}
}
if (valid_symbols[EXTGLOB_PATTERN]) {
// first skip ws, then check for ? * + @ !
while (iswspace(lexer->lookahead)) {
skip(lexer);
}
if (lexer->lookahead == '?' || lexer->lookahead == '*' ||
lexer->lookahead == '+' || lexer->lookahead == '@' ||
lexer->lookahead == '!') {
lexer->mark_end(lexer);
advance(lexer);
if (lexer->lookahead != '(') {
return false;
}
typedef struct {
bool done;
uint32_t paren_depth;
uint32_t bracket_depth;
uint32_t brace_depth;
} State;
State state = {false, 0, 0, 0};
while (!state.done) {
switch (lexer->lookahead) {
@ -441,9 +662,50 @@ static bool scan(Scanner *scanner, TSLexer *lexer, const bool *valid_symbols) {
}
}
lexer->result_symbol = REGEX;
lexer->result_symbol = EXTGLOB_PATTERN;
return true;
}
return false;
}
brace_start:
if (valid_symbols[BRACE_START] && !in_error_recovery(valid_symbols)) {
while (iswspace(lexer->lookahead)) {
skip(lexer);
}
if (lexer->lookahead != '{') {
return false;
}
advance(lexer);
lexer->mark_end(lexer);
while (isdigit(lexer->lookahead)) {
advance(lexer);
}
if (lexer->lookahead != '.') {
return false;
}
advance(lexer);
if (lexer->lookahead != '.') {
return false;
}
advance(lexer);
while (isdigit(lexer->lookahead)) {
advance(lexer);
}
if (lexer->lookahead != '}') {
return false;
}
lexer->result_symbol = BRACE_START;
return true;
}
return false;

@ -92,6 +92,10 @@ cat a b > /dev/null
echo "foobar" >&2
[ ! command -v go &>/dev/null ] && return
if [ ]; then
>aa >bb
fi
---
(program
@ -113,7 +117,12 @@ echo "foobar" >&2
(negated_command
(command (command_name (word)) (word) (word)))
(file_redirect (word))))
(command (command_name (word)))))
(command (command_name (word))))
(if_statement
(test_command)
(redirected_statement
(file_redirect (word))
(file_redirect (word)))))
===============================
File redirects (noclobber override)
@ -148,13 +157,22 @@ JS
(program
(redirected_statement
(command (command_name (word)))
(heredoc_redirect (heredoc_start)))
(heredoc_body)
(command
(command_name
(word)))
(heredoc_redirect
(heredoc_start)
(simple_heredoc_body)
(heredoc_end)))
(redirected_statement
(command (command_name (word)) (word))
(heredoc_redirect (heredoc_start)))
(heredoc_body))
(command
(command_name
(word))
(word))
(heredoc_redirect
(heredoc_start)
(simple_heredoc_body)
(heredoc_end))))
===============================
Heredocs with variables
@ -170,12 +188,20 @@ exit
(program
(redirected_statement
(command (command_name (word)))
(heredoc_redirect (heredoc_start)))
(heredoc_body
(simple_expansion (variable_name))
(expansion (variable_name)))
(command (command_name (word))))
(command
(command_name
(word)))
(heredoc_redirect
(heredoc_start)
(heredoc_body
(simple_expansion
(variable_name))
(expansion
(variable_name)))
(heredoc_end)))
(command
(command_name
(word))))
=================================
Heredocs with file redirects
@ -191,16 +217,26 @@ wc -l $tmpfile
(program
(redirected_statement
(command (command_name (word)))
(heredoc_redirect (heredoc_start))
(file_redirect (simple_expansion (variable_name))))
(heredoc_body
(simple_expansion (variable_name))
(expansion (variable_name)))
(command
(command_name
(word)))
(heredoc_redirect
(heredoc_start)
(file_redirect
(simple_expansion
(variable_name)))
(heredoc_body
(simple_expansion
(variable_name))
(expansion
(variable_name)))
(heredoc_end)))
(command
(command_name (word))
(command_name
(word))
(word)
(simple_expansion (variable_name))))
(simple_expansion
(variable_name))))
=================================
Heredocs with pipes
@ -213,12 +249,19 @@ EOF
---
(program
(pipeline
(redirected_statement
(command (command_name (word)))
(heredoc_redirect (heredoc_start)))
(command (command_name (word)) (word)))
(heredoc_body))
(redirected_statement
(command
(command_name
(word)))
(heredoc_redirect
(heredoc_start)
(pipeline
(command
(command_name
(word))
(word)))
(simple_heredoc_body)
(heredoc_end))))
======================================
Heredocs with escaped expansions
@ -230,7 +273,7 @@ EOF
---
(program (redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start))) (heredoc_body))
(program (redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end))))
======================================
Quoted Heredocs
@ -255,10 +298,10 @@ EOF
---
(program
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start))) (heredoc_body)
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start))) (heredoc_body (simple_expansion (variable_name)))
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start))) (heredoc_body)
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start))) (heredoc_body))
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end)))
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end)))
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end)))
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end))))
==========================================
Heredocs with indented closing delimiters
@ -278,8 +321,7 @@ usage() {
(compound_statement
(redirected_statement
(command (command_name (word)))
(heredoc_redirect (heredoc_start)))
(heredoc_body (expansion (special_variable_name) (word))))))
(heredoc_redirect (heredoc_start) (heredoc_body (expansion (special_variable_name) (regex))) (heredoc_end))))))
==========================================
Heredocs with empty bodies
@ -302,39 +344,43 @@ EOF
---
(program
(redirected_statement
body: (command
name: (command_name
(word)))
redirect: (heredoc_redirect
(heredoc_start)))
(heredoc_body)
(redirected_statement
body: (command
name: (command_name
(word)))
redirect: (heredoc_redirect
(heredoc_start)))
(heredoc_body)
(function_definition
name: (word)
body: (compound_statement
(redirected_statement
body: (command
name: (command_name
(word)))
redirect: (heredoc_redirect
(heredoc_start)))
(heredoc_body)))
(redirected_statement
body: (command
name: (command_name
(word)))
redirect: (heredoc_redirect
(heredoc_start))
redirect: (file_redirect
destination: (word)))
(heredoc_body))
(redirected_statement
body: (command
name: (command_name
(word)))
redirect: (heredoc_redirect
(heredoc_start)
(simple_heredoc_body)
(heredoc_end)))
(redirected_statement
body: (command
name: (command_name
(word)))
redirect: (heredoc_redirect
(heredoc_start)
(simple_heredoc_body)
(heredoc_end)))
(function_definition
name: (word)
body: (compound_statement
(redirected_statement
body: (command
name: (command_name
(word)))
redirect: (heredoc_redirect
(heredoc_start)
(simple_heredoc_body)
(heredoc_end)))))
(redirected_statement
body: (command
name: (command_name
(word)))
redirect: (heredoc_redirect
(heredoc_start)
(file_redirect
destination: (word))
(simple_heredoc_body)
(heredoc_end))))
==========================================
Heredocs with weird characters
@ -363,11 +409,11 @@ Hello.
---
(program
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start))) (heredoc_body)
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start))) (heredoc_body)
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start))) (heredoc_body)
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start))) (heredoc_body)
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start))) (heredoc_body))
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end)))
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end)))
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end)))
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end)))
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end))))
==========================================
Herestrings
@ -399,6 +445,10 @@ echo ${b[1234 % 2]}
${words[++counter]}
${array[(($number+1))]}
${array[((number+1))]}
---
(program
@ -413,4 +463,29 @@ ${words[++counter]}
(command
(command_name
(expansion
(subscript (variable_name) (unary_expression (word)))))))
(subscript (variable_name) (unary_expression (word))))))
(command
(command_name
(expansion
(subscript
(variable_name)
(arithmetic_expansion (binary_expression (simple_expansion (variable_name)) (number)))))))
(command
(command_name
(expansion
(subscript
(variable_name)
(arithmetic_expansion (binary_expression (variable_name) (number))))))))
==========================================
Bare $
==========================================
echo $
---
(program
(command
(command_name
(word))))

@ -33,16 +33,26 @@ echo ]]] ===
(command_name
(word))
(concatenation
(word)
(word)
(word)
(word)
(word)
(word)))
(command
(command_name
(word))
(concatenation))
(concatenation
(word)
(word)
(word)))
(command
(command_name
(word))
(concatenation)
(concatenation
(word)
(word)
(word))
(word)))
================================================================================
@ -111,7 +121,7 @@ echo ${abc:
(word))
(expansion
(variable_name)
(word)))
(regex)))
(command
(command_name
(word))
@ -158,6 +168,8 @@ X="${Y%<}"
Z="${A#*<B>}"
C="${D%</E>*}"
F="${#!}"
G=${H,,[I]}
J=${K^^[L]}
--------------------------------------------------------------------------------
@ -180,78 +192,180 @@ F="${#!}"
(string
(expansion
(variable_name)
(word)
(word))))
(regex))))
(variable_assignment
(variable_name)
(string
(expansion
(variable_name)
(word))))
(regex))))
(variable_assignment
(variable_name)
(string
(expansion
(variable_name)
(word))))
(regex))))
(variable_assignment
(variable_name)
(string
(expansion
(variable_name)
(word))))
(regex))))
(variable_assignment
(variable_name)
(string
(expansion
(variable_name)
(word)
(word))))
(regex))))
(variable_assignment
(variable_name)
(string
(expansion
(variable_name)
(word))))
(regex))))
(variable_assignment
(variable_name)
(string
(expansion
(variable_name)
(array))))
(regex))))
(variable_assignment
(variable_name)
(string
(expansion
(variable_name))))
(variable_name)
(regex))))
(variable_assignment
(variable_name)
(string
(expansion
(variable_name))))
(variable_name)
(regex))))
(variable_assignment
(variable_name)
(string
(expansion
(variable_name))))
(variable_name)
(regex))))
(variable_assignment
(variable_name)
(string
(expansion
(variable_name)
(word)
(word))))
(regex))))
(variable_assignment
(variable_name)
(string
(expansion
(variable_name)
(word)
(word))))
(regex))))
(variable_assignment
(variable_name)
(string
(expansion))))
(expansion)))
(variable_assignment
(variable_name)
(expansion
(variable_name)
(regex)))
(variable_assignment
(variable_name)
(expansion
(variable_name)
(regex))))
================================================================================
More Variable expansions with operators
================================================================================
${parameter-default}
${parameter:-default}
${parameter=default}
${parameter:=default}
${parameter+alt_value}
${parameter:+alt_value}
${parameter?err_msg}
${parameter:?err_msg}
${var%Pattern}
${var%%Pattern}
${var:pos}
${var:pos:len}
${MATRIX:$(($RANDOM%${#MATRIX})):1}
--------------------------------------------------------------------------------
(program
(command
(command_name
(expansion
(variable_name)
(word))))
(command
(command_name
(expansion
(variable_name)
(word))))
(command
(command_name
(expansion
(variable_name)
(word))))
(command
(command_name
(expansion
(variable_name)
(word))))
(command
(command_name
(expansion
(variable_name)
(word))))
(command
(command_name
(expansion
(variable_name)
(word))))
(command
(command_name
(expansion
(variable_name)
(word))))
(command
(command_name
(expansion
(variable_name)
(word))))
(command
(command_name
(expansion
(variable_name)
(regex))))
(command
(command_name
(expansion
(variable_name)
(regex))))
(command
(command_name
(expansion
(variable_name)
(variable_name))))
(command
(command_name
(expansion
(variable_name)
(variable_name)
(variable_name))))
(command
(command_name
(expansion
(variable_name)
(arithmetic_expansion
(binary_expression
(variable_name)
(expansion
(variable_name))))
(number)))))
================================================================================
Variable expansions in strings
@ -300,7 +414,8 @@ C=${D/;\ *;|}
(variable_name)
(expansion
(variable_name)
(regex)))
(regex)
(ansi_c_string)))
(comment)
(variable_assignment
(variable_name)
@ -337,8 +452,7 @@ cat ${BAR} ${ABC=def} ${GHI:?jkl}
(string
(expansion
(variable_name)
(concatenation
(word)))))))
(regex))))))
================================================================================
Words ending with '$'
@ -366,6 +480,10 @@ echo `echo hi; echo there`
echo $(echo $(echo hi))
echo $(< some-file)
# both of these are concatenations!
echo `echo otherword`word
echo word`echo otherword`
--------------------------------------------------------------------------------
(program
@ -406,7 +524,28 @@ echo $(< some-file)
(word))
(command_substitution
(file_redirect
(word)))))
(word))))
(comment)
(command
(command_name
(word))
(concatenation
(command_substitution
(command
(command_name
(word))
(word)))
(word)))
(command
(command_name
(word))
(concatenation
(word)
(command_substitution
(command
(command_name
(word))
(word))))))
================================================================================
Process substitutions
@ -794,6 +933,11 @@ Arithmetic expansions
echo $((1 + 2 - 3 * 4 / 5))
a=$((6 % 7 ** 8 << 9 >> 10 & 11 | 12 ^ 13))
$(((${1:-${SECONDS}} % 12) + 144))
((foo=0))
echo $((bar=1))
echo $((-1, 1))
echo $((! -a || ~ +b || ++c || --d))
echo $((foo-- || bar++))
--------------------------------------------------------------------------------
@ -840,4 +984,115 @@ $(((${1:-${SECONDS}} % 12) + 144))
(expansion
(variable_name)))
(number)))
(number))))))
(number)))))
(command
(command_name
(arithmetic_expansion
(binary_expression
(variable_name)
(number)))))
(command
(command_name
(word))
(arithmetic_expansion
(binary_expression
(variable_name)
(number))))
(command
(command_name
(word))
(arithmetic_expansion
(unary_expression
(number))
(number)))
(command
(command_name
(word))
(arithmetic_expansion
(binary_expression
(binary_expression
(binary_expression
(unary_expression
(test_operator))
(unary_expression
(unary_expression
(variable_name))))
(unary_expression
(variable_name)))
(unary_expression
(variable_name)))))
(command
(command_name
(word))
(arithmetic_expansion
(postfix_expression
(binary_expression
(postfix_expression
(variable_name))
(variable_name))))))
================================================================================
Concatenation with double backticks
================================================================================
main() {
local foo="asd"`
`"fgh"
}
---
(program
(function_definition
(word)
(compound_statement
(declaration_command
(variable_assignment
(variable_name)
(concatenation
(string)
(string)))))))
================================================================================
Brace expressions and lookalikes
================================================================================
echo {1..2}
echo {0..5}
echo {0..2 # not a brace expression
echo }{0..2}
echo {0..n} # not a brace expression
echo {0..n..2} # not a brace expression
echo {0..2}{1..2}
---
(program
(command
(command_name (word))
(brace_expression (number) (number)))
(command
(command_name (word))
(brace_expression (number) (number)))
(command
(command_name (word))
(concatenation (word) (word)))
(comment)
(command
(command_name (word))
(concatenation
(word)
(brace_expression (number) (number))))
(command
(command_name (word))
(concatenation (word) (word) (word)))
(comment)
(command
(command_name (word))
(concatenation (word) (word) (word)))
(comment)
(command
(command_name (word))
(concatenation
(brace_expression (number) (number))
(brace_expression (number) (number)))))

@ -70,6 +70,10 @@ while something happens; do
echo b
done
while local name="$1" val="$2"; shift 2; do
printf "%s (%s)\n" "$val" "$name"
done
--------------------------------------------------------------------------------
(program
@ -86,7 +90,34 @@ done
(command
name: (command_name
(word))
argument: (word)))))
argument: (word))))
(while_statement
condition: (declaration_command
(variable_assignment
name: (variable_name)
value: (string
(simple_expansion
(variable_name))))
(variable_assignment
name: (variable_name)
value: (string
(simple_expansion
(variable_name)))))
condition: (command
name: (command_name
(word))
argument: (number))
body: (do_group
(command
name: (command_name
(word))
argument: (string)
argument: (string
(simple_expansion
(variable_name)))
argument: (string
(simple_expansion
(variable_name)))))))
================================================================================
Until statements
@ -270,6 +301,21 @@ for ((cx = 0; c = $cx / $pf, c < $wc - $k; )); do
echo "$cx"
done
for (( i = 4;;i--)) ; do echo $i; if (( $i == 0 )); then break; fi; done
# added post-bash-4.2
for (( i = j = k = 1; i % 9 || (j *= -1, $( ((i%9)) || printf " " >&2; echo 0), k++ <= 10); i += j ))
do
printf "$i"
done
echo
( for (( i = j = k = 1; i % 9 || (j *= -1, $( ((i%9)) || printf " " >&2; echo 0), k++ <= 10); i += j ))
do
printf "$i"
done )
--------------------------------------------------------------------------------
(program
@ -333,7 +379,133 @@ done
(word))
(string
(simple_expansion
(variable_name)))))))
(variable_name))))))
(c_style_for_statement
(variable_assignment
(variable_name)
(number))
(postfix_expression
(word))
(do_group
(command
(command_name
(word))
(simple_expansion
(variable_name)))
(if_statement
(command
(command_name
(arithmetic_expansion
(binary_expression
(simple_expansion
(variable_name))
(number)))))
(command
(command_name
(word))))))
(comment)
(c_style_for_statement
(variable_assignment
(variable_name)
(variable_assignment
(variable_name)
(variable_assignment
(variable_name)
(number))))
(binary_expression
(word)
(binary_expression
(number)
(parenthesized_expression
(binary_expression
(word)
(number))
(command_substitution
(redirected_statement
(list
(command
(command_name
(arithmetic_expansion
(binary_expression
(variable_name)
(number)))))
(command
(command_name
(word))
(string)))
(file_redirect
(number)))
(command
(command_name
(word))
(number)))
(binary_expression
(postfix_expression
(word))
(number)))))
(binary_expression
(word)
(word))
(do_group
(command
(command_name
(word))
(string
(simple_expansion
(variable_name))))))
(command
(command_name
(word)))
(subshell
(c_style_for_statement
(variable_assignment
(variable_name)
(variable_assignment
(variable_name)
(variable_assignment
(variable_name)
(number))))
(binary_expression
(word)
(binary_expression
(number)
(parenthesized_expression
(binary_expression
(word)
(number))
(command_substitution
(redirected_statement
(list
(command
(command_name
(arithmetic_expansion
(binary_expression
(variable_name)
(number)))))
(command
(command_name
(word))
(string)))
(file_redirect
(number)))
(command
(command_name
(word))
(number)))
(binary_expression
(postfix_expression
(word))
(number)))))
(binary_expression
(word)
(word))
(do_group
(command
(command_name
(word))
(string
(simple_expansion
(variable_name))))))))
================================================================================
If statements
@ -463,7 +635,8 @@ fi
argument: (word)
argument: (word)))
(command
name: (command_name (word))
name: (command_name
(word))
argument: (raw_string))))
================================================================================
@ -484,7 +657,8 @@ fi
argument: (word)
argument: (word))
(command
name: (command_name (word))
name: (command_name
(word))
argument: (raw_string))))
================================================================================
@ -500,10 +674,15 @@ fi
(program
(if_statement
condition: (variable_assignment
name: (variable_name)
value: (command_substitution (command name: (command_name (word)) argument: (raw_string))))
name: (variable_name)
value: (command_substitution
(command
name: (command_name
(word))
argument: (raw_string))))
(command
name: (command_name (word))
name: (command_name
(word))
argument: (raw_string))))
================================================================================
@ -518,7 +697,7 @@ fi
(program
(if_statement
condition: (negated_command
condition: (negated_command
(variable_assignment
name: (variable_name)
value: (command_substitution
@ -527,7 +706,8 @@ fi
(word))
argument: (raw_string)))))
(command
name: (command_name (word))
name: (command_name
(word))
argument: (raw_string))))
================================================================================
@ -543,10 +723,11 @@ fi
(program
(if_statement
condition: (variable_assignment
name: (variable_name)
value: (number))
name: (variable_name)
value: (number))
(command
name: (command_name (word))
name: (command_name
(word))
argument: (raw_string))))
================================================================================
@ -566,7 +747,8 @@ fi
name: (variable_name)
value: (number)))
(command
name: (command_name (word))
name: (command_name
(word))
argument: (raw_string))))
================================================================================
@ -612,7 +794,11 @@ esac
case x in x) echo meow ;; esac
case foo in
bar\ baz) : ;;
bar\ baz) : ;;
esac
case "$arg" in
*([0-9])([0-9])) echo "$arg"
esac
--------------------------------------------------------------------------------
@ -674,7 +860,9 @@ esac
(case_item
(concatenation
(word)
(number))
(word)
(number)
(word))
(command
(command_name
(word))
@ -694,7 +882,19 @@ esac
(word)
(command
(command_name
(word))))))
(word)))))
(case_statement
(string
(simple_expansion
(variable_name)))
(case_item
(extglob_pattern)
(command
(command_name
(word))
(string
(simple_expansion
(variable_name)))))))
================================================================================
Test commands
@ -745,13 +945,15 @@ fi
(program
(if_statement
(test_command
(ternary_expression
(binary_expression
(number)
(number))
(number)
(number)))
(command
(command_name
(arithmetic_expansion
(ternary_expression
(binary_expression
(number)
(number))
(number)
(number)))))
(command
(command_name
(word))
@ -773,9 +975,9 @@ $((${n} < 10 ? ${n} : 10))
(arithmetic_expansion
(ternary_expression
(binary_expression
(word)
(variable_name)
(number))
(word)
(variable_name)
(number)))))
(command
(command_name
@ -913,6 +1115,8 @@ function do_yet_another_thing {
echo ok
} 2>&1
do_nothing() { return 0; }
--------------------------------------------------------------------------------
(program
@ -946,23 +1150,33 @@ function do_yet_another_thing {
(command_name
(word))
(concatenation
(word)
(word)
(word))
(word)
(concatenation
(word)
(word)
(word))
(word)
(word)))))
(redirected_statement
(function_definition
(word)
(compound_statement
(command
(command_name
(word))
(word))))
(function_definition
(word)
(compound_statement
(command
(command_name
(word))
(word)))
(file_redirect
(file_descriptor)
(number))))
(number)))
(function_definition
(word)
(compound_statement
(command
(command_name
(word))
(number)))))
================================================================================
Variable declaration: declare & typeset
@ -1124,12 +1338,16 @@ export "$(echo ${key} | tr [:lower:] [:upper:])=${p_key#*=}"
(command_name
(word))
(concatenation
(word)
(word)
(word))
(concatenation
(word)
(word)
(word)))))
(expansion
(variable_name)
(word)))))
(regex)))))
================================================================================
Unset commands