Skip to content

Commit

Permalink
lexer: allow extglob wildcards as function names
Browse files Browse the repository at this point in the history
the lexer assumes an extglob token if any of the wildcards expressions
(such as `@`, and `+`) are succeeded by a left parenthesis but that
proves to be an issue if the wildcard is used as a function name.

example input:
```
$ cat in.sh
@() {
  echo "$@";
}
```

`bash` and `gosh` comparison:
```
$ bash ./in.sh
hello
$ ./gosh in.sh
in.sh:5:1: "}" can only be used to close a block
```

given `in.sh`, gosh reports about a syntax error - this is because
a closing bracket is found while the lexer isn't assuming a function
block

fix the issue by assuming a function if one of the conditions below is
true:
* if the expression is found at the beginning of the statement or if its
  preceded by a "function"
* if `(` is immediately succeeded by a `)` - although this is a valid
  bash syntax, we'll operate on the likelihood that it is a function

fixes #739
  • Loading branch information
riacataquian committed Dec 11, 2021
1 parent f965c62 commit 0503b14
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 2 deletions.
14 changes: 14 additions & 0 deletions interp/interp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3117,6 +3117,20 @@ hello, world
hello, world
`,
},
{
// globbing wildcard as function name
`@() { echo "$@"; }; @ lala; function +() { echo "$@"; }; + foo`,
"lala\nfoo\n",
},
{
` @() { echo "$@"; }; @ lala;`,
"lala\n",
},
{
// globbing wildcard as function name but with space after the name
`+ () { echo "$@"; }; + foo; @ () { echo "$@"; }; @ lala; ? () { echo "$@"; }; ? bar`,
"foo\nlala\nbar\n",
},
}

var runTestsWindows = []runTest{
Expand Down
26 changes: 24 additions & 2 deletions syntax/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ skipSpace:
p.advanceLitNone(r)
}
case '?', '*', '+', '@', '!':
if p.peekByte('(') {
if p.tokenizeGlob() {
switch r {
case '?':
p.tok = globQuest
Expand Down Expand Up @@ -346,6 +346,28 @@ skipSpace:
}
}

// tokenizeGlob determines whether the expression should be tokenized as a glob literal
func (p *Parser) tokenizeGlob() bool {
if p.val == "function" {
return false
}
// NOTE: empty pattern list is a valid globbing syntax, eg @()
// but we'll operate on the "likelihood" that it is a function;
// only tokenize if its a non-empty pattern list
if p.peekBytes("()") {
return false
}
return p.peekByte('(')
}

func (p *Parser) peekBytes(s string) bool {
for p.bsp+(len(p.bs)-1) >= len(p.bs) {
p.fill()
}
bw := p.bsp + len(s)
return bw < len(p.bs) && bytes.HasPrefix(p.bs[p.bsp:bw], []byte(s))
}

func (p *Parser) peekByte(b byte) bool {
if p.bsp == len(p.bs) {
p.fill()
Expand Down Expand Up @@ -882,7 +904,7 @@ loop:
tok = _Lit
break loop
case '?', '*', '+', '@', '!':
if p.peekByte('(') {
if p.tokenizeGlob() {
tok = _Lit
break loop
}
Expand Down

0 comments on commit 0503b14

Please sign in to comment.