summaryrefslogtreecommitdiff
path: root/bos-mop.sh
blob: a1b5f20ffd2d613c061f3cc318619ee17b5e8ed7 (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
#!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 mro "$1" "$2"
}
 
# 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 bos_mop_base_find_method_result="$4"
 
    local mro_store; $self mro-for-into "$class" mro_store
    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"
    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 "$class" "$start_from_class" "$method" "$ffsname"
 
    # TODO: make the fields accessible! 
 
    bos-object-id/pack-self-into "$class" "$object_id" self
 
    # 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 "$class" object_id_store
    local -n object_id="$object_id_store"
    (( ++object_id ))
 
    bos-object-id/pack-self-into "$class" "$object_id" bos_mop_base_create_object_result
 
    # TODO invoke BUILD/BUILDALL? probably in a different metaclass, though 
 
    return 0
}