Fix handling of reserved words in commands

A reserved word should not be special when it is an argument to a
command.  This commit makes the parser treat reserved words as normal
arguments.  Note that this change exposed problems in many of the
parser tests, which relied on reserved words delimiting commands where
they should not.  Those are now fixed.

* geesh/parser.scm (make-parser): Add reserved words to the default
'WORD*' rule, and use a new rule without reserved words for command
names.
* tests/parser.scm: Add a test for reserved words as arguments, and
fix old tests that relied on the old, incorrect behaviour.
This commit is contained in:
Timothy Sample 2018-07-12 15:14:48 -04:00
parent 7d27433a32
commit 7cf2c3d806
2 changed files with 52 additions and 23 deletions

View File

@ -320,7 +320,7 @@ the same number of times.)"
: `(,$2 ,$4))
(pattern
(WORD*-with-non-Esac-keywords)
(WORD*-without-Esac)
: `(,$1)
(pattern PIPE WORD*)
: (append $1 `(,$3)))
@ -404,11 +404,11 @@ the same number of times.)"
: `(<sh-exec> ,$1))
(cmd-name
(WORD*-without-ASSIGNMENT-WORD)
(WORD*-without-keywords-or-ASSIGNMENT-WORD)
: $1)
(cmd-word
(WORD*-without-ASSIGNMENT-WORD)
(WORD*-without-keywords-or-ASSIGNMENT-WORD)
: $1)
(cmd-prefix
@ -510,16 +510,39 @@ the same number of times.)"
;; Rules added to emulate the POSIX context-sensitive lexer
;; approach.
;; Accept all the specializations of a normal word. This corresponds
;; to "rule 1" in the POSIX specification.
;; Accept all the specializations of a normal word and all
;; keywords. This is the default case.
(WORD*
(WORD) : $1
(NAME) : $1
(ASSIGNMENT-WORD) : $1
(If) : $1
(Then) : $1
(Else) : $1
(Elif) : $1
(Fi) : $1
(Do) : $1
(Done) : $1
(Case) : $1
(Esac) : $1
(While) : $1
(Until) : $1
(For) : $1
(Lbrace) : $1
(Rbrace) : $1
(Bang) : $1
(In) : $1)
;; Just like 'WORD*', but no keywords. This corresponds to "rule
;; 1" in the POSIX specification.
(WORD*-without-keywords
(WORD) : $1
(NAME) : $1
(ASSIGNMENT-WORD) : $1)
;; Accept all keywords except "esac". This corresponds to "rule 4"
;; in the POSIX specification.
(WORD*-with-non-Esac-keywords
;; Just like 'WORD*', but without the "esac" keyword. This
;; corresponds to "rule 4" in the POSIX specification.
(WORD*-without-Esac
(WORD) : $1
(NAME) : $1
(ASSIGNMENT-WORD) : $1
@ -563,7 +586,7 @@ the same number of times.)"
;; Accept any "WORD*" token except for "ASSIGNMENT-WORD". This
;; corresponds to "rule 7" in the POSIX specification.
(WORD*-without-ASSIGNMENT-WORD
(WORD*-without-keywords-or-ASSIGNMENT-WORD
(WORD) : $1
(NAME) : $1)

View File

@ -38,6 +38,10 @@
'(<sh-exec> "echo" "foo")
(parse "echo foo"))
(test-equal "Parses command with keywords as arguments"
'(<sh-exec> "echo" "{" "!" "for" "}")
(parse "echo { ! for }"))
(test-equal "Parses command lists"
'(<sh-begin> (<sh-exec> "echo" "foo")
(<sh-exec> "echo" "bar"))
@ -127,7 +131,7 @@
'(<sh-begin> (<sh-exec> "echo" "foo")
(<sh-exec> "echo" "bar"))
(parse "{ echo foo
echo bar }"))
echo bar; }"))
(test-equal "Parses subshells"
'(<sh-subshell> (<sh-begin> (<sh-exec> "echo" "foo")
@ -139,22 +143,22 @@
(test-equal "Parses for loops over parameters without seperator"
'(<sh-for> ("x" (<sh-ref> "@"))
(<sh-exec> "echo" (<sh-ref> "x")))
(parse "for x do echo $x done"))
(parse "for x do echo $x; done"))
(test-equal "Parses for loops over parameters with seperator"
'(<sh-for> ("x" (<sh-ref> "@"))
(<sh-exec> "echo" (<sh-ref> "x")))
(parse "for x; do echo $x done"))
(parse "for x; do echo $x; done"))
(test-equal "Parses for loops over parameters with \"in\""
'(<sh-for> ("x" (<sh-ref> "@"))
(<sh-exec> "echo" (<sh-ref> "x")))
(parse "for x in; do echo $x done"))
(parse "for x in; do echo $x; done"))
(test-equal "Parses for loops over word lists"
'(<sh-for> ("x" ("foo" "bar" "baz"))
(<sh-exec> "echo" (<sh-ref> "x")))
(parse "for x in foo bar baz; do echo $x done"))
(parse "for x in foo bar baz; do echo $x; done"))
;; Case statements
@ -166,7 +170,7 @@
(test-equal "Parses case statements without final seperator"
'(<sh-case> (<sh-ref> "foo")
(("bar") (<sh-exec> "echo" "bar")))
(parse "case $foo in bar) echo bar esac"))
(parse "case $foo in bar) echo bar; esac"))
(test-equal "Parses empty case statements"
'(<sh-case> (<sh-ref> "foo"))
@ -181,7 +185,7 @@
'(<sh-case> (<sh-ref> "foo")
(("bar") (<sh-exec> "echo" "bar"))
(("baz") (<sh-exec> "echo" "baz")))
(parse "case $foo in bar) echo bar ;; baz) echo baz esac"))
(parse "case $foo in bar) echo bar ;; baz) echo baz; esac"))
(test-equal "Parses case statements with compound patterns"
'(<sh-case> (<sh-ref> "foo")
@ -194,7 +198,7 @@
'(<sh-cond>
((<sh-exec> "[" (<sh-ref> "foo") "=" "bar" "]")
(<sh-exec> "echo" "bar")))
(parse "if [ $foo = bar ] then echo bar fi"))
(parse "if [ $foo = bar ]; then echo bar; fi"))
(test-equal "Parses two-branch if statements"
'(<sh-cond>
@ -202,7 +206,7 @@
(<sh-exec> "echo" "bar"))
(<sh-else>
(<sh-exec> "echo" "baz")))
(parse "if [ $foo = bar ] then echo bar else echo baz fi"))
(parse "if [ $foo = bar ]; then echo bar; else echo baz; fi"))
(test-equal "Parses multi-branch if statements"
'(<sh-cond>
@ -212,9 +216,11 @@
(<sh-exec> "echo" "baz"))
(<sh-else>
(<sh-exec> "echo" "quux")))
(parse "if [ $foo = bar ] then
(parse "if [ $foo = bar ]
then
echo bar
elif [ $foo = baz ] then
elif [ $foo = baz ]
then
echo baz
else
echo quux
@ -225,19 +231,19 @@
(test-equal "Parses while loops"
'(<sh-while> (<sh-exec> "is-foo-time")
(<sh-exec> "foo"))
(parse "while is-foo-time do foo done"))
(parse "while is-foo-time; do foo; done"))
(test-equal "Parses until loops"
'(<sh-until> (<sh-exec> "is-no-longer-foo-time")
(<sh-exec> "foo"))
(parse "until is-no-longer-foo-time do foo done"))
(parse "until is-no-longer-foo-time; do foo; done"))
;; Functions
(test-equal "Parses functions"
'(<sh-define> ("foo")
(<sh-exec> "echo" "foo"))
(parse "foo() { echo foo }"))
(parse "foo() { echo foo; }"))
;; Nested commands