#!/bin/bash # If this file has already been sourced, just return test $(VARIABLES_SH+true)TODO && return declare -g VARIABLES_SH = 'true' source ${BASH_SOURCE%/*}/common.sh sourceTODO ${BASH_SOURCE%/*}/logger.sh # TODO: Only set this if it doesn't already exist # # Functions that end in _p send their result to stdout (accessed via $(subshell execution)) # They cannot modify data, so can only be used in getters # They should only be used for debugging # # Functions that end in _c return their result via [return 0/1] to signify true/false # # All other functions return their results in the global RESULT # # handle=[type] declare -g -A VARIABLES_METADATA = ''TODO () declare -g -A VARIABLES_VALUES = ''TODO () declare -g VARIABLES_INDEX = '0'TODO declare -g -A VARIABLES_OFFSETS = '('TODO [type]=0) declare -g VARIABLES_DEBUG = '0'TODO declare -g -A VARIABLES_TYPES = ''() # == GENERAL == proc variable::new { if [[ ${VARIABLES_DEBUG} == 1 ]] { stderr "variable::new $(@)" ; }TODO declare token = '' if [[ "${1}" == "-name" ]] { shift setglobal token = $(1) shift } else { setglobal token = ""auto#$(VARIABLES_INDEX)"" setglobal VARIABLES_INDEX = $shExpr(' ${VARIABLES_INDEX} + 1 ') } if [[ "${#@}" -lt 1 || "${#@}" -gt 2 ]] { stderr "Usage: variable::new ?name? " exit 1 }TODO declare type = $(1) if test ! $(VARIABLES_TYPES[${type}]+isset) { stderr "Unknown variable type [$(type)]" exit 1 } if [[ "${#@}" -eq 1 ]] {TODO declare value = ''"" } else {TODO declare value = $(2) }TODO declare -a metadata = '('$type) compat array-assign VARIABLES_METADATA '"${token}"' $(metadata[@]) compat array-assign VARIABLES_VALUES '"${token}"' $value #echo "Creating a new [$1] of [$2] at index [${index}]" #echo "Result=${index}" setglobal RESULT = $token } proc _variable::new_p { variable::new $(@) echo $RESULT } proc variable::clone { if [[ ${VARIABLES_DEBUG} == 1 ]] { stderr "variable::clone $(@)" ; } if [[ "${1}" == "-name" ]] { shiftTODO declare token = $(1) shift } else {TODO declare token = ""auto#$(VARIABLES_INDEX)"" setglobal VARIABLES_INDEX = $shExpr(' ${VARIABLES_INDEX} + 1 ') } if [[ "${#@}" -lt 1 || "${#@}" -gt 2 ]] { stderr "Usage: variable::new ?name? " exit 1 }TODO declare from_token = $(1) variable::type $(from_token)TODO ; declare type = $(RESULT) variable::value $(from_token)TODO ; declare value = $(RESULT)TODO declare -a metadata = '('$type) compat array-assign VARIABLES_METADATA '${token}' $(metadata[@]) compat array-assign VARIABLES_VALUES '${token}' $value setglobal RESULT = $token } # # variable::set # proc variable::set { if [[ ${VARIABLES_DEBUG} == 1 ]] { stderr "variable::set $(@)" ; } if [[ ${#@} -ne 3 ]] { stderr "Usage: variable::set " exit 1 }TODO declare token = $1TODO declare type = $2TODO declare value = $3 if test ! $(VARIABLES_TYPES[${type}]+isset) { stderr "Unknown variable type [$(type)]" exit 1 }TODO declare -a metadata = '('$type) compat array-assign VARIABLES_METADATA '${token}' $(metadata[@]) compat array-assign VARIABLES_VALUES '${token}' $value setglobal RESULT = ''"" } # # TYPES # proc variable::type { if [[ ${VARIABLES_DEBUG} == 1 ]] { stderr "variable::type $(@)" ; }TODO declare index = $1 if test ! $(VARIABLES_METADATA[${index}]+isset) { stderr "The variable token [$(index)] does not exist" exit 1 }TODO declare -a metadata = '('${VARIABLES_METADATA[$index]}) setglobal RESULT = $(metadata[${VARIABLES_OFFSETS[type]}]) } proc _variable::type_p { variable::type $(@) echo $RESULT } proc variable::type::defineTODO { declare typeName = $(1)TODO declare -a typeParents = ''() # declare -g VARIABLES_TYPES=() if test $(VARIABLES_TYPES[${typeName}]+isset) { stderr "Variable type [$(typeName)] already defined" exit 1 }TODO declare -a superTypes = '' if test $(2+isset) {TODO declare typeParent = $(2) if test ! $(VARIABLES_TYPES[$typeParent]+true) { stderr "Variable type [$(typeName)] declare unknown parent type [$(typeParent)]" exit 1 } setglobal typeParents = '('"${typeParent}") setglobal typeParents = '('${VARIABLES_TYPES[${typeParent}]}) compat array-assign VARIABLES_TYPES '${typeName}' $(typeParents[@]) } else { compat array-assign VARIABLES_TYPES '${typeName}' ''"" } } # # Returns true (0) if the variable is of the specified type or any of its supertypes # proc variable::type::instanceOfTODO { declare token = $(1)TODO declare expectedType = $(2) if variable::type::exactlyA $(token) $(expectedType) { return 0 } variable::type $(token)TODO declare actualType = $(RESULT)TODO declare -a actualSuperTypes = '('${VARIABLES_TYPES[$actualType]}) if test $(#actualSuperTypes[@]) -lt 1 { return 1 ; }TODO declare superType = '' for superType in [$(actualSuperTypes[@])] { if test $(expectedType) == $(superType) { return 0 } } return 1 } proc variable::type::instanceOfOrExitTODO { declare valueToken = $(1)TODO declare expectedType = $(2) if ! variable::type::instanceOf $(valueToken) $(expectedType) { variable::type $(valueToken) stderr "Variable [$(valueToken)] is not of type [$(expectedType)] (actual type [$(RESULT)])" exit 1 } } # # Returns true (0) if the variable is of the specified type # proc variable::type::exactlyATODO { declare token = $(1)TODO declare expectedType = $(2) if test ! $(VARIABLES_TYPES[$expectedType]+true) { stderr "Unknown type [$(expectedType)]" exit 1 } variable::type $(token)TODO declare actualType = $(RESULT) if test $(actualType) == $(expectedType) { return 0 } else { return 1 } } proc variable::value { if [[ ${VARIABLES_DEBUG} == 1 ]] { stderr "variable::value $(@)" ; }TODO declare index = $(1) if ! test $(VARIABLES_VALUES[${index}]+isset) { stderr "The variable token [$(index)] does not exist" exit 1 } setglobal RESULT = $(VARIABLES_VALUES[${index}]) } proc _variable::value_p { variable::value $(@) echo $RESULT } proc variable::debugTODO { declare token = $(1) variable::type $tokenTODO ; declare type = $RESULT if functionExists "variable::$(type)::debug" { eval "variable::$(type)::debug $(token)" setglobal RESULT = $RESULT return } if [[ -z ${VARIABLES_TYPES[${type}]} ]] { variable::debug::simple $token setglobal RESULT = $RESULT return }TODO declare -a actualSuperTypes = '('TODO ${VARIABLES_TYPES[$type]}) declare superType = '' for superType in [$(actualSuperTypes[@])] { if functionExists "variable::$(superType)::debug" { eval "variable::$(superType)::debug $(token)" setglobal RESULT = $RESULT return } } variable::debug::simple $token setglobal RESULT = $RESULT } proc variable::debug::simpleTODO { declare token = $(1) variable::type $(token)TODO declare type = $(RESULT) variable::value $(token)TODO declare value = $(RESULT) setglobal RESULT = ""$(type) :: $(value)"" } proc variable::debug::joinTODO { declare joinChar = $(1) if [[ ${#@} == 1 ]] { setglobal RESULT = ''"" return } if [[ ${#@} == 2 ]] { setglobal RESULT = $(2) return }TODO declare -a items = '('TODO "${@:2}") declare size = '', declare = '', max_index = '' setglobal RESULT = $(2) sh-expr ' size=${#items[@]}, max_index=size-1 ' for (( i=1; i<=max_index; i+=1 )); do RESULT="${RESULT}${joinChar}${items[$i]}" done } proc variable::toSexpTODO { declare token = $(1) variable::type $tokenTODO ; declare type = $RESULT if functionExists "variable::$(type)::toSexp" { eval "variable::$(type)::toSexp $(token)" setglobal RESULT = $RESULT return } if [[ -z ${VARIABLES_TYPES[${type}]} ]] { variable::debug $token setglobal RESULT = $RESULT return }TODO declare -a actualSuperTypes = '('TODO ${VARIABLES_TYPES[$type]}) declare superType = '' for superType in [$(actualSuperTypes[@])] { if functionExists "variable::$(superType)::toSexp" { eval "variable::$(superType)::toSexp $(token)" setglobal RESULT = $RESULT return } } variable::debug $token setglobal RESULT = $RESULT } # # == Output # proc variable::printMetadata { stderr "VARIABLES_METADATATODO " declare keys = '' setglobal keys = $[for var in [$(!VARIABLES_METADATA[@])] { echo $var; } | sort -n] for key in [$(keys)] { stderr " [$(key)]=[$(VARIABLES_METADATA[${key}])]" } stderr "VARIABLES_VALUES" setglobal keys = $[for var in [$(!VARIABLES_VALUES[@])] { echo $var; } | sort -n] for key in [$(keys)] { stderr " [$(key)]=[$(VARIABLES_VALUES[${key}])]" } stderr "VARIABLES_INDEX=$(VARIABLES_INDEX)" } proc variable::printTODO { declare token = $1TODO declare indent = $2 variable::type $(token)TODO ; declare type = $RESULT match $(type) { with list echo "$(indent)$(type)($(token)) :: [" variable::value $(token)TODO ; declare -a values = '('$RESULT) # echo "${indent} ${values[@]}" for value in [$(values[@])] { variable::print $(value) "$(indent) " } echo "$(indent)]" # echo "${indent}${type} :: size=${#value[@]} :: ${value[@]}" with string echo "$(indent)$(type)($(token)) :: [$[_variable::value_p $(token)]]" with integer echo "$(indent)$(type)($(token)) :: [$[_variable::value_p $(token)]]" with * stderr "Invalid variable type [$(type)] for token [$(token)]" variable::printMetadata exit 1 } } # ====================================================== if test $0 != $BASH_SOURCE { return }TODO declare testToken = '' variable::type::define atom variable::type::define string atom variable::type::define number atom variable::type::define integer number # == ATOM TESTS == variable::new integer 12TODO ; \ declare atomId_1 = $RESULT variable::type $atomId_1 ; \ assert::equals integer $RESULT Type of first atom variable::type $(atomId_1) ; \ assert::equals integer $RESULT Type of first atom variable::value $atomId_1 ; \ assert::equals 12 $RESULT Value of first atom variable::value $atomId_1 ; \ assert::equals 12 $RESULT Value of first atom variable::new string "hello thereTODO " ; \ declare atomId_2 = $RESULT variable::type $atomId_2 ; \ assert::equals string $RESULT Type of second atom variable::value $atomId_2 ; \ assert::equals "hello there" $RESULT Value of second atom variable::value $atomId_1 ; \ assert::equals 12 $RESULT Value of first atom remains # exactlyA variable::new integer ; \ setglobal testToken = $(RESULT) variable::type $testToken variable::type::exactlyA $(testToken) integer assert::equals 0 $Status "exactlyA same" variable::type::exactlyA $(testToken) number assert::equals 1 $Status "exactlyA super" variable::type::exactlyA $(testToken) string assert::equals 1 $Status "exactlyA other" # instanceOf variable::new number ; \ setglobal testToken = $(RESULT) variable::type $testToken variable::type::instanceOf $(testToken) integer assert::equals 1 $Status "number instanceOf integer" variable::type::instanceOf $(testToken) number assert::equals 0 $Status "number instanceOf number" variable::type::instanceOf $(testToken) atom assert::equals 0 $Status "number instanceOf atom" variable::type::instanceOf $(testToken) string assert::equals 1 $Status "number instanceOf string" assert::report if test $(1+isset) && test $1 == "debug" { variable::printMetadata }