Nim语言中实现字符串映射函数及编译期生成Grammar表的方法
问题背景
我在实现翻译器时,需要根据Token执行对应操作。我知道用条件分支实现更规范,但想尝试另一种方案:根据Token名称执行对应交互。
最简便的方式是使用Table[system.string, proc(x:Token):string],但不知道如何在编译期生成该表。我有少量PHP经验,知道PHP支持字符串作为函数调用,示例如下:
function a(int $num){ return $num + 3; } echo "a"(1); // 输出4
想问:
- Nim语言中是否支持预先声明一组函数并以类似方式调用?
- 针对该问题有没有更规范的实现方案?
相关定义
- 给定:Token序列与Grammar
- 定义:
- Token := tuple[name:string, value:string, parent:Token]
- Grammar := Table[system.string, proc(x:Token):string]
- 返回:经Grammar处理后的Token值字符串
解答
一、Nim中实现类似PHP的字符串调用函数
Nim本身不直接支持像PHP那样用字符串直接调用函数,但可以通过手动构建函数表或编译期宏自动生成表实现类似效果:
- 手动构建运行期函数表
先定义Token类型和对应处理函数,再将函数存入Table:
type Token* = tuple[name: string, value: string, parent: Token] proc handleAdd(t: Token): string = # 处理add类型Token的逻辑 result = "处理add:" & t.value proc handleSub(t: Token): string = # 处理sub类型Token的逻辑 result = "处理sub:" & t.value var grammarTable = initTable[string, proc(t: Token): string]() grammarTable["add"] = handleAdd grammarTable["sub"] = handleSub # 使用示例 let testToken = (name: "add", value: "1+2", parent: (name: "", value: "", parent: (name: "", value: "", parent: (name: "", value: "", parent: ()))) echo grammarTable[testToken.name](testToken)
- 编译期自动生成函数表
利用Nim的宏特性,自动收集符合规则的处理函数并生成表,无需手动注册:
import macros, tables type Token* = tuple[name: string, value: string, parent: Token] # 定义宏自动收集处理函数 macro registerHandlers(): untyped = result = newStmtList() var tableDef = newIdentNode("grammarTable") result.add newVarStmt(tableDef, newCall(ident("initTable"), ident("string"), ident("proc(t: Token): string"))) # 遍历模块中符合签名的函数 for sym in getModuleSymbols(): if sym.kind == skProc and sym.params.len == 1 and sym.params[0].typ.name == "Token" and sym.resultType.name == "string": # 提取函数名中的Token类型(如handleAdd对应"add") let tokenName = sym.name.substr(6) # 去掉前缀"handle" result.add newCall(tableDef, ident("[]="), newStrLitNode(tokenName), sym.name) result # 定义处理函数,统一使用handle前缀 proc handleAdd(t: Token): string = "处理add:" & t.value proc handleSub(t: Token): string = "处理sub:" & t.value # 编译期生成函数表 registerHandlers() # 使用示例 let testToken = (name: "add", value: "1+2", parent: (name: "", value: "", parent: (name: "", value: "", parent: (name: "", value: "", parent: ()))) echo grammarTable[testToken.name](testToken)
二、更规范的实现方案
如果追求类型安全与编译期检查,推荐以下两种方案:
- 枚举+模式匹配
将Token类型定义为枚举,通过模式匹配处理,编译期即可检查是否有未处理的Token类型:
type TokenKind* = enum tkAdd, tkSub Token* = object kind: TokenKind value: string parent: Token proc processToken(t: Token): string = case t.kind: of tkAdd: result = "处理add:" & t.value of tkSub: result = "处理sub:" & t.value # 使用示例 let testToken = Token(kind: tkAdd, value: "1+2", parent: Token(kind: tkAdd, value: "", parent: Token(kind: tkAdd, value: "", parent: Token()))) echo processToken(testToken)
- 对象继承+多态
为每种Token定义对应类,实现统一处理方法,利用多态调用实现扩展:
type Token* = ref object of RootObj value: string parent: Token AddToken* = ref object of Token SubToken* = ref object of Token proc process(t: Token): string {.base.} = "" proc process(t: AddToken): string = "处理add:" & t.value proc process(t: SubToken): string = "处理sub:" & t.value # 使用示例 let testToken = AddToken(value: "1+2", parent: AddToken(value: "", parent: AddToken(value: "", parent: Token()))) echo testToken.process()
内容的提问来源于stack exchange,提问作者agatzan
相关产品推荐
相关产品推荐

