I normally post to Slack, but seems too long winded for that venue.
I’m having some trouble that might be related to a macro I’ve defined, but as near as I can tell the macro expands as I expect.
I’m hoping you can help me figure out what’s going wrong.
Here’s the relevant output snippet from my unit tests (it explicitly lists the mehods for debugging):
# 3 methods for generic function "install" from Rete:
[1] install(root::BasicReteNode, ::JoinSequentialLetterDigram2)
@ Main C:\Users\Mark Nahabedian\.julia\dev\Rete\src\rules.jl:56
[2] install(root::BasicReteNode, ::JoinSequentialLetterTrigram2)
@ Main C:\Users\Mark Nahabedian\.julia\dev\Rete\src\rules.jl:56
[3] install(root::BasicReteNode, rule_group)
@ C:\Users\Mark Nahabedian\.julia\dev\Rete\src\basic_node.jl:32
rule example 2: Error During Test at C:\Users\Mark Nahabedian\.julia\dev\Rete\test\rule_example_2.jl:14
Got exception outside of a @test
MethodError: no method matching install(::BasicReteNode, ::JoinSequentialLetterDigram2)
Stacktrace:
[1] macro expansion
@ C:\Users\Mark Nahabedian\.julia\dev\Rete\test\rule_example_2.jl:17 [inlined]
As near as I can tell, the method it claims is missing is the first on the list. It results from (line numbers elided for readability):
@macroexpand @rule JoinSequentialLetterDigram2(a::Char, b::Char) begin
if codepoint(a) + 1 == codepoint(b)
emit((a, b))
end
end
quote
struct JoinSequentialLetterDigram2 <: Rule
end
function Rete.install(root::BasicReteNode, ::JoinSequentialLetterDigram2)
join = JoinNode("JoinSequentialLetterDigram2", JoinSequentialLetterDigram2())
connect_a(ensure_IsaMemoryNode(root, Char), join)
connect_b(ensure_IsaMemoryNode(root, Char), join)
connect(join, root)
end
function (::JoinSequentialLetterDigram2)(node::JoinNode, a::Char, b::Char)
begin
if codepoint(a) + 1 == codepoint(b)
emit(node, (a, b))
end
end
end
end
Here’s the macro. Most of it is parameter decoding. I use esc
because I don’t think I’m using any identifiers that require alphatization and, in my observations, esc
does something completely random. Maybe Dave Moon’s fixes will be incorporated in my lifetime, but I’m not holding my breath.
macro rule(call, body)
if !isexpr(call, :call)
error("The first expression of rule should look like a call")
end
supertype = Rule
rule_name = call.args[1]
if isexpr(rule_name, :(.))
supertype = rule_name.args[1]
rule_name = rule_name.args[2].value # Unwrap QuoteNode
end
rule_name_str = string(rule_name)
@assert isexpr(call.args[2], :(::))
@assert isexpr(call.args[3], :(::))
a_var = call.args[2].args[1]
a_type = call.args[2].args[2]
b_var = call.args[3].args[1]
b_type = call.args[3].args[2]
# Add the node argument to all calls to emit:
body = postwalk(body) do e
if isexpr(e, :call) && e.args[1] == :emit
Expr(:call, :emit, :node, e.args[2])
else
e
end
end
esc(quote
struct $rule_name <: $supertype end
function Rete.install(root::BasicReteNode, ::$rule_name)
join = JoinNode($rule_name_str, $rule_name())
connect_a(ensure_IsaMemoryNode(root, $a_type), join)
connect_b(ensure_IsaMemoryNode(root, $b_type), join)
connect(join, root)
end
function(::$rule_name)(node::JoinNode,
$a_var::$a_type,
$b_var::$b_type)
$body
end
end)
end
Thanks.