10.12. AST pattern matching

AST pattern matching via reverse reification. Matches expressions and blocks against structural patterns using the same tag system as qmacro but in reverse: $e clones a matched expression, $v extracts a constant value, $i extracts an identifier name, $c extracts a call name, $f extracts a field name, $b captures a statement range, $t captures a type, and $a captures remaining arguments.

10.12.1. Enumerations

QMatchError
Values:
  • ok = 0 - Match succeeded.

  • rtti_mismatch = 1 - Expression RTTI type does not match the pattern.

  • field_mismatch = 2 - A named field (name, op, etc.) differs between pattern and expression.

  • const_type_mismatch = 3 - $v extraction failed — expression is not the expected constant type.

  • type_mismatch = 4 - TypeDecl comparison failed — types do not match.

  • list_length = 5 - Block statement count mismatch (after wildcard resolution).

  • wildcard_not_found = 6 - Wildcard could not find a matching statement.

  • null_expression = 7 - A null expression was passed where a non-null was expected.

10.12.2. Structures

QMatchResult
Fields:
  • matched : bool - True if the pattern matched the expression.

  • error : QMatchError - Error code describing why the match failed (ok when matched).

  • expr : Expression? - Pointer to the expression where matching failed (null on success).

10.12.3. Call macros

qmatch

Function annotation qmatch

qmatch_block

Function annotation qmatch_block

qmatch_function

qmatch_function(func, block_pattern) — match function body against block pattern.

10.12.4. Match execution

qm_run(expr: Expression?; blk: block<(var arg:Expression?):QMatchResult> ): QMatchResult

Invoke a matching block on the expression. Used internally by the qmatch macro.

Arguments:
qm_run_function(func: FunctionPtr; blk: block<(var body:Expression?;var func_ptr:Function?):QMatchResult> ): QMatchResult

Run matching block on a function, passing both body and Function pointer.

Arguments:

10.12.5. Call matching

match_call_in_linq(expr: Expression?; name: string ): ExprCall?

Match an ExprCall to name in the linq module. Thin wrapper on match_call_in_module kept for readability at the heavy-call-site files (linq_fold, sqlite_linq, decs_boost).

Arguments:
match_call_in_module(expr: Expression?; name: string; modName: string ): ExprCall?

Match an ExprCall to name in module modName, transparent to generic instantiation. Returns the call on match; null otherwise. func.fromGeneric is consulted as a fallback so generic functions match their authored (name, modName), not the instantiation’s mangled name.

Arguments:
  • expr : Expression?

  • name : string

  • modName : string

10.12.6. Result builders

qm_fail_const(expr: Expression? ): QMatchResult

Return a failure — constant type does not match $v extraction target.

Arguments:
qm_fail_field(expr: Expression? ): QMatchResult

Return a failure — a named field differs between pattern and expression.

Arguments:
qm_fail_list(expr: Expression? ): QMatchResult

Return a failure — block statement count mismatch.

Arguments:
qm_fail_null(): QMatchResult

Return a failure — null expression encountered.

qm_fail_rtti(expr: Expression? ): QMatchResult

Return a failure — expression RTTI type does not match the pattern.

Arguments:
qm_fail_type(expr: Expression? ): QMatchResult

Return a failure — TypeDecl comparison failed.

Arguments:
qm_fail_wildcard(expr: Expression? ): QMatchResult

Return a failure — wildcard could not find a matching statement.

Arguments:
qm_ok(): QMatchResult

Return a successful match result.

10.12.7. Value extraction

extract_const_string(e: Expression? ): tuple<bool;string>

For ExprConstString constants return (true, value); otherwise (false, ""). Use to consume compile-time string literals threaded through macro args before splicing.

Arguments:

10.12.7.1. qm_extract

qm_extract(expr: Expression?; val: bool& ): QMatchResult

Extract bool from ExprConstBool. Fails if wrong type.

Arguments:
qm_extract(expr: Expression?; val: double& ): QMatchResult
qm_extract(expr: Expression?; val: float& ): QMatchResult
qm_extract(expr: Expression?; val: int& ): QMatchResult
qm_extract(expr: Expression?; val: int64& ): QMatchResult
qm_extract(expr: Expression?; val: string& ): QMatchResult
qm_extract(expr: Expression?; val: uint& ): QMatchResult

10.12.8. Name and type extraction

qm_call_name_matches(call: ExprCall?; expected_name: string ): bool

Check if a call’s name matches, accounting for generic instantiation. When a generic function is instantiated, the call name is mangled (e.g. generic_add`123456). This checks the mangled name first, then falls back to the fromGeneric original name.

Arguments:
  • call : ExprCall?

  • expected_name : string

qm_extract_call_name(expr: Expression? ): string

Extract call name from ExprCall. If the call targets a generic instance, returns the original generic name (before mangling).

Arguments:
qm_extract_field_name(expr: Expression? ): string

Extract field name from ExprField, ExprSafeField, or variant expressions.

Arguments:
qm_extract_name(expr: Expression? ): string

Extract the “name” field from common expression types. Works for ExprVar, ExprCall, ExprField, ExprSafeField, ExprLet variables, etc.

Arguments:
qm_extract_type(expr: Expression?; out_type: TypeDeclPtr ): QMatchResult

Extract TypeDeclPtr from ExprTypeDecl.

Arguments:
qm_rtti(expr: Expression const? ): string

Return the RTTI type name of an expression.

Arguments:

10.12.9. Argument filtering

qm_count_real_args(args: dasvector`ptr`Variable ): int

Count arguments excluding fakeContext and fakeLineInfo.

Arguments:
  • args : vector<Variable*>

qm_count_real_call_args(args: dasvector`ptr`Expression ): int

Count call arguments excluding fakeContext and fakeLineInfo expressions.

Arguments:
  • args : vector<Expression*>

qm_extract_remaining_args(args: dasvector`ptr`Variable; from_logical: int; result: array<VariablePtr> )

Collect remaining real arguments (skipping fakeContext/fakeLineInfo) starting from logical index.

Arguments:
  • args : vector<Variable*>

  • from_logical : int

  • result : array< VariablePtr>

qm_is_fake_arg(arg: Expression? ): bool

Returns true if the expression is a compiler-injected fakeContext or fakeLineInfo.

Arguments:
qm_real_arg_index(args: dasvector`ptr`Variable; logical_idx: int ): int

Map logical argument index (skipping fakeContext/fakeLineInfo) to physical index.

Arguments:
  • args : vector<Variable*>

  • logical_idx : int

qm_real_call_arg_index(args: dasvector`ptr`Expression; logical_idx: int ): int

Map logical call argument index (skipping fake args) to physical index.

Arguments:
  • args : vector<Expression*>

  • logical_idx : int

10.12.10. Block scanning

qm_extract_stmts(blk_expr: Expression?; from_pos: int; to_pos: int; result: array<Expression?> )

Extract statements [from_pos, to_pos) from a block into an array (cloned).

Arguments:
qm_scan(blk_expr: Expression?&; pos: int&; len: int; matcher: block<(var elem:Expression?):QMatchResult> ): QMatchResult

Scan block statements from pos to len, trying matcher at each position. On first success, update pos to the position after the match and return ok.

Arguments:

10.12.11. Constant construction

qm_make_zero_const(call_name: string ): Expression?

Create a zero-value ExprConst for the range first component. range → 0, urange → 0u, range64 → 0l, urange64 → 0ul.

Arguments:
  • call_name : string

10.12.12. AST reconstruction

qm_convert_comprehension(expr: Expression? ): Expression?

Reverse generateComprehension: ExprInvoke(invoke, ExprMakeBlock(closure)) → ExprArrayComprehension. Detects the pattern by checking ExprReturn.fromComprehension flag. Returns null if the expression is not a generated comprehension.

Arguments:
qm_convert_local_function(expr: Expression? ): Expression?

Reverse generateLocalFunction: ExprAddr → ExprMakeBlock(isLocalFunction=true). Returns null if the expression is not a generated local function.

Arguments:
qm_resolve_comprehension(expr: Expression? ): Expression?

If expr is ExprArrayComprehension, shallow-clone it. If ExprInvoke (post-inference comprehension), reconstruct ExprArrayComprehension. Returns null if neither works.

Arguments:
qm_resolve_local_function(expr: Expression? ): Expression?

If expr is ExprMakeBlock, clone it. If ExprAddr (post-inference local function), convert back to ExprMakeBlock. Returns null if neither works.

Arguments:

10.12.13. AST normalization

peel_lambda_rename_2vars(expr: Expression?; arg0Name: string; arg1Name: string ): Expression?

2-arg peel variant for shapes like aggregate’s block<(acc, x) : AGG>. Returns null when the input is not a peelable 2-arg, single-return lambda — caller decides the fallback (this differs from the 1-arg form, which falls back to invoke).

Arguments:
  • expr : Expression?

  • arg0Name : string

  • arg1Name : string

peel_lambda_rename_var(expr: Expression?; argName: string ): Expression?

Peel a single-arg, single-return lambda by renaming its bound variable to argName in the cloned body. If the input is not a peelable lambda, fall back to invoke(expr, argName) so callers can splice the result unconditionally.

Arguments:
peel_lambda_replace_var(expr: Expression?; replacement: Expression? ): Expression?

Variant of peel_lambda_rename_var — substitutes the bound variable with an arbitrary expression in the cloned body. replaceVariable is now peel-aware (single unified path strips typer-inserted ExprRef2Value wrappers when present). Falls back to invoke(expr, replacement) when non-peelable.

Arguments:
peel_lambda_single_return(lam: Expression? ): Expression?

For a single-argument, single-return lambda @(x : T) => expr, return the body expr. Returns null if lam is not an ExprMakeBlock of that exact shape.

Arguments:
peel_tuple_field_read(expr: Expression?; bindName: string; fieldIndex: int ): bool

true when expr matches <bindName>._<fieldIndex> — a tuple-slot read on a named bind. Single-level ExprRef2Value peel on each side (the typer never nests these wrappers).

Arguments:
  • expr : Expression?

  • bindName : string

  • fieldIndex : int

qm_peel_ref2value(e: Expression?& )

Strip ExprRef2Value wrappers from e in place. The typer inserts these post-inference around any read of a reference; they are invisible to user-written code and the matcher peels them so a clean pattern (_.X) matches a wrapped source (ExprRef2Value(_.X)) and vice versa. Emitted at the top of every generate_match dispatch.

Arguments:

10.12.14. Hygienic names

qn(prefix: string; at: LineInfo ): string

Build a backtick-quoted qname unique to at for the given prefix. Use to name synthesized locals emitted by macros so multiple expansions in one file don’t collide. at.line / at.column are interpolated via the default uint formatter (hex), so output for at.line=42u, at.column=7u is \`<prefix>\`0x2a\`0x7. Deterministic — distinct (prefix, at.line, at.column) triples produce distinct names; synthesized LineInfo() (line=0, column=0) produces \`<prefix>\`0x0\`0x0 and WILL collide across distinct synth sites with the same prefix. Build a synth-specific name yourself if that matters at the call site.

Arguments: