Skip to content

Commit

Permalink
Rule groups. Why is install different from Rete.install?
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkNahabedian committed Feb 28, 2024
1 parent 4fc12d9 commit a74a5bd
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 7 deletions.
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ authors = ["MarkNahabedian <[email protected]> and contributors"]
version = "1.0.0-DEV"

[deps]
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"

Expand Down
2 changes: 2 additions & 0 deletions src/Rete.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module Rete

using InteractiveUtils

include("node_abstraction.jl")
include("basic_node.jl")
include("memory_nodes.jl")
Expand Down
20 changes: 19 additions & 1 deletion src/node_abstraction.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export AbstractReteNode, AbstractMemoryNode, AbstractReteJoinNode
export label, inputs, outputs, connect, emit, receive
export label, inputs, outputs, connect, emit, receive, install


"""
Expand Down Expand Up @@ -80,3 +80,21 @@ function connect(from::AbstractReteNode, to::AbstractReteNode)
nothing
end



"""
install(root, rule)
Installs the rule or rule group into the Rete rooted at `root`.
"""
function install end

function install(root::AbstractReteNode, rule_group)
if isconcretetype(rule_group)
install(root, rule_group())
else
for r in subtypes(rule_group)
install(root, r)
end
end
end
16 changes: 14 additions & 2 deletions src/rules.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,24 @@ Defines a rule named `Rulename`. A singleton type named `Rulename`
will be defined to represent the rule. An `install` method is defined
which can be used to add the nodes necessary to implement the rule to
a Rete.
The default supertype of a rule struct is `Rule`. When it is
desirable to group rules together, one can define an abstract type
that is a type descendant of Rule and use that as a dotted prefix to
`RuleName`. The the `RuleName` in the @rule invocation is
`MyGroup.MyRule` then the supertype of MyRule will be MyGroup, rather
than Rule.
"""
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], :(::))
Expand All @@ -39,9 +51,9 @@ macro rule(call, body)
end

esc(quote
struct $rule_name <: Rule end
struct $rule_name <: $supertype end

function install(root::BasicReteNode, ::$rule_name)
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)
Expand Down
9 changes: 5 additions & 4 deletions test/rule_example_2.jl
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@

@rule JoinSewquentialLetterDigram2(a::Char, b::Char) begin
@rule JoinSequentialLetterDigram2(a::Char, b::Char) begin
if codepoint(a) + 1 == codepoint(b)
emit((a, b))
end
end

@rule JoinSewquentialLetterTrigram2(p::Tuple{Char, Char}, c::Char) begin
@rule JoinSequentialLetterTrigram2(p::Tuple{Char, Char}, c::Char) begin
if codepoint(p[2]) + 1 == codepoint(c)
emit(p[1] * p[2] * c)
end
end

@testset "rule example 2" begin
root = BasicReteNode("root")
install(root, JoinSewquentialLetterDigram2())
install(root, JoinSewquentialLetterTrigram2())
Rete.install(root, JoinSequentialLetterDigram2())
Rete.install(root, JoinSequentialLetterTrigram2())
conclusions = ensure_IsaMemoryNode(root, String)
@test length(root.inputs) == 2
@test length(root.outputs) == 3
Expand All @@ -24,3 +24,4 @@ end
@test conclusions.memory ==
Set{String}(["abc", "bcd", "cde", "def", "efg"])
end

31 changes: 31 additions & 0 deletions test/rule_grouping.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

abstract type RuleGroup1 <: Rule end

@rule RuleGroup1.AdjacentLetters(a::Char, b::Char) begin
if codepoint(a) + 1 == codepoint(b)
emit((a, b))
end
end

@rule RuleGroup1.DigitBetweenLetters(letters::Tuple{Char, Char},
digit::Int) begin
emit(letters[1] * string(digit) * letters[2])
end

@testset "rule grouping" begin
root = BasicReteNode("root")
Rete.install(root, RuleGroup1)
conclusions = ensure_IsaMemoryNode(root, String)
for c in 'a':'c'
receive(root, c)
end
for i in 1:3
receive(root, i)
end
@test conclusions.memory ==
Set{String}([
"a1b", "a2b", "a3b",
"b1c", "b2c", "b3c"
])
end

1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,5 @@ end

include("rule_example_1.jl")
include("rule_example_2.jl")
include("rule_grouping.jl")

0 comments on commit a74a5bd

Please sign in to comment.