ExAST.Pattern (ExAST v0.11.0)

Copy Markdown View Source

AST pattern matching with captures.

Patterns are valid Elixir syntax where:

  • name, expr — capture the matched AST node
  • _ or _name — wildcard, matches anything, not captured
  • ... — matches zero or more nodes (args, list items, block statements)
  • Structs and maps match partially (only specified keys must be present)
  • Pipes are normalized (data |> Enum.map(f) matches Enum.map(data, f))
  • Multi-statement patterns match contiguous sequences in blocks

Repeated variable names require the same value at every position.

Patterns can be given as strings or as quoted expressions:

Pattern.match(node, "IO.inspect(_)")
Pattern.match(node, quote(do: IO.inspect(_)))

Use ... for variable-arity matching:

Pattern.match(node, "IO.inspect(...)")       # any arity
Pattern.match(node, "foo(first, ...)")        # 1+ args, capture first
Pattern.match(node, "def foo(_) do ... end")  # any body

Summary

Functions

Matches an AST node against a pattern.

Matches an AST node against an already-parsed pattern AST.

Finds all contiguous subsequences of nodes matching pattern_asts.

Returns true if the pattern contains multiple statements (separated by ; or newlines), enabling sequential matching.

Returns the individual pattern ASTs from a (possibly multi-node) pattern.

Substitutes captured values into a replacement template AST.

Types

captures()

@type captures() :: %{required(atom()) => term()}

pattern()

@type pattern() :: String.t() | Macro.t()

Functions

match(node, pattern)

@spec match(Macro.t(), pattern()) :: {:ok, captures()} | :error

Matches an AST node against a pattern.

The pattern can be a string or a quoted expression. Returns {:ok, captures} on match, :error otherwise.

Alias directives found in the surrounding AST can be expanded before matching, so alias AshPhoenix.Form followed by Form.for_update(...) matches AshPhoenix.Form.for_update(...).

match(node, pattern, alias_env)

@spec match(Macro.t(), pattern(), %{optional(atom()) => [atom()]}) ::
  {:ok, captures()} | :error

match_ast(node, pattern_ast)

@spec match_ast(Macro.t(), Macro.t()) :: {:ok, captures()} | :error

Matches an AST node against an already-parsed pattern AST.

Equivalent to match/2 with a quoted pattern, kept for backward compatibility.

match_ast(node, pattern_ast, alias_env)

@spec match_ast(Macro.t(), Macro.t(), %{optional(atom()) => [atom()]}) ::
  {:ok, captures()} | :error

match_sequences(nodes, pattern_asts)

@spec match_sequences([Macro.t()], [Macro.t()]) :: [{captures(), Range.t()}]

Finds all contiguous subsequences of nodes matching pattern_asts.

Returns a list of {captures, start_index..end_index} tuples. Captures are accumulated across all matched nodes and must be consistent.

match_sequences(nodes, pattern_asts, alias_env)

@spec match_sequences([Macro.t()], [Macro.t()], %{optional(atom()) => [atom()]}) :: [
  {captures(), Range.t()}
]

multi_node?(pattern)

@spec multi_node?(pattern()) :: boolean()

Returns true if the pattern contains multiple statements (separated by ; or newlines), enabling sequential matching.

pattern_nodes(pattern)

@spec pattern_nodes(pattern()) :: [Macro.t()]

Returns the individual pattern ASTs from a (possibly multi-node) pattern.

substitute(template_ast, captures)

@spec substitute(Macro.t(), captures()) :: Macro.t()

Substitutes captured values into a replacement template AST.

Variables in the template that match capture names are replaced with the captured AST nodes.