From cee9a0785ccbff993fbe52924299b0f065228546 Mon Sep 17 00:00:00 2001 From: Jaap de Jong Date: Thu, 29 Jun 2023 10:53:55 +0200 Subject: [PATCH 1/4] Add documentation --- src/HerbConstraints.jl | 42 +++++++++++++++++ src/localconstraints/local_forbidden.jl | 2 + src/propagatorconstraints/comesafter.jl | 34 ++++++++++++-- src/propagatorconstraints/forbidden.jl | 46 +++++++++++++++---- src/propagatorconstraints/forbidden_path.jl | 17 +++++-- src/propagatorconstraints/ordered.jl | 50 +++++++++++++++++---- 6 files changed, 168 insertions(+), 23 deletions(-) diff --git a/src/HerbConstraints.jl b/src/HerbConstraints.jl index 70bc374..61d56cf 100644 --- a/src/HerbConstraints.jl +++ b/src/HerbConstraints.jl @@ -1,9 +1,51 @@ module HerbConstraints +using ..HerbCore using ..HerbGrammar +""" + PropagatorConstraint <: Constraint + +Abstract type representing all propagator constraints. +Each propagator constraint has an implementation of a [`propagate`](@ref)-function that takes + +- the [`PropagatorConstraint`](@ref) +- a [`Grammar`](@ref) +- a [`GrammarContext`](@ref), which most importantly contains the tree and the location + in the tree where propagation should take place. +- The `domain` which the [`propagate`](@ref)-function prunes. + +The [`propagate`](@ref)-function returns a tuple containing + +- The pruned `domain` +- A list of new [`LocalConstraint`](@ref)s +""" abstract type PropagatorConstraint <: Constraint end +""" + LocalConstraint <: Constraint + +Abstract type representing all local constraints. +Local constraints correspond to a specific (partial) [`AbstractRuleNode`](@ref) tree. +Each local constraint contains a `path` to a specific location in the tree. +Each local constraint has an implementation of a [`propagate`](@ref)-function that takes + +- the [`LocalConstraint`](@ref) +- a [`Grammar`](@ref) +- a [`GrammarContext`](@ref), which most importantly contains the tree and the location + in the tree where propagation should take place. +- The `domain` which the [`propagate`](@ref)-function prunes. + +The [`propagate`](@ref)-function returns a tuple containing + +- The pruned `domain` +- A list of new [`LocalConstraint`](@ref)s + +!!! warning + By default, [`LocalConstraint`](@ref)s are only propagated once. + Constraints that have to be propagated more frequently should return + themselves in the list of new local constraints. +""" abstract type LocalConstraint <: Constraint end include("matchfail.jl") diff --git a/src/localconstraints/local_forbidden.jl b/src/localconstraints/local_forbidden.jl index ce6a17a..9e1e87f 100644 --- a/src/localconstraints/local_forbidden.jl +++ b/src/localconstraints/local_forbidden.jl @@ -1,5 +1,7 @@ """ + LocalForbidden + Forbids the a subtree that matches the MatchNode tree to be generated at the location provided by the path. Use a `Forbidden` constraint for enforcing this throughout the entire search space. diff --git a/src/propagatorconstraints/comesafter.jl b/src/propagatorconstraints/comesafter.jl index c84b90a..a073687 100644 --- a/src/propagatorconstraints/comesafter.jl +++ b/src/propagatorconstraints/comesafter.jl @@ -1,21 +1,47 @@ """ -Derivation rule can only appear in a derivation tree if the predecessors are in the path to the current node (in order) + ComesAfter <: PropagatorConstraint + +A [`ComesAfter`](@ref) constraint is a [`PropagatorConstraint`](@ref) containing the following: + +- `rule::Int`: A reference to a rule in the grammar +- `predecessors`: A list of rules in the grammar + +This [`Constraint`](@ref) enforces that the `rule` can only be applied if every rule in +`predecessors` is used in the path from the root of the tree to the current hole in the order +that they are given. Even though the rules must be in order, there might be other rules inbetween. + +For example, consider the tree `1(a, 2(b, 3(c, d))))`: + +- `ComesAfter(4, [2, 3])` would enforce that rule `4` can only be used if `2` and `3` + are used in the path from the root in that order. Therefore, only hole `c` and `d` can be filled with `4`. +- `ComesAfter(4, [1, 3])` also allows `c` and `d` to be filled, since `1` and `3` are still used in the + correct order. It does not matter that `2` is also used in the path to the root. +- `ComesAfter(4, [3, 2])` does not allow any hole to be filled with `4`, since either the predecessors are + either not in the path or in the wrong order for each of the holes. """ struct ComesAfter <: PropagatorConstraint rule::Int predecessors::Vector{Int} end + +""" + ComesAfter(rule::Int, predecessor::Int) + +Creates a [`ComesAfter`](@ref) constraint with only a single `predecessor`. +""" ComesAfter(rule::Int, predecessor::Int) = ComesAfter(rule, [predecessor]) """ -Propagates the ComesAfter constraint. -It removes the rule from the domain if the predecessors sequence is in the ancestors. + propagate(c::ComesAfter, ::Grammar, context::GrammarContext, domain::Vector{Int})::Tuple{Vector{Int}, Vector{LocalConstraint}} + +Propagates the [`ComesAfter`](@ref) [`PropagatorConstraint`](@ref). +Rules in the domain that would violate the [`ComesAfter`](@ref) constraint are removed. """ function propagate(c::ComesAfter, ::Grammar, context::GrammarContext, domain::Vector{Int})::Tuple{Vector{Int}, Vector{LocalConstraint}} ancestors = get_rulesequence(context.originalExpr, context.nodeLocation[begin:end-1]) # remove the current node from the node sequence - if c.rule in domain # if rule is in domain, check the ancestors + if c.rule ∈ domain # if rule is in domain, check the ancestors if containedin(c.predecessors, ancestors) return domain, [] else diff --git a/src/propagatorconstraints/forbidden.jl b/src/propagatorconstraints/forbidden.jl index 1d439ba..d4a2340 100644 --- a/src/propagatorconstraints/forbidden.jl +++ b/src/propagatorconstraints/forbidden.jl @@ -1,9 +1,29 @@ """ -Forbids the subtree to be generated. -A subtree is defined as a tree of `AbstractMatchNode`s. -Such a node can either be a `MatchNode`, which contains a rule index corresponding to the -rule index in the grammar of the rulenode we are trying to match. -It can also contain a `MatchVar`, which contains a single identifier symbol. + Forbidden <: PropagatorConstraint + +This [`PropagatorConstraint`] forbids any subtree that matches the pattern given by `tree` to be generated. +A pattern is a tree of [`AbstractMatchNode`](@ref)s. +Such a node can either be a [`MatchNode`](@ref), which contains a rule index corresponding to the +rule index in the [`Grammar`](@ref) and the appropriate number of children, similar to [`RuleNode`](@ref)s. +It can also contain a [`MatchVar`](@ref), which contains a single identifier symbol. +A [`MatchVar`](@ref) can match any subtree, but if there are multiple instances of the same +variable in the pattern, the matched subtrees must be identical. +Any rule in the domain that makes the match attempt successful is removed. + +For example, consider the tree `1(a, 2(b, 3(c, 4))))`: + +- `Forbidden(MatchNode(3, [MatchNode(5), MatchNode(4)]))` forbids `c` to be filled with `5`. +- `Forbidden(MatchNode(3, [MatchVar(:v), MatchNode(4)]))` forbids `c` to be filled, since a [`MatchVar`] can + match any rule, thus making the match attempt successful for the entire domain of `c`. + Therefore, this tree invalid. +- `Forbidden(MatchNode(3, [MatchVar(:v), MatchVar(:v)]))` forbids `c` to be filled with `4`, since that would + make both assignments to `v` equal, which causes a successful match. + +!!! warning + The [`Forbidden`](@ref) constraint makes use of [`LocalConstraint`](@ref)s to make sure that constraints + are also enforced in the future when the context of a [`Hole`](@ref) changes. + Therefore, [`Forbidden`](@ref) can only be used in implementations that keep track of the + [`LocalConstraint`](@ref)s and propagate them at the right moments. """ struct Forbidden <: PropagatorConstraint tree::AbstractMatchNode @@ -11,8 +31,16 @@ end """ -Propagates the Forbidden constraint. -It removes the elements from the domain that would complete the forbidden tree. + propagate(c::Forbidden, g::Grammar, context::GrammarContext, domain::Vector{Int})::Tuple{Vector{Int}, Vector{LocalConstraint}} + +Propagates the [`Forbidden`](@ref) constraint. +It removes the rules from the `domain` that would complete the forbidden tree. + +!!! warning + The [`Forbidden`](@ref) constraint makes use of [`LocalConstraint`](@ref)s to make sure that constraints + are also enforced in the future when the context of a [`Hole`](@ref) changes. + Therefore, [`Forbidden`](@ref) can only be used in implementations that keep track of the + [`LocalConstraint`](@ref)s and propagate them at the right moments. """ function propagate(c::Forbidden, g::Grammar, context::GrammarContext, domain::Vector{Int})::Tuple{Vector{Int}, Vector{LocalConstraint}} notequals_constraint = LocalForbidden(context.nodeLocation, c.tree) @@ -21,7 +49,9 @@ function propagate(c::Forbidden, g::Grammar, context::GrammarContext, domain::Ve end """ -Checks if the given tree abides the constraint. + check_tree(c::Forbidden, g::Grammar, tree::RuleNode)::Bool + +Checks if the given [`AbstractRuleNode`](@ref) tree abides the [`Forbidden`](@ref) constraint. """ function check_tree(c::Forbidden, g::Grammar, tree::RuleNode)::Bool vars = Dict{Symbol, AbstractRuleNode}() diff --git a/src/propagatorconstraints/forbidden_path.jl b/src/propagatorconstraints/forbidden_path.jl index cd8c6d8..4f160d6 100644 --- a/src/propagatorconstraints/forbidden_path.jl +++ b/src/propagatorconstraints/forbidden_path.jl @@ -1,6 +1,17 @@ """ -Forbids the derivation specified as a path in an expression tree. -The rules need to be in the exact order + ForbiddenPath <: PropagatorConstraint + +A [`PropagatorConstraint`] that forbids a certain derivation sequence. +`sequence` defines the forbidden sequence. +Each rule that would complete the sequence when expanding a [`Hole`](@ref) in an +[`AbstractRuleNode`](@ref) tree is removed from the domain. +The derivation sequence is the path from the root to the hole. + +For example, consider the tree `1(a, 2(b, 3(c, d))))`: + +- `ForbiddenPath([1, 2, 4])` enforces that rule `4` cannot be applied at `b`, + since it completes the sequence. However, it can be applied at `a`, `c` and `d`. +- `ForbiddenPath([3, 1])` enforces that rule `1` cannot be applied at `c` or `d`. """ struct ForbiddenPath <: PropagatorConstraint sequence::Vector{Int} @@ -8,7 +19,7 @@ end """ -Propagates the ForbiddenPath constraint. +Propagates the [`ForbiddenPath`](@ref) constraint. It removes the elements from the domain that would complete the forbidden sequence. """ function propagate(c::ForbiddenPath, ::Grammar, context::GrammarContext, domain::Vector{Int})::Tuple{Vector{Int}, Vector{LocalConstraint}} diff --git a/src/propagatorconstraints/ordered.jl b/src/propagatorconstraints/ordered.jl index ef7e0ab..e8c9632 100644 --- a/src/propagatorconstraints/ordered.jl +++ b/src/propagatorconstraints/ordered.jl @@ -1,12 +1,35 @@ """ -Enforces a specific order in MatchVar assignments in the grammar -The tree is defined as a tree of `AbstractMatchNode`s. -Such a node can either be a `MatchNode`, which contains a rule index corresponding to the -rule index in the grammar of the rulenode we are trying to match. -It can also contain a `MatchVar`, which contains a single identifier symbol. -The order defines in what order the variable assignments should be. + Ordered <: PropagatorConstraint + +A [`PropagatorConstraint`](@ref) that enforces a specific order in [`MatchVar`](@ref) +assignments in the pattern defined by `tree`. +A pattern is a tree of [`AbstractMatchNode`](@ref)s. +Such a node can either be a [`MatchNode`](@ref), which contains a rule index corresponding to the +rule index in the [`Grammar`](@ref) and the appropriate number of children, similar to [`RuleNode`](@ref)s. +It can also contain a [`MatchVar`](@ref), which contains a single identifier symbol. +A [`MatchVar`](@ref) can match any subtree, but if there are multiple instances of the same +variable in the pattern, the matched subtrees must be identical. + +The `order` defines an order between the variable assignments. For example, if the order is `[x, y]`, the constraint will require the assignment to `x` to be less than or equal to the assignment to `y`. +The order is recursively defined by [`RuleNode`](@ref) indices. +For more information, see [`Base.isless(rn₁::AbstractRuleNode, rn₂::AbstractRuleNode)`](@ref). + +For example, consider the tree `1(a, 2(b, 3(c, 4))))`: + +- `Ordered(MatchNode(3, [MatchVar(:v), MatchVar(:w)]), [:v, :w])` removes every rule + with an index of 5 or greater from the domain of `c`, since that would make the index of the + assignment to `v` greater than the index of the assignment to `w`, violating the order. +- `Ordered(MatchNode(3, [MatchVar(:v), MatchVar(:w)]), [:w, :v])` removes every rule + with an index of 4 or less from the domain of `c`, since that would make the index of the + assignment to `v` less than the index of the assignment to `w`, violating the order. + +!!! warning + The [`Ordered`](@ref) constraint makes use of [`LocalConstraint`](@ref)s to make sure that constraints + are also enforced in the future when the context of a [`Hole`](@ref) changes. + Therefore, [`Ordered`](@ref) can only be used in implementations that keep track of the + [`LocalConstraint`](@ref)s and propagate them at the right moments. """ struct Ordered <: PropagatorConstraint tree::AbstractMatchNode @@ -15,7 +38,16 @@ end """ -Propagates the Ordered constraint. + propagate(c::Ordered, g::Grammar, context::GrammarContext, domain::Vector{Int})::Tuple{Vector{Int}, Vector{LocalConstraint}} + +Propagates the [`Ordered`](@ref) constraint. +Any rule that violates the order as defined by the contraint is removed from the `domain`. + +!!! warning + The [`Ordered`](@ref) constraint makes use of [`LocalConstraint`](@ref)s to make sure that constraints + are also enforced in the future when the context of a [`Hole`](@ref) changes. + Therefore, [`Ordered`](@ref) can only be used in implementations that keep track of the + [`LocalConstraint`](@ref)s and propagate them at the right moments. """ function propagate(c::Ordered, g::Grammar, context::GrammarContext, domain::Vector{Int})::Tuple{Vector{Int}, Vector{LocalConstraint}} ordered_constraint = LocalOrdered(context.nodeLocation, c.tree, c.order) @@ -24,7 +56,9 @@ function propagate(c::Ordered, g::Grammar, context::GrammarContext, domain::Vect end """ -Checks if the given tree abides the constraint. + check_tree(c::Ordered, g::Grammar, tree::RuleNode)::Bool + +Checks if the given [`AbstractRuleNode`](@ref) tree abides the [`Ordered`](@ref) constraint. """ function check_tree(c::Ordered, g::Grammar, tree::RuleNode)::Bool vars = Dict{Symbol, AbstractRuleNode}() From dc8a932174fa50a728bcaf0f6b4c33e24467e518 Mon Sep 17 00:00:00 2001 From: Tilman Hinnerichs Date: Fri, 30 Jun 2023 22:45:36 +0200 Subject: [PATCH 2/4] Update changes to docs --- src/context.jl | 8 ++++++- src/matchfail.jl | 4 +++- src/matchnode.jl | 57 ++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/src/context.jl b/src/context.jl index 2eaeac1..d8571c1 100644 --- a/src/context.jl +++ b/src/context.jl @@ -1,4 +1,6 @@ """ + mutable struct GrammarContext + Structure used to track the context. Contains: - the expression being modified @@ -15,6 +17,8 @@ end GrammarContext(originalExpr::AbstractRuleNode) = GrammarContext(originalExpr, [], []) """ + addparent!(context::GrammarContext, parent::Int) + Adds a parent to the context. The parent is defined by the grammar rule id. """ @@ -24,10 +28,12 @@ end """ + copy_and_insert(old_context::GrammarContext, parent::Int) + Copies the given context and insert the parent in the node location. """ function copy_and_insert(old_context::GrammarContext, parent::Int) new_context = GrammarContext(old_context.originalExpr, deepcopy(old_context.nodeLocation), deepcopy(old_context.constraints)) push!(new_context.nodeLocation, parent) new_context -end \ No newline at end of file +end diff --git a/src/matchfail.jl b/src/matchfail.jl index 045f2e7..39dbeb8 100644 --- a/src/matchfail.jl +++ b/src/matchfail.jl @@ -1,8 +1,10 @@ """ + @enum MatchFail hardfail softfail + This enum is used for distinguishing between two types of failures when trying to match a `RuleNode` either with another `RuleNode` or with an `AbstractMatchNode` - Hardfail means that there is no match, and there is no way to fill in the holes to get a match. - Softfail means that there is no match, but there *might* be a way to fill the holes that results in a match. """ -@enum MatchFail hardfail softfail \ No newline at end of file +@enum MatchFail hardfail softfail diff --git a/src/matchnode.jl b/src/matchnode.jl index 4d2e007..e6551ab 100644 --- a/src/matchnode.jl +++ b/src/matchnode.jl @@ -1,4 +1,6 @@ """ + abstract type AbstractMatchNode + Tree structure to which rulenode trees can be matched. Consists of MatchNodes, which can match a specific RuleNode, and MatchVars, which is a variable that can be filled in with any RuleNode. @@ -6,6 +8,8 @@ and MatchVars, which is a variable that can be filled in with any RuleNode. abstract type AbstractMatchNode end """ + struct MatchNode <: AbstractMatchNode + Match a specific rulenode, where the grammar rule index is `rule_ind` and `children` matches the children of the RuleNode. Example usage: @@ -22,6 +26,8 @@ end MatchNode(rule_ind::Int) = MatchNode(rule_ind, []) """ + struct MatchVar <: AbstractMatchNode + Matches anything and assigns it to a variable. The `ForbiddenTree` constraint will not match if identical variable symbols match to different trees. Example usage: @@ -34,6 +40,11 @@ struct MatchVar <: AbstractMatchNode var_name::Symbol end +""" + Base.show(io::IO, node::MatchNode; separator=",", last_child::Bool=true) + +Prints a found [`MatchNode`](@ref) given an and the respective children to `IO`. +""" function Base.show(io::IO, node::MatchNode; separator=",", last_child::Bool=true) print(io, node.rule_ind) if !isempty(node.children) @@ -47,6 +58,11 @@ function Base.show(io::IO, node::MatchNode; separator=",", last_child::Bool=true end end +""" + Base.show(io::IO, node::MatchVar; separator=",", last_child::Bool=true) + +Prints a matching variable assignment described by [`MatchVar`](@ref) to `IO`. +""" function Base.show(io::IO, node::MatchVar; separator=",", last_child::Bool=true) print(io, node.var_name) if !last_child @@ -62,8 +78,10 @@ contains_var(mn::MatchNode, var::Symbol) = any(contains_var(c, var) for c ∈ mn """ + matchnode2expr(pattern::MatchNode, grammar::Grammar) + Converts a MatchNode tree into a Julia expression. -This is primarily useful for pretty-printing a pattern. +This is primarily useful for pretty-printing a pattern. Returns the corresponding expression. """ function matchnode2expr(pattern::MatchNode, grammar::Grammar) root = deepcopy(grammar.rules[pattern.rule_ind]) @@ -73,10 +91,21 @@ function matchnode2expr(pattern::MatchNode, grammar::Grammar) return root end +""" + matchnode2expr(pattern::MatchVar, grammar::Grammar) + +Converts a MatchVar into an expression by returning the variable directly. +This is primarily useful for pretty-printing a pattern. +""" function matchnode2expr(pattern::MatchVar, ::Grammar) return pattern.var_name end +""" + _matchnode2expr(expr::Expr, pattern::MatchNode, grammar::Grammar, j=0) + +Internal function for [`matchnode2expr`](@ref), recursively iterating over a matched pattern and converting it to an expression. This is primarily useful for pretty-printing a pattern. Returns the corresponding expression and the current child index. +""" function _matchnode2expr(expr::Expr, pattern::MatchNode, grammar::Grammar, j=0) for (k,arg) ∈ enumerate(expr.args) if isa(arg, Expr) @@ -96,6 +125,12 @@ function _matchnode2expr(expr::Expr, pattern::MatchNode, grammar::Grammar, j=0) return expr, j end + +""" + _matchnode2expr(expr::Expr, pattern::MatchVar, grammar::Grammar, j=0) + +Internal function for [`matchnode2expr`](@ref), recursively iterating over a matched variable and converting it to an expression. This is primarily useful for pretty-printing a pattern. Returns the corresponding expression and the current child index. +""" function _matchnode2expr(expr::Expr, pattern::MatchVar, grammar::Grammar, j=0) for (k,arg) ∈ enumerate(expr.args) if isa(arg, Expr) @@ -111,10 +146,12 @@ function _matchnode2expr(expr::Expr, pattern::MatchVar, grammar::Grammar, j=0) return expr, j end -function _matchnode2expr(typ::Symbol, pattern::MatchVar, grammar::Grammar, j=0) - return pattern.var_name, j -end +""" + _matchnode2expr(typ::Symbol, pattern::MatchNode, grammar::Grammar, j=0) + +Internal function for [`matchnode2expr`](@ref), returning the matched translated symbol. This is primarily useful for pretty-printing a pattern. Returns the corresponding expression, i.e. the variable name and the current child index. +""" function _matchnode2expr(typ::Symbol, pattern::MatchNode, grammar::Grammar, j=0) retval = typ if haskey(grammar.bytype, typ) @@ -130,3 +167,15 @@ function _matchnode2expr(typ::Symbol, pattern::MatchNode, grammar::Grammar, j=0) end return retval, j end + + +""" + _matchnode2expr(typ::Symbol, pattern::MatchVar, grammar::Grammar, j=0) + +Internal function for [`matchnode2expr`](@ref). This is primarily useful for pretty-printing a pattern. Returns the corresponding expression, i.e. the variable name and the current child index. +""" +function _matchnode2expr(typ::Symbol, pattern::MatchVar, grammar::Grammar, j=0) + return pattern.var_name, j +end + + From 7ab80f5ae64994ef6fefc22a51075d33c569f491 Mon Sep 17 00:00:00 2001 From: Tilman Hinnerichs Date: Fri, 30 Jun 2023 23:03:54 +0200 Subject: [PATCH 3/4] Update changes to docs --- src/HerbConstraints.jl | 2 +- src/patternmatch.jl | 43 +++++++++++++++++++++++++++++++----------- src/rulenodematch.jl | 4 +++- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/HerbConstraints.jl b/src/HerbConstraints.jl index 61d56cf..686537c 100644 --- a/src/HerbConstraints.jl +++ b/src/HerbConstraints.jl @@ -23,7 +23,7 @@ The [`propagate`](@ref)-function returns a tuple containing abstract type PropagatorConstraint <: Constraint end """ - LocalConstraint <: Constraint + abstract type LocalConstraint <: Constraint Abstract type representing all local constraints. Local constraints correspond to a specific (partial) [`AbstractRuleNode`](@ref) tree. diff --git a/src/patternmatch.jl b/src/patternmatch.jl index acacae4..0bb2bca 100644 --- a/src/patternmatch.jl +++ b/src/patternmatch.jl @@ -1,7 +1,8 @@ """ -Tries to match RuleNode `rn` with MatchNode `mn` and fill in -the domain of the hole at `hole_location`. + _pattern_match_with_hole(rn::RuleNode, mn::MatchNode, hole_location::Vector{Int}, vars::Dict{Symbol, AbstractRuleNode})::Union{Int, Symbol, MatchFail, Tuple{Symbol, Vector{Int}}} + +Tries to match [`RuleNode`](@ref) `rn` with [`MatchNode`](@ref) `mn` and fill in the domain of the hole at `hole_location`. Returns if match is successful either: - The id for the node which fills the hole - A symbol for the variable that fills the hole @@ -53,7 +54,12 @@ function _pattern_match_with_hole( end end -# Matching RuleNode with MatchVar +""" + _pattern_match_with_hole(rn::RuleNode, mv::MatchVar, hole_location::Vector{Int}, vars::Dict{Symbol, AbstractRuleNode})::Union{Int, Symbol, MatchFail, Tuple{Symbol, Vector{Int}}} + +Tries to match [`RuleNode`](@ref) `rn` with [`MatchVar`](@ref) `mv` and fill in the domain of the hole at `hole_location`. +If the variable name is already assigned in `vars`, the rulenode is matched with the hole. Otherwise the variable and the hole location are returned. +""" function _pattern_match_with_hole( rn::RuleNode, mv::MatchVar, @@ -68,12 +74,22 @@ function _pattern_match_with_hole( end end -# Matching Hole with MatchNode +""" + _pattern_match_with_hole(::Hole, mn::MatchNode, hole_location::Vector{Int}, ::Dict{Symbol, AbstractRuleNode})::Union{Int, Symbol, MatchFail, Tuple{Symbol, Vector{Int}}} + +Matches the [`Hole`](@ref) with the given [`MatchNode`](@ref). + +TODO check this behaviour? +""" _pattern_match_with_hole(::Hole, mn::MatchNode, hole_location::Vector{Int}, ::Dict{Symbol, AbstractRuleNode} )::Union{Int, Symbol, MatchFail, Tuple{Symbol, Vector{Int}}} = hole_location == [] && mn.children == [] ? mn.rule_ind : softfail -# Matching Hole with MatchVar +""" + _pattern_match_with_hole(::Hole, mn::MatchNode, hole_location::Vector{Int}, ::Dict{Symbol, AbstractRuleNode})::Union{Int, Symbol, MatchFail, Tuple{Symbol, Vector{Int}}} + +Matches the [`Hole`](@ref) with the given [`MatchVar`](@ref), similar to [`_pattern_match_with_hole`](@ref). +""" function _pattern_match_with_hole(h::Hole, mv::MatchVar, hole_location::Vector{Int}, vars::Dict{Symbol, AbstractRuleNode} )::Union{Int, Symbol, MatchFail, Tuple{Symbol, Vector{Int}}} @assert hole_location == [] @@ -85,13 +101,13 @@ function _pattern_match_with_hole(h::Hole, mv::MatchVar, hole_location::Vector{I end end -# Matching RuleNode with MatchNode """ -Tries to match RuleNode `rn` with MatchNode `mn`. + _pattern_match(rn::RuleNode, mn::MatchNode, vars::Dict{Symbol, AbstractRuleNode})::Union{Nothing, MatchFail} + +Tries to match [`RuleNode`](@ref) `rn` with [`MatchNode`](@ref) `mn`. Modifies the variable assignment dictionary `vars`. -Returns nothing if the match is successful. -If the match is unsuccessful, it returns whether it -is a softfail or hardfail (see MatchFail docstring) +Returns `nothing` if the match is successful. +If the match is unsuccessful, it returns whether it is a softfail or hardfail (see [`MatchFail`](@ref) docstring) """ function _pattern_match(rn::RuleNode, mn::MatchNode, vars::Dict{Symbol, AbstractRuleNode})::Union{Nothing, MatchFail} if rn.ind ≠ mn.rule_ind || length(rn.children) ≠ length(mn.children) @@ -109,7 +125,12 @@ function _pattern_match(rn::RuleNode, mn::MatchNode, vars::Dict{Symbol, Abstract return nothing end -# Matching RuleNode with MatchVar +""" + _pattern_match(rn::RuleNode, mv::MatchVar, vars::Dict{Symbol, AbstractRuleNode})::Union{Nothing, MatchFail} + +Matching [`RuleNode`](@ref) `rn` with [`MatchVar`](@ref) `mv`. If the variable is already assigned, the rulenode is matched with the specific variable value. Returns `nothing` if the match is succesful. +If the match is unsuccessful, it returns whether it is a softfail or hardfail (see [`MatchFail`](@ref) docstring) +""" function _pattern_match(rn::RuleNode, mv::MatchVar, vars::Dict{Symbol, AbstractRuleNode})::Union{Nothing, MatchFail} if mv.var_name ∈ keys(vars) # If the assignments are unequal, the match is unsuccessful diff --git a/src/rulenodematch.jl b/src/rulenodematch.jl index 0b9b6b4..4948cdd 100644 --- a/src/rulenodematch.jl +++ b/src/rulenodematch.jl @@ -1,5 +1,7 @@ """ + _rulenode_match_with_hole(rn₁::RuleNode, rn₂::RuleNode, hole_location::Vector{Int})::Union{Int, MatchFail} + Matches two rulenodes. Returns how to fill in the hole in rn₁ to make it match rn₂ if: - rn₁ has a single hole at the provided location @@ -55,4 +57,4 @@ end _rulenode_match(rn::RuleNode, h::Hole)::Union{Nothing, MatchFail} = h.domain[rn.ind] ? softfail : hardfail _rulenode_match(h::Hole, rn::RuleNode)::Union{Nothing, MatchFail} = h.domain[rn.ind] ? softfail : hardfail # TODO: It might be worth checking if domains don't overlap and getting a hardfail -_rulenode_match(::Hole, ::Hole)::Union{Nothing, MatchFail} = softfail \ No newline at end of file +_rulenode_match(::Hole, ::Hole)::Union{Nothing, MatchFail} = softfail From 66577d3b6aba8eb00013692854433de5913cdbdc Mon Sep 17 00:00:00 2001 From: Tilman Hinnerichs Date: Wed, 16 Aug 2023 10:24:39 +0200 Subject: [PATCH 4/4] Add Readme for HerbConstraints --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 74ece0d..462e31b 100644 --- a/README.md +++ b/README.md @@ -1 +1,9 @@ -# Constraints.jl \ No newline at end of file +# HerbConstraints.jl + +This package contains the functionality to formulate, represent and use constraints within `Herb`. + +Constraints are formulated as context-sensitive grammars. Further, they are divided into global and local constraints, that may be partially mapped to each other. + +`HerbConstraints.jl` provides functionality to propagate constraints and match constraint patterns efficiently. Further, provides error handling through `matchfail` and `matchnode`. + +