- 人工智能
- AI 技能
- 文档
- 开发工具
【免费下载链接】DocAgent
MacroExpandExpr是仓颉语言标准库std.ast包中用于表示宏调用表达式节点的类。凡是出现在表达式位置的宏调用(如@testDef(1)、@M (a is Int64)),在语法树中都以MacroExpandExpr节点承载。本文基于 MacroExpandExpr_class.md 展开,系统梳理该节点的类定位、两种构造函数、8 个可变属性及各自的操作约束,并结合仓库中ast包的类定义、解析示例与宏上下文示例,说明如何解析、读写和验证宏调用表达式节点,帮助你在编写仓颉宏或做 AST 变换时准确操作这一节点。
一、类定位:宏调用在语法树中的表达形式
在std.ast的类体系中,MacroExpandExpr 的完整类声明如下(见 ast_package_classes.md):
public class MacroExpandExpr <: Expr { public init() public init(inputs: Tokens) }- 它继承自
Expr(所有表达式节点的父类,继承自Node,见 Expr_class.md),因此属于表达式节点,可以出现在let a = @M(...)、@M(...) + 1等表达式位置。 - 一个典型的
MacroExpandExpr节点示例为@M (a is Int64),即由@操作符、宏标识符M以及括号内的输入词法单元构成。
与同类宏调用节点的区别
std.ast中与宏调用相关的节点一共有三个,它们分别对应宏调用出现的不同语法位置:
| 节点 | 父类型 | 表示的语法位置 | 对应文档 |
|---|---|---|---|
MacroExpandExpr | Expr | 表达式位置的宏调用(如@testDef(1)、@M (a is Int64)) | MacroExpandExpr_class.md |
MacroExpandDecl | Decl | 声明位置的宏调用(如@Outter var a = 0、@test var x: Int64) | MacroExpandDecl_class.md |
MacroExpandParam | FuncParam | 函数参数位置的宏调用(宏展开产出参数声明) | MacroExpandParam_class.md |
三者结构相似(都包含lParen/rParen、lSquare/rSquare、macroAttrs、macroInputs等属性),区别在于继承链与附加字段:
MacroExpandExpr使用identifier保存宏标识符;MacroExpandDecl/MacroExpandParam使用fullIdentifier保存完整标识符,并额外提供macroInputDecl: Decl承载被宏包裹的声明节点;- 此外,
MacroExpandExpr仅有的at属性(@操作符)是另外两个节点没有的。
两种宏调用形态
从属性设计可以看出,MacroExpandExpr需要同时表达两种宏调用形态:
- 普通宏调用:
@M(inputs),其中@、标识符M、左括号lParen、输入macroInputs、右括号rParen分别对应一个 Token / Tokens; - 属性宏调用:
@Mattrs,即在普通宏调用的标识符之后增加[(lSquare)、属性宏输入(macroAttrs)、](rSquare),随后才是括号输入。
正因如此,节点中同时存在lParen与lSquare两组括号 Token、macroInputs与macroAttrs两组输入 Tokens。
二、构造函数
MacroExpandExpr提供两个重载的init构造函数(原型与说明见 ast_package_classes.md):
| 原型 | 说明 |
|---|---|
() | 构造一个默认的MacroExpandExpr对象。 |
(inputs: Tokens) | 根据传入的词法单元集合,构造一个MacroExpandExpr对象。 |
使用要点:
- 默认构造:得到一个所有字段为空的节点对象,通常用于先建节点、再逐个通过属性赋值填充内容的场景;
- 基于 Tokens 构造:
inputs是待构造节点的词法单元集合(Tokens 类型),一般来自quote(...)表达式或宏展开时收到的输入参数。
异常约束:当传入的
Tokens无法构造为MacroExpandExpr节点时(例如内容不是合法的宏调用表达式语法),构造函数会抛出 ASTException。
三、成员属性详解
MacroExpandExpr的 8 个成员均为public mut prop,即可读可写。属性与说明汇总如下(完整定义见 ast_package_classes.md):
| 属性 | 原型 | 类型 | 说明 |
|---|---|---|---|
at | public mut prop at: Token | Token | 获取或设置节点中的@操作符。 |
identifier | public mut prop identifier: Token | Token | 获取或设置宏调用节点的标识符。 |
lParen | public mut prop lParen: Token | Token | 获取或设置宏调用的"("。 |
lSquare | public mut prop lSquare: Token | Token | 获取或设置属性宏调用的"["。 |
macroAttrs | public mut prop macroAttrs: Tokens | Tokens | 获取或设置属性宏调用的输入。 |
macroInputs | public mut prop macroInputs: Tokens | Tokens | 获取或设置宏调用的输入。 |
rParen | public mut prop rParen: Token | Token | 获取或设置宏调用的")"。 |
rSquare | public mut prop rSquare: Token | Token | 获取或设置属性宏调用的"]"。 |
3.1 语法骨架属性:at、identifier
at保存宏调用起始处的@操作符。它是本节点区分于普通函数调用CallExpr的关键标记。设置该属性时,传入的 Token 必须是@操作符,否则抛出ASTException(见 ast_package_classes.md)。identifier保存宏名称标识符,例如@testDef(1)中的testDef。注意与MacroExpandDecl/MacroExpandParam的fullIdentifier不同,表达式版本记录的是标识符 Token,对应 Token 类型。
3.2 普通宏调用的括号与输入:lParen、macroInputs、rParen
lParen/rParen分别对应宏调用输入的两侧圆括号(/)。设置时必须传入对应的括号 Token,否则抛出ASTException。macroInputs是Tokens类型,保存括号内的完整输入词法单元序列。例如@M (a is Int64)中,macroInputs就是a is Int64对应的全部 Token。它是宏体处理的核心输入——宏定义形如macro M(input: Tokens): Tokens,input正是这里的macroInputs。
3.3 属性宏的方括号与输入:lSquare、macroAttrs、rSquare
lSquare/rSquare对应属性宏调用的[/]。与lParen/rParen相同,设置错误类型 Token 时会抛出ASTException。macroAttrs保存方括号内的属性宏输入(Tokens类型)。属性宏调用@Mattrs的attrs部分即保存在此;普通宏调用不涉及方括号时,lSquare、macroAttrs、rSquare可以为空或 ILLEGAL 状态的占位 Token。
3.4 Token 与 Tokens 的类型说明
属性涉及的两个基础类型:
- Token:词法单元类型,是构成仓颉源码的最小单元,包含
kind: TokenKind(词法单元类型)、value: String(字面量值)、pos: Position(位置信息)等成员。@、(、)、[、]、宏标识符都各自是一个 Token。 - Tokens:对
Token序列进行封装的类型,macroInputs与macroAttrs都是该类型,支持按索引访问(如input[i],参见 report.md 中的遍历示例)。
四、实战:解析宏调用表达式并读写节点属性
结合 parse.md 与 dump.md 中演示的「quote→ 解析函数 →dump」范式,我们可以用parseExpr将一段含宏调用的代码解析为MacroExpandExpr节点,再通过属性读取宏名与输入。
import std.ast.* main() { // 表达式位置的宏调用:对应 MacroExpandExpr 节点 let input: Tokens = quote( @testDef(1 + a) ) // 解析为表达式节点,具体类型为 MacroExpandExpr let expr = parseExpr(input) var macroExpr = match (expr as MacroExpandExpr) { case Some(v) => v case None => throw Exception() } // 读取语法骨架:@ 操作符与宏标识符 println("at value is ${macroExpr.at.value}") println("identifier value is ${macroExpr.identifier.value}") // 读取宏调用的输入词法单元(括号内的内容) println("macroInputs size is ${macroExpr.macroInputs.size}") // 打印节点整体结构,便于核对各属性 macroExpr.dump() }要点说明:
quote(...)把仓颉代码片段转换为 Tokens;parseExpr(input)返回一个Expr,具体类型可用as MacroExpandExpr做类型转换(此模式与 parse.md 中decl as ClassDecl的用法一致);at.value、identifier.value取 Token 的字面量值(Token 的value成员见 Token_struct.md);macroInputs是Tokens,其size表示输入词法单元个数;- 每个 AST 节点都实现了
dump函数,可以实时打印节点整体结构(见 dump.md),当节点属性较多、容易混淆时,这是核对MacroExpandExpr内部字段最直接的手段。
此外,属性都是mut可写的,因此可以在宏展开或 AST 变换中对节点做改写,例如替换identifier指向另一个宏、替换macroInputs为新的词法单元序列。改写时需要注意 3.1~3.3 节提到的 Token 类型校验,写错会触发ASTException。
五、在宏展开上下文中的使用
MacroExpandExpr是宏展开机制在语法树层面的载体。std.ast提供了「Macro With Context」系列函数,让嵌套宏之间可以传递信息(见 ast_package_overview.md 与 context.md):
- assertParentContext(String):检查当前宏调用是否嵌套在指定外层宏调用中,不符合时编译器报错;
- insideParentContext(String):同上,但返回布尔值;
- setItem(String, ...):内层宏向外层宏发送
Bool/Int64/String/Tokens类型信息; - getChildMessages(String):外层宏获取内层宏发送的信息对象列表(MacroMessage),再通过
getString/getInt64/getBool/getTokens/hasItem取值。
以 context.md 的第二个示例为参考:外层宏Outter与内层宏Inner在调用端以@Outter(...)包裹@Inner var cnt = 0的形式嵌套出现。其中处于表达式位置的@Outter(...)调用即对应MacroExpandExpr节点;编译时Inner通过assertParentContext("Outter")校验自己位于Outter宏调用内部,通过setItem("key1", "value1")发送数据,Outter通过getChildMessages("Inner")接收并打印:
value1: value1 value2: value2这一机制说明:理解MacroExpandExpr各属性(特别是identifier与macroInputs),是识别「当前展开的是哪个宏、输入是什么」的基础;而assertParentContext这类上下文函数则进一步把节点间嵌套关系转化为可编程的校验与通信能力。
六、注意事项
- 属性赋值强校验:
at必须是@,lParen/rParen必须是(/),lSquare/rSquare必须是[/],否则抛出ASTException(详见 ast_package_classes.md 中各属性的异常说明); - 区分表达式 / 声明 / 参数三种宏调用节点:处理声明(如
@Outter var a = 0)时使用 MacroExpandDecl,处理表达式(如@testDef(1))时才使用MacroExpandExpr,二者不可混用; macroAttrs与macroInputs的语义不同:macroAttrs对应属性宏的[...]输入,macroInputs对应普通宏调用的(...)输入,改写时不要弄反;- 解析失败会抛异常:用
init(Tokens)构造或调用parseExpr解析非法内容时,会抛出ASTException/ParseASTException(见 ASTException_class.md 与 ParseASTException_class.md); - 结合 dump 验证:节点字段多、命名相似,实际开发时优先用
dump输出节点结构(dump.md),避免凭直觉操作出错。
延伸阅读
- 类完整定义与属性异常说明:ast_package_classes.md
- 包级功能总览:ast_package_overview.md、readme.md
- 宏上下文(嵌套宏通信)示例:context.md
- AST 解析与 dump 示例:parse.md、dump.md
- 相关节点:MacroExpandDecl_class.md、MacroExpandParam_class.md、Expr_class.md
- 人工智能
- AI 技能
- 文档
- 开发工具
【免费下载链接】DocAgent
相关推荐
仓颉 std.ast 包 Constructor 节点详解:enum 构造器的 AST 表示与操作实践
仓颉 std.ast 包 Constructor 节点详解:enum 构造器的 AST 表示与操作实践 在仓颉语言的标准库 std.ast 包中,抽象语法树(A
人工智能AI 技能文档开发工具仓颉语言 std.ast 宏调用节点详解:MacroExpandDecl 类结构、构造方式与属性全解析
仓颉语言 std.ast 宏调用节点详解:MacroExpandDecl 类结构、构造方式与属性全解析 本文围绕仓颉标准库 std.ast 中表示 声明级宏调用
人工智能AI 技能文档开发工具仓颉 std.ast 中 DoWhileExpr 语法树节点详解:do-while 表达式的 AST 表示、构造与读写
仓颉 std.ast 中 DoWhileExpr 语法树节点详解:do while 表达式的 AST 表示、构造与读写 DoWhileExpr 是仓颉标准库 s
人工智能AI 技能文档开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考