From 7cf2c3d806e2d95f4204a0af3d4e5374d2eb9518 Mon Sep 17 00:00:00 2001 From: Timothy Sample Date: Thu, 12 Jul 2018 15:14:48 -0400 Subject: [PATCH] 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. --- geesh/parser.scm | 41 ++++++++++++++++++++++++++++++++--------- tests/parser.scm | 34 ++++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/geesh/parser.scm b/geesh/parser.scm index b615c85..b229290 100644 --- a/geesh/parser.scm +++ b/geesh/parser.scm @@ -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.)" : `( ,$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) diff --git a/tests/parser.scm b/tests/parser.scm index fd73768..629861b 100644 --- a/tests/parser.scm +++ b/tests/parser.scm @@ -38,6 +38,10 @@ '( "echo" "foo") (parse "echo foo")) +(test-equal "Parses command with keywords as arguments" + '( "echo" "{" "!" "for" "}") + (parse "echo { ! for }")) + (test-equal "Parses command lists" '( ( "echo" "foo") ( "echo" "bar")) @@ -127,7 +131,7 @@ '( ( "echo" "foo") ( "echo" "bar")) (parse "{ echo foo - echo bar }")) + echo bar; }")) (test-equal "Parses subshells" '( ( ( "echo" "foo") @@ -139,22 +143,22 @@ (test-equal "Parses for loops over parameters without seperator" '( ("x" ( "@")) ( "echo" ( "x"))) - (parse "for x do echo $x done")) + (parse "for x do echo $x; done")) (test-equal "Parses for loops over parameters with seperator" '( ("x" ( "@")) ( "echo" ( "x"))) - (parse "for x; do echo $x done")) + (parse "for x; do echo $x; done")) (test-equal "Parses for loops over parameters with \"in\"" '( ("x" ( "@")) ( "echo" ( "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" '( ("x" ("foo" "bar" "baz")) ( "echo" ( "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" '( ( "foo") (("bar") ( "echo" "bar"))) - (parse "case $foo in bar) echo bar esac")) + (parse "case $foo in bar) echo bar; esac")) (test-equal "Parses empty case statements" '( ( "foo")) @@ -181,7 +185,7 @@ '( ( "foo") (("bar") ( "echo" "bar")) (("baz") ( "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" '( ( "foo") @@ -194,7 +198,7 @@ '( (( "[" ( "foo") "=" "bar" "]") ( "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" '( @@ -202,7 +206,7 @@ ( "echo" "bar")) ( ( "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" '( @@ -212,9 +216,11 @@ ( "echo" "baz")) ( ( "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" '( ( "is-foo-time") ( "foo")) - (parse "while is-foo-time do foo done")) + (parse "while is-foo-time; do foo; done")) (test-equal "Parses until loops" '( ( "is-no-longer-foo-time") ( "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" '( ("foo") ( "echo" "foo")) - (parse "foo() { echo foo }")) + (parse "foo() { echo foo; }")) ;; Nested commands