-
-
Notifications
You must be signed in to change notification settings - Fork 45
/
matchcore_compiler.jl
78 lines (64 loc) · 2.91 KB
/
matchcore_compiler.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
# core mechanism of extending Taine Zhao's @thautwarm 's MatchCore pattern matching.
using MatchCore
#using GeneralizedGenerated
using RuntimeGeneratedFunctions
const RGF = RuntimeGeneratedFunctions
RGF.init(@__MODULE__)
## compile (quote) left and right hands of a rule
# escape symbols to create MLStyle compatible patterns
# compile left hand of rule
# if it's the first time seeing v, add it to the "seen symbols" Set
# insert a :& expression only if v has not been seen before
function c_left(v::Symbol, s)
if Base.isbinaryoperator(v) return v end
(v ∉ s ? (push!(s, v); v) : amp(v)) |> dollar
end
c_left(v::Expr, s) = v.head ∈ add_dollar ? dollar(v) : v
# support symbol literals in left hand
c_left(v::QuoteNode, s) = v.value isa Symbol ? dollar(v) : v
c_left(v, s) = v # ignore other types
c_right(v::Symbol) = Base.isbinaryoperator(v) ? v : dollar(v)
function c_right(v::Expr)
v.head ∈ add_dollar ? dollar(v) : v
end
c_right(v) = v #ignore other types
# add dollar in front of the expressions with those symbols as head
const add_dollar = [:(::), :(...)]
# don't walk down on these symbols
const skips = [:(::), :(...)]
# Compile rules from Metatheory format to MatchCore format
function compile_rule(rule::Rule)::Expr
le = df_walk(c_left, rule.left, Vector{Symbol}(); skip=skips, skip_call=true) |> quot
if rule.mode == :dynamic # regular pattern matching
# right side not quoted! needed to evaluate expressions in right hand.
re = rule.right
elseif rule.mode == :rewrite # right side is quoted, symbolic replacement
re = df_walk(c_right, rule.right; skip=skips, skip_call=true) |> quot
else
error(`rule "$e" is not in valid form.\n`)
end
return :($le => $re)
end
# catch-all for reductions
identity_axiom = :($(quot(dollar(:i))) => i)
# TODO analyse theory before compiling. Identify associativity and commutativity
# and other loop-creating problems. Generate a pattern matching block with the
# correct rule order and expansions for associativity and distributivity
theory_block(t::Vector{Rule}) = block(map(compile_rule, t)..., identity_axiom)
# Compile a theory to a closure that does the pattern matching job
# RETURNS A RuntimeGeneratedFunction 🔥
function compile_theory(theory::Vector{Rule}, mod::Module; __source__=LineNumberNode(0))
(mod != @__MODULE__) && !isdefined(mod, RGF._tagname) && RGF.init(mod)
# generate an unique parameter name
# TODO needed? consider just calling it expr for access in right hand
parameter = Meta.gensym(:reducing_expression)
block = theory_block(theory)
matching = MatchCore.gen_match(parameter, block, __source__, mod)
matching = MatchCore.AbstractPatterns.init_cfg(matching)
ex = :(($parameter) -> $matching)
RuntimeGeneratedFunction(mod, mod, ex)
end
# Compile a theory at runtime to a closure that does the pattern matching job
macro compile_theory(theory)
gettheory(theory, __module__)
end