diff options
-rw-r--r-- | defs.sh | 311 |
1 files changed, 0 insertions, 311 deletions
diff --git a/defs.sh b/defs.sh deleted file mode 100644 index 1ab6fbe..0000000 --- a/defs.sh +++ /dev/null @@ -1,311 +0,0 @@ -#!bash - -### Bash Object System - -## namespace handling - -declare -A bos_saved_funcs - -# save all currently-visible functions in an associative array -function bos_save_funcs() { - local _ name - - while read -r _ _ name; do - bos_saved_funcs+=( ["$name"]="$(declare -pf "$name")" ) - done < <( declare -pF ) - - return 0 -} - -# diff all currently-visible function against the saved ones, list the -# ones that differ -function bos_list_new_funcs() { - local _ name new_function - - while read -r _ _ name; do - new_function="$(declare -pf "$name")" - if [[ "$new_function" != "${bos_saved_funcs["$name"]}" ]]; then - echo "${name}" - fi - done < <( declare -pF ) - - return 0 -} - -# rename a bunch of functions by prepending a prefix; restore the -# saved function of the same name, if it exists -function bos_qualify_funcs() { - local prefix="$1" - shift - local _ name new_function - - for name in "$@"; do - new_function="$(declare -pf "$name")" - eval "${prefix}.${new_function}" - if [[ -n "${bos_saved_funcs["$name"]}" ]]; then - eval "${bos_saved_funcs["$name"]}" - else - unset -f "$name" - fi - done - - return 0 -} - -## the MOP - -bos_current_class='' - -# begin the definition of a class (not really MOP, is it?) -function bos_begin_class() { - bos_current_class="$1" - - bos_save_funcs - - return 0 -} - -# set the superclasses of the current class -# TODO: split this into MOP + helper -function bos_set_superclasses() { - local isa_name="${bos_current_class}__bos_isa" - declare -ga "$isa_name" - local -n isa="$isa_name" - - isa=( "$@" ) - - return 0 -} - -# get the superclasses of the given class -function bos_get_superclasses_for() { - local class="$1" - local -n dest="$2" - local isa_name="${class}__bos_isa" - local -n isa="$isa_name" - - dest=( "${isa[@]}" ) - - return 0 -} - -# given a class and an array, sets the array to the list of clasess to -# look into when resolving a method call for the given class, in order -function bos_linearised_isa() { - local class="$1" - local -n _linearised_isa="$2" - - # TODO: use C3 and support multiple inheritance - - _linearised_isa=( "$class" ) - local -a nextclasses - bos_get_superclasses_for "$class" nextclasses - - while [[ "${#nextclasses}" -gt 0 ]]; do - _linearised_isa+=( "${nextclasses[0]}" ) - class="${nextclasses[0]}" - bos_get_superclasses_for "$class" nextclasses - done - - return 0 -} - -# ends the definition of a class, moving its functions into the class -# namespace and calculating the MRO -# TODO: split this into MOP + helper -# TODO: delay MRO computation until first method call? -function bos_end_class() { - local mro_name="${bos_current_class}__bos_mro" - declare -ga "$mro_name" - - readarray -t method_names < <(bos_list_new_funcs) - bos_qualify_funcs "$bos_current_class" "${method_names[@]}" - bos_linearised_isa "$bos_current_class" "$mro_name" - - return 0 -} - -# find the first class (in MRO) that defines a given method -function bos_find_class_for_method() { - # the class that started the method dispatch (the class of $self, - # usually) - local class="$1" - # what class invoked next.method (if we're doing next.method) - local start_from_class="$2" - # the method to look for - local method="$3" - - local -n mro="${class}__bos_mro" - local idx=0 - - # if we don't have a $start_from_class, it means we're doing the - # initial method resolution, so don't skip any class in $mro - if [[ -n "$start_from_class" ]]; then - for (( ; idx < "${#mro}" ; ++idx )); do - if [[ "${mro[$idx]}" == "$start_from_class" ]]; then - (( ++idx )) - break - fi - done - fi - - for (( ; idx < "${#mro}" ; ++idx )); do - local full_name="${mro[$idx]}.${method}" - if declare -F "$full_name" >/dev/null; then - echo "${mro[$idx]}" - return 0 - fi - done - - # TODO: better error / failure - >&2 echo "method $method not found" -} - -# objects are values, so they're stored in variables! -# -# the shape of that value is… a call to bos_invoke curry-ed on class and -# id, so that `$my_obj the-method 1 2` is equivalent to `bos_invoke -# TheClass $the_obj_id the-method 1 2` -function bos_self_from_class_id() { - local class="$1" - local self_id="$2" - echo "bos_invoke $class $self_id" -} - -# get object id from a $self string -function bos_id_from_self() { - local -a parts=( $1 ) - echo "${parts[2]}" -} - -function bos_storage_var_for_object() { - local self_id="$(bos_id_from_self "$1")" - local var="bos_obj_storage_${self_id}" - echo "$var" -} - -function bos_set_attribute_value() { - local self="$1";shift - local field="$1";shift - local -n storage="$(bos_storage_var_for_object "$self")" - storage[$field]="$*" -} - -function bos_get_attribute_value() { - local self="$1";shift - local field="$1";shift - local -n storage="$(bos_storage_var_for_object "$self")" - echo "${storage[$field]}" -} - -# declare getter / setter for an attribute in the current class -# -# TODO: maybe split? bos_add_attribute just declares the attribute, -# then have MOP functions to create getter and/or setter, then join -# the two in bos_end_class -function bos_add_attribute() { - local name="$1" - - eval " -function get_${name}() { - bos_get_attribute_value \"\$self\" $name -} -function set_${name}() { - bos_set_attribute_value \"\$self\" $name \"\$@\" -} -" -} - -# return the closest non-BOS function name from the call stack -function bos_caller() { - local idx - for caller in "${FUNCNAME[@]}"; do - if [[ ! $caller =~ ^bos_ ]]; then - echo "$caller" - return 0 - fi - done -} - -# the method dispatcher -function bos_invoke() { - local class="$1";shift - local self_id="$1";shift - local method="$1";shift - local self="$(bos_self_from_class_id $class $self_id)" - - local start_from_class='' - - if [[ "$method" == 'next.method' ]]; then - # get the method and the calling class, so - # bos_find_class_for_method will return the *next* class to - # use - local caller="$(bos_caller)" - method="${caller##*.}" - start_from_class="${caller%.*}" - fi - - # get the first class that defines the method - class="$(bos_find_class_for_method "$class" "$start_from_class" "$method")" - - # call it - "${class}.${method}" "$@" -} - -bos_object_id=0 -# constructor: given a variable name and a class, create an object of -# that class, build it, and assign it to the variable -function bos_create_object() { - local -n dest="$1";shift - local class="$1";shift - - (( ++bos_object_id )) - - local self="$(bos_self_from_class_id $class $bos_object_id)" - declare -gA "$(bos_storage_var_for_object "$self")" - - $self BUILD "$@" - - dest="$self" -} - -# ---- test ---- - -bos_begin_class one - -bos_add_attribute first - -BUILD() { - echo "building one" - $self set_first "$1" -} - -function doit() { - echo "one method ($*) first=$( $self get_first )" -} - -bos_end_class - -bos_begin_class two - -bos_set_superclasses one - -bos_add_attribute second - -BUILD() { - echo "building two" - $self set_second "$1" - $self next.method "$2" -} - -function doit() { - echo "two method ($*) second=$( $self get_second )" - - $self next.method "$@" -} - -bos_end_class - -bos_create_object A two 16 23 - -$A doit 1 2 3 |