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
case "$Z" in
ab*|cd*) ef
esac
---
(program
@ -122,7 +126,10 @@ esac
(case_item (word)
(command (command_name (word)) (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

View File

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

214
src/grammar.json vendored
View File

@ -116,6 +116,26 @@
{
"type": "SYMBOL",
"name": "do_group"
},
{
"type": "REPEAT",
"content": {
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "file_redirect"
},
{
"type": "SYMBOL",
"name": "heredoc_redirect"
},
{
"type": "SYMBOL",
"name": "herestring_redirect"
}
]
}
}
]
},
@ -258,11 +278,33 @@
"name": "_terminator"
},
{
"type": "REPEAT",
"content": {
"type": "SYMBOL",
"name": "case_item"
}
"type": "CHOICE",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "REPEAT",
"content": {
"type": "SYMBOL",
"name": "case_item"
}
},
{
"type": "ALIAS",
"content": {
"type": "SYMBOL",
"name": "last_case_item"
},
"named": true,
"value": "case_item"
}
]
},
{
"type": "BLANK"
}
]
},
{
"type": "STRING",
@ -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": {
"type": "SEQ",
"members": [
@ -1132,13 +1222,8 @@
"type": "CHOICE",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "_expression"
}
]
"type": "SYMBOL",
"name": "_expression"
},
{
"type": "BLANK"
@ -1168,67 +1253,48 @@
]
},
{
"type": "CHOICE",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": ":"
},
{
"type": "STRING",
"value": ":?"
},
{
"type": "STRING",
"value": "="
},
{
"type": "STRING",
"value": ":-"
},
{
"type": "STRING",
"value": "%"
},
{
"type": "STRING",
"value": "/"
},
{
"type": "STRING",
"value": "-"
}
]
},
{
"type": "CHOICE",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "_expression"
}
]
},
{
"type": "BLANK"
}
]
}
]
},
{
"type": "BLANK"
}
]
"type": "REPEAT",
"content": {
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "_expression"
},
{
"type": "STRING",
"value": ":"
},
{
"type": "STRING",
"value": ":?"
},
{
"type": "STRING",
"value": "="
},
{
"type": "STRING",
"value": ":-"
},
{
"type": "STRING",
"value": "%"
},
{
"type": "STRING",
"value": "/"
},
{
"type": "STRING",
"value": "-"
},
{
"type": "STRING",
"value": "#"
}
]
}
}
]
}
@ -1376,7 +1442,7 @@
"members": [
{
"type": "PATTERN",
"value": "[^'\"<>{}\\[\\]()`$&;\\\\\\s#]"
"value": "[^'\"<>{}\\[\\]()`$|&;\\\\\\s#]"
},
{
"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) {
if (valid_symbols[CONCAT]) {
if (!(
lexer->lookahead == 0 ||
iswspace(lexer->lookahead) ||
lexer->lookahead == '>' ||
lexer->lookahead == '<' ||
@ -93,8 +94,8 @@ struct Scanner {
lexer->lookahead == '(' ||
lexer->lookahead == ';' ||
lexer->lookahead == '&' ||
lexer->lookahead == '|' ||
lexer->lookahead == '`' ||
lexer->lookahead == 0 ||
(lexer->lookahead == '}' && valid_symbols[CLOSING_BRACE]) ||
(lexer->lookahead == ']' && valid_symbols[CLOSING_BRACKET])
)) {