是的,对于生成器来说很难猜测语法设计者的意图
以endline
为例,这里展示一下如何生成cSYM
function visit_endline(nodes)
var idx = 0
# Condition
block
var matched = false
if !matched && (typeid nodes[idx] == typeid parsergen.token_type && nodes[idx].type == "endl")
matched = true
# Visit endl token
++idx; target.println(""); csym_map.push_back(nodes[idx - 1].pos[1] - 1)
end
if !matched && (typeid nodes[idx] == typeid parsergen.token_type && nodes[idx].data == ";")
matched = true
# Visit term ";"
++idx; target.println(""); csym_map.push_back(nodes[idx - 1].pos[1])
end
if !matched
# Error
return
end
end
end
这里 csym_map
是一个数组,我们只需要在每次打印换行的时候同步在数组里添加一个对应的行号就可以了。ParserGen 会自动给所有的词元插入位置信息,所以我们只需要定位到最近的词元并通过 pos
成员拿到所在的行号
这里有一个小 Tricky,对于换行符来说,应该把行号减一,否则就对应到下一行去了
然后就可以生成最终的 cSYM 文件了
var ofs = iostream.ofstream("./a.csym")
var dbg_info = "#$cSYM/1.0(" + gen.file_name + "):"
foreach it in gen.csym_map do dbg_info += to_string(it) + ","
dbg_info.cut(1)
ofs.println(dbg_info)
foreach line in gen.code_buff do ofs.println(line)
首先就是生成 cSYM 头,直接拼接字符串就可以了
然后因为 ParserGen 会保存文件缓存,所以直接将 gen.code_buff
输出到文件里即可
这样我们就得到一个 .csym
文件,可以通过 read_csym.csc
来验证
read_csym.csc
的参数是不带后缀的文件名。一般我们在生成的时候,如 a.cpl
,会分别生成 a.csc
和 a.csym
,也就是说文件名相同而后缀名不同。这种情况下当运行命令:
cs misc/read_csym.csc a
程序会分别读取 a.csc
和 a.csym
最后,加了一下小功能,目前可以做到:
ecs cpl.ecs run 源文件
和
ecs cpl.ecs debug 源文件
会分别调用解释器和调试器
虽然目前解释器和调试器的报错和交互还是英文的,但我们也有办法翻译
后面我会想办法增加本地化的功能
可以看到,Cov3 本身作为一门图灵完全、自举的通用型动态语言,去支持一门中文编程语言可以说是绰绰有余。就算需要更复杂的语义,在 AST Visitor 的基础上增加类型检查之类的也非常简单,或者遍历生成 CFG 实现代码分析、补全等
未来我还会进一步完善以 CovScript 为核心的语言生态,不仅服务于 Cov 自身,也服务于更多热爱 PL 的朋友们
本帖暂时完结
1 个赞
统计了一下,算上生成的代码,一共也就1000行,简单的很