|
5 | 5 | # 2. Dictionary
|
6 | 6 | # 3. Callback: takes arguments Dictionary × Number of elements matched
|
7 | 7 | #
|
| 8 | + |
8 | 9 | function matcher(val::Any)
|
9 |
| - iscall(val) && return term_matcher(val) |
| 10 | + # if val is a call (like an operation) creates a term matcher or term matcher with defslot |
| 11 | + if iscall(val) |
| 12 | + # if has two arguments and one of them is a DefSlot, create a term matcher with defslot |
| 13 | + if length(arguments(val)) == 2 && any(x -> isa(x, DefSlot), arguments(val)) |
| 14 | + return defslot_term_matcher_constructor(val) |
| 15 | + # else return a normal term matcher |
| 16 | + else |
| 17 | + return term_matcher_constructor(val) |
| 18 | + end |
| 19 | + end |
| 20 | + |
10 | 21 | function literal_matcher(next, data, bindings)
|
| 22 | + # car data is the first element of data |
11 | 23 | islist(data) && isequal(car(data), val) ? next(bindings, 1) : nothing
|
12 | 24 | end
|
13 | 25 | end
|
14 | 26 |
|
15 | 27 | function matcher(slot::Slot)
|
16 | 28 | function slot_matcher(next, data, bindings)
|
17 |
| - !islist(data) && return |
| 29 | + !islist(data) && return nothing |
18 | 30 | val = get(bindings, slot.name, nothing)
|
| 31 | + # if slot name already is in bindings, check if it matches |
19 | 32 | if val !== nothing
|
20 | 33 | if isequal(val, car(data))
|
21 | 34 | return next(bindings, 1)
|
22 | 35 | end
|
23 |
| - else |
24 |
| - if slot.predicate(car(data)) |
25 |
| - next(assoc(bindings, slot.name, car(data)), 1) |
26 |
| - end |
| 36 | + # elseif the first element of data matches the slot predicate, add it to bindings and call next |
| 37 | + elseif slot.predicate(car(data)) |
| 38 | + next(assoc(bindings, slot.name, car(data)), 1) |
27 | 39 | end
|
28 | 40 | end
|
29 | 41 | end
|
30 | 42 |
|
| 43 | +# this is called only when defslot_term_matcher finds the operation and tries |
| 44 | +# to match it, so no default value used. So the same function as slot_matcher |
| 45 | +# can be used |
| 46 | +function matcher(defslot::DefSlot) |
| 47 | + matcher(Slot(defslot.name, defslot.predicate)) |
| 48 | +end |
| 49 | + |
31 | 50 | # returns n == offset, 0 if failed
|
32 | 51 | function trymatchexpr(data, value, n)
|
33 | 52 | if !islist(value)
|
@@ -84,13 +103,73 @@ function matcher(segment::Segment)
|
84 | 103 | end
|
85 | 104 | end
|
86 | 105 |
|
87 |
| -function term_matcher(term) |
| 106 | +function term_matcher_constructor(term) |
88 | 107 | matchers = (matcher(operation(term)), map(matcher, arguments(term))...,)
|
| 108 | + |
89 | 109 | function term_matcher(success, data, bindings)
|
| 110 | + !islist(data) && return nothing # if data is not a list, return nothing |
| 111 | + !iscall(car(data)) && return nothing # if first element is not a call, return nothing |
| 112 | + |
| 113 | + function loop(term, bindings′, matchers′) # Get it to compile faster |
| 114 | + if !islist(matchers′) |
| 115 | + if !islist(term) |
| 116 | + return success(bindings′, 1) |
| 117 | + end |
| 118 | + return nothing |
| 119 | + end |
| 120 | + car(matchers′)(term, bindings′) do b, n |
| 121 | + loop(drop_n(term, n), b, cdr(matchers′)) |
| 122 | + end |
| 123 | + # explenation of above 3 lines: |
| 124 | + # car(matchers′)(b,n -> loop(drop_n(term, n), b, cdr(matchers′)), term, bindings′) |
| 125 | + # <------ next(b,n) ----------------------------> |
| 126 | + # car = first element of list, cdr = rest of the list, drop_n = drop first n elements of list |
| 127 | + # Calls the first matcher, with the "next" function being loop again but with n terms dropepd from term |
| 128 | + # Term is a linked list (a list and a index). drop n advances the index. when the index sorpasses |
| 129 | + # the length of the list, is considered empty |
| 130 | + end |
| 131 | + |
| 132 | + loop(car(data), bindings, matchers) # Try to eat exactly one term |
| 133 | + end |
| 134 | +end |
| 135 | + |
| 136 | +# creates a matcher for a term containing a defslot, such as: |
| 137 | +# (~x + ...complicated pattern...) * ~!y |
| 138 | +# normal part (can bee a tree) operation defslot part |
90 | 139 |
|
| 140 | +# defslot_term_matcher works like this: |
| 141 | +# checks wether data starts with the default operation. |
| 142 | +# if yes (1): continues like term_matcher |
| 143 | +# if no checks wether data matches the normal part |
| 144 | +# if no returns nothing, rule is not applied |
| 145 | +# if yes (2): adds the pair (default value name, default value) to the found bindings and |
| 146 | +# calls the success function like term_matcher would do |
| 147 | + |
| 148 | +function defslot_term_matcher_constructor(term) |
| 149 | + a = arguments(term) # lenght two bc defslot term matcher is allowed only with +,* and ^, that accept two arguments |
| 150 | + matchers = (matcher(operation(term)), map(matcher, a)...) # create matchers for the operation and the two arguments of the term |
| 151 | + |
| 152 | + defslot_index = findfirst(x -> isa(x, DefSlot), a) # find the defslot in the term |
| 153 | + defslot = a[defslot_index] |
| 154 | + |
| 155 | + function defslot_term_matcher(success, data, bindings) |
| 156 | + # if data is not a list, return nothing |
91 | 157 | !islist(data) && return nothing
|
92 |
| - !iscall(car(data)) && return nothing |
| 158 | + # if data (is not a tree and is just a symbol) or (is a tree not starting with the default operation) |
| 159 | + if !iscall(car(data)) || (iscall(car(data)) && nameof(operation(car(data))) != defslot.operation) |
| 160 | + other_part_matcher = matchers[defslot_index==2 ? 2 : 3] # find the matcher of the normal part |
| 161 | + |
| 162 | + # checks wether it matches the normal part |
| 163 | + # <-----------------(2)-------------------------------> |
| 164 | + bindings = other_part_matcher((b,n) -> assoc(b, defslot.name, defslot.defaultValue), data, bindings) |
| 165 | + |
| 166 | + if bindings === nothing |
| 167 | + return nothing |
| 168 | + end |
| 169 | + return success(bindings, 1) |
| 170 | + end |
93 | 171 |
|
| 172 | + # (1) |
94 | 173 | function loop(term, bindings′, matchers′) # Get it to compile faster
|
95 | 174 | if !islist(matchers′)
|
96 | 175 | if !islist(term)
|
|
0 commit comments