479 lines
12 KiB
YAML
479 lines
12 KiB
YAML
name: "Arithmetic"
|
|
cases:
|
|
- name: "Empty expression"
|
|
stdin: |
|
|
echo $(())
|
|
|
|
- name: "Unquoted arithmetic"
|
|
stdin: |
|
|
echo $((1+1))
|
|
|
|
- name: "Arithmetic literals"
|
|
stdin: |
|
|
echo "$((0))"
|
|
echo "$((1))"
|
|
echo "$((10))"
|
|
echo "$((010))"
|
|
echo "$((0010))"
|
|
echo "$((0x10))"
|
|
echo "$((0x010))"
|
|
|
|
- name: "Arithmetic literals with explicit bases"
|
|
stdin: |
|
|
echo "$((2#1010))"
|
|
|
|
echo "$((8#16))"
|
|
|
|
echo "$((16#faf))"
|
|
echo "$((16#FAF))"
|
|
|
|
echo "$((36#vvv))"
|
|
echo "$((36#VVV))"
|
|
|
|
# Base 37: A-Z map to 36-61
|
|
echo "$((37#a))"
|
|
echo "$((37#a0))"
|
|
echo "$((37#A))"
|
|
|
|
# Base 62: a-z=10-35, A-Z=36-61
|
|
echo "$((62#A))"
|
|
echo "$((62#a))"
|
|
echo "$((62#Z))"
|
|
echo "$((62#z))"
|
|
|
|
# Base 63: @ = 62
|
|
echo "$((63#@))"
|
|
echo "$((63#@9))"
|
|
|
|
# Base 64: _ = 63
|
|
echo "$((64#_))"
|
|
echo "$((64#@))"
|
|
echo "$((64#_1))"
|
|
echo "$((64#zzz))"
|
|
|
|
- name: "Unary arithmetic operators with space"
|
|
stdin: |
|
|
echo "$(( - 1 ))"
|
|
echo "$(( + 1 ))"
|
|
echo "$(( ~ 10 ))"
|
|
echo "$(( ! 10 ))"
|
|
|
|
- name: "Parentheses"
|
|
stdin: |
|
|
echo "$(((10)))"
|
|
echo "$((((10))))"
|
|
echo "$(((((10)))))"
|
|
|
|
echo "$(( (10) ))"
|
|
echo "$(( (1+2) ))"
|
|
echo "$(( (1 + 2) ))"
|
|
|
|
echo "$(( ((1+2)) ))"
|
|
echo "$(( ((1 + 2)) ))"
|
|
|
|
echo "$(((1+2)*3))"
|
|
echo "$(((1 + 2) * 3))"
|
|
echo "$(( (1 + 2) * 3 ))"
|
|
|
|
echo "$((((1+2)*3)))"
|
|
echo "$(( ((1 + 2) * 3) ))"
|
|
echo "$(( ( (1 + 2) * 3 ) ))"
|
|
|
|
- name: "Basic quoted arithmetic"
|
|
stdin: |
|
|
echo " 1 + 1 == $((1+1))"
|
|
echo " 2 * 3 == $((2*3))"
|
|
echo " 9 / 3 == $((9/3))"
|
|
echo "10 % 3 == $((10%3))"
|
|
echo " 2 ** 3 == $((2**3))"
|
|
echo " 2 | 4 == $((2|4))"
|
|
echo " 2 & 4 == $((2&4))"
|
|
echo " 2 ^ 3 == $((2^3))"
|
|
echo " 1 && 1 == $((1&&1))"
|
|
echo " 1 && 0 == $((1&&0))"
|
|
echo " 1 || 0 == $((1||0))"
|
|
echo " 0 || 1 == $((1||0))"
|
|
echo " 1 , 2 == $((1,2))"
|
|
|
|
- name: "Unary operators"
|
|
stdin: |
|
|
echo " -1 == $((-1))"
|
|
echo " +1 == $((+1))"
|
|
echo " ~10 == $((~10))"
|
|
echo " !10 == $((!10))"
|
|
|
|
- name: "Conditional operator"
|
|
stdin: |
|
|
echo "1 ? 2 : 3 == $((1?2:3))"
|
|
echo "0 ? 2 : 3 == $((0?2:3))"
|
|
|
|
- name: "Arithmetic with spacing"
|
|
stdin: |
|
|
echo $(( 75 + 68 ))
|
|
|
|
- name: "Divide by zero"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "1 / 0 == $((1/0))"
|
|
echo "Result: $?"
|
|
|
|
echo "1 % 0 == $((1%0))"
|
|
echo "Result: $?"
|
|
|
|
- name: "Shift arithmetic"
|
|
stdin: |
|
|
echo "32 >> 2 == $((32>>2))"
|
|
echo " 1 << 4 == $((1<<4))"
|
|
|
|
- name: Shift arithmetic in conditional"
|
|
stdin: |
|
|
(( 1 >> 1 )) && echo "1. true"
|
|
(( 1 << 1 )) && echo "2. true"
|
|
|
|
- name: "Variable references"
|
|
stdin: |
|
|
x=10
|
|
echo "x => $((x))"
|
|
echo "x + 1 => $((x+1))"
|
|
|
|
y=
|
|
echo "y => $((y))"
|
|
echo "y + 1 => $((y+1))"
|
|
|
|
z=notnumber
|
|
echo "z => $((z))"
|
|
echo "z + 1 => $((z+1))"
|
|
|
|
- name: "Complex variable references"
|
|
stdin: |
|
|
x=10
|
|
y=20
|
|
z=x+y
|
|
a=z+z
|
|
|
|
echo "z => $((z))"
|
|
echo "a => $((a))"
|
|
|
|
- name: "Nested expressions"
|
|
stdin: |
|
|
echo "1: $(($(echo -n 1; echo 2) + 37))"
|
|
|
|
op="+"
|
|
echo "2: $((10 ${op} 20))"
|
|
|
|
expr="13 * 7"
|
|
echo "3: $(($expr))"
|
|
|
|
- name: "Assignment arithmetic"
|
|
stdin: |
|
|
x=0
|
|
|
|
echo "x = 1 => $((x = 1))"
|
|
echo "x is now $x"
|
|
|
|
echo "x += 3 => $((x += 3))"
|
|
echo "x is now $x"
|
|
|
|
echo "x -= 1 => $((x -= 1))"
|
|
echo "x is now $x"
|
|
|
|
echo "x *= 2 => $((x *= 2))"
|
|
echo "x is now $x"
|
|
|
|
echo "x++ == $((x++))"
|
|
echo "x is now $x"
|
|
|
|
echo "++x == $((++x))"
|
|
echo "x is now $x"
|
|
|
|
echo "x-- == $((x--))"
|
|
echo "x is now $x"
|
|
|
|
echo "--x == $((--x))"
|
|
echo "x is now $x"
|
|
|
|
echo "x |= 3 => $((x |= 3))"
|
|
echo "x is now $x"
|
|
|
|
echo "x &= 12 => $((x &= 12))"
|
|
echo "x is now $x"
|
|
|
|
echo "x %= 8 => $((x %= 8))"
|
|
echo "x is now $x"
|
|
|
|
echo "x ^= 7 => $((x ^= 7))"
|
|
echo "x is now $x"
|
|
|
|
- name: "Assignment with references"
|
|
stdin: |
|
|
x=10+3
|
|
echo "y = x => $((y = x))"
|
|
echo "y is now $y"
|
|
|
|
- name: "Assignments in logical boolean expressions"
|
|
stdin: |
|
|
x=0
|
|
echo "0 && x+=1 => $(( 0 && (x+=1) ))"
|
|
echo "x: $x"
|
|
|
|
x=0
|
|
echo "1 && x+=1 => $(( 1 && (x+=1) ))"
|
|
echo "x: $x"
|
|
|
|
x=0
|
|
echo "0 || x+=1 => $(( 0 || (x+=1) ))"
|
|
echo "x: $x"
|
|
|
|
x=0
|
|
echo "1 || x+=1 => $(( 1 || (x+=1) ))"
|
|
echo "x: $x"
|
|
|
|
- name: "Array arithmetic"
|
|
stdin: |
|
|
a=(1 2 3)
|
|
declare -p a
|
|
echo "a[0]: $((a[0]))"
|
|
echo "a[2]: $((a[2]))"
|
|
echo "a[3]: $((a[3]))"
|
|
|
|
echo "b[2]: $((b[2]))"
|
|
|
|
echo "a[1]=4 => $((a[1]=4))"
|
|
echo "a[1]: $((a[1]))"
|
|
|
|
echo "a[1]+=1 => $((a[1]+=1))"
|
|
echo "a[1]: $((a[1]))"
|
|
|
|
echo "c[1] += 3 => $((c[1] += 3))"
|
|
|
|
- name: "Basic arithmetic comparison"
|
|
stdin: |
|
|
echo "0 < 1: $((0 < 1))"
|
|
echo "0 <= 1: $((0 <= 1))"
|
|
echo "0 == 1: $((0 == 1))"
|
|
echo "0 != 1: $((0 != 1))"
|
|
echo "0 > 1: $((0 > 1))"
|
|
echo "0 >= 1: $((0 >= 1))"
|
|
echo "0 < 0: $((0 < 0))"
|
|
echo "0 <= 0: $((0 <= 0))"
|
|
echo "0 == 0: $((0 == 0))"
|
|
echo "0 != 0: $((0 != 0))"
|
|
echo "0 > 0: $((0 > 0))"
|
|
echo "0 >= 0: $((0 >= 0))"
|
|
echo "1 < 0: $((1 < 0))"
|
|
echo "1 <= 0: $((1 <= 0))"
|
|
echo "1 == 0: $((1 == 0))"
|
|
echo "1 != 0: $((1 != 0))"
|
|
echo "1 > 0: $((1 > 0))"
|
|
echo "1 >= 0: $((1 >= 0))"
|
|
|
|
- name: "Legacy bracket arithmetic syntax"
|
|
stdin: |
|
|
x=5
|
|
x=$[x + 1]
|
|
echo "x after increment: $x"
|
|
echo "Direct: $[1 + 2]"
|
|
|
|
- name: "Legacy bracket arithmetic in extended test"
|
|
stdin: |
|
|
number=4
|
|
[[ $[number % 2] == 0 ]] && echo "even" || echo "odd"
|
|
number=5
|
|
[[ $[number % 2] == 0 ]] && echo "even" || echo "odd"
|
|
|
|
- name: "Legacy bracket arithmetic in double-quoted string"
|
|
stdin: |
|
|
echo "Quoted: result: $[2 * 3]"
|
|
|
|
- name: "Legacy bracket arithmetic complex expression"
|
|
stdin: |
|
|
echo "Complex: $[1 + 2 * 3 - 4 / 2 % 2]"
|
|
|
|
- name: "Legacy bracket arithmetic with array subscript"
|
|
stdin: |
|
|
arr=(10 20 30)
|
|
echo "Array elem: $[arr[1] + 5]"
|
|
|
|
- name: "Prefix increment/decrement with space before lvalue"
|
|
stdin: |
|
|
x=5
|
|
echo "++ x => $(( ++ x ))"
|
|
echo "x is now $x"
|
|
echo "-- x => $(( -- x ))"
|
|
echo "x is now $x"
|
|
|
|
# Also test in (( )) command form
|
|
y=10
|
|
(( ++ y ))
|
|
echo "y after ++ y: $y"
|
|
(( -- y ))
|
|
echo "y after -- y: $y"
|
|
|
|
- name: "INT64_MIN and wrapping arithmetic"
|
|
stdin: |
|
|
# INT64_MIN via unary minus of the literal
|
|
echo "$(( -9223372036854775808 ))"
|
|
|
|
# Wrapping negation of INT64_MIN
|
|
echo "$(( - (-9223372036854775808) ))"
|
|
|
|
# INT64_MIN % -1 should be 0 (not crash)
|
|
echo "$(( (-9223372036854775808) % (-1) ))"
|
|
|
|
# INT64_MIN / -1 wraps
|
|
echo "$(( (-9223372036854775808) / (-1) ))"
|
|
|
|
# Wrapping increment/decrement near boundaries
|
|
echo "$(( 9223372036854775807 + 1 ))" # wraps to INT64_MIN
|
|
|
|
- name: "Non-cyclic variable dereference chain"
|
|
stdin: |
|
|
a=b
|
|
b=c
|
|
c=42
|
|
echo "$((a))"
|
|
|
|
- name: "Self-referencing variable in arithmetic"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
a=a
|
|
echo "$(( a ))"
|
|
echo "exit: $?"
|
|
|
|
- name: "Recursive variable evaluation with depth limit"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
# Recursive variables should not cause stack overflow
|
|
a=b
|
|
b=a
|
|
echo "$(( a ))"
|
|
echo "exit: $?"
|
|
|
|
- name: "Arithmetic with nameref variable"
|
|
known_failure: true
|
|
stdin: |
|
|
counter=10
|
|
declare -n ref=counter
|
|
echo "before: $counter"
|
|
(( ref++ ))
|
|
echo "after ++: $counter"
|
|
(( ref += 5 ))
|
|
echo "after +=5: $counter"
|
|
echo "expr: $(( ref * 2 ))"
|
|
|
|
- name: "Legacy arithmetic expansion with array subscripts"
|
|
stdin: |
|
|
a=(5 6)
|
|
echo $[ a[1] + 1 ] $[a[a[0]-4]*2] $[ a[1]*a[0] ]
|
|
|
|
- name: "Unmatched bracket in arithmetic expansion does not execute commands"
|
|
ignore_stderr: true # arithmetic error wording differs
|
|
stdin: |
|
|
echo $((echo unexpected [))
|
|
echo "not reached"
|
|
|
|
- name: "Legacy arithmetic expansion containing nested expansions"
|
|
stdin: |
|
|
a=(5 6)
|
|
echo $[ ${a[1]} + 1 ] $[ ${#a[@]} ] $[ $[1] + 1 ] $[ a[$[1]] ]
|
|
|
|
- name: "Blanks inside array subscripts in arithmetic"
|
|
known_failure: true # TODO(arithmetic): the arithmetic parser rejects blanks inside a subscript's brackets
|
|
stdin: |
|
|
a=(1 2)
|
|
echo $(( a[ 1 ] )) $(( a[ a[0] ] ))
|
|
|
|
- name: "Double quotes inside legacy arithmetic expansion in double quotes"
|
|
known_failure: true # TODO(tokenizer): in double quotes, bash doesn't nest quotes inside '$[...]', so the '$[' is left unclosed (a bad substitution); brush expands it
|
|
ignore_stderr: true # error wording differs
|
|
stdin: |
|
|
echo "[$[ "1" + 2 ]]"
|
|
echo "rc=$?"
|
|
|
|
- name: "Shift after nested parentheses in arithmetic command"
|
|
stdin: |
|
|
(( y = (1 + (2)) << 2 )); echo "$y"
|
|
(( y = ((1)) << 2 )); echo "$y"
|
|
x=$( (( ((1)) << 2 )); echo ok ); echo "$x"
|
|
(( ((1)) << 2 )); cat <<EOF
|
|
body
|
|
EOF
|
|
|
|
- name: "Nested subshell starting with double parentheses"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
((echo a) )
|
|
echo "rc=$?"
|
|
|
|
#
|
|
# A failed arithmetic evaluation is a command that returns non-zero, not a fatal error: a shell
|
|
# reports it and carries on with the rest of the list. brush abandons the remaining commands in
|
|
# the list, so the status line never prints. Each of these passes when the commands are written
|
|
# on separate lines; only sequencing within one list diverges.
|
|
#
|
|
- name: "Division by zero yields a non-zero status without aborting the list"
|
|
known_failure: true # TODO: arithmetic errors should not abandon the rest of a command list
|
|
stdin: |
|
|
(( 1 / 0 )); echo "status: $?"
|
|
|
|
- name: "Arithmetic assignment to a readonly variable does not abort the list"
|
|
known_failure: true # TODO: arithmetic errors should not abandon the rest of a command list
|
|
stdin: |
|
|
a=(one); readonly a; (( a[0] = 9 )); echo "status: $?"
|
|
|
|
- name: "Arithmetic error on its own line leaves the following command reachable"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
(( 1 / 0 ))
|
|
echo "status: $?"
|
|
|
|
- name: "Arithmetic command with line continuations between opening parentheses"
|
|
stdin: |
|
|
(\
|
|
(\
|
|
10+3)) && echo "arithmetic"
|
|
|
|
- name: "An empty arithmetic expression evaluates to 0"
|
|
stdin: |
|
|
echo "[$(( ))]"
|
|
x=" "
|
|
echo "[$(( x ))]"
|
|
echo "[$(( $x ))]"
|
|
|
|
#
|
|
# An expression that is empty or holds nothing but whitespace evaluates to 0, whichever entry
|
|
# point reaches the arithmetic parser: an expansion, a `((...))` command, `let`, or an array
|
|
# subscript.
|
|
#
|
|
- name: "A whitespace-only arithmetic expression evaluates to 0"
|
|
stdin: |
|
|
echo "[$(( ))]"
|
|
echo "[$(( ))]"
|
|
s=" "
|
|
echo "[$(( s ))]"
|
|
echo "[$(( s + 1 ))]"
|
|
|
|
- name: "let with an empty or blank expression fails without a diagnostic"
|
|
stdin: |
|
|
let ""
|
|
echo "empty: $?"
|
|
let " "
|
|
echo "blank: $?"
|
|
let "" 1
|
|
echo "trailing: $?"
|
|
|
|
- name: "(( )) with a blank expression is false"
|
|
stdin: |
|
|
(( ))
|
|
echo "empty: $?"
|
|
(( ))
|
|
echo "blank: $?"
|
|
|
|
- name: "A blank array subscript is element 0"
|
|
stdin: |
|
|
zz_b=(x y z)
|
|
unset 'zz_b[ ]'
|
|
declare -p zz_b
|
|
zz_c=(p q)
|
|
echo "[${zz_c[ ]}]"
|