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:
expr : Expression?
blk : block<(arg: Expression?): QMatchResult>
- 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:
func : FunctionPtr
blk : block<(body: Expression?;func_ptr: Function?): QMatchResult>
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:
expr : Expression?
name : string
- 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:
expr : Expression?
- qm_fail_field(expr: Expression? ): QMatchResult
Return a failure — a named field differs between pattern and expression.
- Arguments:
expr : Expression?
- qm_fail_list(expr: Expression? ): QMatchResult
Return a failure — block statement count mismatch.
- Arguments:
expr : Expression?
- 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:
expr : Expression?
- qm_fail_type(expr: Expression? ): QMatchResult
Return a failure — TypeDecl comparison failed.
- Arguments:
expr : Expression?
- qm_fail_wildcard(expr: Expression? ): QMatchResult
Return a failure — wildcard could not find a matching statement.
- Arguments:
expr : Expression?
- qm_ok(): QMatchResult
Return a successful match result.
10.12.7. Value extraction
qm_extract (var expr: Expression?; var val: bool&) : QMatchResult
qm_extract (var expr: Expression?; var val: double&) : QMatchResult
qm_extract (var expr: Expression?; var val: float&) : QMatchResult
qm_extract (var expr: Expression?; var val: int&) : QMatchResult
qm_extract (var expr: Expression?; var val: int64&) : QMatchResult
qm_extract (var expr: Expression?; var val: string&) : QMatchResult
qm_extract (var expr: Expression?; var val: uint&) : QMatchResult
- 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:
e : Expression?
10.12.7.1. qm_extract
- qm_extract(expr: Expression?; val: bool& ): QMatchResult
Extract bool from ExprConstBool. Fails if wrong type.
- Arguments:
expr : Expression?
val : bool&
- 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:
expr : Expression?
- qm_extract_field_name(expr: Expression? ): string
Extract field name from ExprField, ExprSafeField, or variant expressions.
- Arguments:
expr : Expression?
- qm_extract_name(expr: Expression? ): string
Extract the “name” field from common expression types. Works for ExprVar, ExprCall, ExprField, ExprSafeField, ExprLet variables, etc.
- Arguments:
expr : Expression?
- qm_extract_type(expr: Expression?; out_type: TypeDeclPtr ): QMatchResult
Extract TypeDeclPtr from ExprTypeDecl.
- Arguments:
expr : Expression?
out_type : TypeDeclPtr
- qm_rtti(expr: Expression const? ): string
Return the RTTI type name of an expression.
- Arguments:
expr : Expression?
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:
arg : Expression?
- 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:
blk_expr : Expression?
from_pos : int
to_pos : int
result : array< Expression?>
- 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:
blk_expr : Expression?&
pos : int&
len : int
matcher : block<(elem: Expression?): QMatchResult>
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:
expr : Expression?
- 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:
expr : Expression?
- 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:
expr : Expression?
- 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:
expr : Expression?
10.12.13. AST normalization
peel_lambda_rename_2vars (expr: Expression?; arg0Name: string; arg1Name: string) : Expression?
peel_lambda_rename_var (expr: Expression?; argName: string) : Expression?
peel_lambda_replace_var (expr: Expression?; var replacement: Expression?) : Expression?
peel_lambda_single_return (var lam: Expression?) : Expression?
peel_tuple_field_read (expr: Expression?; bindName: string; fieldIndex: int) : bool
- 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:
expr : Expression?
argName : string
- 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:
expr : Expression?
replacement : Expression?
- 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:
lam : Expression?
- 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:
e : Expression?&
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:
prefix : string
at : LineInfo