.. _stdlib_ast_match: ==================== AST pattern matching ==================== .. das:module:: ast_match 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. ++++++++++++ Enumerations ++++++++++++ .. _enum-ast_match-QMatchError: .. das:attribute:: 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. ++++++++++ Structures ++++++++++ .. _struct-ast_match-QMatchResult: .. das:attribute:: QMatchResult :Fields: * **matched** : bool - True if the pattern matched the expression. * **error** : :ref:`QMatchError ` - Error code describing why the match failed (ok when matched). * **expr** : :ref:`Expression `? - Pointer to the expression where matching failed (null on success). +++++++++++ Call macros +++++++++++ .. _call-macro-ast_match-qmatch_block: .. das:attribute:: qmatch_block Function annotation qmatch_block .. _call-macro-ast_match-qmatch: .. das:attribute:: qmatch Function annotation qmatch .. _call-macro-ast_match-qmatch_function: .. das:attribute:: qmatch_function ``qmatch_function(func, block_pattern)`` — match function body against block pattern. +++++++++++++++ Match execution +++++++++++++++ * :ref:`qm_run (var expr: Expression?; blk: block\<(var arg:Expression?):QMatchResult\>) : QMatchResult ` * :ref:`qm_run_function (var func: FunctionPtr; blk: block\<(var body:Expression?;var func_ptr:Function?):QMatchResult\>) : QMatchResult ` .. _function-ast_match_qm_run_Expression_q__block_ls_var_arg_c_Expression_q__c_QMatchResult_gr_: .. das:function:: 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** : :ref:`Expression `? * **blk** : block<(arg: :ref:`Expression `?): :ref:`QMatchResult `> .. _function-ast_match_qm_run_function_FunctionPtr_block_ls_var_body_c_Expression_q_;var_func_ptr_c_Function_q__c_QMatchResult_gr_: .. das:function:: 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** : :ref:`FunctionPtr ` * **blk** : block<(body: :ref:`Expression `?;func_ptr: :ref:`Function `?): :ref:`QMatchResult `> +++++++++++++++ Result builders +++++++++++++++ * :ref:`qm_fail_const (var expr: Expression?) : QMatchResult ` * :ref:`qm_fail_field (var expr: Expression?) : QMatchResult ` * :ref:`qm_fail_list (var expr: Expression?) : QMatchResult ` * :ref:`qm_fail_null () : QMatchResult ` * :ref:`qm_fail_rtti (var expr: Expression?) : QMatchResult ` * :ref:`qm_fail_type (var expr: Expression?) : QMatchResult ` * :ref:`qm_fail_wildcard (var expr: Expression?) : QMatchResult ` * :ref:`qm_ok () : QMatchResult ` .. _function-ast_match_qm_fail_const_Expression_q_: .. das:function:: qm_fail_const(expr: Expression?) : QMatchResult Return a failure — constant type does not match $v extraction target. :Arguments: * **expr** : :ref:`Expression `? .. _function-ast_match_qm_fail_field_Expression_q_: .. das:function:: qm_fail_field(expr: Expression?) : QMatchResult Return a failure — a named field differs between pattern and expression. :Arguments: * **expr** : :ref:`Expression `? .. _function-ast_match_qm_fail_list_Expression_q_: .. das:function:: qm_fail_list(expr: Expression?) : QMatchResult Return a failure — block statement count mismatch. :Arguments: * **expr** : :ref:`Expression `? .. _function-ast_match_qm_fail_null: .. das:function:: qm_fail_null() : QMatchResult Return a failure — null expression encountered. .. _function-ast_match_qm_fail_rtti_Expression_q_: .. das:function:: qm_fail_rtti(expr: Expression?) : QMatchResult Return a failure — expression RTTI type does not match the pattern. :Arguments: * **expr** : :ref:`Expression `? .. _function-ast_match_qm_fail_type_Expression_q_: .. das:function:: qm_fail_type(expr: Expression?) : QMatchResult Return a failure — TypeDecl comparison failed. :Arguments: * **expr** : :ref:`Expression `? .. _function-ast_match_qm_fail_wildcard_Expression_q_: .. das:function:: qm_fail_wildcard(expr: Expression?) : QMatchResult Return a failure — wildcard could not find a matching statement. :Arguments: * **expr** : :ref:`Expression `? .. _function-ast_match_qm_ok: .. das:function:: qm_ok() : QMatchResult Return a successful match result. ++++++++++++++++ Value extraction ++++++++++++++++ * :ref:`qm_extract (var expr: Expression?; var val: float&) : QMatchResult ` * :ref:`qm_extract (var expr: Expression?; var val: int&) : QMatchResult ` * :ref:`qm_extract (var expr: Expression?; var val: string&) : QMatchResult ` * :ref:`qm_extract (var expr: Expression?; var val: bool&) : QMatchResult ` * :ref:`qm_extract (var expr: Expression?; var val: double&) : QMatchResult ` * :ref:`qm_extract (var expr: Expression?; var val: uint&) : QMatchResult ` * :ref:`qm_extract (var expr: Expression?; var val: int64&) : QMatchResult ` qm_extract ^^^^^^^^^^ .. _function-ast_match_qm_extract_Expression_q__float_ref_: .. das:function:: qm_extract(expr: Expression?; val: float&) : QMatchResult Extract float from ExprConstFloat. Fails if wrong type. :Arguments: * **expr** : :ref:`Expression `? * **val** : float\ & .. _function-ast_match_qm_extract_Expression_q__int_ref_: .. das:function:: qm_extract(expr: Expression?; val: int&) : QMatchResult .. _function-ast_match_qm_extract_Expression_q__string_ref_: .. das:function:: qm_extract(expr: Expression?; val: string&) : QMatchResult .. _function-ast_match_qm_extract_Expression_q__bool_ref_: .. das:function:: qm_extract(expr: Expression?; val: bool&) : QMatchResult .. _function-ast_match_qm_extract_Expression_q__double_ref_: .. das:function:: qm_extract(expr: Expression?; val: double&) : QMatchResult .. _function-ast_match_qm_extract_Expression_q__uint_ref_: .. das:function:: qm_extract(expr: Expression?; val: uint&) : QMatchResult .. _function-ast_match_qm_extract_Expression_q__int64_ref_: .. das:function:: qm_extract(expr: Expression?; val: int64&) : QMatchResult ++++++++++++++++++++++++ Name and type extraction ++++++++++++++++++++++++ * :ref:`qm_call_name_matches (call: ExprCall?; expected_name: string) : bool ` * :ref:`qm_extract_call_name (var expr: Expression?) : string ` * :ref:`qm_extract_field_name (var expr: Expression?) : string ` * :ref:`qm_extract_name (var expr: Expression?) : string ` * :ref:`qm_extract_type (var expr: Expression?; var out_type: TypeDeclPtr) : QMatchResult ` * :ref:`qm_rtti (expr: Expression const?) : string ` .. _function-ast_match_qm_call_name_matches_ExprCall_q__string: .. das:function:: 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** : :ref:`ExprCall `? * **expected_name** : string .. _function-ast_match_qm_extract_call_name_Expression_q_: .. das:function:: 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** : :ref:`Expression `? .. _function-ast_match_qm_extract_field_name_Expression_q_: .. das:function:: qm_extract_field_name(expr: Expression?) : string Extract field name from ExprField, ExprSafeField, or variant expressions. :Arguments: * **expr** : :ref:`Expression `? .. _function-ast_match_qm_extract_name_Expression_q_: .. das:function:: 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** : :ref:`Expression `? .. _function-ast_match_qm_extract_type_Expression_q__TypeDeclPtr: .. das:function:: qm_extract_type(expr: Expression?; out_type: TypeDeclPtr) : QMatchResult Extract TypeDeclPtr from ExprTypeDecl. :Arguments: * **expr** : :ref:`Expression `? * **out_type** : :ref:`TypeDeclPtr ` .. _function-ast_match_qm_rtti_Expression_const_q_: .. das:function:: qm_rtti(expr: Expression const?) : string Return the RTTI type name of an expression. :Arguments: * **expr** : :ref:`Expression `? ++++++++++++++++++ Argument filtering ++++++++++++++++++ * :ref:`qm_count_real_args (args: dasvector`ptr`Variable) : int ` * :ref:`qm_count_real_call_args (args: dasvector`ptr`Expression) : int ` * :ref:`qm_extract_remaining_args (args: dasvector`ptr`Variable; from_logical: int; var result: array\) ` * :ref:`qm_is_fake_arg (arg: Expression?) : bool ` * :ref:`qm_real_arg_index (args: dasvector`ptr`Variable; logical_idx: int) : int ` * :ref:`qm_real_call_arg_index (args: dasvector`ptr`Expression; logical_idx: int) : int ` .. _function-ast_match_qm_count_real_args_dasvector_rq_ptr_rq_Variable: .. das:function:: qm_count_real_args(args: dasvector`ptr`Variable) : int Count arguments excluding fakeContext and fakeLineInfo. :Arguments: * **args** : vector .. _function-ast_match_qm_count_real_call_args_dasvector_rq_ptr_rq_Expression: .. das:function:: qm_count_real_call_args(args: dasvector`ptr`Expression) : int Count call arguments excluding fakeContext and fakeLineInfo expressions. :Arguments: * **args** : vector .. _function-ast_match_qm_extract_remaining_args_dasvector_rq_ptr_rq_Variable_int_array_ls_VariablePtr_gr_: .. das:function:: qm_extract_remaining_args(args: dasvector`ptr`Variable; from_logical: int; result: array) Collect remaining real arguments (skipping fakeContext/fakeLineInfo) starting from logical index. :Arguments: * **args** : vector * **from_logical** : int * **result** : array< :ref:`VariablePtr `> .. _function-ast_match_qm_is_fake_arg_Expression_q_: .. das:function:: qm_is_fake_arg(arg: Expression?) : bool Returns true if the expression is a compiler-injected fakeContext or fakeLineInfo. :Arguments: * **arg** : :ref:`Expression `? .. _function-ast_match_qm_real_arg_index_dasvector_rq_ptr_rq_Variable_int: .. das:function:: 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 * **logical_idx** : int .. _function-ast_match_qm_real_call_arg_index_dasvector_rq_ptr_rq_Expression_int: .. das:function:: 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 * **logical_idx** : int ++++++++++++++ Block scanning ++++++++++++++ * :ref:`qm_extract_stmts (var blk_expr: Expression?; from_pos: int; to_pos: int; var result: array\) ` * :ref:`qm_scan (var blk_expr: Expression?&; var pos: int&; len: int; matcher: block\<(var elem:Expression?):QMatchResult\>) : QMatchResult ` .. _function-ast_match_qm_extract_stmts_Expression_q__int_int_array_ls_Expression_q__gr_: .. das:function:: qm_extract_stmts(blk_expr: Expression?; from_pos: int; to_pos: int; result: array) Extract statements [from_pos, to_pos) from a block into an array (cloned). :Arguments: * **blk_expr** : :ref:`Expression `? * **from_pos** : int * **to_pos** : int * **result** : array< :ref:`Expression `?> .. _function-ast_match_qm_scan_Expression_q__ref__int_ref__int_block_ls_var_elem_c_Expression_q__c_QMatchResult_gr_: .. das:function:: 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** : :ref:`Expression `?\ & * **pos** : int\ & * **len** : int * **matcher** : block<(elem: :ref:`Expression `?): :ref:`QMatchResult `> +++++++++++++++++++++ Constant construction +++++++++++++++++++++ * :ref:`qm_make_zero_const (call_name: string) : Expression? ` .. _function-ast_match_qm_make_zero_const_string: .. das:function:: 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 ++++++++++++++++++ AST reconstruction ++++++++++++++++++ * :ref:`qm_convert_comprehension (var expr: Expression?) : Expression? ` * :ref:`qm_convert_local_function (var expr: Expression?) : Expression? ` * :ref:`qm_resolve_comprehension (var expr: Expression?) : Expression? ` * :ref:`qm_resolve_local_function (var expr: Expression?) : Expression? ` .. _function-ast_match_qm_convert_comprehension_Expression_q_: .. das:function:: 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** : :ref:`Expression `? .. _function-ast_match_qm_convert_local_function_Expression_q_: .. das:function:: 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** : :ref:`Expression `? .. _function-ast_match_qm_resolve_comprehension_Expression_q_: .. das:function:: 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** : :ref:`Expression `? .. _function-ast_match_qm_resolve_local_function_Expression_q_: .. das:function:: 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** : :ref:`Expression `?