diff --git a/osh/word_eval.py b/osh/word_eval.py index 31d895b89..19733fe56 100644 --- a/osh/word_eval.py +++ b/osh/word_eval.py @@ -218,6 +218,10 @@ def _ValueToPartValue(val, quoted, part_loc): val = cast(value.BashArray, UP_val) return part_value.Array(bash_impl.BashArray_GetValues(val)) + elif case(value_e.SparseArray): + val = cast(value.SparseArray, UP_val) + return part_value.Array(bash_impl.SparseArray_GetValues(val)) + elif case(value_e.BashAssoc): val = cast(value.BashAssoc, UP_val) # bash behavior: splice values! @@ -685,10 +689,14 @@ def _ApplyTestOp( else: is_falsey = False - elif case(value_e.BashArray, value_e.BashAssoc): + elif case(value_e.BashArray, value_e.SparseArray, + value_e.BashAssoc): if val.tag() == value_e.BashArray: val = cast(value.BashArray, UP_val) strs = bash_impl.BashArray_GetValues(val) + elif val.tag() == value_e.SparseArray: + val = cast(value.SparseArray, UP_val) + strs = bash_impl.SparseArray_GetValues(val) elif val.tag() == value_e.BashAssoc: val = cast(value.BashAssoc, UP_val) strs = bash_impl.BashAssoc_GetValues(val) @@ -961,11 +969,15 @@ def _ApplyUnarySuffixOp(self, val, op): #log('%r %r -> %r', val.s, arg_val.s, s) new_val = value.Str(s) # type: value_t - elif case(value_e.BashArray, value_e.BashAssoc): + elif case(value_e.BashArray, value_e.SparseArray, + value_e.BashAssoc): # get values if val.tag() == value_e.BashArray: val = cast(value.BashArray, UP_val) values = bash_impl.BashArray_GetValues(val) + elif val.tag() == value_e.SparseArray: + val = cast(value.SparseArray, UP_val) + values = bash_impl.SparseArray_GetValues(val) elif val.tag() == value_e.BashAssoc: val = cast(value.BashAssoc, UP_val) values = bash_impl.BashAssoc_GetValues(val) @@ -1024,10 +1036,14 @@ def _PatSub(self, val, op): s = replacer.Replace(str_val.s, op) val = value.Str(s) - elif case2(value_e.BashArray, value_e.BashAssoc): + elif case2(value_e.BashArray, value_e.SparseArray, + value_e.BashAssoc): if val.tag() == value_e.BashArray: array_val = cast(value.BashArray, val) values = bash_impl.BashArray_GetValues(array_val) + elif val.tag() == value_e.SparseArray: + sparse_val = cast(value.SparseArray, val) + values = bash_impl.SparseArray_GetValues(sparse_val) elif val.tag() == value_e.BashAssoc: assoc_val = cast(value.BashAssoc, val) values = bash_impl.BashAssoc_GetValues(assoc_val) @@ -1092,13 +1108,17 @@ def _Nullary(self, val, op, var_name, vsub_token, vsub_state): # readline gets rid of these, so we should too. p = prompt.replace('\x01', '').replace('\x02', '') result = value.Str(p) - elif case(value_e.BashArray, value_e.BashAssoc): + elif case(value_e.BashArray, value_e.SparseArray, + value_e.BashAssoc): if val.tag() == value_e.BashArray: val = cast(value.BashArray, UP_val) values = [ s for s in bash_impl.BashArray_GetValues(val) if s is not None ] + elif val.tag() == value_e.SparseArray: + val = cast(value.SparseArray, UP_val) + values = bash_impl.SparseArray_GetValues(val) elif val.tag() == value_e.BashAssoc: val = cast(value.BashAssoc, UP_val) values = bash_impl.BashAssoc_GetValues(val) @@ -1135,10 +1155,17 @@ def _Nullary(self, val, op, var_name, vsub_token, vsub_state): # oddly, 'echo ${x@Q}' is equivalent to 'echo "${x@Q}"' in # bash quoted2 = True - elif case(value_e.BashArray, value_e.BashAssoc): + elif case(value_e.BashArray, value_e.SparseArray, + value_e.BashAssoc): if val.tag() == value_e.BashArray: val = cast(value.BashArray, UP_val) - values = [s for s in bash_impl.BashArray_GetValues(val) if s is not None] + values = [ + s for s in bash_impl.BashArray_GetValues(val) + if s is not None + ] + elif val.tag() == value_e.SparseArray: + val = cast(value.SparseArray, UP_val) + values = bash_impl.SparseArray_GetValues(val) elif val.tag() == value_e.BashAssoc: val = cast(value.BashAssoc, UP_val) values = bash_impl.BashAssoc_GetValues(val) @@ -1160,7 +1187,7 @@ def _Nullary(self, val, op, var_name, vsub_token, vsub_state): # spec/ble-idioms.test.sh. chars = [] # type: List[str] with tagswitch(vsub_state.h_value) as case: - if case(value_e.BashArray): + if case(value_e.BashArray, value_e.SparseArray): chars.append('a') elif case(value_e.BashAssoc): chars.append('A') @@ -1182,6 +1209,9 @@ def _Nullary(self, val, op, var_name, vsub_token, vsub_state): elif case(value_e.BashArray): val = cast(value.BashArray, UP_val) count = bash_impl.BashArray_Count(val) + elif case(value_e.SparseArray): + val = cast(value.SparseArray, UP_val) + count = bash_impl.SparseArray_Count(val) elif case(value_e.BashAssoc): val = cast(value.BashAssoc, UP_val) count = bash_impl.BashAssoc_Count(val) diff --git a/spec/ble-idioms.test.sh b/spec/ble-idioms.test.sh index fc14f0eac..1c3cd72c6 100644 --- a/spec/ble-idioms.test.sh +++ b/spec/ble-idioms.test.sh @@ -1064,6 +1064,153 @@ echo "[${a[@]: -4}][${a[*]: -4}]" ## END +#### SparseArray: ${a[@]} + +case $SH in zsh|mksh|ash) exit ;; esac + +a=(v{0..9}) +unset -v 'a[2]' 'a[3]' 'a[4]' 'a[7]' +case ${SH##*/} in osh) eval 'var a = _a2sp(a)' ;; esac + +argv.py "${a[@]}" +argv.py "abc${a[@]}xyz" + +## STDOUT: +['v0', 'v1', 'v5', 'v6', 'v8', 'v9'] +['abcv0', 'v1', 'v5', 'v6', 'v8', 'v9xyz'] +## END + +## N-I zsh/mksh/ash STDOUT: +## END + + +#### SparseArray: ${a[@]#...} + +case $SH in zsh|mksh|ash) exit ;; esac + +a=(v{0..9}) +unset -v 'a[2]' 'a[3]' 'a[4]' 'a[7]' +case ${SH##*/} in osh) eval 'var a = _a2sp(a)' ;; esac + +argv.py "${a[@]#v}" +argv.py "abc${a[@]#v}xyz" +argv.py "${a[@]%[0-5]}" +argv.py "abc${a[@]%[0-5]}xyz" +argv.py "${a[@]#v?}" + +## STDOUT: +['0', '1', '5', '6', '8', '9'] +['abc0', '1', '5', '6', '8', '9xyz'] +['v', 'v', 'v', 'v6', 'v8', 'v9'] +['abcv', 'v', 'v', 'v6', 'v8', 'v9xyz'] +['', '', '', '', '', ''] +## END + +## N-I zsh/mksh/ash STDOUT: +## END + + +#### SparseArray: ${a[@]/pat/rep} + +case $SH in zsh|mksh|ash) exit ;; esac + +a=(v{0..9}) +unset -v 'a[2]' 'a[3]' 'a[4]' 'a[7]' +case ${SH##*/} in osh) eval 'var a = _a2sp(a)' ;; esac + +argv.py "${a[@]/?}" +argv.py "${a[@]//?}" +argv.py "${a[@]/#?}" +argv.py "${a[@]/%?}" + +argv.py "${a[@]/v/x}" +argv.py "${a[@]//v/x}" +argv.py "${a[@]/[0-5]/D}" +argv.py "${a[@]//[!0-5]/_}" + +## STDOUT: +['0', '1', '5', '6', '8', '9'] +['', '', '', '', '', ''] +['0', '1', '5', '6', '8', '9'] +['v', 'v', 'v', 'v', 'v', 'v'] +['x0', 'x1', 'x5', 'x6', 'x8', 'x9'] +['x0', 'x1', 'x5', 'x6', 'x8', 'x9'] +['vD', 'vD', 'vD', 'v6', 'v8', 'v9'] +['_0', '_1', '_5', '__', '__', '__'] +## END + +## N-I zsh/mksh/ash STDOUT: +## END + + +#### SparseArray: ${a[@]@P}, ${a[@]@Q}, and ${a[@]@a} +case $SH in zsh|mksh|ash) exit ;; esac + +a=(v{0..9}) +unset -v 'a[2]' 'a[3]' 'a[4]' 'a[7]' +case ${SH##*/} in osh) eval 'var a = _a2sp(a)' ;; esac + +argv.py "${a[@]@P}" +argv.py "${a[*]@P}" +argv.py "${a[@]@Q}" +argv.py "${a[*]@Q}" +argv.py "${a[@]@a}" +argv.py "${a[*]@a}" + +## STDOUT: +['v0', 'v1', 'v5', 'v6', 'v8', 'v9'] +['v0 v1 v5 v6 v8 v9'] +['v0', 'v1', 'v5', 'v6', 'v8', 'v9'] +['v0 v1 v5 v6 v8 v9'] +['a', 'a', 'a', 'a', 'a', 'a'] +['a a a a a a'] +## END + +## OK bash STDOUT: +['v0', 'v1', 'v5', 'v6', 'v8', 'v9'] +['v0 v1 v5 v6 v8 v9'] +["'v0'", "'v1'", "'v5'", "'v6'", "'v8'", "'v9'"] +["'v0' 'v1' 'v5' 'v6' 'v8' 'v9'"] +['a', 'a', 'a', 'a', 'a', 'a'] +['a a a a a a'] +## END + +## N-I zsh/mksh/ash STDOUT: +## END + + +#### SparseArray: ${a[@]-unset}, ${a[@]:-empty}, etc. +case $SH in zsh|mksh|ash) exit ;; esac + +a1=() +a2=("") +a3=("" "") + +case $SH in +bash) ;; +*) eval "var a1 = _a2sp(a1); var a2 = _a2sp(a2); var a3 = _a2sp(a3)" ;; +esac + +echo "a1 unset: [${a1[@]-unset}]" +echo "a1 empty: [${a1[@]:-empty}]" +echo "a2 unset: [${a2[@]-unset}]" +echo "a2 empty: [${a2[@]:-empty}]" +echo "a3 unset: [${a3[@]-unset}]" +echo "a3 empty: [${a3[@]:-empty}]" + +## STDOUT: +a1 unset: [unset] +a1 empty: [empty] +a2 unset: [] +a2 empty: [empty] +a3 unset: [ ] +a3 empty: [ ] +## END + +## N-I zsh/mksh/ash STDOUT: +## END + + #### SparseArray: compgen -F _set_COMPREPLY case $SH in zsh|mksh|ash) exit ;; esac