summaryrefslogtreecommitdiff
path: root/bos-mop.sh
blob: b4eddc37ab3946d7e23c8a1b0bddb6701ff11cf4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#!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
}