Skip to content

Commit

Permalink
interp: support parentheses in classic test commands
Browse files Browse the repository at this point in the history
This mimics the logic in the bash test expression parser
in the syntax package, which supported them for years.

Fixes #1036.
  • Loading branch information
mvdan committed Oct 14, 2023
1 parent 5544a66 commit 8b67386
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 2 deletions.
4 changes: 4 additions & 0 deletions interp/interp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1611,6 +1611,8 @@ var runTests = []runTest{
{"set -o pipefail; [[ -o pipefail ]]", ""},
// TODO: we don't implement precedence of && over ||.
// {"[[ a == x && b == x || c == c ]]", ""},
{"[[ (a == x && b == x) || c == c ]]", ""},
{"[[ a == x && (b == x || c == c) ]]", "exit status 1"},

// classic test
{
Expand Down Expand Up @@ -1710,6 +1712,8 @@ var runTests = []runTest{
{"[ abc != ab* ]", ""},
// TODO: we don't implement precedence of -a over -o.
// {"[ a = x -a b = x -o c = c ]", ""},
{`[ \( a = x -a b = x \) -o c = c ]`, ""},
{`[ a = x -a \( b = x -o c = c \) ]`, "exit status 1"},

// arithm
{
Expand Down
15 changes: 13 additions & 2 deletions interp/test_classic.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (p *testParser) classicTest(fval string, pastAndOr bool) syntax.TestExpr {
} else {
left = p.classicTest(fval, true)
}
if left == nil || p.eof {
if left == nil || p.eof || p.val == ")" {
return left
}
opStr := p.val
Expand All @@ -76,7 +76,7 @@ func (p *testParser) classicTest(fval string, pastAndOr bool) syntax.TestExpr {
}

func (p *testParser) testExprBase(fval string) syntax.TestExpr {
if p.eof {
if p.eof || p.val == ")" {
return nil
}
op := testUnaryOp(p.val)
Expand All @@ -86,6 +86,15 @@ func (p *testParser) testExprBase(fval string) syntax.TestExpr {
p.next()
u.X = p.classicTest(op.String(), false)
return u
case syntax.TsParen:
pe := &syntax.ParenTest{}
p.next()
pe.X = p.classicTest(op.String(), false)
if p.val != ")" {
p.errf("reached %s without matching ( with )", p.val)
}
p.next()
return pe
case illegalTok:
return p.followWord(fval)
default:
Expand All @@ -108,6 +117,8 @@ func testUnaryOp(val string) syntax.UnTestOperator {
switch val {
case "!":
return syntax.TsNot
case "(":
return syntax.TsParen
case "-e", "-a":
return syntax.TsExists
case "-f":
Expand Down
1 change: 1 addition & 0 deletions syntax/tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ const (
TsVarSet // -v
TsRefVar // -R
TsNot = UnTestOperator(exclMark) // !
TsParen = UnTestOperator(leftParen) // (
)

type BinTestOperator token
Expand Down

0 comments on commit 8b67386

Please sign in to comment.