diff --git a/scanner/lex.go b/scanner/lex.go index 564df6bb..4e698e13 100644 --- a/scanner/lex.go +++ b/scanner/lex.go @@ -283,10 +283,22 @@ func lexStart(l *Lexer) stateFn { absorbIdentifier(l) next := l.peek() - if next != eof && !isSpace(next) && - !isEndOfLine(next) && next != ';' && - next != ')' && next != ',' && next != '+' && - next != '[' && next != ']' && next != '(' { + + // required to avoid the sintaxes below: + // rm -rf $HOME/projects + // rm -rf $GOPATH/test + // for being interpreted as: + // rm -rf $HOME /projects + // rm -rf $GOPATH /test + // + // Below are the valid suffixes for variables: + // $HOME; + // $HOME[ # used in: $HOME[0] + // $HOME( # used in: $callback() + // $HOME+ # used in: $HOME+"/src" + // $HOME] # used in: $list[$index] + // $HOME) # used in: call($HOME) + if !isValidVariableSuffix(next) { l.errorf("Unrecognized character in action: %#U", next) return nil } @@ -550,3 +562,11 @@ func isAlpha(r rune) bool { func isEndOfLine(r rune) bool { return r == '\r' || r == '\n' } + +func isValidVariableSuffix(r rune) bool { + return r == eof || isSpace(r) || + isEndOfLine(r) || r == ';' || + r == ')' || r == ',' || r == '+' || + r == '[' || r == ']' || r == '(' || + r == '|' +} diff --git a/scanner/lex_regression_test.go b/scanner/lex_regression_test.go index eec53de4..33e65376 100644 --- a/scanner/lex_regression_test.go +++ b/scanner/lex_regression_test.go @@ -207,4 +207,13 @@ func TestLexerIssue127(t *testing.T) { } testTable("test127", `rm -rf $HOME/.vim`, expected, t) + + expected = []Token{ + {typ: token.Ident, val: "rm"}, + {typ: token.Arg, val: "-rf"}, + {typ: token.Illegal, val: "test127:1:12: Unrecognized character in action: U+002E '.'"}, + {typ: token.EOF}, + } + + testTable("test127", `rm -rf $HOME.vim`, expected, t) } diff --git a/scanner/lex_test.go b/scanner/lex_test.go index 6897cc0c..669afa5c 100644 --- a/scanner/lex_test.go +++ b/scanner/lex_test.go @@ -1827,3 +1827,52 @@ func TestLexerLongAssignment(t *testing.T) { jq ".GroupId" | xargs echo -n)`, expected, t) } + +func TestLexerVariable(t *testing.T) { + base := []Token{ + {typ: token.Variable, val: "$HOME"}, + } + + for r, tok := range map[rune]token.Token{ + ';': token.Semicolon, + '+': token.Plus, + '[': token.LBrack, + ']': token.RBrack, + '(': token.LParen, + ')': token.RParen, + ',': token.Comma, + '|': token.Pipe, + } { + expected := append(base, Token{typ: tok, val: string(r)}) + + if r == ')' { + expected = append(expected, Token{typ: token.Semicolon, val: ";"}) + } + + expected = append(expected, Token{typ: token.EOF}) + testTable("test variable", `$HOME`+string(r), expected, t) + } + + // must fail + for _, r := range []rune{ + '!', '@', '$', '%', '&', '*', '-', '=', '"', '\'', + '`', '{', '}', '<', '>', '.', ':', '?', '/', '\\', + } { + l := Lex("test", "$GOPATH"+string(r)) + + var results []Token + + for tok := range l.Tokens { + results = append(results, tok) + } + + if len(results) != 2 { + t.Fatalf("Expected 2 token, found %d", len(results)) + } + + if results[0].typ != token.Illegal { + t.Fatalf("Expected illegal token, but found %v", results[0]) + } + } + +}