From 81a723086f99f8270563dbab28f6a70866dde02d Mon Sep 17 00:00:00 2001 From: dakkar Date: Sat, 22 Jul 2023 15:57:30 +0100 Subject: single meta class --- bos-meta-class.sh | 152 +++++++++++++++++++++++++++++++++++++++++++++++++ bos-mop-inheritance.sh | 51 ----------------- bos-mop.sh | 106 ---------------------------------- bos-sugar.sh | 2 +- meta-stuff.rst.txt | 8 +-- t/testlib.sh | 3 +- 6 files changed, 158 insertions(+), 164 deletions(-) create mode 100644 bos-meta-class.sh delete mode 100644 bos-mop-inheritance.sh delete mode 100644 bos-mop.sh 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 +} diff --git a/bos-mop-inheritance.sh b/bos-mop-inheritance.sh deleted file mode 100644 index cc3d604..0000000 --- a/bos-mop-inheritance.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!bash - -# this encoding must match bos-namespaces/encode-into -bos_5fbos_2fmop_2finheritance_5fmeta="bos-dispatch/invoke bos/mop/base 0" -declare -a bos_5fbos_2fmop_2finheritance_5fmro=( "bos/mop/inheritance" "bos/mop/base" ) - -function bos/mop/inheritance/isa-for-into() { - bos-namespaces/store-array-for-into "$1" isa "$2" -} - -function bos/mop/inheritance/set-superclasses-for() { - local isa_name; $self isa-for-into isa_name "$1" - local -n isa="$isa_name" - shift - - isa=( "$@" ) - - return 0 -} - -function bos/mop/inheritance/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 -} - -# 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/mop/inheritance/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 -} diff --git a/bos-mop.sh b/bos-mop.sh deleted file mode 100644 index b4eddc3..0000000 --- a/bos-mop.sh +++ /dev/null @@ -1,106 +0,0 @@ -#!bash - -# this encoding must match bos-namespaces/encode-into -bos_5fbos_2fmop_2fbase_5fmeta="bos-dipatch/invoke bos/mop/base 0" -declare -a bos_5fbos_2fmop_2fbase_5fmro=( "bos/mop/base" ) - -# these are instance methods of the base metaclass - -function bos/mop/base/mro-for-into() { - bos-namespaces/store-array-for-into "$1" mro "$2" -} - -# find the first class (in MRO) that defines a given method -function bos/mop/base/find-method-into() { - local -n bos_mop_base_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_mop_base_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/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 - - # 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/mop/base/create-object-into() { - local -n bos_mop_base_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_mop_base_create_object_result "$class" "$object_id" - - # TODO invoke BUILD/BUILDALL? probably in a different metaclass, though - - return 0 -} diff --git a/bos-sugar.sh b/bos-sugar.sh index f211f97..f7e790a 100644 --- a/bos-sugar.sh +++ b/bos-sugar.sh @@ -35,7 +35,7 @@ function bos-sugar/set-metaclass-for() { local class="$1" local metaclass_ref; bos-namespaces/store-scalar-for-into metaclass_ref meta "$class" local -n metaclass_object="$metaclass_ref" - metaclass_object='bos-dispatch/invoke bos/mop/inheritance 0' + metaclass_object='bos-dispatch/invoke bos/meta/class 0' } function bos-sugar/class-open() { diff --git a/meta-stuff.rst.txt b/meta-stuff.rst.txt index c53fb7d..46fabf5 100644 --- a/meta-stuff.rst.txt +++ b/meta-stuff.rst.txt @@ -8,13 +8,13 @@ done bits - use them inside ``class_open`` / ``class_close`` +* ``bos/mop/base`` + ``bos/mop/inheritance`` become ``bos/meta/class`` + next bits… * formalise that ``bos-dispatch/invoke $class 0 $method $args`` is a class method invocation -* ``bos/mop/base`` + ``bos/mop/inheritance`` become ``bos/meta/class`` - * add ``bos/meta/attribute`` - store values in ``bos-namespaces/store-*-for-into attribute @@ -38,7 +38,7 @@ next bits… - when creating a class, create a metaclass instance and store it - the store must be pre-populated with metaclass instances for: - + ``bos/mop/class`` → ``bos-dispatch/invoke bos/mop/class 1`` - + ``bos/mop/attribute`` → ``bos-dispatch/invoke bos/mop/class 2`` + + ``bos/meta/class`` → ``bos-dispatch/invoke bos/meta/class 1`` + + ``bos/meta/attribute`` → ``bos-dispatch/invoke bos/meta/class 2`` and those instances' attributes must be manually stored diff --git a/t/testlib.sh b/t/testlib.sh index 6da46f5..00b82af 100644 --- a/t/testlib.sh +++ b/t/testlib.sh @@ -4,9 +4,8 @@ PS4='[${#FUNCNAME[*]}] ${BASH_SOURCE[0]}:${LINENO} (${FUNCNAME[0]}) +' . bos-namespaces.sh . bos-object-id.sh -. bos-mop.sh +. bos-meta-class.sh . bos-dispatch.sh -. bos-mop-inheritance.sh . bos-sugar.sh . minitap.sh -- cgit v1.2.3