语法制导把表达式翻译成逆波兰式
表达式转换成逆波兰式

表达式转换成逆波兰式
早在上世纪20年代,逆波兰式(Reverse Polish Notation,RPN)就出现了。
它的目的是提高计算机程序执行速度,以减少计算机
程序员使用时间,同时提高计算机程序操作的准确性。
因此,在程序
设计中它得到了广泛应用,成为一种基础的数学表达符号。
逆波兰表达式(RPN)指的是把一个表达式按照一定的规则转换
成一种逆波兰的表达式。
这种表达式的每个元素都是单个操作数或操
作符号,操作符放在操作数之前,各操作符号和操作数按照一定次序
跟随在一起。
RPN有许多优势,如解释性强、空间利用率高、更易于扩展和容
错率高等,且不会因为把操作符和操作数放置次序反而影响计算结果。
例如,将一个表达式 sus=(a+b)*c转化成它的逆波兰式即:a b + c * sus =
RPN的算法更加容易理解,它使用的操作数及操作符只有当前字
符的操作数及操作符,而不需要像结构化算法那样维护进入和离开栈
的字符(操作数和操作符)的相关信息。
RPN算法也在某些场合得到广泛的应用,尤其是在使用的机器无
法处理复杂程序时,如小型计算机或一些移动设备。
同时,RPN也是一种被广泛使用的量化计算方法,可以节省时间和精力,提高数学计算
过程的准确性。
总之,逆波兰表达式是一种有效的、普遍应用并且在不同场合用
途十分重要的广泛运用于数学计算中的技术。
它可以消除操作符在表
达式中的优先级差异,将一个复杂的表达式简化为另一个同意义的,
更简洁的表达式,从而提高计算的准确性和速度。
把表达式转换为逆波兰表达式的方法

把表达式转换为逆波兰表达式的方法把表达式转换为逆波兰表达式的方法什么是逆波兰表达式?逆波兰表达式(Reverse Polish Notation,简称RPN)是一种数学表达式的表示方法,其中运算符位于操作数的后面,而不是中间。
例如,将中缀表达式 3 + 4转换为逆波兰表达式的结果是 3 4 +。
为什么要使用逆波兰表达式?逆波兰表达式具有以下优点:1.不需要括号:逆波兰表达式由于运算符在操作数的后面,所以不需要括号来定义运算次序。
2.方便计算机计算:逆波兰表达式可以直接由计算机进行计算,减少了计算机解析表达式的复杂性。
3.避免二义性:逆波兰表达式的书写方式清晰明确,没有二义性。
方法一:使用栈实现转换1.初始化一个空栈和一个空列表用于存储转换后的逆波兰表达式。
2.从左到右扫描表达式。
3.如果遇到操作数(数字),则直接添加到逆波兰表达式列表中。
4.如果遇到运算符,按照以下规则处理:–如果栈为空或栈顶为左括号(或无运算符优先级低于当前运算符),则直接将运算符入栈。
–如果栈顶为运算符,并且其优先级大于等于当前运算符,将栈顶运算符出栈并添加到逆波兰表达式列表中,直到栈为空或栈顶为左括号(或无运算符优先级低于当前运算符)为止,然后将当前运算符入栈。
–如果当前运算符为右括号,则将栈顶运算符依次出栈并添加到逆波兰表达式列表中,直到遇到左括号为止。
5.扫描完成后,将栈中剩余的运算符依次出栈并添加到逆波兰表达式列表中。
6.逆波兰表达式列表即为转换后的结果。
方法二:使用递归实现转换1.定义一个递归函数,传入表达式和当前处理位置。
2.判断当前位置处的字符:–如果是数字,则直接返回该数字。
–如果是运算符,则递归调用函数获取左右两个操作数,并根据运算符将其组合成一个逆波兰表达式返回。
–如果是左括号,则继续向后处理直到找到对应的右括号。
3.递归函数返回结果即为转换后的逆波兰表达式。
方法三:使用中缀转后缀的方法1.定义两个栈,一个用于临时存储运算符,一个用于存储转换后的逆波兰表达式。
逆波兰表达式

逆波兰表达式逆波兰表达式表达式⼀般由操作数(Operand)、运算符(Operator)组成,例如算术表达式中,通常把运算符放在两个操作数的中间,这称为中缀表达式(Infix Expression),如A+B。
波兰数学家Jan Lukasiewicz提出了另⼀种数学表⽰法,它有两种表⽰形式:把运算符写在操作数之前,称为波兰表达式(Polish Expression)或前缀表达式(Prefix Expression),如+AB;把运算符写在操作数之后,称为逆波兰表达式(Reverse Polish Expression)或后缀表达式(Suffix Expression),如AB+;其中,逆波兰表达式在编译技术中有着普遍的应⽤。
算法:⼀、将中缀表达式转换成后缀表达式算法:1、从左⾄右扫描⼀中缀表达式。
2、若读取的是操作数,则判断该操作数的类型,并将该操作数存⼊操作数堆栈3、若读取的是运算符(1) 该运算符为左括号"(",则直接存⼊运算符堆栈。
(2) 该运算符为右括号")",则输出运算符堆栈中的运算符到操作数堆栈,直到遇到左括号为⽌。
(3) 该运算符为⾮括号运算符:(a) 若运算符堆栈栈顶的运算符为括号,则直接存⼊运算符堆栈。
(b) 若⽐运算符堆栈栈顶的运算符优先级⾼或相等,则直接存⼊运算符堆栈。
(c) 若⽐运算符堆栈栈顶的运算符优先级低,则输出栈顶运算符到操作数堆栈,并将当前运算符压⼊运算符堆栈。
4、当表达式读取完成后运算符堆栈中尚有运算符时,则依序取出运算符到操作数堆栈,直到运算符堆栈为空。
⼆、逆波兰表达式求值算法:1、循环扫描语法单元的项⽬。
2、如果扫描的项⽬是操作数,则将其压⼊操作数堆栈,并扫描下⼀个项⽬。
3、如果扫描的项⽬是⼀个⼆元运算符,则对栈的顶上两个操作数执⾏该运算。
4、如果扫描的项⽬是⼀个⼀元运算符,则对栈的最顶上操作数执⾏该运算。
5、将运算结果重新压⼊堆栈。
编译原理实验报告6逆波兰式的翻译和计算

实验6 逆波兰式的翻译和计算一、实验目的通过实验加深对语法指导翻译原理的理解,掌握算符优先分析的方法,将语法分析所识别的表达式变换成中间代码的翻译方法。
二、实验内容设计一个表示能把普通表达式(中缀式)翻译成后缀式,并计算出结果的程序。
三、实验要求1、给出文法如下:G[E]E->T|E+T;T->F|T*F;F->i(E);对应的转化为逆波兰式的语义动作如下: E-> E (1)op E (2) {E.CODE:= E (1).CODE||E (2).CODE||op} E->(E (1)) { E.CODE := E (1).CODE}E->id { E.CODE := id}2、利用实验5中的算符优先分析算法,结合上面给出的语义动作实现逆波兰式的构造;3、利用栈,计算生成的逆波兰式,步骤如下:1) 中缀表达式,从文本文件读入,每一行存放一个表达式,为了降低难度,表达式采用常数表达式;2) 利用结合语法制导翻译的算符优先分析,构造逆波兰式;3) 利用栈计算出后缀式的结果,并输出;四、实验环境PC 微机DOS 操作系统或 Windows 操作系统Turbo C 程序集成环境或 Visual C++ 程序集成环境五、实验步骤1、了解语法制导翻译的方法,学习后缀式构造的语义动作;2、结合实验5的算符优先程序,设计程序构造后缀式;3、利用栈,编程实现后缀式的计算;4、测试程序运行效果:从文本文件中读表达式,在屏幕上输出,检查输出结果。
六、测试数据输入数据:编辑一个文本文文件expression.txt,在文件中输入如下内容:正确结果:(1)1+2;输出:1,2,+ 3(2)(1+2)*3;输出:1,2,+,3,* 9(3)(10+20)*30+(50+60*70)输出:10,20,+30,*50,60,70,*,+,+ 5150七、实验报告要求实验报告应包括以下几个部分:1、构造逆波兰式的语义动作;2、结合算符优先分析构造逆波兰式的算法和过程;3、语法制导翻译的运行方法;4、程序的测试结果和问题;5、实验总结。
算术表达式转换为用逆波兰式来表示的算术表达式

j=1;
while(j<6&&x!=vt[j]) j++;
if(j>=6){
printf("error\n");
return 0;
}}
if(action[y][j]==NULL){
printf("error\n");
return 0;
}
strcpy(copy,action[y][j]);
}
}while(1);
}
int compvalue(){ /*计算后缀表达式的值*/
int stack[10]; /*作为栈使用*/
int s=0,top=0; /*t为ex下标,top为stack下标*/
while(d[s]!='#'){
switch(d[s]){
case '+':
stack[top-2]=stack[top-2]+stack[top-1];
printf("请输入表达式\n");
do{
scanf("%c",&c1);
c[top3]=c1;
top3=top3+1;
}while(c1!='#');
i=judge( );
if(i==1)printf("acc\n");
d[t++]='#';
for( i=0;i<t;i++)
printf("%c",d[i]);
中缀表达式变后缀表达式、后缀表达式(逆波兰)求值(python版本)

中缀表达式变后缀表达式、后缀表达式(逆波兰)求值(python版本)定义:中缀表达式:在通常的表达式中,⼆元运算符总是置于与之相关的两个运算对象之间,这种表⽰法也称为中缀表达式后缀表达式:⼜叫逆波兰表达式,不包含括号,放在两个运算对象的后⾯,所有的计算按运算符出现的顺序,严格从左向右进⾏(不再考虑运算符的优先规则,如:(2 + 1) * 3 ,即2 1 + 3 *⼀个字符串表达式s = “9 + ( 3 - 1 ) * 3 + 10 / 2”)求值的过程分为两步(⼀) 将中缀表达式s变为后缀表达式s_after = "9 3 1 - 3 * + 10 2 / +",具体的规则如下:⾸先维护两个空栈,(stack_exp)存放逆波兰表达式,(stack_ops)暂存操作符,运算结束后stack_ops必为空循环遍历字符串(将表达式分为四种元素 1、数值; 2、操作符; 3、左括号; 4、右括号),具体情况如下1、遇到数值,将该值⼊栈stack_exp2、遇到左括号,将左括号⼊栈stack_ops3、遇到右括号,将stack_ops中的操作符从栈顶依次出栈并⼊栈stack_exp,直到第⼀次遇到左括号终⽌操作(注意:该左括号出栈stack_ops但不⼊栈stack_exp)⾄此消除表达式中的⼀对括号4、遇到四则运算操作符号(+ - * /)4-1、如果stack_ops为空,操作符⼊栈stack_ops4-2、如果stack_ops不空,将stack_ops栈顶操作符与遍历到的操作符(op)⽐较:4-2-1:如果stack_ops栈顶操作符为左括或者op优先级⾼于栈顶操作符优先级, op⼊栈stack_ops,当前遍历结束4-2-2:如果op优先级⼩于或者等于stack_ops栈顶操作符, stack_ops栈顶操作符出栈并⼊栈stack_exp,重复4-1、 4-2直到op⼊栈stack_ops5、字符串遍历结束后如果stack_ops栈不为空,则依次将操作符出栈并⼊栈stack_exppython代码实现如下:ops_rule = {'+': 1,'-': 1,'*': 2,'/': 2}def middle_to_after(s):expression = []ops = []ss = s.split('')for item in ss:if item in ['+', '-', '*', '/']:while len(ops) >= 0:if len(ops) == 0:ops.append(item)breakop = ops.pop()if op == '('or ops_rule[item] > ops_rule[op]:ops.append(op)ops.append(item)breakelse:expression.append(op)elif item == '(':ops.append(item)elif item == ')':while len(ops) > 0:op = ops.pop()if op == '(':breakelse:expression.append(op)else:expression.append(item)while len(ops) > 0:expression.append(ops.pop())return expression(⼆) 将后缀表达式s_after = "9 3 1 - 3 * + 10 2 / +" 求值,具体的规则如下:初始化⼀个空栈stack_value,⽤于存放数值循环s_after1、如果遇到数字,⼊栈stack_value;2、如果遇到运算符,从stack_value中依次出栈两个数(先出栈的在右,后出栈的在左)连同遍历到的运算符组成⼆⽬运算,求值后将结果压栈stack_value3、继续遍历下⼀个元素,直到结束遍历完后stack_value中的结果便是表达式的值python代码实现如下:def expression_to_value(expression):stack_value = []for item in expression:if item in ['+', '-', '*', '/']:n2 = stack_value.pop()n1 = stack_value.pop()result = cal(n1, n2, item)stack_value.append(result)else:stack_value.append(int(item))return stack_value[0]def cal(n1, n2, op):if op == '+':return n1 + n2if op == '-':return n1 - n2if op == '*':return n1 * n2if op == '/':return n1 / n2if __name__ == '__main__':expression = middle_to_after('9 + ( 3 * ( 4 - 2 ) ) * 3 + 10 / 2')value = expression_to_value(expression)print value。
语法制导的翻译

– 自底向上:
•
在构造分析树的结点的同时计算相关的属性(此时其子 结点的属性必然已经计算完毕)
– 自顶向下:
• 递 A调归用子的程其序他法过中程,(在对过应程于A子()的结最构后)计已算经A调的用属完性毕()此时
a
22
在分析树上计算SDD
• 按照后序遍历的顺序计算属性值即可
postorder(N)
第五章 语法制导的翻译
赵建华 南京大学计算机系
2010年3月
a
1
介绍
• 使用上下文无关文法引导语言的翻译
– CFG的非终结符号代表了语言的某个构造 – 程序设计语言的构造由更小的构造组合而成 – 一个构造的语义可以由小构造的含义综合而来
• 比如:表达式x+y的类型由x、y的类型和运算符+决 定。
– 也可以从附近的构造继承而来
– 规则和产生式相关联
• 对于文法符号X和属性a,我们用X.a表示分 析树中的某个标号为X的结点的值。
• 一个分析树结点和它的分支对应于一个产 生式规则,而对应的语义规则确定了这些 结点上的属性的取值。
a
4
分析树和属性值(1)
• 假设我们需要知道一个表达式的类型,以及对 应代码将它的值存放在何处,我们就需要两个
{ for(从左边开始,对N的每个子结点C)
postorder(c); //递归调用返回时,各子结点的属性计算完毕 对N的各个属性求值;
} • 在LR分析过程中,我们实际上不需要构造分析树的
结点。
a
23
L属性的SDD
• 每个属性
– 要么是综合属性,
– 要规么则是只继能承使属用性,且产生式AX1X2…Xn中计算Xi.a的
《编译原理》实验报告

ch2=='e'||ch2=='$' )
zf=1; elsh
zf=0; } if(ch1=='e'){ if(ch2>='0' && ch2<='9' && h<=1||ch2=='+'||ch2=='-')
zf=1; else zf=0; } if(ch1=='+' || ch1=='-'){
if(i==0) break; if(ch2>='0' && ch2<='9'||ch2=='$') zf=1; else zf=0; } if (zf==0) break; else i++; } if(zf==0) printf("Input number are error!\n"); else printf("Input number are right!\n"); }
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
实验报告成绩:指导教师审核(签名):年月日
预习报告□实验报告□
语法制导把表达式翻译成逆波兰式
(一)实验目的
进一步掌握语法制导翻译的概念,理解中间语言,设计出错处理程序方法,掌握把表达式翻译成中间语言的算法。
(二)实验内容
1.从左到右扫描中缀表达式,经语法分析找出中缀表达式出现的错误并给出错误的具体位置和类型。
一个运算符栈存放暂时不能出现的运算符,逆波兰区存放逆波兰表达式。
2.测试所编程序,给出正确和错误的结果。
(三)实验要求
1.学生课前要认真阅读实验指导,理解实验内容与相关理论知识的关系,并完成预习报告
2.用C语言或其它高级语言编写程序
3.写出实验报告
实验报告成绩:指导教师审核(签名):年月日
预习报告□实验报告□
语法制导把表达式翻译成逆波兰式
(一)实验目的
通过上机实习加深对语法指导翻译原理的理解,掌握运算符优先权的算法,将语法分析所识别的表达式变换成中间代码的翻译方法。
(二)实验内容
同预习报告
(三)实验要求
1.学生课前要认真阅读实验指导,理解实验内容与相关理论知识的关系,并完成预习报告
2.用C语言或其它高级语言编写程序
3.写出实验报告
(四)实验设计思路
1)表达式生成逆波兰式的算法
1、初始化△送到运算符栈。
2、扫描左括号“(”,把△送到运算符栈。
3、扫描到变量,把它送到逆波兰区。
4、扫描到运算符
( 1)栈内运算符比较
a.栈内运算符>=栈外运算符,把栈内运算符送到逆波兰区。
b.栈内运算符<栈外运算符,把栈外运算符入栈。
( 2 ) 栈内是△把运算符入栈。
5、扫描右括号“)”。
( 1 )栈内是运算符,把栈内运算符送到逆波兰区。
( 2 )栈内是△则△退栈,读入下一个字符。
6、扫描到#(结束符)
( 1 )栈内是运算符,把栈内运算符送到逆波兰区。
( 2 )栈内是△结束,否则继续分析。
(五)程序流程图
(五)程序代码
// zty2.cpp : 定义控制台应用程序的入口点。
//
#include<stdio.h>
#include<string.h>
#define LEN 50
int main()
{
char stack[LEN];
char exp[LEN];
char str[LEN];
char ch;
int i=-1,j=0,top=0,flag=0;
printf("请输入表达式以#号结束:\n");
scanf("%s",str);
lab:i++;
ch=str[i];
if(ch=='#')goto lab1;
else if((ch>='0'&&ch<='9')||(ch>='a'&&ch<='z')) {
exp[j]=ch;
j++;
goto lab;
}
else if(ch=='('||ch=='^')
{
top++;
stack[top]=ch;
goto lab;
}
else if(ch=='+'||ch=='-')
{
while(top!=0&&stack[top]!='(')
{
exp[j]=stack[top];
top--;
j++;
}
top++;
stack[top]=ch;
goto lab;
}
else if(ch=='*'||ch=='/')
{
while(stack[top]=='*'||stack[top]=='/')
{
exp[j]=stack[top];
top--;
j++;
}
top++;
stack[top]=ch;
goto lab;
}
else if(ch==')')
{
while(stack[top]!='(')
{
exp[j]=stack[top];
j++;
top--;
}
top--;
goto lab;
}
else
{
printf("出错字符是:%c\n",str[i]);
printf("出错位置是:%d\n",i+1);
flag=1;
}
lab1:while(!flag&&top!=0)
{
exp[j]=stack[top];
j++;
top--;
}
top=j;
if(!flag)
{
j=0;
printf("逆波兰式为:\n");
while(j<top+1)
{
printf("%c",exp[j]);
j++;
}
}
return 0;
}
(六)程序运行结果测试
测试结如下图,测试结果符合实验要求。
表达式转化为逆波兰式的算法和对语法制导翻
译原理结果如下图:
输入:a+(b*(c-d^e)+f)/m# 时,输出正确的逆波兰式。
输入:a+2@3*5#时,输出输出错误的位置和字符。
(七)调试程序出现的问题及解决的方法
实验中编程基本没什么问题,数据结构实验时曾做过逆波兰式的实验,所以比较能理解,但是对实验的理解没有吃透,对语法制导翻译的原理理解不太清楚,所以,碰到了一些难度。
(八)实验心得体会
通过本次试验,掌握了表达式转化为逆波兰式的算法,并对语法制导翻译原理有了进一步的理解。
让我知道了我们编写程序的编译器是怎么编译程序和怎么工作的,也对编译课的理论知识有了一些加深的了解。
这次试验通过实践把老师讲的理论知识得到了充分的展示,对我们的编程打下了坚实的理论知识,除此之外,它使我对编译原理这门课所涉及的理论与方法有了更清晰的、更系统的认识。
所以,这次试验让我受益匪浅。