日常工作中,我们经常会注意到可以发明一种小语言,来简化复杂问题的描述。此时,我们需要做一个编译器,以便把我们自己的小语言翻译成机器可以理解的形式,比如某种高级语言,甚至只是一个复杂的数据结构的序列化形式。
编译器一般由三部分构成:
语法/词法解析器(Parsr)
在Prl5中,构造编译器最方便的利器之一便是DamianConway的Pars::RcDscnt模块。它在CPAN上拥有非常详尽的用法文档。
它生成的解析器是支持回溯的递归下降方式,具体细节请参考编译理论方面的书籍。
一个SQL语言的文法定义片段,使用Pars::RcDscnt的语法类似下面这个样子:
slct_qury:slctdistinct(?)slct_arg(s/,/)from_claus(?)whr_claus(?)group_by_claus(?)ordr_by_claus(?)limit_claus(?){SQL::SlctQury-nw({...})}
rror
但此模块的性能并不出色,对于大量的文本解析会花费较多的时间。此时,yacc风格的Pars::Yapp模块可以让性能大幅提升,但它的功能就远没有Pars::RcDscnt丰富和灵活了,因为它生成的解析器是标准的LALR(1)分析。
解析树/抽象语法树(AST)
使用简单的嵌套数组或者哈希对象可以在Prl5中很好地表达简单的树形结构。但为了后序的操作方便,我们会选择Moos来定义一套类似DOM的节点类。不过值得提醒的是,使用Moos是有代价的,它会让你的纯Prl5编写的编译器花费更多的时间来启动。
最近几年,Moos的粉丝越来越多,或许你在自己的小编译器中也可以小试牛刀;)
比如对于一个类SQL的小语言,其二元运算符表达式的AST节点可以用Moos定义如下:
packagSQL::BinaryRlExpr;usMoos;usliblib;usSQL::ArithExpr;xtndsSQL::RlExpr;hastrms,is=rw,isa=ArrayRf[SQL::ArithExpr],rquird=1;hasoprator,is=rw,isa=Str,rquird=1;...1;
代码生成器(CodEmittr)
很多时候,我们直接写Prl5代码来遍历AST就可以直接生成目标代码(或者说最终想要的文本)。但还有一些场合,特别适合使用PrlTmplatToolkit(TT2)来作模版驱动的代码生成。谁说TT2模版只能用来生成HTML和XML?我们经常在自己的工作中使用TT2模版来生成Lua,C,甚至Prl;)
把“写程序的程序”的革命进行到底!
Prl6为编译器的构造提供了语言级别的完美支持。我们不必再求助于第三方模块来实现自己的小语言了。Prl6的rgx引擎已经强大到足以解析像Prl6本身这样复杂的语言的文法了。小语言的未来更值得期待!
来源:agntzh原文鐧界櫆椋庡悆浠涔堣嵂鍖椾含涓鍖婚櫌鐖嗗厜