summaryrefslogtreecommitdiff
path: root/bos-meta-class.sh
diff options
context:
space:
mode:
Diffstat (limited to 'bos-meta-class.sh')
-rw-r--r--bos-meta-class.sh152
1 files changed, 152 insertions, 0 deletions
diff --git a/bos-meta-class.sh b/bos-meta-class.sh
new file mode 100644
index 0000000..4d701b5
--- /dev/null
+++ b/bos-meta-class.sh
@@ -0,0 +1,152 @@
+#!bash
+
+# this encoding must match bos-namespaces/encode-into
+bos_5fbos_2fmeta_2fclass_5fmeta="bos-dipatch/invoke bos/meta/class 0"
+declare -a bos_5fbos_2fmeta_2fclass_5fmro=( "bos/meta/class" )
+
+# these are instance methods of the base metaclass
+
+function bos/meta/class/isa-for-into() {
+ bos-namespaces/store-array-for-into "$1" isa "$2"
+}
+
+function bos/meta/class/set-superclasses-for() {
+ local isa_name; $self isa-for-into isa_name "$1"
+ local -n isa="$isa_name"
+ shift
+
+ isa=( "$@" )
+
+ return 0
+}
+
+function bos/meta/class/get-superclasses-for-into() {
+ local -n dest="$1"
+ local isa_name; $self isa-for-into isa_name "$2"
+ local -n isa="$isa_name"
+
+ dest=( "${isa[@]}" )
+
+ return 0
+}
+
+function bos/meta/class/mro-for-into() {
+ bos-namespaces/store-array-for-into "$1" mro "$2"
+}
+
+# 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/meta/class/make-mro-for() {
+ local class="$1"
+ local mro_name; $self mro-for-into mro_name "$class"
+ local -n mro="$mro_name"
+
+ # TODO: use C3 and support multiple inheritance
+
+ mro=( "$class" )
+ local -a nextclasses
+ $self get-superclasses-for-into nextclasses "$class"
+
+ while [[ "${#nextclasses}" -gt 0 ]]; do
+ mro+=( "${nextclasses[0]}" )
+ class="${nextclasses[0]}"
+ $self get-superclasses-for-into nextclasses "$class"
+ done
+
+ return 0
+}
+
+# find the first class (in MRO) that defines a given method
+function bos/meta/class/find-method-into() {
+ local -n bos_meta_class_find_method_result="$1"
+ # the class that started the method dispatch (the class of $self,
+ # usually)
+ local class="$2"
+ # what class invoked next/method (if we're doing next/method)
+ local start_from_class="$3"
+ # the method to look for
+ local method="$4"
+
+ local mro_store; $self mro-for-into mro_store "$class"
+ local -n mro="$mro_store"
+
+ # minimal MRO: the class itself
+ if [[ "${#mro[@]}" -eq 0 ]]; then
+ mro=( "$class" )
+ fi
+
+ 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
+ bos_meta_class_find_method_result="${full_name}"
+ return 0
+ fi
+ done
+
+ # TODO: better error / failure
+ >&2 echo "method $method not found via class $class"
+ return 1
+}
+
+# the method dispatcher
+function bos/meta/class/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
+
+ # there is some fuckery going on with namerefs, recursion, and
+ # local; this is a probably-bad-but-works workaround: use a
+ # different variable name in different recursive calls
+ local ffsname="to_invoke_${#FUNCNAME[*]}"
+ $self find-method-into "$ffsname" "$class" "$start_from_class" "$method"
+
+ # TODO: make the fields accessible!
+
+ local self
+ bos-object-id/pack-self-into self "$class" "$object_id"
+
+ # call it
+ eval "\"\$$ffsname\" \"\$@\""
+ return $?
+}
+
+function bos/meta/class/create-object-into() {
+ local -n bos_meta_class_create_object_result="$1";shift
+ local class="$1";shift
+
+ local object_id_store; bos-namespaces/store-scalar-for-into object_id_store object_id "$class"
+ local -n object_id="$object_id_store"
+ (( ++object_id ))
+
+ bos-object-id/pack-self-into bos_meta_class_create_object_result "$class" "$object_id"
+
+ # TODO invoke BUILD/BUILDALL?
+
+ return 0
+}