文章使用Word博客工具发布
代码以及文档请参考文末下载链接
《编译原理》课程设计报告
目录
1
一、目的 1
二、内容及要求 1
三、实现原理 1
ANTLR及实现原理 1
(一)计算器 3
1.功能 3
2. g文件 3
3.代码文件及使用 5
(二)关系代数到SQL语句 9
1.功能 10
2.g文件 10
3.代码文件及使用 12
(三) C语言子集一遍扫描编译 14
1.词法分析 14
2.语法分析 14
3.语义分析 15
四、算法实现流程图 15
(一)计算器 15
1.词法分析及语法分析 16
2.语义分析 17
(二) 关系代数到SQL语句 18
(三) C语言子集一遍扫描编译 21
1.主程序 21
2.词法分析 21
3.语法分析 21
五、测试数据 23
(一)计算器 23
(二)关系代数到SQL语句 24
(三) C语言子集一遍扫描编译 25
六、结果输出及分析 25
(一)计算器 25
1.输出结果 25
2.分析 26
(二) 关系代数到SQL语句 32
1.输出结果 32
2.分析 32
(三) C语言子集一遍扫描编译 35
七、软件运行环境及限制 37
(一)C#版计算器及关系代数到SQL语句 37
(二)Java版计算器及关系代数到SQL语句 38
(三)C语言子集一遍扫描编译 38
八、心得体会 38
九、参考文献 39
十、附录 39
(一)C#版计算机及关系代数到SQL语句 39
(二)Java版计算机及关系代数到SQL语句 39
1.计算器 39
2.关系代数到SQL语句 40
(三)C语言子集一遍扫描编译 41
通过课程设计进一步理解高级语言在计算机中的执行过程,加深对编译原理中重点算法和编译技术的理解,提高自己的编程能力,培养好的程序设计风格。同时通过某种可视化编程语言的应用,具备初步的Windows环境下的编程思想。
按照实例学习使用ANTLR(ANother Tool for Language Recognition)。具体内容是学习实践利用ANTLR从关系代数到SQL语句的转化实现过程,深刻理解编译理论在软件开发过程中的实际应用价值,以此来学习将程序设计与编译理论相结合的现代高效编程方法和技术。进一步加深对编译理论的理解,并把理论应用于实践中,还可以引起同学们对编译器设计的思考。
要求使用ANTLR实现一个计算器,之后再利用ANTLR实现从关系代数到SQL语句的转化。
实现语言ANTLR-3.4 C#以及Java,这里仅介绍C#版,Java版与C#版原理基本一样,主要是{}中的代码由于与具体语言有关,故实现不一样,Java版附上了源文件及测试数据和运行结果(见附录)。C#版与Java版功能一样。
计算器中实现了要求添加的功能(1)浮点(2)括号(3)负数(4)指数(5)三角(6)识别变量(7)人性化提示(即输入有误的时候,能给出提示)。
关系代数添加了(1)块注释功能(2)输出空行功能(3)一次输入多个数据(4)错误提示的功能。
ANTLR是由旧金山大学的Terence Parr 领导开发的(以前叫做PCCTS,Purdue Compiler Construction Tool Set,普渡大学编译器构建工具集),是一种分析器自动生成工具,它可以接受语言的文法描述,并能产生识别这些语言的程序。它还允许编程员在文法描述中插入特定的语义动作(可以使用Java,C++,C#或者Python语言,其生成的分析器默认使用Java描述),告诉ANTLR怎样去创建抽象语法树(AST)和怎样产生输出。
ANTLR是一种基于LL(k)文法的语法分析程序(以下简称分析器)生成工具。ANTLR可以构造识别程序并且将语法结构应用到三种不同的输入上:(i) 字符流, (ii) 标记(token)流,(iii) 二维树结构。本质上这些对应了词法分析器(Lexer),解析器(Parser)和Tree Walker。用于制定这些语法(Grammar)的句法(Syntax),被称为meta-language,在所有情况下是独立的。
ANTLR通过对.g文件的读取,生成与.g文件中的巴克斯-瑙尔范式相应的词法与语法分析器。
程序运行时词法分析器(lexer)读取字符流(characters),分析出单词符号(tokens),语法分析器(parser)对单词进行语法分析,生成数据(符号表,抽象语法树等等),语义分析器(tree walker)对语法树进行语义分析,输出特定的格式或运算结果。如下面两图所示。
本计算器,首先分析一个计算表达式组成的语句串,然后计算出表达式的结果并输出。计算器中能实现以下功能:
2.1. Calc.g文件
grammar Calc;
options
{
output=AST;
ASTLabelType=CommonTree;
language=CSharp3;
}
tokens
{
NEGSYM;
}
public
prog: ( stat {System.Console.WriteLine($stat.tree==null?"null":$stat.tree.ToStringTree());} )+ ;
stat: expr NEWLINE -> expr
| ID '=' expr NEWLINE -> ^('=' ID expr)
| NEWLINE ->
;
expr: multExpr (('+'^ |'-' ^) multExpr)*;
multExpr: atomSYM (('*'^ |'/' ^) atomSYM)* ;
atomSYM :
'-' atom -> ^(NEGSYM atom)
| atom
;
atom: INT
| ID
| DOUBLE
| '(' ! expr ')' !
| 'Sin''(' ! expr')' !
| 'Cos''(' ! expr')' !
| 'Tan''(' ! expr')' !
| '^''(' ! expr ',' ! expr')' !
| 'Ln''(' ! expr')' !
| 'Lg''(' ! expr')' !
| 'Log''(' ! expr ',' ! expr')' !
;
ID : ('a'..'z'|'A'..'Z')+ ;
INT : '0'..'9'+ ;
DOUBLE :'_'?
(('0'..'9')+ '.' ('0'..'9')* EXPONENT?
| '.' ('0'..'9')+ EXPONENT?
| ('0'..'9')+ EXPONENT)
;
WS : (' '|'\t'|'\r'|'\n')+ { Skip(); } ;
NEWLINE: (';')+ ;
EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;
2.2. CalcTree.g文件
tree grammar CalcTree;
options
{
tokenVocab=Calc;
ASTLabelType=CommonTree;
language=CSharp3;
}
@header
{
using System;
using System.Collections;
}
@members { Hashtable memory = new Hashtable();}
public
prog: stat+ ;
public
stat: expr {System.Console.WriteLine($expr.value);}
| ^('=' ID expr) {memory.Add($ID.text, $expr.value);}
;
public
expr returns [double value]
: ^('+' a=expr b=expr){$value = a+b;}
| ^('-' a=expr b=expr){$value = a-b;}
| ^('*' a=expr b=expr) {$value = a*b;}
| ^('/' a=expr b=expr) {$value = a/b;}
| ^(NEGSYM a=expr ) {$value = -1*a;}
| #('^' a=expr b=expr) {$value = Math.Pow(a,b);}
| #('Sin' x=expr) {$value=Math.Sin(x);}
| #('Cos' x=expr){$value=Math.Cos(x);}
| #('Tan' x=expr){$value=Math.Tan(x);}
| #('Ln' x=expr){$value=Math.Log(x);}
| #('Lg' x=expr){$value=Math.Log10(x);}
| #('Log' a=expr b=expr){$value=Math.Log(a)/Math.Log(b);}
| ID
{
$value = 0;
if ( memory.Contains($ID.text)) $value = (double)memory[$ID.text];
else System.Console.WriteLine("undefined variable "+$ID.text);
}
| INT
{
$value = (double)System.Convert.ToDouble($INT.text);
}
| DOUBLE
{
$value = System.Convert.ToDouble($DOUBLE.text);
}
;
使用antlrworks把g文件生成了相应的C#代码文件。可使用CTRL+SHIFT+G键生成,CTRL+R可检测语法错误。启动antlrworks 1.4.3使用java –jar antlrworks-1.4.3.jar,见下图:
Calc.g文件经编译生成了CalcLexer.cs,CalcParser.cs,Calc.tokens文件。
CalcTree.g文件经编译生成了CalcTree.cs,CalcTree.tokens文件。
然后在Visual Studio 2010中新建一个Calc项目,同时添加ANTLR运行库文件Antlr3.Runtime.dll。并且把生成的文件添加到Calc项目中,并且增加input.txt输入文件(存放输入数据)以及Calc.cs主文件(Main函数所在)。最终完成后如图所示:
为了不必每次都要重新添加g文件生成的代码到文件,让他们自动替换,我把antlrworks的代码生成目录设置为Calc项目代码所在文件。见下图
现在就该编写Calc.cs文件了,文件代码如下:
using System;
using System.Collections.Generic;
using System.Text;
using Antlr.Runtime;
using Antlr.Runtime.Tree;
using System.IO;
namespace Calc
{
class Calc
{
public static void Main(String[] args)
{
try
{
Console.WriteLine("CSharp计算器 V1.0 By wangyun 12.6.6\n");
Console.WriteLine("【输入】");
Console.WriteLine("从input.txt文件读取表达式...");
string filePath = "input.txt";
FileStream forOutput = File.Open(filePath, FileMode.Open);
StreamReader sr = new StreamReader(forOutput);
Console.WriteLine("读入表达式:");
Console.WriteLine(sr.ReadToEnd());
Console.WriteLine("表达式读入已完成");
sr.Close();
forOutput.Close();
FileStream fs = File.Open(filePath, FileMode.Open);
ANTLRInputStream input = new ANTLRInputStream(fs);
fs.Close();
CalcLexer lexer = new CalcLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CalcParser parser = new CalcParser(tokens);
Console.WriteLine("\n【输出】");
Console.WriteLine("语法树:");
AstParserRuleReturnScope<CommonTree, IToken> t = parser.prog();//取得语法树
CommonTree ct = (CommonTree)t.Tree;
CommonTreeNodeStream nodes = new CommonTreeNodeStream(ct);
CalcTree walker = new CalcTree(nodes);
Console.WriteLine("计算结果:");
walker.prog();//计算结果
}
catch (System.Exception ex)
{
Console.Write("出现错误:");
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
}
}
然后在input.txt文件里面输入需要测试的语句,保存,然后生成Calc。之后运行就可查看结果。这里以3.0+(-4);为例,input文件及运行结果如下:
关系代数是对关系运算的综合描述,它以集合代数为基础,以关系为运算对象。传统的集合运算有:并、差、交,专门的集合运算有:投影、选择、连接等。传统的集合运算是从关系的水平方向进行的,专门的集合运算既可以从关系的水平方向进行,也可以从关系的垂直方向进行。
可以使用扩展的巴克斯-瑙尔范式在计算机上描述关系代数语言,考虑到某些关系运算符号无法在计算机中显示,故作以下规定:
关系运算 |
运算符号 |
代替符号 |
Unoin |
* |
|
Intersection |
~ |
|
Difference |
- |
- |
Devide |
/ |
|
Projection |
H |
|
Selection |
G |
|
Join |
& |
|
And |
@ |
|
Or |
^ |
关系代数语言的符号表示描述为:
本程序实现关系代数到SQL语句的转换。
关系代数中有选择,投影,除,并,交,自然连接等六种常见的运算。在程序中,对于关系代数语言有如下限定:
1.选择和投影可以交错执行。选择和投影的运算符个数加起来,总和不超过12 (由temp,temp2数组的元素个数决定);
2.最多可以允许三个表做自然连接运算;
3.交,并运算单独进行,不可与自然连接,除混合进行;
4.除运算中,在作为"除数"的关系里,只有一层选择或投影的关系,不能包含自然连接,交,并运算。
根据以上的规定,可得到关系代数语言的词法规则,即终结符。
我在原来的功能上,增加了以下功能
根据以上功能,定义RelationToSQL.g文件如下
grammar RelationToSQL;
options
{
language=CSharp3;
}
WS:(' '|'\t'|'\n'|'\r'){Skip();};
PAI:'H';
IDENT:('a'..'z')+;
LEFT:'(';
RIGHT:')';
XIGEMA:'G';
EQUAL:'=';
NUM:('0'..'9')+;
AND:'@';
DOU:',';
OR:'^';
LESS:'<';
MORE:'>';
NEQUAL:'!=';
BING:'*';
JIAO:'~';
NJOIN:'&';
CHU:'/';
SEMI:';';
COMMENT
: '/*' ( options {greedy=false;} : . )* '*/' {$channel=TokenChannels.Hidden;}
;
CRLF : 'CR';
public
prog : ((choose|project|crlf) SEMI COMMENT*)+ ;
projectstart
: #(LEFT (projectsub|choosesub)* aa=IDENT RIGHT){System.Console.Write("\nFROM "+aa.Text);};
project : #(PAI projectColumns (loopProjectColumns)* projectstart (relation exprsub)*);
choose : #(XIGEMA chooseColumns (equal|notequal|small|big) (loopChooseColumns chooseColumnsAfterAndOr (equal|notequal|small|big))* (relation exprsub)*);
projectColumns
: (a=IDENT) {System.Console.Write("\nSELECT "+a.Text);};
chooseColumns
: (ac=IDENT) {System.Console.Write("\nWHERE "+ac.Text);};
loopProjectColumns
: #(b=DOU c=IDENT) {System.Console.Write(b.Text+c.Text);};
relation: (bin|jia|njo|chu);
bin : (BING) {System.Console.Write("\nUNION");};
jia : (JIAO) {System.Console.Write("\nINTERSECTION");};
njo : (NJOIN){System.Console.Write("\nNJOIN");};
chu : (CHU){System.Console.Write("\nCHU");};
exprsub : (choosesub|projectsub);
loopChooseColumns
: (AND) {System.Console.Write(" AND ");}|(OR) {System.Console.Write(" OR ");};
chooseColumnsAfterAndOr
: (aj=IDENT) {System.Console.Write(aj.Text);};
projectsub
: #(PAI projectColumns LEFT (projectsub|choosesub)* ab=IDENT RIGHT){System.Console.WriteLine("\nFROM "+ab.Text);};
choosesub
: #(XIGEMA chooseColumns (equal|notequal|small|big) (loopChooseColumns chooseColumnsAfterAndOr (equal|notequal|small|big))*);
equal : #(ae=EQUAL b=NUM) {System.Console.Write(ae.Text+b.Text);};
notequal: #(an=NEQUAL b=NUM) {System.Console.Write(an.Text+b.Text);};
small : #(af=LESS b=NUM) {System.Console.Write(af.Text+b.Text);};
big : #(ag=MORE b=NUM) {System.Console.Write(ag.Text+b.Text);};
crlf : CRLF{System.Console.WriteLine();};
RelationToSQL.g文件经antlrworks编译生成了RelationToSQLLexercs,RelationToSQLParser.cs,RelationToSQL.tokens文件。
然后在Visual Studio 2010中新建一个RelationToSQ项目(可与Cal在同一个解决方案中),同时添加ANTLR运行库文件Antlr3.Runtime.dll。并且把生成的文件添加到RelationToSQL项目中,并且增加input.txt输入文件(存放输入数据)以及RelationToSQL.cs主文件(Main函数所在)。最终完成后如图所示:
其他设置类似Calc,RelationToSQL.cs代码主文件如下:
using System;
using System.Collections.Generic;
using System.Text;
using Antlr.Runtime;
using Antlr.Runtime.Tree;
using System.IO;
namespace RelationToSQL
{
class RelationToSQL
{
public static void Main(String[] args)
{
try
{
Console.WriteLine("RelationToSQL CSharp V1.0 By wangyun 12.6.6\n");
Console.WriteLine("【输入】");
Console.WriteLine("从input.txt文件读取表达式...");
string filePath = "input.txt";
FileStream forOutput = File.Open(filePath, FileMode.Open);
StreamReader sr = new StreamReader(forOutput);
Console.WriteLine("读入表达式:");
Console.WriteLine(sr.ReadToEnd());
Console.WriteLine("表达式读入已完成");
sr.Close();
forOutput.Close();
FileStream fs = File.Open(filePath, FileMode.Open);
ANTLRInputStream input = new ANTLRInputStream(fs);
fs.Close();
RelationToSQLLexer lexer = new RelationToSQLLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
RelationToSQLParser parser = new RelationToSQLParser(tokens);
Console.WriteLine("\n【输出】");
Console.Write("输出的SQL语句为:");
parser.prog();
}
catch (System.Exception ex)
{
Console.Write("出现错误:");
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
}
}
1.1单词符号及其种别码如下表:
下面是用C语言对一个C语言的子集编制的一个一遍扫描的编译程序,其单词符号及其种别码的定义见表1。
表1
单词符号 |
类别码 |
单词符号 |
类别码 |
单词符号 |
类别码 |
||||
main |
1 |
- |
23 |
; |
34 |
||||
int |
2 |
* |
24 |
> |
35 |
||||
Char |
3 |
/ |
25 |
< |
36 |
||||
if |
4 |
( |
26 |
>= |
37 |
||||
else |
5 |
) |
27 |
<= |
38 |
||||
for |
6 |
[ |
28 |
== |
39 |
||||
while |
7 |
] |
29 |
!= |
40 |
||||
ID |
10 |
{ |
30 |
'\0' |
1000 |
||||
NUM |
20 |
} |
31 |
ERROR |
-1 |
||||
= |
21 |
, |
32 |
||||||
+ |
22 |
: |
33 |
1.2词法分析功能:
输入:无,待处理的程序段存储在数组里。
输出:二元组(种别码,token)构成的序列,token为单词自身字符串。
1.3词法分析过程:
词法分析是从字符串表示的源程序中识别出具有独立意义的单词符号。
2.1 C语言子集语法
用扩充的BNF表示如下:
(1)<程序>→main()<语句块>
(2)<语句块>→'{'<语句串>'}'
(3)<语句串>→<语句>{;<语句>};
(4)<语句>→<赋值语句>|<条件语句>|<循环语句>
(5)<赋值语句>→ID=<表达式>
(6)<条件语句>→if(<条件表达式>)<语句块>
(7)<循环语句>→while(<条件表达式>)<语句块>
(8)<条件表达式>→<表达式><关系运算符><表达式>
(9)<表达式>→<项>{+<项>|-<项>}
(10)<项>→<项>{*<因子>|/<因子>}
(11)<因子>→ID|NUM|(<表达式>)
(12)<关系运算符>→<|<=|>|>=|==|!=
2.2语法分析功能
对C语言程序段,进行语法分析,若是语法正确,则输出程序正确,否则进行报错。
3.1 C语言子集语义:
将语法分析提供的正确单词串,输出四元式序列。
如语句串:a=8*9+2;输出四元式序列如下:
1 *,2,3,T1
2 +, T1,4, T2
3 :,T2, , i
3.2语义分析主要函数
(1)int gen(op,arg1,arg2,result)
该函数是将四元式(op,arg1,arg2,result)送到四元式表中。
(2)Char *newtemp()
该函数回送一个新的临时变量名,临时变量名产生顺序为:T1,T2,T3…
(3)int merg(p1,p2)
该函数将以p1和p2为头指针的两条链合并为一,合并后的链首为返回值。
(4)int bp(p,t)
该函数将p所链接的每个四元式的第四段区都填t。
(5)void lrparse(void)
该函数进行语法分析和语义分析。
3.3语义分析过程
将输入串翻译成四元式序列,对表达式,int类型,Char类型,if语句,else语句,while语句作了翻译。
为了实现实验原理中的功能,定义了计算器的Calc.g(词法分析与语法分析)以及CalcTree.g(语义分析)。
本功能由Calc.g文件定义。
终结符有ID(变量)、INT(整型)、DOUBLE(浮点)、WS(WHITESPACE忽略空白空格换行等字符),NEWLINE,EXPONENT(辅助DOUBLE实现科学数表示):
非终结符有prog,stat,expr,mulExpr,atomSYM,atom:
本功能由CalcTree.g文件定义。
含有prog(处理语句),state(处理表达式),expr(处理各种运算,比如加减乘除)。
为了实现所说的功能,设计语法RelationToSQL如下:
终结符有PAI、IDENT、LPAREN、RPAREN、XIGEMA、EQUAL、NUM、AND、COMMA、OR、LESS、MORE、NEQUAL、UNION、INTERSECTION、NJOIN、DIV、SEMI,这些终结符的定义如前面所述,其中SEMI代表分号。另外还有WS(跳过空字符)、COMMENT(跳过块注释),CRLF(换行),块注释,换行的产生定义如下所示:
需要说明的是,在ANTLR中,"."代表任意一个可以识别的字符。
非终结符定义如下,其中prog是文法的开始符:
在此程序中,语句经过分析后并没有使用语法树,而是输出了相应的语句。
示意结构图(如下):
置初值 |
|
调用扫描子程序 |
|
输出单词四元组 |
|
直至输入串结束 |
扫描子程序(scaner)
首先设置三个变量:token用来存放构成单词符号的字符串;sum用来存放整型单词;syn用来存放单词的类别码。扫描子程序主要部分N—S图如下:
变量初始化 |
|||||
忽略空格 |
|||||
文件是否结束 |
|||||
T |
F |
||||
是否字母 |
|||||
T |
F |
||||
拼字符串 |
是否数字 |
||||
是否关键字 |
T |
F |
|||
T |
F |
拼数 |
是否运算符、界符等符号 |
||
syn为对应关键字的类别码 |
syn=10 |
syn=20 |
T |
F |
|
给出相应的syn值 |
报错 |
3.1.主程序结构示意图如下:
置初值 |
调用scaner读下一个单词符号 |
调用lrparser |
结束 |
3.2.递归下降分析程序结构示意图如下:
注:上接lrparser |
||
是否单词串main() |
||
T |
F |
|
调用scaner |
出错处理 |
|
调用语句块分析函数 |
||
源程序是否结束 |
||
T |
F |
|
输出分析成功 |
出错处理 |
3.3.语句块分析结构示意图。
是否{ |
||
T |
F |
|
调用scaner |
出错处理 |
|
调用语句串分析函数(过程) |
||
是否} |
||
T |
F |
|
出错处理 |
3.4.语句串分析结构示意图如下:
调用statement函数 |
|
当为;时 |
|
调用scaner |
|
调用statement函数 |
|
出错处理 |
3.5.statement函数N—S图如下:
是否标识符 |
||||
T |
F |
|||
调用scaner |
是否if |
|||
是否= |
T |
F |
||
T |
F |
调用scaner |
是否while |
|
调用scaner |
出错处理 |
调用condition |
T |
F |
调用expression |
调用语句块 |
调用scaner |
出错处理 |
|
调用condition |
||||
调用语句块 |
3.6.expression函数结构示意图如下:
调用term |
|
是否+、- |
|
T |
F |
调用scaner |
出错处理 |
调用term |
3.7.term函数结构示意图如下:
调用factor |
|
是否*、/ |
|
T |
F |
调用scaner |
出错处理 |
调用factor |
3.8.condition函数结构示意图如下:
调用expression |
|
是否逻辑运算符 |
|
T |
F |
调用scaner |
出错处理 |
调用expression |
3.9.factor函数结构示意图如下:
是否标识符 |
||||
T |
F |
|||
调用scaner |
是否数字 |
|||
T |
F |
|||
调用scaner |
是否( |
|||
T |
F |
|||
调用scaner |
出错处理 |
|||
调用expression |
||||
是否) |
||||
T |
F |
|||
调用scaner |
出错处理 |
4.语义分析
主程序示意图如下:
置初值 |
调用scaner |
…… |
调用lrparser |
打印四元式列表 |
结束 |
要求输入的算式为:2+3*3*5 ;5+6;4*3;3*6/10;输出结果截屏
自己补充的为
-3+4.0*5+(-5.0);
Sin(0.5)*5+5.0*Ln(5);
^(2,3);
4.3-(-4.5-4)*2+((3-(-3)*2/1)*5);
a=1;b=2;a+b;
c+e5;
此表达式测试了负数,浮点,三角,对数,幂函数,变量,人性化错误提示,括号。
输入数据时把表达式放在input.txt文件中,测试数据如下
2+3*3*5 ;
5+6;
4*3;
3*6/10;
-3+4.0*5+(-5.0);
Sin(0.5)*5+5.0*Ln(5);
^(2,3);
4.3-(-4.5-4)*2+((3-(-3)*2/1)*5);
a=1;b=2;a+b;
c+e5;
第一组:Hname,gender(student);
第二组:Gage>25;
第三组:Gscore>90*Gage<25;
第四组:Hname(student)~Hscore(course);
第五组:Ga>5@b!=3~Gc<6^d>8;
第六组:Ha(dd);
输入数据时把表达式放在input.txt文件中,测试数据如下,/**/表述其中的为注释,注释限定为只能放在每一行的末尾,CR表示输出一个空行,这样可以使输出结果不至于全混合在一起。
Hname,gender(student);/*this a comment*/
CR;/*change line*/
Gage>25;
CR;
Gscore>90*Gage<25;
CR;
Hname(student)~Hscore(course);
CR;
Ga>5@b!=3~Gc<6^d>8;
CR;
Ha(dd);
CR;
CLanguage.cpp文件第745行,修改str数组的相应内容即可。
这里使用两组测试数组:
第一组:main(){i=2*3-4;if(i<10) {j=3;}while (j>0) {k=5;}};
第二组:main(){j=2;while (j>0) {k=k-1;}};
举例如下(以第一组为例)
main(){i=2*3-4;if(i<10) {j=3;}while (j>0) {k=5;}}
上图为读入的数据(输入)
上图为输出的语法树,<NEGSYM 4.5>表示-4.5,最行一行出现错误,因为不符合语法规范。
上图为输出的运算结果,截图最后两行是c+e5;的错误,原因是c未定义,变量名只能是字母。
分析分别如图所示
2+3*3*5 ;
5+6;
4*3;
3*6/10;
-3+4.0*5+(-5.0);
Sin(0.5)*5+5.0*Ln(5);
^(2,3);
4.3-(-4.5-4)*2+((3-(-3)*2/1)*5);
a=1;b=2;a+b;
c+e5;
Hname,gender(student);/*this a comment*/
CR;/*change line*/
Gage>25;
Gscore>90*Gage<25;
Hname(student)~Hscore(course);
Ga>5@b!=3~Gc<6^d>8;
Ha(dd);
输出结果
(1)
(2)
使用语言:C#
操作系统:win7旗舰版
AntlrWorks版本:antlrworks-1.4.3.jar
编译器:Visual Studio 2010旗舰版
ANTLR运行库:Antlr3.Runtime.dll
使用语言:Java
操作系统:win7旗舰版
AntlrWorks版本:antlrworks-1.4.3.jar
编译器:Eclipse 3.6.2
ANTLR运行库:antlr-3.4-complete.jar
JDK:1.7.0-b147
使用语言:C
操作系统:win7旗舰版
编译器:Visual Studio 2010旗舰版
完成这个实验,收获了很多,也感概颇多。过程比较艰难,各种问题,特别是由于我做的C#,老师没有给相关的资料,于是上网查找Antlr C#相关的各种资料,一度想放弃,前面一直到第二天上午(耗费了一天半)卡在一个问题,即表达式的计算上,输入的表达式可以正确分析出Tokens,但计算时却出错了,后来回想,是g文件中代码有误。一直卡在这后,看到有几个人都做了出来,有点着急,于是决定用Java试一试,发现Java实现有老师给的各种资料,一路几乎没什么问题,当然实现负数还有遇到了一些问题,最近在6月5号晚上实现了Java版。后来明白整个实验后,想着C#版的失败,十分不甘心。于是决定用C#再次实现,经过努力,终于在6月6日凌晨3点左右完全实现。回去安心的睡了一觉直到9点多。哎,不容易啊!不过也再一次挑战了一下自己,成功的感觉很棒。
经过实验,也总结了一些感悟:
1.时间紧急的话,不要贸然尝试新的方法,老老实实的按照老师说的做。这样至少结果有一个保证,可以保质保量。C#版可以使用StringTemplate(stg文件,可替代g文件),但由于比较复杂,估计时间来不及,我就没有尝试。
2.要勇于尝试新的方法,在时间充足的条件下。不要老是限制在旧方法里,当然旧方法可以参考。当在一定的时间内,新方法进展不顺时,要考虑重新使用旧方法,毕竟最用你要完成任务。旧方法完成后,有时间再去尝试新方法。本次实验我做了1天半的C#无进展后,决定使用Java,完成Java版后,再用C#,顺利完成。
3.善于利用搜索引擎,当出现问题或者没头绪时,利用他们来获取你需要的信息。比如我完成"负数"功能时,就使用Google,搜索"antlr negative",获取了了一种新方法。
4.边做边学习。知识永远是无止境的。你不能把所有知识学完,更不可能提前在做实验前把所有知识先学完。这样费时费力,学了还不一定用得着。边做边学习,动手做可以加强印象,而且节省时间,大大提高自己的学习能力和效率。
5.要谦虚,乐于向师姐,老师以及其他同学请教。
6.对编译原理有了更深入的了解,同时也学会了编写一门新的语言。
最后感谢老师、师姐的帮助,感谢段熊春同学的建议,感谢周金辉同学翻译的ANTLR语法说明。
[1].编译原理.张素琴
[2].www.antlr.org
[3] .ANTLR Reference Manual
[4]. The Definitive ANTLR Reference. Terence Parr
注意C#与Java测试数据中函数名有所不同,比如三角函数Sin与sin,另外,c+e5;是为了验证程序能提示错误信息的。
代码请使用Visual Studio 2010打开
代码请使用Eclipse打开
测试数据及运算结果
测试数据
2+3*3*5 ;
5+6;
4*3;
3*6/10;
-3+4.0*5+(-5.0);
sin(0.5)*5+5.0*ln(5);
^(2,3);
4.3-(-4.5-4)*2+((3-(-3)*2/1)*5);
a=1;b=2;a+b;
c+e5;
运行结果:
测试数据及运算结果
测试数据:
Hname,gender(student);/*this a comment*/
CR;/*change line*/
Gage>25;
CR;
Gscore>90*Gage<25;
CR;
Hname(student)~Hscore(course);
CR;
Ga>5@b!=3~Gc<6^d>8;
CR;
Ha(dd);
CR;
运行结果:
代码请使用Visual Studio 2010打开