Skip to content

Commit

Permalink
[osh] Allow (( )) and $(( ))
Browse files Browse the repository at this point in the history
They implicit evaluate to zero, and in the (( )) case, the exit code
becomes 1.

Shells widely agree on this, and Samuel found a use by Nix

After preprocessing

    if (( @isClang )); then

It became

    if (( )); then
  • Loading branch information
Andy C committed Jun 26, 2024
1 parent f582f63 commit b8ec0f7
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 29 deletions.
32 changes: 17 additions & 15 deletions osh/word_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1479,14 +1479,15 @@ def _ReadArithSub(self):
# $((echo * foo)) # looks like multiplication
# $((echo / foo)) # looks like division

self._SetNext(lex_mode_e.Arith)
anode = self._ReadArithExpr(Id.Arith_RParen)
anode = arith_expr.EmptyZero
self._SetNextNonSpace()

if self.token_type != Id.Arith_RParen:
anode = self._ReadArithExpr(Id.Arith_RParen)

# TODO: This could be DQ or Arith too
self._SetNext(lex_mode_e.ShCommand)

# PROBLEM: $(echo $(( 1 + 2 )) )
# Two right parens break the Id.Eof_RParen scheme
# Ensure we get closing )
self._GetToken()
if self.token_type != Id.Right_DollarDParen:
p_die('Expected second ) to end arith sub', self.cur_token)
Expand All @@ -1501,24 +1502,25 @@ def ReadDParen(self):
We're using the word parser because it's very similar to _ReadArithExpr
above.
This also returns the terminating `Op_DRightParen` token for use as location
tracking.
This also returns the terminating Id.Op_DRightParen token for location
info.
"""
# The second one needs to be disambiguated in stuff like stuff like:
# TODO: Be consistent with ReadForExpression below and use lex_mode_e.Arith?
# Then you can get rid of this.
anode = arith_expr.EmptyZero # (( ))

self.lexer.PushHint(Id.Op_RParen, Id.Op_DRightParen)

self._SetNext(lex_mode_e.Arith)
anode = self._ReadArithExpr(Id.Arith_RParen)
self._SetNextNonSpace()
self._GetToken()
if self.token_type != Id.Arith_RParen:
anode = self._ReadArithExpr(Id.Arith_RParen)

self._SetNext(lex_mode_e.ShCommand)

# PROBLEM: $(echo $(( 1 + 2 )) )
# Ensure we get the second )
self._GetToken()
right = self.cur_token
if self.token_type != Id.Op_DRightParen:
p_die('Expected second ) to end arith statement', self.cur_token)
if right.id != Id.Op_DRightParen:
p_die('Expected second ) to end arith statement', right)

self._SetNext(lex_mode_e.ShCommand)

Expand Down
2 changes: 1 addition & 1 deletion spec/arith-context.test.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
## compare_shells: bash mksh zsh
## oils_failures_allowed: 2
## oils_failures_allowed: 1

# Test arithmetic expressions in all their different contexts.

Expand Down
49 changes: 36 additions & 13 deletions test/parse-errors.sh
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,42 @@ test-word-parse() {
_osh-parse-error '${x:'
}

test-dparen() {
# (( ))

_osh-should-parse '(())'
_osh-should-parse '(( ))'
_osh-parse-error '(( )'
_osh-parse-error '(( )x'
#_osh-should-parse '$(echo $(( 1 + 2 )) )'

# Hard case
_osh-should-parse '$(echo $(( 1 + 2 )))'
_osh-should-parse '$( (()))'

# More
_osh-parse-error '(( 1 + 2 /'
_osh-parse-error '(( 1 + 2 )/'
_osh-parse-error '(( 1'
_osh-parse-error '(('
}

test-arith-sub() {
# $(( ))

_osh-should-parse 'echo $(( ))'
_osh-should-parse 'echo $(())'
_osh-parse-error 'echo $(()x'

_osh-parse-error 'echo $(()'

_osh-parse-error 'echo $(( 1 + 2 ;'
_osh-parse-error 'echo $(( 1 + 2 );'
_osh-parse-error 'echo $(( '
_osh-parse-error 'echo $(( 1'
}


test-array-literal() {
# Array literal with invalid TokenWord.
_osh-parse-error 'a=(1 & 2)'
Expand All @@ -126,12 +162,6 @@ test-array-literal() {
}

test-arith-context() {
# $(( ))
_osh-parse-error 'echo $(( 1 + 2 ;'
_osh-parse-error 'echo $(( 1 + 2 );'
_osh-parse-error 'echo $(( '
_osh-parse-error 'echo $(( 1'

# Disable Oil stuff for osh_{parse,eval}.asan
if false; then
# Non-standard arith sub $[1 + 2]
Expand All @@ -144,12 +174,6 @@ test-arith-context() {
_osh-parse-error 'echo $['
fi

# (( ))
_osh-parse-error '(( 1 + 2 /'
_osh-parse-error '(( 1 + 2 )/'
_osh-parse-error '(( 1'
_osh-parse-error '(('

# Should be an error
_osh-parse-error 'a[x+]=1'

Expand Down Expand Up @@ -751,7 +775,6 @@ esac'
echo bash=$?
set -o errexit
done

}

all() {
Expand Down

0 comments on commit b8ec0f7

Please sign in to comment.