Skip to content

Commit

Permalink
Introduces the Map pseudodatastructure
Browse files Browse the repository at this point in the history
The Map uses the Set for its list of keys, then hijacks the shell
variable namespace to store the mappings for each key.

Keys must be, therefore, valid variable names. This might change
in the future when we introduce hashing.

Changes were made to exp, dump and toenv to also work with it.
  • Loading branch information
alganet committed Jul 8, 2024
1 parent f80d998 commit dea9671
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 13 deletions.
55 changes: 47 additions & 8 deletions idiom/data.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# Variables in this file are intentionally short because
# it is eval heavy and it matters for some shells.

_T=0 _L=0 _S=0 _A=0 # Some counters for Txt, Lst, Set and Arr
_T=0 _L=0 _S=0 _A=0 _M=0 # Some counters for Txt, Lst, Set, Arr and Map

# Evaluates an expression within brackets
exp () {
Expand Down Expand Up @@ -35,13 +35,14 @@ exp () {
_e="${1:-}${1:+ }$_R"
shift
;;
_[TLSA][0-9]*)
\:*)
_e="${_e:-}${_e:+ }$_t"
;;
[A-Z][a-z]*)
test -z "$_e" &&
_e="${_e:-}${_e:+ }$_t" ||
eval "_e=\"\${_e:-}\${_e:+ }\$$_t\""
_[TLSAM][0-9]*)
_e="${_e:-}${_e:+ }$_t"
;;
Txt|Lst|Set|Arr|Map)
_e="${_e:-}${_e:+ }$_t"
;;
*)
_write "bad expression: '$_t'"
Expand All @@ -50,8 +51,8 @@ exp () {
esac
done
case $_e in
_[TLSA][0-9]*) _R=$_e ;;
*) eval $_e ;;
_[TLSAM][0-9]*) _R=$_e ;;
*) eval $_e ;;
esac
}

Expand Down Expand Up @@ -94,6 +95,16 @@ dump () {
done
dump="$dump]"
;;
_M[0-9]*)
eval "REPLY=\"\$$1\""
dump="[ Map "
for item in $REPLY
do
eval "dump \$$1i$item"
dump="$dump:$item $REPLY "
done
dump="$dump]"
;;
*)
_write "bad data: '$1'"
exit 1
Expand Down Expand Up @@ -127,6 +138,15 @@ toenv () {
count=$((count + 1))
done
;;
_M[0-9]*)
eval "REPLY=\"\$$1\""
eval dump=\"$1=\'\$$1\'\${__EOL__}\"
for item in $REPLY
do
eval "toenv \$$1i$item"
eval "dump=\"\${dump}$1i$item=\$$1i$item\"\${__EOL__}"
done
;;
*)
_write "bad data: '$1'"
exit 1
Expand Down Expand Up @@ -195,3 +215,22 @@ Arr_add () {
do shift ; eval "${_R}i$(($_R - $#))=\$1"
done
}

# The Map pseudotype constructor
Map () {
_M=$((_M + 1))
_R=_M$_M
eval "$_R="
Map_add $_R "$@"
}

Map_add () {
_R=$1
shift
while test $# -gt 0
do
Set_add $_R ${1#\:}
eval "${_R}i${1#\:}=\${2:-}"
shift 2
done
}
41 changes: 41 additions & 0 deletions test/_idiom/006-Map.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright (c) Alexandre Gomes Gaigalas <[email protected]>
# SPDX-License-Identifier: ISC

test_Map_noargs () {
Map
eval deref=\$$_R
tap_assert "" "$deref"
}

test_Map_simple () {
Map :mykey myvalue
eval deref=\$$_R
tap_assert "mykey" "$deref"
eval deref=\$${_R}imykey
tap_assert "myvalue" "$deref"
}

test_Map_multiple_keys () {
Map :mykey myvalue :mysecond anothervalue
eval deref=\$$_R
tap_assert "mykey${__EOL__}mysecond" "$deref"
eval deref=\$${_R}imysecond
tap_assert "anothervalue" "$deref"
}

test_Map_add_to_existing () {
Map :mykey myvalue
Map_add $_R :mysecond anothervalue
eval deref=\$$_R
tap_assert "mykey${__EOL__}mysecond" "$deref"
eval deref=\$${_R}imysecond
tap_assert "anothervalue" "$deref"
}

test_Map_key_uniqueness_retains_last_value () {
Map :mykey myvalue :mykey whichvalue
eval deref=\$$_R
tap_assert "mykey" "$deref"
eval deref=\$${_R}imykey
tap_assert "whichvalue" "$deref"
}
File renamed without changes.
29 changes: 24 additions & 5 deletions test/_idiom/007-dump.sh → test/_idiom/008-dump.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ test_dump_Lst () {
tap_assert "[ Lst 'Lorem' 'Ipsum' ]" "$REPLY"
}

test_dump_nested_Lst () {
val first_name = "Lorem"
val last_name = "Ipsum"
val name_parts = [ Lst $first_name $last_name ]
val person = [ Arr $name_parts ]
dump $person
tap_assert "[ Arr [ Lst 'Lorem' 'Ipsum' ] ]" "$REPLY"
}

test_toenv_Lst () {
val first_name = "Lorem"
local r1=$_R
Expand Down Expand Up @@ -71,11 +80,21 @@ test_toenv_Arr () {
tap_assert "$r3='2'${__EOL__}${r3}i0=$r1${__EOL__}$r1='Lorem'${__EOL__}${r3}i1=$r2${__EOL__}$r2='Ipsum'${__EOL__}" "$REPLY"
}

test_dump_nested_Lst () {
test_dump_Map () {
val first_name = "Lorem"
val last_name = "Ipsum"
val name_parts = [ Lst $first_name $last_name ]
val person = [ Arr $name_parts ]
dump $person
tap_assert "[ Arr [ Lst 'Lorem' 'Ipsum' ] ]" "$REPLY"
val profile = [ Map :fname $first_name :lname $last_name ]
dump $profile
tap_assert "[ Map :fname 'Lorem' :lname 'Ipsum' ]" "$REPLY"
}

test_toenv_Map () {
val first_name = "Lorem"
local r1=$_R
val last_name = "Ipsum"
local r2=$_R
val profile = [ Map :fname $first_name :lname $last_name ]
local r3=$_R
toenv $profile
tap_assert "$r3='fname${__EOL__}lname'${__EOL__}${r3}ifname=$r1${__EOL__}${r3}ilname=$r2${__EOL__}" "$REPLY"
}

0 comments on commit dea9671

Please sign in to comment.