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)