summaryrefslogtreecommitdiff
path: root/bos-meta-class.sh
blob: 4d701b57b6af05ed366922ded85d9790ef9ab550 (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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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
}