Skip to content

Commit b8ec0f7

Browse files
author
Andy C
committed
[osh] Allow (( )) and $(( ))
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
1 parent f582f63 commit b8ec0f7

File tree

3 files changed

+54
-29
lines changed

3 files changed

+54
-29
lines changed

osh/word_parse.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,14 +1479,15 @@ def _ReadArithSub(self):
14791479
# $((echo * foo)) # looks like multiplication
14801480
# $((echo / foo)) # looks like division
14811481

1482-
self._SetNext(lex_mode_e.Arith)
1483-
anode = self._ReadArithExpr(Id.Arith_RParen)
1482+
anode = arith_expr.EmptyZero
1483+
self._SetNextNonSpace()
1484+
1485+
if self.token_type != Id.Arith_RParen:
1486+
anode = self._ReadArithExpr(Id.Arith_RParen)
14841487

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

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

1512-
self._SetNext(lex_mode_e.Arith)
1513-
anode = self._ReadArithExpr(Id.Arith_RParen)
1512+
self._SetNextNonSpace()
1513+
self._GetToken()
1514+
if self.token_type != Id.Arith_RParen:
1515+
anode = self._ReadArithExpr(Id.Arith_RParen)
15141516

15151517
self._SetNext(lex_mode_e.ShCommand)
15161518

1517-
# PROBLEM: $(echo $(( 1 + 2 )) )
1519+
# Ensure we get the second )
15181520
self._GetToken()
15191521
right = self.cur_token
1520-
if self.token_type != Id.Op_DRightParen:
1521-
p_die('Expected second ) to end arith statement', self.cur_token)
1522+
if right.id != Id.Op_DRightParen:
1523+
p_die('Expected second ) to end arith statement', right)
15221524

15231525
self._SetNext(lex_mode_e.ShCommand)
15241526

spec/arith-context.test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
## compare_shells: bash mksh zsh
2-
## oils_failures_allowed: 2
2+
## oils_failures_allowed: 1
33

44
# Test arithmetic expressions in all their different contexts.
55

test/parse-errors.sh

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,42 @@ test-word-parse() {
117117
_osh-parse-error '${x:'
118118
}
119119

120+
test-dparen() {
121+
# (( ))
122+
123+
_osh-should-parse '(())'
124+
_osh-should-parse '(( ))'
125+
_osh-parse-error '(( )'
126+
_osh-parse-error '(( )x'
127+
#_osh-should-parse '$(echo $(( 1 + 2 )) )'
128+
129+
# Hard case
130+
_osh-should-parse '$(echo $(( 1 + 2 )))'
131+
_osh-should-parse '$( (()))'
132+
133+
# More
134+
_osh-parse-error '(( 1 + 2 /'
135+
_osh-parse-error '(( 1 + 2 )/'
136+
_osh-parse-error '(( 1'
137+
_osh-parse-error '(('
138+
}
139+
140+
test-arith-sub() {
141+
# $(( ))
142+
143+
_osh-should-parse 'echo $(( ))'
144+
_osh-should-parse 'echo $(())'
145+
_osh-parse-error 'echo $(()x'
146+
147+
_osh-parse-error 'echo $(()'
148+
149+
_osh-parse-error 'echo $(( 1 + 2 ;'
150+
_osh-parse-error 'echo $(( 1 + 2 );'
151+
_osh-parse-error 'echo $(( '
152+
_osh-parse-error 'echo $(( 1'
153+
}
154+
155+
120156
test-array-literal() {
121157
# Array literal with invalid TokenWord.
122158
_osh-parse-error 'a=(1 & 2)'
@@ -126,12 +162,6 @@ test-array-literal() {
126162
}
127163

128164
test-arith-context() {
129-
# $(( ))
130-
_osh-parse-error 'echo $(( 1 + 2 ;'
131-
_osh-parse-error 'echo $(( 1 + 2 );'
132-
_osh-parse-error 'echo $(( '
133-
_osh-parse-error 'echo $(( 1'
134-
135165
# Disable Oil stuff for osh_{parse,eval}.asan
136166
if false; then
137167
# Non-standard arith sub $[1 + 2]
@@ -144,12 +174,6 @@ test-arith-context() {
144174
_osh-parse-error 'echo $['
145175
fi
146176

147-
# (( ))
148-
_osh-parse-error '(( 1 + 2 /'
149-
_osh-parse-error '(( 1 + 2 )/'
150-
_osh-parse-error '(( 1'
151-
_osh-parse-error '(('
152-
153177
# Should be an error
154178
_osh-parse-error 'a[x+]=1'
155179

@@ -751,7 +775,6 @@ esac'
751775
echo bash=$?
752776
set -o errexit
753777
done
754-
755778
}
756779

757780
all() {

0 commit comments

Comments
 (0)