Patterns
Patterns are defined with the @pattern macro.
help?> @pattern
@pattern(expr)
Create a Pattern from the given expression. Special syntax can be escaped by wrapping it in an @esc macro. Match and fail conditions are defined with the @when and @fail macros respectively.
@esc usage:
• @esc(ex): Escape everything inside ex.
• @esc(ex, depth): Escape ex only up to depth.
@when usage:
• @when(pattern_vars, condition): Match only if condition is satisfied. The pattern variables found in condition must be given as a vector of Symbols.
@fail usage:
• @fail(pattern_vars, condition, msg): Fail with the message msg if condition is satisfied. The pattern variables found in condition must be given as a vector of Symbols.
Examples
≡≡≡≡≡≡≡≡
julia> @pattern a + b # Simple expression with no pattern variables.
Pattern:
[call-i]
a :: Identifier
+ :: Identifier
b :: Identifier
julia> assign_to_x = @pattern begin
{x:::assign} # Pattern variable matching an assignment.
@fail [:x] x.lhs.name == "x" "assignment to x" # The matching fails if the rhs variable's name is "x".
end
Pattern:
[~and]
x:::assign :: ~var
[~fail]
[vect]
[quote-:]
x :: Identifier
[call-i]
[.]
[.]
x :: Identifier
lhs :: Identifier
name :: Identifier
== :: Identifier
[string]
"x" :: String
julia> syntax_match(assign_to_x, JuliaSyntax.parsestmt(JuliaSyntax.SyntaxNode, "x = 2"))
MatchFail("assignment to x")
julia> const_reassign = @pattern begin
const {a:::identifier} = {_}
{_}...
const {a:::identifier} = {_} # Reassignment of `const`.
end
Pattern:
[toplevel]
[const]
[=]
a:::identifier :: ~var
_:::expr :: ~var
[~rep]
_:::expr :: ~var
[const]
[=]
a:::identifier :: ~var
_:::expr :: ~var
julia> syntax_match(const_reassign, parseall(SyntaxNode,
"""
const x = 2
other_ex
const x = 3
"""))
BindingSet @ 0:0 with 1 entries:
:a => Binding:
Name: :a
Bound source: x @ 3:7
Ellipsis depth: 0
Sub-bindings:
BindingSet @ 0:0 with 1 entries:
:_id => Binding:
Name: :_id
Bound source: x @ 3:7
Ellipsis depth: 0
Sub-bindings:
BindingSet @ 0:0 with 0 entries
julia> esc_pattern = @pattern @esc(x...)
Pattern:
[...]
x :: Identifier
julia> syntax_match(esc_pattern, parsestmt(SyntaxNode, "x..."))
BindingSet @ 0:0 with 0 entries
julia> esc1_pattern = @pattern @esc({x}..., 1)
Pattern:
[...]
x:::expr :: ~var
julia> syntax_match(esc1_pattern, parsestmt(SyntaxNode, "x..."))
BindingSet @ 0:0 with 1 entries:
:x => Binding:
Name: :x
Bound source: x @ 1:1
Ellipsis depth: 0
Sub-bindings:
BindingSet @ 0:0 with 0 entries
Note: @when, @fail and @esc macros only exist inside @pattern bodies.
julia> @when [] :false
ERROR: LoadError: UndefVarError: `@when` not defined in `Main`
Suggestion: check for spelling errors or missing imports.
in expression starting at ...
julia> @fail [] :false ""
ERROR: LoadError: UndefVarError: `@fail` not defined in `Main`
Suggestion: check for spelling errors or missing imports.
in expression starting at ...
julia> @esc
ERROR: LoadError: UndefVarError: `@esc` not defined in `Main`
Suggestion: check for spelling errors or missing imports.
Hint: a global variable of this name also exists in MacroTools.
in expression starting at ...A simple pattern to experiment with is the match-all pattern:
julia> expr = @pattern {x}
Pattern:
x:::expr :: ~varThis pattern matches any Julia expression. It has one pattern variable: x. The pretty print tells us that the pattern consists of a ~var node which constraints x to be an expr. ~var and expr are explained later (see Pattern forms and Syntax classes).
Patterns can be matched against Julia expressions.
julia> using JuliaSyntax: parsestmt, SyntaxNode
julia> syntax_match(expr, parsestmt(SyntaxNode, "1 + 2"))
BindingSet @ 0:0 with 1 entry:
:x => Binding:
Name: :x
Bound source: (call-i 1 + 2) @ 1:1
Ellipsis depth: 0
Sub-bindings:
BindingSet @ 0:0 with 0 entries
julia> syntax_match(expr, parsestmt(SyntaxNode, "f(x::Int) = x * 2"))
BindingSet @ 0:0 with 1 entry:
:x => Binding:
Name: :x
Bound source: (function-= (call f (::-i x Int)) (call-i x * 2)) @ 1:1
Ellipsis depth: 0
Sub-bindings:
BindingSet @ 0:0 with 0 entriesThe result of a successful match is a BindingSet — a dictionary with bound pattern variables (Bindings). A Binding contains information about a pattern variable that matched (part of) the source, as well as the matched source node.
In the second example above, the pretty print tells us that the pattern variable x was bound to the function definition found at line 1, column 1 in the source. The fifth line of the pretty print tells us that x has ellipsis depth 0. This is because x is not surrounded by any ellipses (... – see Ellipses).
syntax_match matches a pattern with a source node exactly. It may also be useful to extract all the pattern's occurences in a source node. syntax_match_all does just that:
julia> syntax_match_all(expr, parsestmt(SyntaxNode, "1 + 2"))
MatchResults with 4 matches and 0 failures:
Matches:
BindingSet(:x => Binding(:x, (call-i 1 + 2) @ 1:1, BindingSet()))
BindingSet(:x => Binding(:x, 1 @ 1:1, BindingSet()))
BindingSet(:x => Binding(:x, + @ 1:3, BindingSet()))
BindingSet(:x => Binding(:x, 2 @ 1:5, BindingSet()))