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 在评论结束时发生。

beforeStructureafterStructure 在每个结构或类声明之前和之后出现,无论它是否具有注释。

beforeStructureFieldsafterStructureFields 在每个结构或类字段之前和之后出现,无论它是否有注释。

afterStructureField 在每个字段声明之后出现。

beforeFunctionafterFunction 在每个函数声明之前和之后发生,无论它是否有注释。

beforeGlobalVariablesafterGlobalVariables 在每个全局变量声明之前和之后出现,无论它是否具有注释。

afterGlobalVariable 在每个单独的全局变量声明之后发生。

beforeVariantafterVariant 在每个 variant 声明之前和之后出现,无论它是否具有 comments。

beforeEnumerationafterEnumeration 在每个枚举声明之前和之后出现,无论它是否具有注释。

beforeAliasafterAlias 在每个别名类型声明之前和之后出现,无论它是否具有注释。

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)