文档库 最新最全的文档下载
当前位置:文档库 › 中间代码生成

中间代码生成

中间代码生成
中间代码生成

计算机科学与工程学院

课程设计报告

题目全称:常用边缘算法的实现

学生学号: 2506203010 姓名:王嘉

指导老师:职称:

指导老师评语:

签字:课程设计成绩:

编译器中间代码生成的研究与实现

作者:王嘉学号:2506203010指导老师:吴洪

摘要:在编译器的翻译流水线中,中间代码生成是处于核心地位的关键步骤。它的实现基于语法分析器的框架,并为目标机器代码的实现提供依据。虽然在理论上没有中间代码生成器的编译器也可以工作,但这将会带来编译器的高复杂度,低稳定性和难移植性。现代编译理论不仅要求中间代码的生成,还要求基于中间代码的优化。本文研究并实现了一个轻量级类C语言的中间代码生成器,并就中间代码生成的原理进行了细致的阐述。

关键字:中间代码生成、语法制导翻译、翻译模式、语法树、三地址码

一、目的及意义

在编译器的分析综合模型中,前端将源程序翻译成一种中间表示,后端根据这个中间表示生成目标代码。目标语言的细节要尽可能限制在后端。尽管源程序可以直接翻译成目标语言,但使用与机器无关的中间形式具有以下优点:

1.重置目标比较容易:不同机器上的编译器可以在已有前端的基础上附近一个适合这

台新机器的后端来生成。

2.可以在中间表示上应用与机器无关的代码优化器。

本文介绍如何使用语法制导方法,基于一种轻量级的类C语言FineC的词法分析器和语法分析器,一遍地将源程序翻译成中间形式的编程语言结构,如声明、赋值及控制流语句。为简单起见,我们假定源程序已经经过一遍扫描,生成了相应的词法记号流和符号表、词素表结构。基于FineC语法标准的语法分析器框架也已经能够正常工作。我们的任务就是补充这个框架,使其在语法分析的过程中生成相应的中间代码,并将必要的变量和函数声明存放在一个符号表链中。

二、目标语言词法和语法标准:

这里定义一个编程语言称作FineC(“fine”指代轻量、精妙)。它是一种适合编译器设计方案的语言。本质上是C语言的一个限制了数据类型、算术操作、控制结构和处理效率的轻量子集。

1.FineC语言的词法描述:

[1]语言的关键字:

else if return while int void

所有的关键字都是保留字,并且必须是小写

[2]下面是专用符号:

+ - * / < <= > >= == != = ; , { } ( ) /* */

RELOP = {< <= > >= == !=}

ADDOP = {+ -}

MULOP = {* /}

[3]其他标记是NUM和ID,由正则表达式定义:

ID = letter (letter|digit)*

NUM = digit digit*

letter = a|…|z|A|…|Z

digit = 0|…|9

小写和大写字母是有区别的

[4]空格由空白、换行符和制表符组成。空格通常被忽略,除了它必须分开NUM、ID关

键字

[5]注释用通常的C语言符号/*…*/围起来。注释可以放在任何空白出现的位置(即注释

不能放在标记内)上,且可以超过一行。注释不能嵌套。不支持单行//注释。

FineC语言的词法分析器输出记号流,记号是一个二元组(tokentype, lexeme)。tokentype包含了记号的类型,lexeme包含记号的词素。例如一个标识符gcd的记号是(ID, 6)。6表示这个标识符在符号表的第7项里(与首元的距离是6,可以把这个整数看作指向符号表的指针)。词法分析器后面的步骤分析这个标识符时,就可以根据此指针访问符号表,并取出它的词素,也就是字符串“gcd”。又例如一个整型值36的记号是(NUM, 36)。这里的36不是指向符号表的指针,而是NUM类型的数值。编译器会根据tokentype决定lexeme的含义。

2.FineC语言的语法描述

语法分析器调用词法分析器,对源程序做一遍词法分析,返回的记号流放在缓冲区tokens中。在FineC的实践中,我们用一个vector容器来存放词法分析器返回的这些记号。语法分析器在这个缓冲区(容器)之上,进行匹配、回溯等必要的操作,实现语法分析。常见的语法分析方法有三种:带回溯的递归下降法、预测分析法(不带回溯的递归下降)以及常用于语法分析器自动生成的LR文法分析。前两者属于自顶向下的分析,后者属于自底向上的分析。FineC的语法分析器基于带回溯的递归下降法实现,在分析的过程中可能产生递归和回溯。当发生回溯时,意味着出现了某个记号的匹配失败,但在其之前某些记号可能已经被成功匹配并扫描。因此回溯到上层调用时,不仅要恢复指向记号流的指针,也需要考虑是否已经生成了无效的中间代码,并对其进行撤销。

语法分析器的原理和实现不是本文讨论的范畴,这里只给出FineC语言的文法标准和简单的语义解释,供中间代码生成时建立翻译模式使用:

(1)program --> declaration-list

程序由一个声明表组成

(2)declaration-list --> declaration declaration-list | declaration

声明表包含至少一条声明

(3)declaration --> var-declaration | fun-declaration

声明可以是变量声明或函数声明

(4)var-declaration --> “int”ID;

由于FineC只支持整型,所以变量声明也只有“int ID”的形式。注意,不支持变量声明时赋初值。

(5)fun-declaration --> type-specifier ID (params) compound-stmt

| type-specifier ID () compound-stmt

函数的返回类型可以是“int”或“void”,函数可以有参数也可以没有

(6) type-specifier --> "int" | "void"

(7) params --> param params | param

如果函数有形参,则至少要有一个参数

(8) param --> “int”ID

函数的形参也只支持“int”一种

(9) compound-stmt --> {local-declarations statement-list}

函数的主体是一个语句块,包含局部变量的声明和语句表。注意,所有的局部变量声明必须出现在语句之前。

(10) local-declarations --> var-declaration local-declarations | empty

局部变量声明可以为空,一个,或多个

(11) statement-list --> statement statement-list | empty

语句表也可以为空,或有一个或多个语句组成

(12) statement --> expression-stmt | compound-stmt | selection-stmt

| iteration-stmt | return-stmt

语句有五种类型,分别是表达式语句,语句块,选择语句,循环语句和返回语句(13) expression-stmt --> expression; | ;

表达式语句可以没有内容,或者由一个表达式构成。

(14) selection-stmt --> "if" (condition-expression) statement

| “if”(condition-expression) statement “else”statement 选择语句支持一路分支和二路分支。根据condition-expression的真假决定程序控制流的流向。

(15) iteration-stmt --> "while" (condition-expression) statement

循环语句只支持“while”的形式,while语句的含义与C语言一致。

(16) return-stmt --> “return”; | “return”expression;

返回语句可以返回值,也可以什么都不返回。

(17) expression --> ID = additive-expression | additive-expression

表达式可以是赋值语句或者加法运算语句,其中赋值语句返回ID的值。

(18) condition-expression --> additive-expression RELOP additive-expression

条件表达式比较两个整型值的关系,包括大于,小于,大于等于,小于等于,不等于和等于,根据其真值指向不同的语句流向

(19) additive-expression --> addtive-expression ADDOP term | term

(20) term --> term MULOP factor | factor

(21) factor --> (additive-expression) | ID | call | NUM

以上三条文法生成算术表达式,ADDOP包括加和减,MULOP包括乘除。运算的因子可以是变量,整数字面值,表达式或者函数返回的结果。这样安排文法是为了满足运算符的优先级和结合性。即加减比乘除优先级低,加减乘除都是左结合的。

(22) call --> ID (args) | ID ()

函数调用可以有实参也可以没有。

(23) args --> additive-expression, args | additive-expression

三、中间代码生成原理

1.中间代码生成器的作用:

中间代码生成器不是一个独立的部件,而是建立在语法分析器框架中的语义动作。可以把编译器比作一个流水线,源程序是源材料,词法分析器是过滤器,源程序经过词法分析器后形成记号流。语法分析器是一系列相互相关的函数,控制流在这些函数中的转移过程就是语法分析的过程。记号流比作精炼过的材料被送到语法分析器这个流水线上流动。如果没有中间代码生成动作,语法分析器就像是只有履带没有加工机的流水线,记号流流过之后没有任何变化。

由上面的比喻可以看出,中间代码生成和语法分析应该构成一遍(“遍”的概念请参考文献[1]),以词法分析生成的记号流作为输入,以某种表示程序结构的中间表示(语法树或三地址码)作为输出。生成的中间代码是介于源代码和目标代码中间的一种结构化表示。它的意义在于能够把前端和后端分开,提高编译器的可移植性(因为结构清晰,对于编译器研究者来说也提高了可读性)和易优化性(对中间代码的优化可以独立于机器)。

2.语法制导翻译:

语法制导翻译是一种实现翻译的思路,不仅可以用来指导中间代码生成。实际上,应用语法制导翻译的概念,可以实现任何基于语法分析器框架的语义动作,应用非常广泛。比如把源代码中的算术表达式中缀表示化成前缀表达式,或者类似数学排版语言EQN的构造等等。本文不对语法制导定义做广泛抑或深入的探讨,只简单介绍其基本原理,指导中间代码生成器的设计。

[1]语法制导定义:

语法制导定义是对上下文无关文法的推广,其中每个文法符号都有一个相关的属性集。属性分为两个子集,分别称为该文法符号的综合属性和继承属性。属性可以代表任何对象:

字符串、数字、类型、内存单元或其他对象。分析树节点上属性的值由该节点所用产生式的语义规则来定义。节点的综合属性是通过分析树中其子节点的属性值计算出来的;而继承属性值则是由该节点的兄弟节点和父节点的属性值计算出来的。

[2]语法制导定义的形式:

在语法制导定义中,每个产生式A --> a都有一个形如 b := f(c1,c2,…,ck)的语义规则集合与之相关联,其中f是函数,且满足下列两种情况之一:

i) b是A的一个综合属性,且c1,c2,…,ck是该产生式文法符号的属性

ii) b是产生式右部某个文法符号的一个继承属性,且c1,c2,…,ck也是该产生式文法符号的属性。

对这两种情况都称为属性b依赖于属性c1,c2,…,ck。有时,语法制导定义中的某个规则的目的就是为了产生副作用,比如输出某个字符串或者操作某个结构等。这种语义规则一般被写成过程调用或程序段。在针对语法制导定义的翻译中,把副作用看作非终结符的一个虚综合属性值处理会比较方便。

为了便于理解,以下给出一个台式计算器程序的语法制导定义

表1 台式计算器程序的语法制导定义

该定义将一个整数值综合属性val与每个非终结符E,T和F联系起来。对每个以E,T 和F为左部的产生式,语义规则从产生式右部非终结符的val值计算出产生式左部非终结符的val值。

记号digit具有综合属性lexval,其值由词法分析器提供,而产生式L→E n所对应的语义规则只是打印E所产生的算术表达式的值的过程,我们可以认为该规则为非终结符L 定义了一个虚属性。

3.翻译模式

语法制导定义只是给出了属性值的计算标准,并没有给出属性值的计算顺序。实际上,在一个产生式中,右端非终结符的综合属性、继承属性、左端符号的继承属性的计算顺序是有要求的。否则,则会出现引用尚未得到的值的翻译错误。由语法制导定义可以得到某种特定的翻译模式,这个翻译模式不仅包含了语法制导定义的属性关系,还包括了计算它们的顺序。当得到一个具体的翻译模式后,编码将变得非常简单。

[1]L 属性定义

在介绍翻译模式之前,先介绍一种属性定义:L 属性定义。它的特点是其属性总可以用深度优先顺序来计算。基于带回溯的递归下降分析法的语法分析器和L 属性结合,能够对词法记号流进行自顶向下的一遍翻译。

L 属性的规范化定义是:一个语法制导定义是L 属性定义,如果对每个产生式A →X1X2…Xn ,其右部符号Xj (1<= j <= n )的每个继承属性仅依赖于下列属性: i) 产生式中Xj 左边的符号X1,X2,…,Xj-1的属性。 ii) A 的继承属性。

只含有综合属性的语法制导定义也是L 属性定义,因为L 属性定义只规定了继承属性。 [2]翻译模式

翻译模式就是将文法符号同属性相关联的上下文无关文法,而且包含在{ }中的语义动作可以插入产生式右部的任何一个位置。本文所考虑的翻译模式可以同时具有综合属性和继承属性。

下面是一个简单的翻译模式,它把带有加号和减号的中缀表达式翻译成后缀表达式。

表2 一个简单的翻译模式

下图是输入串9-5+2的分析树,每个语义动作都作为产生式左部所对应节点的子节点。实际上,我们将语义动作看成终结符,对确定动作何时执行来说,它是一种方便的助记符号。图中用具体的数字和加(减)运算符代替了记号num 和addop 。当以深度优先顺序执行下图中的动作时,其输出为95-2+。

图1 9-5+2的带有动作的分析树

E

T R

T

{print(‘9’)} -

9

5

R

{print(‘5’)}

{print(‘-’)}

R

+ T 2

{print(‘2’)}

{print(‘+’)} ?

在构造翻译模式时,要注意,如果同时存在继承属性和综合属性,则 (1)产生式右部符号的继承属性必须在这个符号以前的动作中计算出来。 (2)一个动作不能引用该动作右边符号的综合属性。

(3)产生式左部非终结符的综合属性只有在其引用的所有属性都计算出来以后才能计算。计算该属性的动作通常放在产生式右部的末尾。

从一个L 属性语法制导定义,我们总能构造出满足上述3个条件的翻译模式。 4. 语法树

语法树是分析树的压缩形式,对表示语言的结构很有用。如产生式S → if B then S1 else S2 在语法树中可能出现为:

图2 if-then-else 语句的语法树

在语法树中,运算符和关键字不再是叶节点,而是作为内部节点成为分析树中叶节点的父节点。语法树的另一个简化是单产生式(形如A → B )链可能消失

mknode(head, descent1, descent2, …)和mkleaf(ID, id.place)是基于翻译模式构造语法树的两种动作。前者建立一个树节点,如mknode(+, a, b)表示a+b 的结构;后者建立一个叶节点,id.place 指向标识符在符号表中的位置。语法树有两种表示,一种是指针式,另一种是数组式。数组式用数组的下标取代节点的地址,是指针的另一种表现形式而已,本质上没有大的差别。树的实现算法可以参考《数据结构》。 本文的讨论重点是三地址码的生成,故不对语法树做详细的阐述。

5. 三地址码

三地址码是下列一般形式的语句序列: x := y op z 。其中,x 、y 以及z 是名字、常量或编译器生成的临时变量;op 代表操作符,例如定点或者浮点算术操作符,或者是布尔型变量的逻辑操作符。注意这里不允许组合的算术表达式,因为语句右边只有一个操作符。这样,像x + y * z 这样的源语言表达式应该被翻译成如下序列: t1 := y * z t2 := x + t1

t1与t2是编译器生成的临时变量。这种复杂表达式及嵌套控制流语句的拆解使得三地址码适合于目标代码生成及优化。由程序计算出来的中间值的名字的使用使得三地址码容易被重新排列。

if-then-else

B

S1

S2

通用的三地址语句有以下几种

(1) 形如x := y op z的赋值语句,其中,op是二元算术操作符或逻辑操作符。

(2) 形如x := op y的赋值指令,其中op是一元操作符。

(3) 形如x := y的复制语句,将y的值赋给x。

(4) 无条件跳转语句goto L101。接下来将执行标号为L101的三地址语句。

(5) 形如if x relop y goto L101的条件跳转语句。这条指令对x和y应用逻辑操作符(<, ==, >=等),如果x与y满足relop关系则执行带有标号L101的语句。如果不满足,紧接着执行if x relop y goto L101后面的语句,与通常的顺序一样。

(6) 过程调用param x和call p及return y,其中y代表一个返回值,是可选的。它们的典型应用如下面的三地址语句序列:

param x1

param x2

param xn

call p

作为过程调用p(x1,x2,…,xn)的一部分生成。

6.中间代码生成

[1]三地址码的实现

三地址码的实现方法为四元式。四元式是带有四个域的记录结构,即op,arg1,arg2以及result。其中视三地址码的类型,某些域可能为空。arg1,arg2以及result域的内容正常情况下是指向这些域所代表的名字在符号表表项的指针。这样的话,临时名字在生成时一定要被填入符号表。对于支持作用域规则的语言,生成三地址码时需要有一个符号表栈,栈顶的符号表代表当前作用域。源代码中的Par_table_chain结构实现了这个栈及其必须支持的操作。

[2]声明语句的翻译

这里用FineC语言声明语句的翻译模式说明声明语句翻译的原理。FineC语言声明语句的翻译模式及其解释如下:

I.declaration-list --> declaration declaration-list1 | declaration1 这条文法可以产生至少一个声明,declaration-list是起始符

II.declaration --> var-declaration

| { Par_table_chain.mktable() } fun-declaration

{Par_table_chain.jumpout()}这条文法有两个候选式,第一个候选式var-declaration匹配变量声明;当地一个候选式匹配失败时,说明当前记号流是一个函数声明。进入一个函数声明后,会产生一个新的作用域,一个作用域对应一个符号表,即Par_table。在这个函数声明中声明的变量如果和外围作用域的变量重名,则应该在这个函数的符号表中加入这一变量。

{ Par_table_chain.mktable() }在进入下面的函数声明语法分析前,先生成一个新的符号

表,并把它置为当前符号表。外围符号表被挂起,外围符号表的地址被赋予新表。符号表链Par_table_chain实际上是一个栈式结构,它的成员是有相互嵌套关系的符号表。函数声明匹配完后,跳出它的作用域,把外围作用域置为当前活动作用域。

III. var-declaration --> “int”ID; { Par_table_chain.enter(https://www.wendangku.net/doc/d26727861.html,, "int", 4) } 这条文法对应变量声明,当匹配完“分号”时,说明当前记号流的确是一个变量声明(不需要回溯到II)。此时,在当前符号表中加入这个变量,三个参数分别是变量的名字,类型和宽度。符号表中有一个offset值,初始为0,每加入一个变量则把offset值加上此变量的宽度。如此一来,新加入的变量就能总是拥有正确的地址(在符号表中的位置)。

IV. fun-declaration --> type-specifier ID

{ Par_table_chain.set_name_type(https://www.wendangku.net/doc/d26727861.html,,

type-specifier.type)}

{Par_table_chain.add_to_previous()}

{ Quads_code.gen_entry(https://www.wendangku.net/doc/d26727861.html,) }

(params) compound-stmt

| type-specifier ID

{ Par_table_chain.set_name_type(https://www.wendangku.net/doc/d26727861.html,,

type-specifier.type)}

{Par_table_chain.add_to_previous()}

{ Quads_code.gen_entry(https://www.wendangku.net/doc/d26727861.html,) }

() compound-stmt

这条文法很长。当匹配函数声明,得到其返回值类型和函数名时。先在当前函数的符号表中附上当前函数的信息。然后在其外围作用域中加入这个函数项,参数为函数名,类型和当前函数作用域编号(隐含加入,详见源代码)。然后生成一个“entry 函数名”的三地址码,代表函数的入口在这里,调用函数时转向这个入口。Quads_code是三地址码数组。然后视情况判断是否匹配参数,然后匹配函数体。注意,因为作用域的跳出在文法II中,所以在匹配当前函数的参数和函数体时,发生的任何变量或函数声明,都被加入到当前函数域中,也就是我们想要的结果。

V. type-specifier --> "int" { type-specifier.type := "int"}

| "void" { type-specifier.type := "void"}

匹配函数返回值的类型。

VI. params --> param, params | param

这条文法生成至少一个参数。

VII. param --> "int" ID { Par_table_chain.enter(https://www.wendangku.net/doc/d26727861.html,, "int", 4) }

{Quads_code.gen_param_load(Par_table_chain, "load", https://www.wendangku.net/doc/d26727861.html,)} 当发生参数的声明时,把此参数加入当前作用域中,然后生成形如“load形参名”的三地址码,与过程调用时的“param实参名”一一对应。

[3]算术表达式的翻译

FineC算术表达式的翻译模式如下

I. additive-expression --> term {additive-expressionR.i := https://www.wendangku.net/doc/d26727861.html,} additive-expressionR

{https://www.wendangku.net/doc/d26727861.html, := additive-expressionR.s}

additive-expressionR --> ADDOP term {additive-expressionR.n := newtemp}

{Par_table_chain.enter(additive-expressionR.n, "int", 4)}

{Quads_code.gen_tri(Par_table_chain, https://www.wendangku.net/doc/d26727861.html,,

additive-expressionR.n, additive-expressionR.i, https://www.wendangku.net/doc/d26727861.html,)}

{additive-expressionR1.i := additive-expressionR.n}

additive-expressionR1

{additive-expressionR.s := additive-expressionR1.s} additive-expressionR --> empty {additive-expressionR.s := additive-expressionR.i} 上面的文法是经过了消除左递归的(语法制导翻译中消除左递归的算法参见参考文献[1])。newtemp在当前作用域中返回一个临时变量的名字,第一次调用返回$1,第二次调用返回$2,以此类推。然后把这个临时变量放进符号表中。再生成以临时变量为result,以匹配到的标识符为arg1和arg2,(先在符号表中找到它,当前作用域的符号表找不到,则顺着符号表栈往上找,如果找到最外层符号表依然找不到,说明发生了未声明变量的调用,程序出错),以ADDOP的真值(+或-)为op的四元式,放进Quads_code结构中。

II. term --> factor {termR.i := https://www.wendangku.net/doc/d26727861.html,}

termR {https://www.wendangku.net/doc/d26727861.html, := termR.s}

termR --> MULOP factor

{termR.n := newtemp} {Par_table_chain.enter(termR.n, "int", 4)}

{Quads_code.gen_tri(Par_table_chain, https://www.wendangku.net/doc/d26727861.html,, termR.n, termR.i,

https://www.wendangku.net/doc/d26727861.html,)}

{termR1.i := termR.n}

termR1 {termR.s := termR1.s}

termR --> empty {termR.s := termR.i}

文法II实现的功能与I无异, II和I结合,能够实现算术表达式的左结合性和优先级规则。

III.factor --> (additive-expression) { https://www.wendangku.net/doc/d26727861.html, := https://www.wendangku.net/doc/d26727861.html,} | ID {https://www.wendangku.net/doc/d26727861.html, := https://www.wendangku.net/doc/d26727861.html,}

| call {https://www.wendangku.net/doc/d26727861.html, := https://www.wendangku.net/doc/d26727861.html,}

| NUM { https://www.wendangku.net/doc/d26727861.html, := int_to_str(NUM.value) }

操作符运算对象或者是变量,或者是函数调用返回的结果,或者是整型字面值,或者是括号中的表达式求得的值。

[4]控制流语句的翻译

I. selection-stmt --> "if" ( { to_true := newlabel; to_false := newlabel }

{condition-expression.to_true := to_true}

{condition-expression.to_false := to_false}

condition-expression) {Quads_code.gen_label(to_true)}

statement {Quads_code.gen_label(to_false)}

| "if" ( { to_true := newlabel; to_false := newlabel }

{condition-expression.to_true := to_true}

{condition-expression.to_false := to_false}

condition-expression) {Quads_code.gen_label(to_true)}

statement1 {to_next = newlabel}

“else”{Quads_code.gen_goto(to_next)}

{Quads_code.gen_label(to_false)}

statement2 {Quads_code.gen_label(to_next)}

FineC 中的控制流语句有if-else 和while 两种。以上文法是if-else 的翻译模式。if-then ,if-then-else 生成的三地址码如下图所示

图3 if-then , if-then-else 的中间代码

当匹配到"if"后,调用newlabel 返回一个语句编号(第一次调用返回101,第二次102,第三次103,一次类推),to_true, to_false 作为condition-expression 的两个继承属性,用于生成E.code 中条件转向三地址码的goto 目的地。condition-expresson 成功匹配后,生成一个三地址码“L101”,表示当上面的条件为真时,转向这一条语句往下执行,然后调用statement 函数匹配if-then 的执行函数体。匹配结束后,生成一个三地址码“L102”,表示当上面的条件为假是,转向这一条语句往下执行。这样一来,当condition-expression 为假时,就自然地跨过了statement 的语句不执行。

if-then-else 的翻译与if-then 有一样的原理,区别只在于E.false 的指向是另一个statement ,而在Statement1后面放一个无条件跳转语句,即表示“执行完statement1后不需要再执行statement2”。

II. iteration-stmt --> "while" ( {to_begin := newlabel} {Quads_code.gen_label(to_begin)} { to_true := newlabel; to_false := newlabel } {condition-expression.to_true := to_true} {condition-expression.to_false := to_false}

condition-expression) {Quads_code.gen_label(to_true)} statement {Quads_code.gen_goto(to_begin)} {Quads_code.gen_label(to_false)}

while 语句生成的三地址码如下图所示

图4 while语句的中间代码

while语句的翻译思路也与if-then类似,只是在S1.code后面有一条“goto S.begin”,表示函数体执行结束后回到S.begin再次检查E.code的真假,当E.code为假时,跨过“goto S.begin”,也就结束了整个while的执行。注意,if-then语句和while语句的函数体都是statement,也就是说,它们都有各自的作用域。

III. condition-expression --> additive-expression1 RELOP additive-expression2

{ Quads_code.gen_condition(Par_table_chain, https://www.wendangku.net/doc/d26727861.html,,

https://www.wendangku.net/doc/d26727861.html,, https://www.wendangku.net/doc/d26727861.html,,

condition-expression.to_true) }

{ Quads_code.gen_goto(condition-expression.to_false)} 文法III生成布尔表达式E.code的代码,需要与文法I,II的翻译思路相匹配。

[5]过程声明和调用的翻译

函数声明的翻译已经在前面说明,下面是FineC语言中过程调用语句的翻译

I. call --> ID ( {https://www.wendangku.net/doc/d26727861.html, := newtemp } { Par_table_chain.enter(https://www.wendangku.net/doc/d26727861.html,, "int", 4) }

{ Quads_code.gen_uni("begin_args")}

args ) { Quads_code.gen_call(Par_table_chain, https://www.wendangku.net/doc/d26727861.html,) }

| ID ( {https://www.wendangku.net/doc/d26727861.html, := newtemp } { Par_table_chain.enter(https://www.wendangku.net/doc/d26727861.html,, "int", 4)}

{Quads_code.gen_uni("begin_args")}

) { Quads_code.gen_call(Par_table_chain, https://www.wendangku.net/doc/d26727861.html,) }

II. args --> additive-expression

{ Quads_code.gen_param_load(Par_table_chain,

"param",https://www.wendangku.net/doc/d26727861.html,) }

, args

| additive-expression

{ Quads_code.gen_param_load(Par_table_chain, "param",

https://www.wendangku.net/doc/d26727861.html,) }

当发生函数调用时,先生成一个当前作用域下的临时变量,用来代表返回值。然后生成一个“begin_args”的三地址码,表示开始发送实参。然后进入文法II匹配实参,每次匹配

到一个,输出一个“param 实参”形式的三地址码。实参匹配完毕后,生成“call 函数名”语句,转向相应的“enter 函数名”。

四、FineC语言中间代码生成的完整翻译模式

1. program --> { Par_table_chain.mktable() } declaration-list

2.declaration-list --> declaration declaration-list1 | declaration1

3.declaration --> var-declaration

| { Par_table_chain.mktable() } fun-declaration

{Par_table_chain.jumpout()}

4. var-declaration --> “int”ID; { Par_table_chain.enter(https://www.wendangku.net/doc/d26727861.html,, "int", 4) }

5. fun-declaration --> type-specifier ID

{ Par_table_chain.set_name_type(https://www.wendangku.net/doc/d26727861.html,, type-specifier.type) }

{Par_table_chain.add_to_previous()}

{ Quads_code.gen_entry(https://www.wendangku.net/doc/d26727861.html,) }

(params) compound-stmt

| type-specifier ID

{ Par_table_chain.set_name_type(https://www.wendangku.net/doc/d26727861.html,, type-specifier.type) }

{ Par_table_chain.add_to_previous() }

{ Quads_code.gen_entry(https://www.wendangku.net/doc/d26727861.html,) }

() compound-stmt

6. type-specifier --> "int" { type-specifier.type := "int"}

| "void" { type-specifier.type := "void"}

7. params --> param, params | param

8. param --> "int" ID { Par_table_chain.enter(https://www.wendangku.net/doc/d26727861.html,, "int", 4) }

{Quads_code.gen_param_load(Par_table_chain, "load", https://www.wendangku.net/doc/d26727861.html,)}

9. compound-stmt --> {local-declarations statement-list}

10. local-declarations --> var-declaration local-declarations | empty

11. statement-list --> statement statement-list | empty

12. statement --> expression-stmt | selection-stmt | iteration-stmt

| return-stmt

| { Par_table_chain.mktable() } {Par_table_chain.add_to_previous()}

compound-stmt { Par_table_chain.jumpout() }

13. expression-stmt --> expression; | ;

14. selection-stmt --> "if" ( { to_true := newlabel; to_false := newlabel }

{condition-expression.to_true := to_true}

{condition-expression.to_false := to_false}

condition-expression) {Quads_code.gen_label(to_true)}

statement {Quads_code.gen_label(to_false)}

| "if" ( { to_true := newlabel; to_false := newlabel }

{condition-expression.to_true := to_true}

{condition-expression.to_false := to_false}

condition-expression) {Quads_code.gen_label(to_true)}

statement1 {to_next = newlabel}

“else”{Quads_code.gen_goto(to_next)}

{Quads_code.gen_label(to_false)}

statement2 {Quads_code.gen_label(to_next)}

15. iteration-stmt --> "while" ( {to_begin := newlabel}

{Quads_code.gen_label(to_begin)}

{ to_true := newlabel; to_false := newlabel }

{condition-expression.to_true := to_true}

{condition-expression.to_false := to_false}

condition-expression) {Quads_code.gen_label(to_true)}

statement {Quads_code.gen_goto(to_begin)}

{Quads_code.gen_label(to_false)}

16. condition-expression --> additive-expression1 RELOP additive-expression2

{ Quads_code.gen_condition(Par_table_chain, https://www.wendangku.net/doc/d26727861.html,,

https://www.wendangku.net/doc/d26727861.html,, https://www.wendangku.net/doc/d26727861.html,,

condition-expression.to_true)}

{ Quads_code.gen_goto(condition-expression.to_false)}

17.return-stmt --> “return”; { Quads_code.gen_uni("return") }

| “return”expression;

{Quads_code.gen_param_load(Par_table_chain, "return",

https://www.wendangku.net/doc/d26727861.html,)}

18.expression --> ID = expression1

{ Quads_code.gen_bi(Par_table_chain, https://www.wendangku.net/doc/d26727861.html,,https://www.wendangku.net/doc/d26727861.html,)}

{ https://www.wendangku.net/doc/d26727861.html, := https://www.wendangku.net/doc/d26727861.html, }

| additive-expression

{ https://www.wendangku.net/doc/d26727861.html, :=https://www.wendangku.net/doc/d26727861.html, }

19. additive-expression --> term {additive-expressionR.i := https://www.wendangku.net/doc/d26727861.html,} additive-expressionR

{https://www.wendangku.net/doc/d26727861.html, := additive-expressionR.s} 20. additive-expressionR --> ADDOP term {additive-expressionR.n := newtemp}

{Par_table_chain.enter(additive-expressionR.n, "int", 4)}

{Quads_code.gen_tri(Par_table_chain, https://www.wendangku.net/doc/d26727861.html,,

additive-expressionR.n, additive-expressionR.i,

https://www.wendangku.net/doc/d26727861.html,)}

{additive-expressionR1.i := additive-expressionR.n}

additive-expressionR1

{additive-expressionR.s := additive-expressionR1.s} | empty {additive-expressionR.s := additive-expressionR.i}

21. term --> factor {termR.i := https://www.wendangku.net/doc/d26727861.html,}

termR {https://www.wendangku.net/doc/d26727861.html, := termR.s}

22. termR --> MULOP factor {termR.n := newtemp}

{Par_table_chain.enter(termR.n, "int", 4)}

{Quads_code.gen_tri(Par_table_chain, https://www.wendangku.net/doc/d26727861.html,,

termR.n, termR.i, https://www.wendangku.net/doc/d26727861.html,)}

{termR1.i := termR.n}

termR1 {termR.s := termR1.s}

| empty {termR.s := termR.i}

23. factor --> (additive-expression) { https://www.wendangku.net/doc/d26727861.html, := https://www.wendangku.net/doc/d26727861.html,}

| ID {https://www.wendangku.net/doc/d26727861.html, := https://www.wendangku.net/doc/d26727861.html,}

| call {https://www.wendangku.net/doc/d26727861.html, := https://www.wendangku.net/doc/d26727861.html,}

| NUM { https://www.wendangku.net/doc/d26727861.html, := int_to_str(NUM.value) }

24. call --> ID ( {https://www.wendangku.net/doc/d26727861.html, := newtemp } { Par_table_chain.enter(https://www.wendangku.net/doc/d26727861.html,, "int", 4) }

{ Quads_code.gen_uni("begin_args")}

args ) { Quads_code.gen_call(Par_table_chain, https://www.wendangku.net/doc/d26727861.html,) }

| ID ( {https://www.wendangku.net/doc/d26727861.html, := newtemp } { Par_table_chain.enter(https://www.wendangku.net/doc/d26727861.html,, "int", 4)}

{Quads_code.gen_uni("begin_args")}

) { Quads_code.gen_call(Par_table_chain, https://www.wendangku.net/doc/d26727861.html,) }

25. args --> additive-expression

{ Quads_code.gen_param_load(Par_table_chain,

"param",https://www.wendangku.net/doc/d26727861.html,) }

, args

| additive-expression

{ Quads_code.gen_param_load(Par_table_chain, "param",

https://www.wendangku.net/doc/d26727861.html,) }

五、程序运行结果

把如下源程序作为输入:

/* A J */

int gcd (int u, int v)

{ if (v == 0) return u ;

else return gcd(v, u-u/v*v);

/* u-u/v*v == u mod v */

}

void main()

{ int x; int y; int z;

x = 3;

y = 4;

z = gcd(x,y);

}

运行结果是生成的三地址码序列,如下图所示:

六、总结

中间代码生成作为编译器设计中的中间步骤,肩负着与前端和后端协作的重要任务。设计良好的中间代码生成器能够生成结构清晰的中间代码,为其后的中间代码优化和代码生成,乃至最终形成的可执行代码的效率提升打下良好的基础。本文讨论了一个轻量级语言的中间代码生成问题,通过研究FineC语言的中间代码生成器,发现随着语言文法定义变得复杂,中间代码生成器的代码规模将急剧增长。在如此迅速增长的情况下设计出稳定、高效的中间代码生成器对于编译器设计者是一个很大的挑战。遗憾的是由于中间代码生成原理的复杂性,现在还没有找到一种规范化的数学模式,能够构造出中间代码生成器的生成器,而只能靠手工编写。附录中的源代码是作者实践的结果,不足之处依然很明显,尤其在错误处理方面尚待改善。

参考文献

[1]《编译原理:原理、技术与工具》

Alfred V.Aho Ravi Sethi Jeffrey D. Ullman合著

李建中,姜守译机械工业出版社

[2] 《词法分析器构造》王嘉电子科技大学课程设计论文

[3] 《自顶向下语法分析器的实践研究》王嘉电子科技大学课程设计论文

附:程序源代码及其注释

[1] 附属结构:符号表栈、三地址码缓冲

源文件:parser.h

#ifndef GUARD_parser

#define GUARD_parser

#include "Id_table.h"

#include

#include

//////////////////////////////////////

//语法分析器的函数声明

int newlabel();

void parse();

void program();

void declaration_list();

void declaration();

void var_declaration();

void fun_declaration();

std::string type_specifier();

void params();

void param();

void compound_stmt();

void local_declarations();

void statement_list();

void statement();

void expression_stmt();

void selection_stmt();

void iteration_stmt();

void return_stmt();

std::string expression();

void condition_expression(int, int);

std::string additive_expression();

std::string additive_expressionR(std::string);

std::string term();

std::string termR(std::string);

std::string factor();

std::string call();

void args();

std::string int_to_str(int);

class Par_table;

class Par_table_chain;

class Quads //四元式

{

public:

Quads(std::string s, const int res = -1, const int ar1 = -1, const int ar2 = -1): op(s), result(res), arg1(ar1), arg2(ar2) {}

public:

std::string op;

int result;

int arg1;

int arg2;

};

class Quads_code //三地址码序列/四元式序列

{

public:

//生成形如result := arg1 op arg2的三地址码

void gen_tri(Par_table_chain&, std::string, std::string, std::string, std::string);

//生成形如result := arg1的赋值语句三地址码

void gen_bi(Par_table_chain&, std::string, std::string);

//生成形如goto 104的无条件转移语句的三地址码

void gen_goto(int);

//生成形如param arg或load arg的单地址码,用来对应函数调用

void gen_param_load(Par_table_chain&, std::string, std::string);

//生成call f的三地址码

void gen_call(Par_table_chain&, std::string);

//生成entry f的三地址码

void gen_entry(std::string);

//生成形如if arg1 relop arg2 goto 104的语句

void gen_condition(Par_table_chain&, std::string, std::string, std::string, int);

//生成return ;和begin_args的三地址码

void gen_uni(std::string);

//生成L104:这样的label码

void gen_label(int label);

void display();

private:

std::vector code;

std::vector buffer;

};

class Par_table;

struct Name_type_add //一个变量的所有属性:名字,类型,在符号表中的地址

{

std::string name;

std::string type;

int add;

bool isfun;

};

class Par_table //中间代码生成时的符号表

{ //因为支持变量作用域嵌套规则,可能有符号表的嵌套链

public:

Par_table(int previous)

{

ptr = previous;

cnt = -1;

offset = 0;

proc_name = "NOT FUNC";

proc_type = "NULL";

}

std::string newtemp();

int get_ptr(){ return ptr; }

void add_item(std::string, std::string, int, bool);

void set_proc(std::string, std::string);

Name_type_add lookup(std::string);

std::string get_proc_name()

{ return proc_name;}

std::string get_proc_type()

{ return proc_type;}

private:

int ptr; //指向外层过程的符号表,最外层的符号表此域为-1

int cnt; //本层临时名字生成的计数器

std::vector vars;

int offset;

std::string proc_name;

std::string proc_type;

};

class Par_table_chain

{

public:

Par_table_chain()

{

cur = -1;

}

void mktable();

void enter(std::string, std::string, int width);

void add_to_previous();

void jumpout(); //跳出以前把本层符号表加到上层符号表的某一项中std::string newtemp();

Name_type_add lookup(std::string, int&);

void set_name_type(std::string, std::string);

std::string get_proc_name(int ix)

{

return chain[ix].get_proc_name();

}

从simulink模型到PLC代码的自动生成功能

从simulink模型到PLC代码的自动生成是MATLAB2010提供的功能,目前支持的PLC编程工具有: CoDeSys2.3, CoDeSys3.3, RSLogix5000, B&R Automation Studio 3.0, PLCOpen XML, BeckhoffTwinCat 2.11以及Generic。 本文介绍了MATLAB生成TwinCat 2.11的ST文档的方法,并使用TwinCat 2.11建立相应的工程项目。 2. 使用MATLAB/simulink生成PLC的st语言功能函数的方法 MATLAB提供了很多demo一备参考,本文以MATLAB demo中的plcdemo_simple.mdl为例。首先在MATLAB命令行中执行命令demo,选择simulink PLC coder->Demos->Basic Introductions and Examples->Generating Structured Text for a Simple Simulink Subsystem,然后打开该模型。 打开参数配置面板(Simulation->Configuration Parameters),进入PLC Coder的选项页,将Targer IDE选为BeckhoffTwinCat 2.11。然后应用。 回到模型,右键点击SimpleSystem,选择PLC Coder->Generate Code For Subsystem。如果没有错误,将在当前目录下生成plcdemo_simple.exp文件及诊断对话框。该文件即为ST语言的PLC文档。如下

中间代码生成实验报告材料

一、实验目的 通过在实验二的基础上,增加中间代码生成部分,使程序能够对实验二中的识别出的赋值语句,if语句和while语句进行语义分析,生成四元式中间代码。 二、实验方法 实验程序由c语言完成,在Turboc 2.0环境中调试通过。 语义分析程序的基本做法是对文法中的每个产生式分别编写一个语义分析子程序,当程序语法部分进行推倒或规约时,就分别调用各自的语义分析程序。当语法分析结束时,语义分析也就结束了。 在本实验程序中,当语法分析部分识别出语确的句子时,就进入content函数(当语法分析识别出不正确的句子时,不进入content函数,也就是不进行语义分析),然后根据句子的类型进行分类,进入不同的语义处理部分。 对于赋值语句,关键是产生正确的处理算术表达式E的四元式。 程序中的ec函数的功能就是产生算术表达式的四元式,在ec函数中使用了两个栈idshed,opshed,分别是算术表达式的数据栈和符号栈。每次提取一个数字和一个算符,然后将算符与与栈顶算符进行优先级比较,优先级高则将单前数字和算符进栈,低或者相等的话则将当前栈顶元素进行合并,产生四元式。直至整个算术表达式结束。其中还有一些细节问题,具体的做法可以参看程序。 对于实验给定的if语句的文法格式,条件判断式C只中可能是>或者<=两种关系,不可能是布尔表达式,这样程序就简单的多了。 通过ec函数可以产生条件判断式C中的E的四元式,然后只要加上转向四元式就可以了。本实验程序中只给出真出口的转向四元式,没有给出假出口的转向四元式,这在实际中是不可以的,但在

本实验中,实际上是对每条独立的语句进行语法分析,给出假出口转向四元式实际上意义不大,而且假出口转向语句的转移目标必须要到整个语句分析结束以后才可以知道,这样就要建立栈,然后回填,这样会使程序复杂很多,所以没有加上假出口转向四元式。 对于while语句,具体的做法和if语句差不多,所不同的是当while语句结束时,要多出一条无条件转向四元式,重新转到条件判断式C的第一条四元式。当要产生无条件转向四元式时,它的转向目标C的第一条四元式已经产生了,所以具体的做起来是不太困难的。只要记下当前while中的C的第一条四元式的位置,填上就可以了。 整个程序的结束是当读入“. ”时,程序就中止。 程序中还有很多细节问题,具体的可以后面的附录:程序的完整代码。 三、测试程序 ff:=6+6*6-; if sl>89+56*67 then f:=7*7+4; ff:=6+6*6-6%4+8; if sl+78*76>89*56+67 then while a-7>98+45*45 do f:=7*7+4; . 四、运行结果 首先对测试程序进行语法分析,识别出正确的句子,当识别出正确的句子时,就对当前句子进行语义分析,而语法不正确的句子不进行语义分析。 ff:=6+6*6- Error(4):Except ID or NUM ; Error(2):Syntax error if sl>89+56*67 then f:=7*7+4; success!!! (1) [ *, 56, 67, T1 ]

实验五 编译 用语法制导方式生成中间代码生成器

实验5 用语法制导方式生成中间代码生成器 一、实验目的 掌握语法制导定义和翻译的原理和技术,在语法分析器的基础上,加上语义分析,构造一个中间代码生成器。 二、实验内容 在实验四生成的语法分析器基础上加入语义动作,将源程序翻译为对应的中间代码序列。 三、实验要求 1. 个人完成,提交实验报告。实验报告必须包括设计的思路,以及测试报告(输入测试例子,输出结果)。 2. 实验报告中给出采用测试源代码片断,及其对应的三地址码形式(内部表示形式可以自行考虑)。 例如,程序片断 对应的中间代码为:

四、实验过程 本次实验运用flex和bison工具进行中间代码的生成。并自动生成中间代码。 1.首先创建一个example文件夹,该文件夹中包含有flex.exe 2.用文本编译器编辑相应的flex文件mylex.l,此次mylex.l可以在上次实验的 l文件上做一些修改,再利用flex将l文件生成相应的lex.yy.c程序,mylex.l 的代码如下所示: mylex.l %{ #include "myyacc.tab.h" %} delim [ \t\n\r] ws {delim}+ letter [A-Za-z] digit [0-9] id {letter}({letter}|{digit})* integer {digit}+ exponent E[+-]?{integer} number {integer}{exponent}? real integer(\.integer)?{exponent}? %option noyywrap %% "<"|"<="|">"|">="|"!="|"==" { filloperator(&yylval, yytext); return( REL); }

编译方法实验报告(中间代码生成器的设计)

编译方法实验报告 2011年10月

一、实验目的 熟悉算术表达式的语法分析与中间代码生成原理。 二、实验内容 (1)设计语法制导翻译生成表达式的四元式的算法; (2)编写代码并上机调试运行通过。 输入——算术表达式; 输出——语法分析结果; 相应的四元式序列。 (3)设计LL(1)分析法或LR(0)分析法的属性翻译文法,并根据这些属性翻译文法,使用扩展的语法分析器实现语法制导翻译。 三、实验原理及基本步骤 ●算术表达式文法: G(E): E →E ω0 T | T T →T ω1 F | F F → i | (E) ●文法变换: G’(E) E →T {ω0 T} T →F {ω1 F} F → i | (E) ●属性翻译文法: E →T {ω0“push(SYN,w)” T “QUAT”} T →F {ω1“push(SYN, w)” F “QUAT”} F →i “push(SEM, entry(w))” | (E) 其中: push(SYN, w) —当前单词w入算符栈SYN; push(SEM, entry(w)) —当前w在符号表中的入口值压入语义栈SEM; QUA T —生成四元式函数 i.T = newtemp; ii.QT[j] =( SYN[k], SEM[s-1], SEM[s], T); j++; iii.pop( SYN, _ ); pop( SEM, _ ); pop( SEM, _ ); push( SEM, T ); ●递归下降子程序: 数据结构:SYN —算符栈; SEM —语义栈;

四、数据结构设计 使用递归的结构进行四元式的设计,同时,运用堆栈结构将四元式的输出序列打印出来 while ( exp[i]=='+' || exp[i]=='-'){ syn[++i_syn]=exp[i]; //push(SYN,w) i++; //read(w) T(); quat();} while ( exp[i]=='*' || exp[i]=='/'){ syn[++i_syn]=exp[i]; //push(SYN,w) i++; //read(w) F(); quat();} void quat(){ strcpy(qt[j],"(, , , )");

20个代码生成框架

20个代码生成框架 11.1 CodeSmith http: 官方论坛: http: 版权形式:30天试用 开源:否需要先注册确认后才能下载 1.2 MyGenerator MyGenerator是又一个国外很不错的代码生成工具,有人觉得比CodeSmith 简单、好用。 所有api可以在帮助菜单中找到。 http: 官方论坛: 版权形式: 免费 开源:否 1.3 NHibernate. http: 官方论坛: 版权形式:

免费 开源:否 1.4湛蓝.Net代码生成器 http: 官方论坛: http: 版权形式: 免费 开源:否 1.5动软.NET代码自动生成器 一款人气很旺的免费C#代码生成器 http: 官方论坛: 版权形式: 免费 开源:否 1.6 CodePlus 专为sql server c#语言设计的代码生成器,功能还是很强大http: 官方论坛:

版权形式: 需要少量的注册费用 开源:否下载地址很神秘 1.7 CodeMaker http: 官方论坛: 版权形式: 免费 开源:否 https://www.wendangku.net/doc/d26727861.html,代码生成器 可以使用本工具生成https://www.wendangku.net/doc/d26727861.html,和C#语言的代码,以及三层架构与ORM架构代码,并且使用的ORM持久化组件是开源的,您可以在本软件的安装目录下找到它 官方论坛: 版权形式: 免费 开源:否 1.9 BMW业务模型及代码生成器 一款人气很旺的免费C#代码生成器

官方论坛: 版权形式: 免费 开源:否 1.10飞鹰CoolCoder 专门为采用nhibernate做关系对象影射架构的系统提供代码的工具,简单易用,虽然不提供源码,我们可以用反编译工具对其反编译看源码。这是个很不错的学习机会。 官方论坛: 版权形式: 免费 开源:否 1.11 AutoCoder自动代码生成器 AutoCoder自动代码生成器是一个根据模板自动生成代码的代码生成工具,根据模板的不同,可以生成任何语言(如: ASP、C#、C++BUILDER、DELPHI、JAV A、JSP、PHP、V B、https://www.wendangku.net/doc/d26727861.html,……),不同层次结构(B/S、C/S、n-tiger……),基于不同数据库(ORACL E、MSSQL、MYSQL、

编译原理综合性实验报告-分析中间代码生成程序分析

编译原理综合性实验报告-分析中间代码生成程序分析XXXXXX计算机系综合性实验 实验报告 课程名称编译原理实验学期 XXXX 至 XXXX 学年第 X 学期学生所在系部计算机系年级 X 专业班级 XXXXXX 学生姓名 XXX 学号 XXXXXXXXXXXX 任课教师XXX 实验成绩 计算机系制 《编译原理》课程综合性实验报告 开课实验室: 年月日实验题目分析中间代码生成程序 一、实验目的 分析PL/0编译程序的总体结构、代码生成的方法和过程;具体写出一条语句的中间代码生成过程。 二、设备与环境 PC兼容机、Windows操作系统、Turbo Pascal软件等。 三、实验内容 1. 分析PL/0程序的Block子程序,理清PL/0程序结构和语句格式。画出Block 子程序的流程图,写出至少两条PL/0程序语句的语法格式。 2. 分析PL/0程序的Block子程序和Gen子程序,了解代码生成的方法和过程。 使用概要算法来描述语句的代码生成过程。 3. 自己编写一个简单的PL/0程序,能够正确通过编译,得到中间代码。列出自

己编写的源程序和编译后得到的中间代码。 4. 从中选择一个语句或表达式,写出代码生成的过程。要求从自己的源程序中 选择一条语句,结合这条语句写出语义分析和代码生成过程。在描述这个过程中,要说清楚每个功能有哪个子程序的哪条语句来完成,说清楚语句和参数的含义和功能。 四、实验结果及分析 (一)Block子程序分析 1.常量声明的分析: 常量声明部分的语法结构定义为如下形式: -> const ; -> [;] ->id = C 其中C可以是常量标识符或字符串或整数(可带符号)或实数(可带符号)。 常量声明分析程序的主要任务是: (1).扫描整个常量声明部分。 (2).为被声明的常量标识符建立符号表项。 (3).检查重复的声明。 2.变量声明部分的分析: 变量声明部分的语法结构定义为如下形式: -> var -> [;] ->:T ->id[,]

中间代码生成实验报告doc

中间代码生成实验报告 篇一:编译方法实验报告(中间代码生成器) 编译方法实验报告 XX年10月 一、 实验目的 熟悉算术表达式的语法分析与中间代码生成原理。 实验内容二、 (1)设计语法制导翻译生成表达式的四元式的算法; (2)编写代码并上机调试运行通过。 输入——算术表达式; 输出——语法分析结果; 相应的四元式序列。 (3)设计LL(1)分析法或LR(0)分析法的属性翻译文法,并根据这些属性翻译文 法,使用扩展的语法分析器实现语法制导翻译。 三、实验原理及基本步骤 ●算术表达式文法: G(E):E ? E ω0 T | T T ? T ω1 F | F F ? i | (E) ●文法变换:

G’(E) E ? T {ω0 T(本文来自:小草范文网:中间代码生成实验报告)} T ? F {ω1 F} F ? i | (E) ●属性翻译文法: E ? T {ω0 “push(SYN, w)” T “QUAT”} T ? F {ω1 “push(SYN, w)” F “QUAT”} F ? i “push(SEM, entry(w))” | (E) 其中: push(SYN, w) —当前单词w入算符栈SYN; push(SEM, entry(w)) —当前w在符号表中的入口值压入语义栈SEM; QUAT —生成四元式函数 i.T = newtemp; ii.QT[j] =( SYN[k], SEM[s-1], SEM[s], T); j++; iii.pop( SYN, _ ); pop( SEM, _ ); pop( SEM, _ ); push( SEM, T ); ●递归下降子程序: 数据结构:SYN —算符栈; SEM —语义栈; 四、数据结构设计 使用递归的结构进行四元式的设计,同时,运用堆栈

C++代码生成器

MFC三层模式标准版V1.5版演示源码(VC++代码生成,VC非托管代码,基于对话框程序) 分类:猛犸代码生成器C++/VC++2012-10-06 23:2492人阅读评论(0)收藏举报本演示项目由猛犸代码生成器自动生成。 VS2010环境,VC++ 非托管代码,基于对话框程序 VC++代码生成器,VC++三层架构 发布新的VC++代码生成模板:VC++三层标准版,生成的项目已经完成对表格的添加记录、显示记录列表、修改记录、删除记录、浏览记录内容这些基本功能。 运用分层设计思想,分层情况为MODEL,IDAL,DAL和WinForm,DAL层使用SQL参数形式,可以修改使用存储过程,添加记录成功后返回自动增长编号给实体模型。 支持数据库:mssql2000,2005,2008。 (注意:表结构设计第一个主键必须是自动编号列) 文件夹说明 Document 数据库设计文档 Script 数据库脚本文档 SoundCode 生成项目源码 MFC三层标准版演示.xml 为猛犸代码生成器的项目文件 使用方法 请先附加演示数据库mengmaDemoDb 然后在SoundCode\WINFORM\_DBUtility\ADOConn.cpp文件中 strConnect = _T("Driver={SQL Server};Server=大猫-PC\\SQLEXPRESS;Uid=sa;Pwd=123456;database=MengmaDemoDb;"); 修改数据库连接字符串 然后用VS2010 打开SoundCode\Mengma.sln 重新编译项目 然后打开SoundCode\Debug\Mengma.exe ok 项目运行截图

程序代码自动生成系统的设计与实现

万方数据

万方数据

万方数据

万方数据

基于敏捷开发的高校网络评教系统 作者:吴衡, WU Heng 作者单位:天水师范学院物理与信息科学学院,甘肃天水,741001 刊名: 计算技术与自动化 英文刊名:Computing Technology and Automation 年,卷(期):2011,30(4) 被引用次数:1次 参考文献(8条) 1.丁增富;葛信勇构建教学质量监控体系努力提高教学质量[期刊论文]-高等农业教育 2004(03) 2.陈莉和谐校园构建于素质教育双效联动[期刊论文]-中国市场 2007(2-3) 3.成奋华;金敏基于敏捷过程的IT项目范围管理的研究与应用[期刊论文]-计算机技术与发展 2010(10) 4.徐诚斌;王金平MVC在ThinkPHP框架中的应用研究 2011(03) 5.赵国栋;黄永中开源软件在高校的应用与推广策略研究[期刊论文]-中国资源综合利用 2007(01) 6.马文龙;高宝成用php实现基于MVC模式的Web应用程序开发 2008(07) 7.原晓林基于B/S教学管理系统的开发与研究[期刊论文]-山西警官高等专科学校学报 2009(04) 8.蓝蔚青;曹剑敏;张帆高校学生网络评教系统的构建与完善[期刊论文]-高等农业教育 2006(06) 引证文献(1条) 1.蒋建洪电子商务系统协同开发实践教学研究[期刊论文]-中国教育信息化·基础教育 2013(5) 本文链接:https://www.wendangku.net/doc/d26727861.html,/Periodical_jsjsyzdh201104028.aspx

代码自动生成系统

本钢ERP代码自动生成系统的开发 史洪波 (本钢信息自动化公司软件开发事业部) 摘要:本钢ERP系统维护的工作量大,大部分的页面功能类似。在新增业务需求的情况下,可以利用本系统自动修改和生成JSP、Java、XML、SQL、Properties类型的全部代码,生成的代码可以提供新增、修改、删除、导入、树、打印、查询等基本功能。本系统原理是利用各种页面风格的JSP、Java、XML、SQL等文件作为模板,利用配置参数替换掉文件中可变部分,从而形成多种页面风格通用的代码生成系统。本文总结了本钢ERP中常用的6种页面样式,并用Java语言实现了其代码的自动生成过程,只需书写好DAO文件并在系统中填写少许的配置信息,然后点击按钮即可完成代码的产生,节省时间,提高工作效率。 关键词:ERP Java 代码自动生成 Benxi Steel ERP automatic code generation system Shi Hongbo (Benxi Steel Information & Automation Co.Ltd Software Development Department) ABSTRACT:There are a great deal maintain work in Benxi Iron and Steel Co. Ltd. ERP system, most of the page features are similar. In the case of needing new business, the system can automatically generate and modify all of the codes in many types, such as JSP, Java, Xml, SQL, Properties. Codes provide new, modify, delete, import, tree, print, query, and other basic functions and pride many different page styles by replacing the parameter of file. There are 6 common page styles are summarized in the Benxi Iron and Steel Co. Ltd. ERP, and use java language to achieve the automatic code generation process, just to write the file of DAO in the system and a little configuration information, then click the button to complete the code, saved a lot of time and improved the work efficiency. KEY WORD: ERP Java automatic-code-generation 作者简介: 史洪波,男,出生于1974年7月28日,1998年7月毕业于华东冶金学院工业分析专业,同年到本钢技术中心从事钢铁检验工作。2006年3月调至本钢信息自动化公司软件开发事业部从事软件开发工作。Email:mynameshb@https://www.wendangku.net/doc/d26727861.html, 1、引言 本钢ERP系统有着良好的开发规范,命名规则有规律可循,因此在新增业务需求的情况下非常适合用代码自动生成系统来减少开发过程中的工作量,减少了开发人员的Ctrl+C/Ctrl+V操作,节省时间,提高工作效率。本系统原理是利用各种页面风格的JSP、Java、XML、SQL等文件作为模板,利用配置参数替换掉文件中可变部分,从而形成多种页面风格通用的代码生成系统。本系统采用J2SE中的Awt和Swing技术来绘制整个系统的图形界面,使系统界面整洁美观,使用xml文件作为本系统的配置文件,并对XML文件设定了DTD的格式校验,保证XML的格式良好。在系统界面中输入少量的条件即可产生和修改JSP、Java、XML、SQL、Properites等类型的文件,并对已经存在的文件先备份成Bak文件,生成的代码可具有新增、修改、删除、查询、导入、树、打印等功能。由于XML具有便于阅读和理解,可扩展等优点,而DAO文件格式的不规范性,系统中还提供了DAO文件与XML格式的相互转换,转换中使用了XLST技术,简化了转

中间代码生成具体实验过程含代码

实验三中间代码生成 学号:1152185;姓名:马小军 实验目的 1.了解并掌握中间代码的生成过程和作用 2.了解并掌握四元式 3.体会属性文法在中间代码生成过程中的作用 。 实验环境 Windows7操作系统vs2010编程环境 实验内容 从文件中读入表达式,输出其四元式的结果序列 本程序只能生成赋值语句及算数表达式中间代码的四元式不能生成逻辑表达式及其他复杂语句中间代码的四元式 实验原理 三、以逆波兰式为例的实验设计思想及算法 (1)首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。 (2)从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符数字,则分析到该数字串的结束并将该数字存入数组。 (3)如果不是数字,该字符则是运算符,此时需比较优先关系。 做法如下:将该字符与运算符栈顶的运算符的优先关系相比较。如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。倘若不是的话,则将此运算符栈顶的运算符从栈中弹出,将该字符入栈。 (4)重复上述操作(2)-(3)直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,我们便可以将中缀式表示的简单算术表达式转化为四元式。 下面给出算法流程图

实验步骤 打开并运行软件 根据提示输入要分析的源程序(文件目录下有写好的文件源文件1.txt输入即可) 运行输出结果 例如将以下源文件放入test.txt 运行结果 a:=b*c+b*d 思考 同样的思路对算法进行适当改动就可以生成其它形式的中间代码 【其他部分】 设计原理和算法思想参考 《程序设计语言编译原理》第三版国防工业出版社作者陈火旺等

java自动生成代码

1.与数据库建立连接 在DBUtil中设置数据库相关的参数 Class.forName(driverName); conn = DriverManager.getConnection(url, user, password); 2.获得数据库中的所有表 Mapmaps = new HashMap(); PreparedStatement pstate = conn.prepareStatement("show table status"); ResultSet results = pstate.executeQuery(); while (results.next()) { String tableName = results.getString("NAME"); String comment = results.getString("COMMENT"); maps.put(tableName, comment); } 3.获得每个表中的信息

封装一个实体来存储表中的信息 PreparedStatement pstate = conn.prepareStatement("show full fields from " + tableName); ResultSet results = pstate.executeQuery(); List lists = new ArrayList(); while (results.next()) { Column column = new Column(); String field = results.getString("FIELD"); column.setFeildMapper(field); String fieldName = processField(field);

编译原理实验 中间代码生成

实验四中间代码生成 一.实验目的: 掌握中间代码的四种形式(逆波兰式、语法树、三元式、四元式)。 二.实验内容: 1、逆波兰式定义:将运算对象写在前面,而把运算符号写在后面。用这种表示法表示的表 达式也称做后缀式。 2、抽象(语法)树:运算对象作为叶子结点,运算符作为内部结点。 3、三元式:形式序号:(op,arg1,arg2) 4、四元式:形式(op,arg1,arg2,result) 三、以逆波兰式为例的实验设计思想及算法 (1)首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。 (2)读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。 (3)从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。 (4)如果不是数字,该字符则是运算符,此时需比较优先关系。 做法如下:将该字符与运算符栈顶的运算符的优先关系相比较。如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。倘若不是的话,则将此运算符栈顶的运算符从栈中弹出,将该字符入栈。 (5)重复上述操作(1)-(2)直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,我们便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。 四、程序代码: //这是一个由中缀式生成后缀式的程序 #include<> #include<> #include<> #include<> #define maxbuffer 64 void main() { char display_out(char out_ch[maxbuffer], char ch[32]); //int caculate_array(char out_ch[32]); static int i=0; static int j=0; char ch[maxbuffer],s[maxbuffer],out[maxbuffer]; cout<<"请输入中缀表达式: ";

【CN110007962A】一种基于代码自动生成的指令集模拟方法【专利】

(19)中华人民共和国国家知识产权局 (12)发明专利申请 (10)申请公布号 (43)申请公布日 (21)申请号 201910175584.9 (22)申请日 2019.03.08 (71)申请人 浙江大学 地址 310058 浙江省杭州市西湖区余杭塘 路866号 (72)发明人 骆华鲲 蔡铭  (74)专利代理机构 杭州求是专利事务所有限公 司 33200 代理人 邱启旺 (51)Int.Cl. G06F 9/30(2006.01) (54)发明名称 一种基于代码自动生成的指令集模拟方法 (57)摘要 本发明公开了一种基于代码自动生成的指 令集模拟方法。该方法针对目前指令集模拟器人 工开发费时费力的问题,先将指令集信息按特定 编码规则进行保存,再由编码程序解析并提取上 述指令信息。由指令的操作码和各特征段信息, 编码程序可自动生成指令集模拟器的译码模块 代码;由指令执行信息,编码程序可自动生成指 令集模拟器执行模块代码。该方法适用于多种类 型的指令集,能有效降低开发人员的编程工作 量,提高指令集模拟器的开发效率,具有较好的 应用前景。权利要求书1页 说明书3页 附图1页CN 110007962 A 2019.07.12 C N 110007962 A

权 利 要 求 书1/1页CN 110007962 A 1.一种基于代码自动生成的指令集模拟方法,其特征在于,具体包括如下步骤: (1)对目标指令集中所有指令进行编码,得指令编码,并将所有指令编码汇总保存至指令集编码文件中。 (2)读取步骤1的指令集编码文件,汇总所有指令的特征段分布情况,生成指令结构体。 (3)读取步骤1的指令集编码文件,提取所有指令的操作码分布,并按操作码长度对分布作降序排序。依次为所有分布生成相应的译码代码,最终汇总成完整的指令集模拟器译码模块。 (4)读取步骤1的指令集编码文件,提取每条指令的执行信息,根据执行信息生成指令函数,所有指令函数共同组成指令模拟器的执行模块。 (5)指令集模拟器译码模块通过匹配指令操作码确定指令类型,并提取指令各特征段信息。指令集模拟器执行模块根据指令类型,执行指令对应操作。两模块共同组成指令集模拟器。 2.根据权利要求1所述的基于代码自动生成的指令集模拟方法,其特征是,所述步骤1中,指令编码记录了指令的格式信息与执行信息。任意一条指令的编码表示为:NA:指令名 特征段编码 DT:数据类型操作数 EX:指令执行表达式 (DE:延迟周期数) 特征段编码记录指令各特征段的分布情况,编码格式为“特征段名[位置]”。将指令内所有特征段都按上述格式编码,并连接组合,即可得到整条指令的特征段编码。 3.根据权利要求1所述的基于代码自动生成的指令集模拟方法,其特征是,所述步骤2具体为:指令结构体是指令集模拟器运行过程中保存单条指令信息的数据结构,保存指令各特征段信息和指令函数指针。在结构体中生成指令函数指针属性。特征段属性通过下述方法生成:遍历指令集编码文件,归纳整个指令集包含的特征段种类,并统计各特征段长度。若存在同名特征段,其长度按最大值记。根据特征段长度,在指令结构体中定义各特征段同名属性。 4.根据权利要求1所述的基于代码自动生成的指令集模拟方法,其特征是,所述步骤3具体为:提取所有指令的操作码分布,并按操作码长度对分布作降序排序。依次为各操作码分布生成识别语句,且用识别语句包裹该分布下提取指令特征段信息的代码。指令各特征段信息保存在步骤2定义的指令结构体同名属性中。在每条指令的译码代码末尾,对指令结构体的函数指针进行赋值,函数名称统一命名为op_NA,其中NA是编码规则中的指令名。 5.根据权利要求1所述的基于代码自动生成的指令集模拟方法,其特征是,所述步骤4具体为:为每条指令生成一个指令函数,指令函数统一命名为op_NA。所有指令函数均可模块化划分为声明变量、赋值变量、执行操作和结果写回四大模块。其中,指令编码中的DT编码被映射为声明变量和赋值变量模块,EX编码被映射为执行操作模块,DT和DE编码被映射为结果写回模块。 2

PL0语言编译器的中间代码生成

课程设计报告 ( 2016--2017年度第一学期) 名称:编译技术课程设计 题目:PL/0语言编译器的中间代码生成院系:控制与计算机工程 班级:信安1401 学号:1141290105 学生姓名:黄竞昶 指导教师:徐欢 设计周数:一周 成绩: 日期:2016年12 月29日

1 课程设计的目的和要求 1.1 课程设计的目的 本次设计的时间为1周,目的是通过使用高级语言实现部分算法加强对编译技术和理论的理解。设计的题目要求具有一定的规模,应涵盖本课程内容和实际应用相关的主要技术。 1.2 课程设计的要求 1、要求用递归子程序法/或预测分析法实现对表达式、各种说明语句、控制语句进行语法分析。 2、若语法正确,则用语法制导翻译法进行语义翻译:对说明语句,要求将说明的各符号记录到相应符号表中;对可执行语句,应产生出四元式中间代码并填写到三地址码表中; 3、若语法错误,要求指出出错性质和出错位置(行号)。出错处理应设计成一个出错处理子程序。 2 系统描述 PL/0的编译程序和目标程序的解释执行程序都是用JA V A语言书写的,因此PL/0语言可在配备JA V A语言的任何机器上实现。本次课设开发平台即为JA V A。用递归子程序法实现了对表达式、各种说明语句、控制语句进行语法的分析。其中,对各个变量符号保存到了符号表中。对可执行语句,转化成四元式中间代码进行输出。本程序中,通过出错处理子程序,也进行了语法的错误的记录,并且输出了出错的行号和出错原因。 该程序的输入是打开test文件,通过文件输入流读入,输出有生成的中间代码、符号表内容、错误分析三部分内容,分别用java界面控件显示出来。 2.1 文法的描述 在计算机科学中,文法是编译原理的基础,是描述一门程序设计语言和实现其编译器的方法。文法的描述多用BNF(巴克斯范式),而另一个重要的概念:正则表达式,也是文法的另一种形式。 PL/0文法的EBNF表示: <程序>::= <分程序>. <分程序>::= [<常量说明部分>][<变量说明部分>][<过程说明部分>]<语句> <常量说明部分>::= const<常量定义>{,<常量定义>};

信用机构代码生成器