.. _macros: ====== 宏 ====== 在 Daslang 中,宏是允许直接作语法树的机制。 宏通过:ref:`daslib/ast ` 模块和:ref:`daslib/ast_boost `帮助模块公开。 宏在编译时在不同编译过程中进行评估。 每次包含该模块时,分配给特定模块的宏都会作为该模块的一部分进行评估。 ------------------ 编译过程 ------------------ Daslang 编译器按以下顺序为每个模块执行编译过程 (参阅 :ref:`Modules `): #. Parser 将 das 程序转换为 AST #. 如果有任何解析错误,编译将停止 #. 为每个函数或结构调用 `apply` #. 如果有任何错误,编译将停止 #. Infer pass 会重复自身,直到没有报告新的转换 #. 内置推理传递发生 #. 为每个函数或表达式调用 `transform` 宏 #. 发生宏传递 #. 如果仍有任何错误,编译将停止 #. 对所有函数和结构宏调用 `finish` #. 发生 Lint Pass #. 如果有任何错误,编译将停止 #. 优化过程会重复自身,直到没有新的转换报告 #. 发生内置优化传递 #. 宏优化通过发生 #. 如果在优化过程中出现任何错误,编译将停止 #. 如果模块包含任何宏,则进行模拟 #. 如果有任何仿真错误,编译将停止 #. 调用模块宏函数(用 `_macro` 注释) #. 如果有任何错误,编译将停止 模块以 `require` 顺序编译。 --------------- 调用宏 --------------- ``[_macro]`` 注释用于指定应在 compilation time 评估的函数。 考虑以下来自 :ref:`daslib/ast_boost `::的例子:: [_macro,private] def setup { if ( is_compiling_macros_in_module("ast_boost") ) { add_new_function_annotation("macro", new MacroMacro()) } } `setup` 函数在每个模块编译后进行评估,其中包括 ast_boost。 如果当前编译的模块名称与参数匹配,则 ``is_compiling_macros_in_module``函数返回 true。 在这个特定的例子中,函数注解 ``macro``只会添加一次:当模块 `ast_boost` 被编译时。 宏按以下方式调用: #. class 派生自相应的基宏类 #. Adapter 已创建 #. 适配器已注册到模块 例如,这就是为 reader 宏实现此生命周期周期的方式:: def add_new_reader_macro ( name:string; someClassPtr ) { var ann <- make_reader_macro(name, someClassPtr) this_module() |> add_reader_macro(ann) unsafe { delete ann } } --------------------- AstFunctionAnnotation --------------------- ``AstFunctionAnnotation`` 宏允许你作对特定函数及其函数体的调用。 可以将注释添加到常规或通用函数中。 ``add_new_function_annotation`` 向模块添加函数注释。 此外,还有 ``[function_macro]`` 注解可以完成同样的事情。 ``AstFunctionAnnotation`` 允许几种不同的作:: class AstFunctionAnnotation { def abstract transform ( var call : smart_ptr; var errors : das_string ) : ExpressionPtr def abstract verifyCall ( var call : smart_ptr; args,progArgs:AnnotationArgumentList; var errors : das_string ) : bool def abstract apply ( var func:FunctionPtr; var group:ModuleGroup; args:AnnotationArgumentList; var errors : das_string ) : bool def abstract finish ( var func:FunctionPtr; var group:ModuleGroup; args,progArgs:AnnotationArgumentList; var errors : das_string ) : bool def abstract patch ( var func:FunctionPtr; var group:ModuleGroup; args,progArgs:AnnotationArgumentList; var errors : das_string; var astChanged:bool& ) : bool def abstract fixup ( var func:FunctionPtr; var group:ModuleGroup; args,progArgs:AnnotationArgumentList; var errors : das_string ) : bool def abstract lint ( var func:FunctionPtr; var group:ModuleGroup; args,progArgs:AnnotationArgumentList; var errors : das_string ) : bool def abstract complete ( var func:FunctionPtr; var ctx:smart_ptr ) : void def abstract isCompatible ( var func:FunctionPtr; var types:VectorTypeDeclPtr; decl:AnnotationDeclaration; var errors:das_string ) : bool def abstract isSpecialized : bool def abstract appendToMangledName ( func:FunctionPtr; decl:AnnotationDeclaration; var mangledName:das_string ) : void } ``transform`` 允许您更改对函数的调用,并在 infer pass 中应用。 Transform 是用其他语义替换或修改函数调用的最佳方式。 ``verifyCall`` 在每次调用函数的 `lint` 阶段调用,用于检查调用是否有效。 ``apply`` 在 infer pass 之前应用于函数本身。 Apply 通常是全局函数体修改或实例化发生的地方。 ``finish`` 在 infer pass 之后应用于函数本身。 它仅在非泛型函数或泛型函数的实例上调用。 ``finish`` 通常用于注册函数、通知 C++ 代码等。 在此之后,函数将被完全定义和推断,并且无法再修改。 ``patch`` 在 infer pass 之后调用。如果 patch 将 astChanged 设置为 true,则将重复推理过程。 ``fixup`` 在 infer pass 之后调用。它用于修复函数的主体。 ``lint`` 在函数本身的 `lint` 阶段被调用,用于验证函数是否有效。 ``complete``在上下文创建的 '`simulate`' 部分调用。此时,Context 可用。 如果特定函数匹配由合约控制,则 ``isSpecialized`` 必须返回 true。 在这种情况下, ``isCompatible`` 被调用,并考虑结果。 ``isCompatible`` 如果专用函数与给定参数兼容,则返回 true。 如果函数不兼容,则必须指定 errors 字段。 调用 ``appendToMangledName`` 以将损坏的名称附加到函数中。 这样,具有相同类型签名的多个函数可以存在并相互区分。 让我们回顾一下 `ast_boost` 中的以下示例,了解 ``macro`` 注解是如何实现的:: class MacroMacro : AstFunctionAnnotation { def override apply ( var func:FunctionPtr; var group:ModuleGroup; args:AnnotationArgumentList; var errors : das_string ) : bool { compiling_program().flags |= ProgramFlags needMacroModule func.flags |= FunctionFlags init var blk <- new ExprBlock(at=func.at) var ifm <- new ExprCall(at=func.at, name:="is_compiling_macros") var ife <- new ExprIfThenElse(at=func.at, cond<-ifm, if_true<-func.body) push(blk.list,ife) func.body <- blk return true } } 在 `apply` 传递期间,函数体附加了 ``if is_compiling_macros()`` 闭包。 此外,还设置了 ``init`` 标志,它相当于 ``_macro`` 注解。 用 ``[macro]`` 注释的函数在模块编译期间被评估。 ------------------ AstBlockAnnotation ------------------ ``AstBlockAnnotation`` 用于作块表达式(块、lambda、本地函数):: class AstBlockAnnotation { def abstract apply ( var blk:smart_ptr; var group:ModuleGroup; args:AnnotationArgumentList; var errors : das_string ) : bool def abstract finish ( var blk:smart_ptr; var group:ModuleGroup; args,progArgs:AnnotationArgumentList; var errors : das_string ) : bool } ``add_new_block_annotation`` 为模块添加函数注释。 此外,还有 ``[block_macro]`` 注解可以完成同样的事情。 在 Infer Pass 之前为每个块表达式调用 ``apply``。 ``finish`` 在 infer pass 之后为每个块表达式调用。 ---------------------- AstStructureAnnotation ---------------------- ``AstStructureAnnotation``宏允许你通过注解来作结构或类定义:: class AstStructureAnnotation { def abstract apply ( var st:StructurePtr; var group:ModuleGroup; args:AnnotationArgumentList; var errors : das_string ) : bool def abstract finish ( var st:StructurePtr; var group:ModuleGroup; args:AnnotationArgumentList; var errors : das_string ) : bool def abstract patch ( var st:StructurePtr; var group:ModuleGroup; args:AnnotationArgumentList; var errors : das_string; var astChanged:bool& ) : bool def abstract complete ( var st:StructurePtr; var ctx:smart_ptr ) : void } ``add_new_structure_annotation`` 向模块添加函数注释。 此外,还有 ``[structure_macro]`` 注解可以完成同样的事情。 ``AstStructureAnnotation`` 允许 2 种不同的作:: class AstStructureAnnotation { def abstract apply ( var st:StructurePtr; var group:ModuleGroup; args:AnnotationArgumentList; var errors : das_string ) : bool def abstract finish ( var st:StructurePtr; var group:ModuleGroup; args:AnnotationArgumentList; var errors : das_string ) : bool } ``apply`` 在 infer pass 之前调用。现在是修改结构、生成一些代码等的最佳时机。 ``finish`` 在成功的 infer pass 后调用。它通常用于注册结构、执行 RTTI作等。 在此之后,结构将被完全推断和定义,之后无法再修改。 ``patch`` 在 infer pass 之后调用。如果 patch 将 astChanged 设置为 true,则将重复推理过程。 ``complete`` 在上下文创建的 `simulate` 部分调用。此时,Context 可用。 这种注解的一个例子是 :ref:`daslib/ast_boost ` 中的 `SetupAnyAnnotation`。 ------------------------ AstEnumerationAnnotation ------------------------ ``AstStructureAnnotation`` 宏允许你通过注解来作枚举:: class AstEnumerationAnnotation { def abstract apply ( var st:EnumerationPtr; var group:ModuleGroup; args:AnnotationArgumentList; var errors : das_string ) : bool } ``add_new_enumeration_annotation`` 向模块添加 Function Annotation。 此外,还有 ``[enumeration_macro]`` 注解可以完成同样的事情。 ``apply`` 在 infer pass 之前调用。现在是修改枚举、生成一些代码等的最佳时机。 --------------- AstVariantMacro --------------- ``AstVariantMacro`` 专门用于转换 ``is``, ``as``, 和 ``?as`` 表达式。 ``add_new_variant_macro`` 向模块添加一个 variant 宏。 此外,还有 ``[variant_macro]`` 注解可以完成同样的事情。 这 3 种转换中的每一种都包含在适当的抽象函数中:: class AstVariantMacro { def abstract visitExprIsVariant ( prog:ProgramPtr; mod:Module?; expr:smart_ptr ) : ExpressionPtr def abstract visitExprAsVariant ( prog:ProgramPtr; mod:Module?; expr:smart_ptr ) : ExpressionPtr def abstract visitExprSafeAsVariant ( prog:ProgramPtr; mod:Module?; expr:smart_ptr ) : ExpressionPtr } 让我们回顾一下以下示例 :ref:`daslib/ast_boost `:: // replacing ExprIsVariant(value,name) => ExprOp2('==",value.__rtti,"name") // if value is ast::Expr* class BetterRttiVisitor : AstVariantMacro { def override visitExprIsVariant(prog:ProgramPtr; mod:Module?;expr:smart_ptr) : ExpressionPtr { if isExpression(expr.value._type) var vdr <- new ExprField(at=expr.at, name:="__rtti", value <- clone_expression(expr.value)) var cna <- new ExprConstString(at=expr.at, value:=expr.name) var veq <- new ExprOp2(at=expr.at, op:="==", left<-vdr, right<-cna) return veq return default } } // note the following ussage class GetHintFnMacro : AstFunctionAnnotation { def override transform ( var call : smart_ptr; var errors : das_string ) : ExpressionPtr { if ( call.arguments[1] is ExprConstString ) { // HERE EXPRESSION WILL BE REPLACED ... } } } 在这里,宏利用 ExprIsVariant 语法。 它将 ``expr is TYPENAME`` 表达式替换为 ``expr.__rtti = "TYPENAME"`` 表达式。 ``isExpression`` 函数确保 `expr` 来自 `ast::Expr*` 系列,即 Daslang 语法树的一部分。 -------------- AstReaderMacro -------------- ``AstReaderMacro`` 允许在 Daslang 代码中嵌入完全不同的语法。 ``add_new_reader_macro`` 将 Reader 宏添加到模块中。 此外,还有 ``[reader_macro]`` 注解,它基本上自动化了同样的事情。 Reader 宏接受字符,必要时收集它们,并返回 `ast::Expression`:: class AstReaderMacro { def abstract accept ( prog:ProgramPtr; mod:Module?; expr:ExprReader?; ch:int; info:LineInfo ) : bool def abstract visit ( prog:ProgramPtr; mod:Module?; expr:smart_ptr ) : ExpressionPtr } Reader 宏通过 ``% READER_MACRO_NAME ~ character_sequence`` 语法调用。 ``accept``函数通知字符序列的正确终止符:: var x = %arr~\{\}\w\x\y\n%% // invoking reader macro arr, %% is a terminator 考虑上面示例的实现:: [reader_macro(name="arr")] class ArrayReader : AstReaderMacro { def override accept ( prog:ProgramPtr; mod:Module?; var expr:ExprReader?; ch:int; info:LineInfo ) : bool { append(expr.sequence,ch) if ( ends_with(expr.sequence,"%%") ) { let len = length(expr.sequence) resize(expr.sequence,len-2) return false } else { return true } def override visit ( prog:ProgramPtr; mod:Module?; expr:smart_ptr ) : ExpressionPtr { let seqStr = string(expr.sequence) var arrT <- new TypeDecl(baseType=Type tInt) push(arrT.dim,length(seqStr)) var mkArr <- new ExprMakeArray(at = expr.at, makeType <- arrT) for ( x in seqStr ) { var mkC <- new ExprConstInt(at=expr.at, value=x) push(mkArr.values,mkC) } return <- mkArr } 函数 ``accept`` 宏收集序列中的品种。 一旦序列以终止符序列 %% 结束,``accept`` 返回 false 以指示序列的结束。 在 ``visit`` 中,收集到的序列被转换成一个 make 数组 ``[ch1,ch2,..]`` 表达式。 更复杂的示例包括 :ref:`daslib/json_boost ` 中的 JsonReader 宏或 :ref:`daslib/regex_boost ` 中的 RegexReader。 ------------ AstCallMacro ------------ ``AstCallMacro`` 对具有函数调用语法或类似语法的表达式进行作。 它发生在推理过程中。 ``add_new_call_macro``向模块添加一个 call 宏。 ``[call_macro]`` 注解可以自动执行相同的作:: class AstCallMacro { def abstract preVisit ( prog:ProgramPtr; mod:Module?; expr:smart_ptr ) : void def abstract visit ( prog:ProgramPtr; mod:Module?; expr:smart_ptr ) : ExpressionPtr def abstract canVisitArguments ( expr:smart_ptr ) : bool } :ref:`daslib/apply `中的 ``apply`` 就是这种宏的一个例子:: [call_macro(name="apply")] // apply(value, block) class ApplyMacro : AstCallMacro { def override visit ( prog:ProgramPtr; mod:Module?; var expr:smart_ptr ) : ExpressionPtr { ... } } 请注意 ``[call_macro]`` 注解中是如何提供名称的。 ``preVisit`` 在访问参数之前被调用。 ``visit`` 在访问参数后调用。 调用 ``canVisitArguments`` 来确定宏是否可以访问参数。 ------------ AstPassMacro ------------ ``AstPassMacro`` 是一个宏来统治它们。它将整个模块作为输入,并且可以多次调用:: class AstPassMacro { def abstract apply ( prog:ProgramPtr; mod:Module? ) : bool } ``make_pass_macro`` 将一个类注册为 pass 宏。 ``add_new_infer_macro`` 将 pass 宏添加到 infer pass。 ``[infer]`` 注解可以完成同样的事情。 ``add_new_dirty_infer_macro`` 将一个 pass 宏添加到 Infer pass 的 `dirty` 部分。 ``[dirty_infer]`` 注解可以完成同样的事情。 通常,这样的宏会创建一个 ``AstVisitor`` 来执行必要的转换。 ---------------- AstTypeInfoMacro ---------------- ``AstTypeInfoMacro`` 旨在在 typeinfo 表达式中实现自定义类型信息:: class AstTypeInfoMacro { def abstract getAstChange ( expr:smart_ptr; var errors:das_string ) : ExpressionPtr def abstract getAstType ( var lib:ModuleLibrary; expr:smart_ptr; var errors:das_string ) : TypeDeclPtr } ``add_new_typeinfo_macro`` 将 Reader 宏添加到模块中。 此外,还有 ``[typeinfo_macro]`` 注解,它基本上自动化了同样的事情。 ``getAstChange`` 返回为 typeinfo 表达式新生成的 AST。 或者,如果不需要更改或出现错误,则返回 null。 如果出现错误,则必须填写 errors 字符串。 ``getAstType`` 返回新 typeinfo 表达式的类型。 --------------- AstForLoopMacro --------------- ``AstForLoopMacro`` 旨在实现 for 循环表达式的自定义处理:: class AstForLoopMacro { def abstract visitExprFor ( prog:ProgramPtr; mod:Module?; expr:smart_ptr ) : ExpressionPtr } ``add_new_for_loop_macro`` 将 Reader 宏添加到模块中。 此外,还有 ``[for_loop_macro]`` 注解,它基本上自动化了同样的事情。 ``visitExprFor`` 类似于 `AstVisitor`。它返回一个新表达式,如果不需要更改,则返回 null。 --------------- AstCaptureMacro --------------- ``AstCaptureMacro`` 旨在实现 Lambda 表达式的自定义捕获和终结:: class AstCaptureMacro { def abstract captureExpression ( prog:Program?; mod:Module?; expr:ExpressionPtr; etype:TypeDeclPtr ) : ExpressionPtr def abstract captureFunction ( prog:Program?; mod:Module?; var lcs:Structure?; var fun:FunctionPtr ) : void } ``add_new_capture_macro`` 将 Reader 宏添加到模块中。 此外,还有 ``[capture_macro]`` 注解,它基本上自动化了同样的事情。 ``captureExpression`` 在捕获表达式时调用。它返回一个新表达式,如果不需要更改,则返回 null。 ``captureFunction`` 在捕获函数时调用。这是可以将自定义 finalization 添加到函数体的 `final` 部分的地方。 ---------------- AstCommentReader ---------------- ``AstCommentReader`` 旨在实现注释表达式的自定义处理:: class AstCommentReader { def abstract open ( prog:ProgramPtr; mod:Module?; cpp:bool; info:LineInfo ) : void def abstract accept ( prog:ProgramPtr; mod:Module?; ch:int; info:LineInfo ) : void def abstract close ( prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract beforeStructure ( prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract afterStructure ( st:StructurePtr; prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract beforeStructureFields ( prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract afterStructureField ( name:string; prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract afterStructureFields ( prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract beforeFunction ( prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract afterFunction ( fn:FunctionPtr; prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract beforeGlobalVariables ( prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract afterGlobalVariable ( name:string; prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract afterGlobalVariables ( prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract beforeVariant ( prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract afterVariant ( name:string; prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract beforeEnumeration ( prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract afterEnumeration ( name:string; prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract beforeAlias ( prog:ProgramPtr; mod:Module?; info:LineInfo ) : void def abstract afterAlias ( name:string; prog:ProgramPtr; mod:Module?; info:LineInfo ) : void } ``add_new_comment_reader`` 将 Reader 宏添加到模块中。 此外,还有 ``[comment_reader]`` 注解,它基本上自动化了同样的事情。 ``open`` 在开始解析注释时发生。 ``accept`` 对注释的每个字符都会出现。 ``close`` 在评论结束时发生。 ``beforeStructure`` 和 ``afterStructure`` 在每个结构或类声明之前和之后出现,无论它是否具有注释。 ``beforeStructureFields`` 和 ``afterStructureFields`` 在每个结构或类字段之前和之后出现,无论它是否有注释。 ``afterStructureField`` 在每个字段声明之后出现。 ``beforeFunction`` 和 ``afterFunction`` 在每个函数声明之前和之后发生,无论它是否有注释。 ``beforeGlobalVariables`` 和 ``afterGlobalVariables`` 在每个全局变量声明之前和之后出现,无论它是否具有注释。 ``afterGlobalVariable`` 在每个单独的全局变量声明之后发生。 ``beforeVariant`` 和 ``afterVariant`` 在每个 variant 声明之前和之后出现,无论它是否具有 comments。 ``beforeEnumeration`` 和 ``afterEnumeration`` 在每个枚举声明之前和之后出现,无论它是否具有注释。 ``beforeAlias`` 和 ``afterAlias`` 在每个别名类型声明之前和之后出现,无论它是否具有注释。 ---------------- AstSimulateMacro ---------------- `AstSimulateMacro` 旨在自定义程序的模拟:: class AstSimulateMacro { def abstract preSimulate ( prog:Program?; ctx:Context? ) : bool def abstract simulate ( prog:Program?; ctx:Context? ) : bool } ``preSimulate`` 发生在 context 被模拟之后,但在所有 structure 和 function annotation 模拟之前。 ``simulate`` 发生在所有结构和功能注释仿真之后。 ---------- AstVisitor ---------- ``AstVisitor`` 实现 Daslang 表达式树的访客模式。 它包含前缀和后缀形式的每个表达式的回调,以及一些额外的回调:: class AstVisitor { ... // find def abstract preVisitExprFind(expr:smart_ptr) : void // prefix def abstract visitExprFind(expr:smart_ptr) : ExpressionPtr // postifx ... } Postfix 回调可以返回表达式来替换传递给回调的表达式。 `ast_print`示例中的 PrintVisitor 以 Daslang 语法实现每个表达式的打印。 ``make_visitor`` 从类创建一个访问者适配器,该类派生自 ``AstVisitor``。 然后,可以通过 ``visit`` 函数将适配器应用于程序:: var astVisitor = new PrintVisitor() var astVisitorAdapter <- make_visitor(*astVisitor) visit(this_program(), astVisitorAdapter) 如果需要访问表达式,并且可能被完全替换,则应使用 ``visit_expression`` 函数:: expr <- visit_expression(expr,astVisitorAdapter)