declare -A bos_saved_funcs
function bos_save_funcs() {
local _ name
while read -r _ _ name; do
bos_saved_funcs+=( ["$name"]="$(declare -pf "$name")" )
done < <( declare -pF )
return 0
}
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
}
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
}
function bos_set_superclasses() {
local isa_name="${bos_current_class}__bos_isa"
declare -ga "$isa_name"
local -n isa="$isa_name"
isa=( "$@" )
return 0
}
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
}
function bos_linearised_isa() {
local class="$1"
local -n _linearised_isa="$2"
_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
}
bos_current_class=''
function bos_begin_class() {
bos_current_class="$1"
bos_save_funcs
return 0
}
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
}
function bos_find_class_for_method() {
local class="$1"
local start_from_class="$2"
local method="$3"
local -n mro="${class}__bos_mro"
local next_class
local idx=0
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
>&2 echo "method $method not found"
}
function bos_id_from_self() {
local -a parts=( $1 )
echo "${parts[2]}"
}
function bos_self_from_class_id() {
local class="$1"
local self_id="$2"
echo "bos_invoke $class $self_id"
}
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]}"
}
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 \"\$@\"
}
"
}
function bos_find_methods() {
local class="$1"
local method="$2"
local -n dest="$3"
local -n mro="${class}__bos_mro"
dest=()
for class in "${mro[@]}"; do
local full_name="${class}.${method}"
if declare -F "$full_name" >/dev/null; then
dest+=( "$full_name" )
fi
done
return 0
}
function bos_caller() {
local idx
for caller in "${FUNCNAME[@]}"; do
if [[ ! $caller =~ ^bos_ ]]; then
echo "$caller"
return 0
fi
done
}
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
local caller="$(bos_caller)"
method="${caller##*.}"
start_from_class="${caller%.*}"
fi
class="$(bos_find_class_for_method "$class" "$start_from_class" "$method")"
"${class}.${method}" "$@"
}
bos_object_id=0
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"
}
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