diff options
author | Gianni Ceccarelli <gianni.ceccarelli@broadbean.com> | 2023-07-13 17:29:26 +0100 |
---|---|---|
committer | Gianni Ceccarelli <gianni.ceccarelli@broadbean.com> | 2023-07-13 17:29:26 +0100 |
commit | c3141fc3b3c8d6fb7270a3a1cd15be4895ea052d (patch) | |
tree | 51e0ab1cca45f5d9491ab04b2a9ea80425a40474 | |
parent | some docs (diff) | |
download | bash-object-system-c3141fc3b3c8d6fb7270a3a1cd15be4895ea052d.tar.gz bash-object-system-c3141fc3b3c8d6fb7270a3a1cd15be4895ea052d.tar.bz2 bash-object-system-c3141fc3b3c8d6fb7270a3a1cd15be4895ea052d.zip |
flailing about with MOPs
-rw-r--r-- | bos-dispatch.sh | 22 | ||||
-rw-r--r-- | bos-mop.sh | 81 | ||||
-rw-r--r-- | bos-namespaces.sh | 63 | ||||
-rw-r--r-- | bos-object-id.sh | 23 | ||||
-rw-r--r-- | meta-stuff.rst.txt | 14 |
5 files changed, 203 insertions, 0 deletions
diff --git a/bos-dispatch.sh b/bos-dispatch.sh new file mode 100644 index 0000000..47b5f33 --- /dev/null +++ b/bos-dispatch.sh @@ -0,0 +1,22 @@ +#!bash + +# the bare-bones / fallback dispatcher +function bos-dispatch/invoke() { + local class="$1";shift + local self_id="$1";shift + local method="$1";shift + + local self + bos-object-id/pack-self-into "$class" "$self_id" self + + if declare -F "$class/$method" >/dev/null; then + "$class/$method" "$@" + return $? + fi + + local metaclass_ref + bos-namespaces/store-for-into meta "$class" metaclass_ref + local -n metaclass_object="$metaclass_ref" + + $metaclass_object invoke "$class" "$self_id" "$method" "$@" +} diff --git a/bos-mop.sh b/bos-mop.sh new file mode 100644 index 0000000..f9faef2 --- /dev/null +++ b/bos-mop.sh @@ -0,0 +1,81 @@ +#!bash + +bos_class__bos__mop__base__meta="bos-dipatch/invoke bos/mop/base 0" + +# these are instance methods of the base metaclass + +function bos/mop/base/mro-for-into() { + local name="bos_mop__mro__${1//\//__}" + local -n result="$2" + declare -ga "$name" + result="$name" +} + +# find the first class (in MRO) that defines a given method +function bos/mop/base/find-method-into() { + # 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 result="$4" + + local mro_store; $self mro-for-into mro_store + local -n mro="$mro_store" + 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 + result="${full_name}" + return 0 + fi + done + + # TODO: better error / failure + >&2 echo "method $method not found" + return 1 +} + +# the method dispatcher +function bos/mop/base/invoke() { + local class="$1";shift + local object_id="$1";shift + local method="$1";shift + + 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-dispatch/caller-into caller + method="${caller##*/}" + start_from_class="${caller%/*}" + fi + + # get the first class that defines the method + local to_invoke + $self find-method-into "$class" "$start_from_class" "$method" to_invoke + + # TODO: make the fields accessible! + + local self + bos-object-id/pack-self-into "$class" "$object_id" self + + # call it + "$to_invoke" "$@" +} diff --git a/bos-namespaces.sh b/bos-namespaces.sh new file mode 100644 index 0000000..3e9e711 --- /dev/null +++ b/bos-namespaces.sh @@ -0,0 +1,63 @@ +#!bash +## namespace handling + +function bos-namespaces/store-for-into() { + local name="bos_namespaces__${2//[^[:word:]/__}__${1//[^[:word:]]/__}" + local -n result="$2" + declare -gA "$name" + result="$name" +} + +# save all currently-visible functions in an associative array +function bos-namespaces/start() { + local namespace="$1";shift + local ns_store; bos-namespaces/store-for-into saved_funcs "$namespace" ns_store + local -n saved_funcs="$ns_store" + local _ funcname + + while read -r _ _ funcname; do + saved_funcs+=( ["$funcname"]="$(declare -pf "$funcname")" ) + done < <( declare -pF ) + + return 0 +} + +# diff all currently-visible function against the saved ones, list the +# ones that differ +function bos-namespaces/list-new-funcs-into() { + local namespace="$1";shift + local -n result="$1" + local ns_store; bos-namespaces/store-for-into saved_funcs "$namespace" ns_store + local -n saved_funcs="$ns_store" + local _ funcname new_function + + while read -r _ _ funcname; do + new_function="$(declare -pf "$funcname")" + if [[ "$new_function" != "${saved_funcs["$funcname"]}" ]]; then + result+=( "${funcname}" ) + 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-namespaces/qualify-funcs() { + local namespace="$1";shift + local ns_store; bos-namespaces/store-for-into saved_funcs "$namespace" ns_store + local -n saved_funcs="$ns_store" + local _ funcname new_function + + for funcname in "$@"; do + new_function="$(declare -pf "$funcname")" + eval "${namespace}/${new_function}" + if [[ -n "${saved_funcs["$funcname"]}" ]]; then + eval "${saved_funcs["$funcname"]}" + else + unset -f "$funcname" + fi + done + + return 0 +} diff --git a/bos-object-id.sh b/bos-object-id.sh new file mode 100644 index 0000000..2026506 --- /dev/null +++ b/bos-object-id.sh @@ -0,0 +1,23 @@ +#!bash +## object identity + +# 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-object-id/pack-self-into() { + local class="$1" + local self_id="$2" + local -n result="$3" + result="bos-dispatch/invoke $class $self_id" +} + +# get class and object id from a $self string +function bos-object/unpack-self-into() { + local self="$1" + local -n class="$2" + local -n id="$2" + local _ + read -r _ class id <<<"$self" +} diff --git a/meta-stuff.rst.txt b/meta-stuff.rst.txt new file mode 100644 index 0000000..df8061e --- /dev/null +++ b/meta-stuff.rst.txt @@ -0,0 +1,14 @@ +class ``Foo`` has class-level storage vars ``bos_class__Foo__$thing`` + +* ``bos_class__${class}__isa`` +* ``bos_class__${class}__meta`` + +``invoke $class $id $method @etc`` if ``$class/$method`` is defined, +call it; otherwise delegate to the meta-class +``bos_class__${class}__meta`` by calling ``$metaclass/invoke $class +$id $method @etc`` + +then we have ``bos/mop/base/invoke`` and +``bos/mop/base/find-method-into`` and +``bos_class__bos__mop__base__meta="bos-dispatch/invoke bos/mop/base 0"`` +so its metaclass instance is an instance of itself |