Skip to content

Commit

Permalink
[osh] Implement SparseArray ${sp[@]...} (#2218)
Browse files Browse the repository at this point in the history
* [osh] Implement SparseArray `...${sp[@]}...`
* [osh] Implement SparseArray `${sp[@]#...}`, `${sp[@]^...}`, etc.
* [osh] Implement SparseArray `${sp[@]//pat/rep}`
* [osh] Implement SparseArray `${sp[@]@q}`
* [osh] Implement SparseArray `${sp[@]@p}`
* [osh] Implement SparseArray `${sp[@]@A}`
* [osh] Implement SparseArray `${a[@]:-}`, etc.
  • Loading branch information
akinomyoga authored Jan 4, 2025
1 parent 31ce9f6 commit 459d58d
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 7 deletions.
44 changes: 37 additions & 7 deletions osh/word_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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')
Expand All @@ -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)
Expand Down
147 changes: 147 additions & 0 deletions spec/ble-idioms.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 459d58d

Please sign in to comment.