2.29. 宏

在 Daslang 中,宏是允许直接作语法树的机制。

宏通过:ref:daslib/ast <stdlib_ast> 模块和:ref:`daslib/ast_boost <stdlib_ast_boost>`帮助模块公开。

宏在编译时在不同编译过程中进行评估。 每次包含该模块时,分配给特定模块的宏都会作为该模块的一部分进行评估。

2.29.1. 编译过程

Daslang 编译器按以下顺序为每个模块执行编译过程 (参阅 Modules):

  1. Parser 将 das 程序转换为 AST

    1. 如果有任何解析错误,编译将停止

  2. 为每个函数或结构调用 apply

    1. 如果有任何错误,编译将停止

  3. Infer pass 会重复自身,直到没有报告新的转换

    1. 内置推理传递发生

      1. 为每个函数或表达式调用 transform 宏

    2. 发生宏传递

  4. 如果仍有任何错误,编译将停止

  5. 对所有函数和结构宏调用 finish

  6. 发生 Lint Pass

    1. 如果有任何错误,编译将停止

  7. 优化过程会重复自身,直到没有新的转换报告

    1. 发生内置优化传递

    2. 宏优化通过发生

  8. 如果在优化过程中出现任何错误,编译将停止

  9. 如果模块包含任何宏,则进行模拟

    1. 如果有任何仿真错误,编译将停止

    2. 调用模块宏函数(用 _macro 注释)

      1. 如果有任何错误,编译将停止

模块以 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 被编译时。

宏按以下方式调用:

  1. class 派生自相应的基宏类

  2. Adapter 已创建

  3. 适配器已注册到模块

例如,这就是为 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)