-
Notifications
You must be signed in to change notification settings - Fork 116
/
Copy pathmatchers.jl
108 lines (94 loc) · 2.87 KB
/
matchers.jl
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
#### Pattern matching
### Matching procedures
# A matcher is a function which takes 3 arguments
# 1. Expression
# 2. Dictionary
# 3. Callback: takes arguments Dictionary × Number of elements matched
#
function matcher(val::Any)
iscall(val) && return term_matcher(val)
function literal_matcher(next, data, bindings)
islist(data) && isequal(car(data), val) ? next(bindings, 1) : nothing
end
end
function matcher(slot::Slot)
function slot_matcher(next, data, bindings)
!islist(data) && return
val = get(bindings, slot.name, nothing)
if val !== nothing
if isequal(val, car(data))
return next(bindings, 1)
end
else
if slot.predicate(car(data))
next(assoc(bindings, slot.name, car(data)), 1)
end
end
end
end
# returns n == offset, 0 if failed
function trymatchexpr(data, value, n)
if !islist(value)
return n
elseif islist(value) && islist(data)
if !islist(data)
# didn't fully match
return nothing
end
while isequal(car(value), car(data))
n += 1
value = cdr(value)
data = cdr(data)
if !islist(value)
return n
elseif !islist(data)
return nothing
end
end
return !islist(value) ? n : nothing
elseif isequal(value, data)
return n + 1
end
end
function matcher(segment::Segment)
function segment_matcher(success, data, bindings)
val = get(bindings, segment.name, nothing)
if val !== nothing
n = trymatchexpr(data, val, 0)
if n !== nothing
success(bindings, n)
end
else
res = nothing
for i=length(data):-1:0
subexpr = take_n(data, i)
if segment.predicate(subexpr)
res = success(assoc(bindings, segment.name, subexpr), i)
if res !== nothing
break
end
end
end
return res
end
end
end
function term_matcher(term)
matchers = (matcher(operation(term)), map(matcher, arguments(term))...,)
function term_matcher(success, data, bindings)
!islist(data) && return nothing
!iscall(car(data)) && return nothing
function loop(term, bindings′, matchers′) # Get it to compile faster
if !islist(matchers′)
if !islist(term)
return success(bindings′, 1)
end
return nothing
end
car(matchers′)(term, bindings′) do b, n
loop(drop_n(term, n), b, cdr(matchers′))
end
end
loop(car(data), bindings, matchers) # Try to eat exactly one term
end
end