You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否在Nim中全局修改AST?求类似Haxe@:build宏的实现方案

Nim中实现类似Haxe @:build宏的AST修改方案

Nim完全支持在显式宏调用之外修改项目AST,实现类似Haxe @:build宏的效果——无需手动调用宏,就能自动遍历、修改模块内的声明(比如重写函数)。主要可以通过模块级宏或自定义Pragma配合宏两种方式实现,以下是具体示例:

1. 模块级宏:自动修改整个模块的所有函数

模块级宏会接收当前模块的完整AST,可在类型检查前遍历并修改所有声明:

import macros

# 定义模块级宏:遍历模块AST,给所有函数添加入口日志
macro rewriteModule*(m: NimNode): NimNode =
  result = newNimNode(nnkStmtList)
  for stmt in m:
    # 匹配函数声明节点
    if stmt.kind == nnkProcDef:
      let funcName = stmt[0].strVal
      # 在函数体开头插入打印语句
      let logStmt = newCall("echo", newStrLit(s"Entered function: ${funcName}"))
      stmt[4].add(logStmt)  # stmt[4]对应函数体的语句列表
    result.add(stmt)

# 将宏应用到当前模块,自动处理所有声明
{.pragma: rewriteModule.}

# 测试函数(会被自动修改)
proc calculateSum(a, b: int): int =
  return a + b

when isMainModule:
  echo calculateSum(10, 20)

运行这段代码时,calculateSum函数会自动被注入日志逻辑,执行后会先打印Entered function: calculateSum,再输出计算结果。

2. 自定义Pragma:仅修改标记的特定函数

如果不需要修改所有声明,可自定义Pragma,仅对标记的函数进行AST重写:

import macros

# 定义宏:给标记的函数添加调用日志
macro addCallLog*(p: NimNode): NimNode =
  result = p
  if p.kind == nnkProcDef:
    let funcName = p[0].strVal
    let logStmt = newCall("echo", newStrLit(s"Calling function: ${funcName}"))
    # 在函数体最开头插入日志语句
    p[4].insert(0, logStmt)

# 注册自定义Pragma
{.pragma: addCallLog.}

# 标记需要修改的函数
proc multiply(x, y: int): int {.addCallLog.} =
  return x * y

# 未标记的函数不会被修改
proc subtract(x, y: int): int =
  return x - y

when isMainModule:
  echo multiply(5, 3)  # 会先打印日志
  echo subtract(10, 4) # 无日志输出

3. 进阶:遍历复杂AST结构

使用walkAst函数可以更便捷地遍历嵌套的AST节点,适合复杂场景的修改:

import macros

# 模块级宏:给所有函数名添加前缀
macro renameAllProcs*(m: NimNode): NimNode =
  result = m
  walkAst(result):
    case n.kind:
      of nnkProcDef:
        n[0].strVal = "rewritten_" & n[0].strVal
      else:
        discard

{.pragma: renameAllProcs.}

proc greet(name: string): string =
  return s"Hello, ${name}"

when isMainModule:
  # 函数名已被修改,需调用rewritten_greet
  echo rewritten_greet("Bob")

关键说明

  • 模块级宏的执行时机是模块解析完成后、类型检查前,适合在类型检查前调整AST结构;
  • 如果需要在类型检查后修改AST,可以使用{.transform.}Pragma配合宏,不过这类宏只能读取类型信息,不能修改函数签名等核心结构;
  • 所有宏操作都基于Nim的std/macros模块提供的API,可通过遍历节点类型、修改节点属性或子节点实现各种自定义逻辑。

内容的提问来源于stack exchange,提问作者Griffort

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 10:33:25