Generalize case statement parsing

* Fix handling of '|' in case items
* Don't require ;; for last case item

Fixes #8
This commit is contained in:
Max Brunsfeld 2018-02-28 16:29:42 -08:00
parent 2a5b01b885
commit 87b3276186
5 changed files with 51907 additions and 50143 deletions

View File

@ -115,6 +115,10 @@ case "opt" in
;; ;;
esac esac
case "$Z" in
ab*|cd*) ef
esac
--- ---
(program (program
@ -122,7 +126,10 @@ esac
(case_item (word) (case_item (word)
(command (command_name (word)) (word))) (command (command_name (word)) (word)))
(case_item (word) (case_item (word)
(command (command_name (word)) (word))))) (command (command_name (word)) (word))))
(case_statement (string (simple_expansion (variable_name)))
(case_item (word) (word)
(command (command_name (word))))))
=============================== ===============================
Subshells Subshells

View File

@ -5,7 +5,7 @@ const SPECIAL_CHARACTERS = [
'\\[', '\\]', '\\[', '\\]',
'(', ')', '(', ')',
'`', '$', '`', '$',
'&', ';', '|', '&', ';',
'\\', '\\',
'\\s', '\\s',
'#', '#',
@ -120,7 +120,10 @@ module.exports = grammar({
optional($._terminator), optional($._terminator),
'in', 'in',
$._terminator, $._terminator,
optional(seq(
repeat($.case_item), repeat($.case_item),
alias($.last_case_item, $.case_item),
)),
'esac' 'esac'
), ),
@ -132,6 +135,14 @@ module.exports = grammar({
';;' ';;'
), ),
last_case_item: $ => seq(
$._expression,
repeat(seq('|', $._expression)),
')',
repeat($._terminated_statement),
optional(';;')
),
function_definition: $ => seq( function_definition: $ => seq(
choice( choice(
seq('function', $.word, optional(seq('(', ')'))), seq('function', $.word, optional(seq('(', ')'))),

130
src/grammar.json vendored
View File

@ -116,6 +116,26 @@
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "do_group" "name": "do_group"
},
{
"type": "REPEAT",
"content": {
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "file_redirect"
},
{
"type": "SYMBOL",
"name": "heredoc_redirect"
},
{
"type": "SYMBOL",
"name": "herestring_redirect"
}
]
}
} }
] ]
}, },
@ -257,6 +277,12 @@
"type": "SYMBOL", "type": "SYMBOL",
"name": "_terminator" "name": "_terminator"
}, },
{
"type": "CHOICE",
"members": [
{
"type": "SEQ",
"members": [
{ {
"type": "REPEAT", "type": "REPEAT",
"content": { "content": {
@ -264,6 +290,22 @@
"name": "case_item" "name": "case_item"
} }
}, },
{
"type": "ALIAS",
"content": {
"type": "SYMBOL",
"name": "last_case_item"
},
"named": true,
"value": "case_item"
}
]
},
{
"type": "BLANK"
}
]
},
{ {
"type": "STRING", "type": "STRING",
"value": "esac" "value": "esac"
@ -310,6 +352,54 @@
} }
] ]
}, },
"last_case_item": {
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "_expression"
},
{
"type": "REPEAT",
"content": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "|"
},
{
"type": "SYMBOL",
"name": "_expression"
}
]
}
},
{
"type": "STRING",
"value": ")"
},
{
"type": "REPEAT",
"content": {
"type": "SYMBOL",
"name": "_terminated_statement"
}
},
{
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": ";;"
},
{
"type": "BLANK"
}
]
}
]
},
"function_definition": { "function_definition": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
@ -1130,15 +1220,10 @@
}, },
{ {
"type": "CHOICE", "type": "CHOICE",
"members": [
{
"type": "SEQ",
"members": [ "members": [
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "_expression" "name": "_expression"
}
]
}, },
{ {
"type": "BLANK" "type": "BLANK"
@ -1168,14 +1253,14 @@
] ]
}, },
{ {
"type": "REPEAT",
"content": {
"type": "CHOICE", "type": "CHOICE",
"members": [ "members": [
{ {
"type": "SEQ", "type": "SYMBOL",
"members": [ "name": "_expression"
{ },
"type": "CHOICE",
"members": [
{ {
"type": "STRING", "type": "STRING",
"value": ":" "value": ":"
@ -1203,32 +1288,13 @@
{ {
"type": "STRING", "type": "STRING",
"value": "-" "value": "-"
}
]
}, },
{ {
"type": "CHOICE", "type": "STRING",
"members": [ "value": "#"
{
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "_expression"
}
]
},
{
"type": "BLANK"
} }
] ]
} }
]
},
{
"type": "BLANK"
}
]
} }
] ]
} }
@ -1376,7 +1442,7 @@
"members": [ "members": [
{ {
"type": "PATTERN", "type": "PATTERN",
"value": "[^'\"<>{}\\[\\]()`$&;\\\\\\s#]" "value": "[^'\"<>{}\\[\\]()`$|&;\\\\\\s#]"
}, },
{ {
"type": "SEQ", "type": "SEQ",

101809
src/parser.c vendored

File diff suppressed because it is too large Load Diff

3
src/scanner.cc vendored
View File

@ -86,6 +86,7 @@ struct Scanner {
bool scan(TSLexer *lexer, const bool *valid_symbols) { bool scan(TSLexer *lexer, const bool *valid_symbols) {
if (valid_symbols[CONCAT]) { if (valid_symbols[CONCAT]) {
if (!( if (!(
lexer->lookahead == 0 ||
iswspace(lexer->lookahead) || iswspace(lexer->lookahead) ||
lexer->lookahead == '>' || lexer->lookahead == '>' ||
lexer->lookahead == '<' || lexer->lookahead == '<' ||
@ -93,8 +94,8 @@ struct Scanner {
lexer->lookahead == '(' || lexer->lookahead == '(' ||
lexer->lookahead == ';' || lexer->lookahead == ';' ||
lexer->lookahead == '&' || lexer->lookahead == '&' ||
lexer->lookahead == '|' ||
lexer->lookahead == '`' || lexer->lookahead == '`' ||
lexer->lookahead == 0 ||
(lexer->lookahead == '}' && valid_symbols[CLOSING_BRACE]) || (lexer->lookahead == '}' && valid_symbols[CLOSING_BRACE]) ||
(lexer->lookahead == ']' && valid_symbols[CLOSING_BRACKET]) (lexer->lookahead == ']' && valid_symbols[CLOSING_BRACKET])
)) { )) {