Coco/R快速编译器生成

Coco/R 更Cool些,Coco/R 使用增强的 EBNF 文法建立递归下降分析的分析器(Recursive Descent Parser)和扫描器(Scanner)。Coco/R 除了提供调用分析器的主程序模块之外,还提供语义(semantic )模块在文法之中调用(如,符号表处理和代码生成器),可以直接在grammer 内写语意动作【用你自己喜欢的语言。俺是中意Delphi】。

Coco/R (Cocol/R) 的输入语言基于属性文法, 属性文法是由Knuth 作为一种上下文无关的形式化语义语言引入的。
最初编译器完成的分析是静态语义分析如(C语言)。它们描述没有给定顺序的符号属性之间的依赖性, 它们包括构造符号表、记录声明中建立的名字的含义、在表达式和语句中进行类型推断和类型检查以及在语言的类型规则作用域内判断它们的正确性。当时的许多编译器都使用这样的静态语义分析。

Coco/R 是对Coco [ReM?89]版本的增强。它们之间的主要不同在于Coco/R 使用了递归下降分析代替表驱动方式的分析。Coco/R 集成扫描器和分析器描述,避免了在创建的部分之间接口的问题。

http://www.ssw.uni-linz.ac.at/coco/是它的主页
之前我第一个版本的 SqlBroker 是用 VB6 手写词法分析和语法分析器的, 感谢老外写的那本编译原理, 之前看国人的教材,愣是看了和没看没什么区别, 到了 Persistore 的时候,决定重写 SqlBroker,因为C#下还是有些从 Java 移植过来的 Parser Generator 的,于是我找啊找,比较了几个,最后选择了 Coco/R,主要原因是它不用引用额外的 DLL,而且语法很易学,例子也够多,SQL 的也有一个,便于我抄袭。

彼时Antlr也看到了,但其语法实在是有点复杂,Options 就一堆,有点晕,现在 Persistore 因为其复杂性已经被我遗弃了,但 SqlBroker 仍然是一个非常有价值的东西被我用到新的持久层--ObjectDataSet上了,因为 Coco/R对LL(K)的冲突解决并不好,必须加入额外的函数进行 nexttoken 读取并处理,所以我现在准备把 SqlBroker 的语法分析使用 Antlr 来实现。

这几天学习了一下 Antlr的语法(有时候想,这种 Parser Generator 真是好,可以实现自编译,像 Coco/R 本身的 grammer 文件的 parse 就是用它自己生成的 Parser去完成的,这样可以一直递归优化),感觉确实很强大,怪不得成了 java 世界里最流行的不二选择,C# 版本也有,不过命令行工具仍然是调用jar来完成的。

Coco/R 虽然不够 Antlr 强大,但它确实有它的独门利器,举个例子:

COMMENTS FROM "/*" TO "*/"
COMMENTS FROM "--" TO eol

IGNORE eol + cr + tab


换成Antlr,就麻烦多了:

protected
ML_COMMENT
:"/*"
({ LA(2)!='/' }? '*'
|'\n' { newline(); }
|~('*'|'\n')
)*
"*/"
{ $setType(Token.SKIP); }
;

看到没,必须用$setType来标明这个Token不会进入parser,只要碰到符合此条件的,就一律跳过。还有,Coco/R 可以很方便地支持像 python 那种缩进式的语法分析,不过具体如何弄我忘了。Antlr 也有 Python 语法文件,具体如何处理我还不是很清楚。

Coco/R 我用的时间比较长,简单易学,可以很快上手,但对于一些复杂的语法有点力不从心,而 Antlr 很强大,整个社区也很活跃,相比之下,Coco/R 的圈子要小得多,以前我用的一个版本对于 String 的 Scan 会有一个问题,我是发邮件给作者的,不过作者很快就回信了并给出了对应的修复手法,很PF并感谢他。

在初步使用 Antlr 的过程中,碰到一些小麻烦,比如说:
Parser 必须为小写字母开头,Lexer 必须大写开头(不知这样原因何在)
对于参数的定义及初始化,初始化要写在 Assignment 的前面,因为 Assignment 会在 try 内执行的

附上一个简单的 parser (只能处理 select a,b,c....),通过这个例子,我主要是学习了Antlr 的 Lexer 定义,参数使用,基本上对我移植SqlBroker无障碍了。

class SQLParser extends Parser;
options
{
exportVocab = SQL;
buildAST = false;
k = 2;
}
ansisql
returns [Node parent]
{parent = new Node();}
:
SELECT fieldlist[parent] EOF
;

fieldlist
[Node parent]
{Node f = new Node();}
:
f = field
{parent.next = f;}
(COMMA fieldlist[f])*
;

field
returns [Node node]
{node = new Node();}
:
id :ID
{node.value = id.getText();}
;
coco\r是一个类似于YACC/LEX功能的语言编写工具。
根据例子,对着葫芦画瓢,测试了一个计算器程序。
计算器的ATG:

---------------------------------------------------------------------------------------------------------------
COMPILER calc


CHARACTERS
digit = "0123456789".
cr = '\r'.
ln = '\n'.
tab = '\t'.

TOKENS
number=digit{digit}['.'{digit}].

COMMENTS FROM "//" TO cr ln
IGNORE cr+ln+tab

PRODUCTIONS

OPERAND<out double val>
= (. val=0;.)
(
number (. val = Double.Parse(t.val);
.)
|"(" EXPR<out val> ")"
).

EXPR10<out double val>
= (. bool neg=false; .)
{
('-' (. neg=!neg; .)
|'+' (. .)
)
}
OPERAND<out val> (. if(neg) val*=-1; .)
.

EXPR09<out double val>
=
EXPR10<out val>
{ (. double val2; .)
('*'
EXPR10<out val2> (. val*=val2; .)
|'/'
EXPR10<out val2> (. val/=val2; .)
)
}
.

EXPR<out double val>
=
EXPR09<out val>
{
('+'
EXPR09<out val2> (. val+=val2; .)
|'-'
EXPR09<out val2> (. val-=val2; .)
)
}
.
calc
=
EXPR<out result>.
END calc.

需要的Ubuntu工具包:
mono-gmcs coco-cs

编译过程:
1、编写ATG文件
2、产生Parser和Scanner文件
cococs -frames /usr/share/coco-cs calc.TAG
3、编写调用程序:

----------------------------
using System;

public class Calc{
public static void Main(String[] args){
Parser parser=new Parser(new Scanner("calc.test"));
parser.Parse();
Console.WriteLine(parser.result);
}
}
----------------------------

用mono编译以前,需要手工调节一下Parser.cs的内容:
添加两行:
1、在Parser类里面添加:
public double result=0;
2、在Parser的EXPR方法里面添加:
double val2;

3、用mono编译
gmcs *.cs

4、测试,编写一个calc.test文件,内容譬如
3*12+1

5、运行
mono calc.exe
输出结果:
37

如果测试运算比较复杂,可以用bc来检验一下结果,输入:
less calc.test | bc

应该没有问题。

全部代码下载: 这里

另外mono产生的calc.exe文件,可以直接在有.Net Framework 2.0环境的Windows下面运行。
mono感觉还是非常不错的!赞一个。

你可能感兴趣的:(sql,c,python,ubuntu,Delphi)