2.29. 宏
在 Daslang 中,宏是允许直接作语法树的机制。
宏通过:ref:daslib/ast <stdlib_ast> 模块和:ref:`daslib/ast_boost <stdlib_ast_boost>`帮助模块公开。
宏在编译时在不同编译过程中进行评估。 每次包含该模块时,分配给特定模块的宏都会作为该模块的一部分进行评估。
2.29.1. 编译过程
Daslang 编译器按以下顺序为每个模块执行编译过程 (参阅 Modules):
Parser 将 das 程序转换为 AST
如果有任何解析错误,编译将停止
为每个函数或结构调用 apply
如果有任何错误,编译将停止
Infer pass 会重复自身,直到没有报告新的转换
内置推理传递发生
为每个函数或表达式调用 transform 宏
发生宏传递
如果仍有任何错误,编译将停止
对所有函数和结构宏调用 finish
发生 Lint Pass
如果有任何错误,编译将停止
优化过程会重复自身,直到没有新的转换报告
发生内置优化传递
宏优化通过发生
如果在优化过程中出现任何错误,编译将停止
如果模块包含任何宏,则进行模拟
如果有任何仿真错误,编译将停止
调用模块宏函数(用 _macro 注释)
如果有任何错误,编译将停止
模块以 require 顺序编译。
2.29.2. 调用宏
[_macro] 注释用于指定应在 compilation time 评估的函数。
考虑以下来自 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
}
}
2.29.3. AstFunctionAnnotation
AstFunctionAnnotation 宏允许你作对特定函数及其函数体的调用。
可以将注释添加到常规或通用函数中。
add_new_function_annotation 向模块添加函数注释。
此外,还有 [function_macro] 注解可以完成同样的事情。
AstFunctionAnnotation 允许几种不同的作:
class AstFunctionAnnotation {
def abstract transform ( var call : smart_ptr<ExprCallFunc>; var errors : das_string ) : ExpressionPtr
def abstract verifyCall ( var call : smart_ptr<ExprCallFunc>; 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<Context> ) : 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] 注释的函数在模块编译期间被评估。
2.29.4. AstBlockAnnotation
AstBlockAnnotation 用于作块表达式(块、lambda、本地函数):
class AstBlockAnnotation {
def abstract apply ( var blk:smart_ptr<ExprBlock>; var group:ModuleGroup; args:AnnotationArgumentList; var errors : das_string ) : bool
def abstract finish ( var blk:smart_ptr<ExprBlock>; var group:ModuleGroup; args,progArgs:AnnotationArgumentList; var errors : das_string ) : bool
}
add_new_block_annotation 为模块添加函数注释。
此外,还有 [block_macro] 注解可以完成同样的事情。
在 Infer Pass 之前为每个块表达式调用 apply。
finish 在 infer pass 之后为每个块表达式调用。
2.29.5. 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<Context> ) : 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 可用。
这种注解的一个例子是 daslib/ast_boost 中的 SetupAnyAnnotation。
2.29.6. 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 之前调用。现在是修改枚举、生成一些代码等的最佳时机。
2.29.7. 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<ExprIsVariant> ) : ExpressionPtr
def abstract visitExprAsVariant ( prog:ProgramPtr; mod:Module?; expr:smart_ptr<ExprAsVariant> ) : ExpressionPtr
def abstract visitExprSafeAsVariant ( prog:ProgramPtr; mod:Module?; expr:smart_ptr<ExprSafeAsVariant> ) : ExpressionPtr
}
让我们回顾一下以下示例 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<ExprIsVariant>) : 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<ExpressionPtr>
}
}
// note the following ussage
class GetHintFnMacro : AstFunctionAnnotation {
def override transform ( var call : smart_ptr<ExprCall>; 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 语法树的一部分。
2.29.8. 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<ExprReader> ) : 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<ExprReader> ) : 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,..] 表达式。
更复杂的示例包括 daslib/json_boost 中的 JsonReader 宏或 daslib/regex_boost 中的 RegexReader。
2.29.9. AstCallMacro
AstCallMacro 对具有函数调用语法或类似语法的表达式进行作。
它发生在推理过程中。
add_new_call_macro``向模块添加一个 call 宏。
``[call_macro] 注解可以自动执行相同的作:
class AstCallMacro {
def abstract preVisit ( prog:ProgramPtr; mod:Module?; expr:smart_ptr<ExprCallMacro> ) : void
def abstract visit ( prog:ProgramPtr; mod:Module?; expr:smart_ptr<ExprCallMacro> ) : ExpressionPtr
def abstract canVisitArguments ( expr:smart_ptr<ExprCallMacro> ) : bool
}
daslib/apply <stdlib_apply>`中的 ``apply` 就是这种宏的一个例子:
[call_macro(name="apply")] // apply(value, block)
class ApplyMacro : AstCallMacro {
def override visit ( prog:ProgramPtr; mod:Module?; var expr:smart_ptr<ExprCallMacro> ) : ExpressionPtr {
...
}
}
请注意 [call_macro] 注解中是如何提供名称的。
preVisit 在访问参数之前被调用。
visit 在访问参数后调用。
调用 canVisitArguments 来确定宏是否可以访问参数。
2.29.10. 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 来执行必要的转换。
2.29.11. AstTypeInfoMacro
AstTypeInfoMacro 旨在在 typeinfo 表达式中实现自定义类型信息:
class AstTypeInfoMacro {
def abstract getAstChange ( expr:smart_ptr<ExprTypeInfo>; var errors:das_string ) : ExpressionPtr
def abstract getAstType ( var lib:ModuleLibrary; expr:smart_ptr<ExprTypeInfo>; var errors:das_string ) : TypeDeclPtr
}
add_new_typeinfo_macro 将 Reader 宏添加到模块中。
此外,还有 [typeinfo_macro] 注解,它基本上自动化了同样的事情。
getAstChange 返回为 typeinfo 表达式新生成的 AST。
或者,如果不需要更改或出现错误,则返回 null。
如果出现错误,则必须填写 errors 字符串。
getAstType 返回新 typeinfo 表达式的类型。
2.29.12. AstForLoopMacro
AstForLoopMacro 旨在实现 for 循环表达式的自定义处理:
class AstForLoopMacro {
def abstract visitExprFor ( prog:ProgramPtr; mod:Module?; expr:smart_ptr<ExprFor> ) : ExpressionPtr
}
add_new_for_loop_macro 将 Reader 宏添加到模块中。
此外,还有 [for_loop_macro] 注解,它基本上自动化了同样的事情。
visitExprFor 类似于 AstVisitor。它返回一个新表达式,如果不需要更改,则返回 null。
2.29.13. 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 部分的地方。
2.29.14. 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 在每个别名类型声明之前和之后出现,无论它是否具有注释。
2.29.15. 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 发生在所有结构和功能注释仿真之后。
2.29.16. AstVisitor
AstVisitor 实现 Daslang 表达式树的访客模式。
它包含前缀和后缀形式的每个表达式的回调,以及一些额外的回调:
class AstVisitor {
...
// find
def abstract preVisitExprFind(expr:smart_ptr<ExprFind>) : void // prefix
def abstract visitExprFind(expr:smart_ptr<ExprFind>) : 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)