summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bos-dispatch.sh22
-rw-r--r--bos-mop.sh81
-rw-r--r--bos-namespaces.sh63
-rw-r--r--bos-object-id.sh23
-rw-r--r--meta-stuff.rst.txt14
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