Java 实现《编译原理》中间代码生成 -逆波兰式生成与计算 - 程序解析

编译原理学习笔记

(一)逆波兰式是什么?

逆波兰式(Reverse Polish notation,RPN,或逆波兰记法),也叫 后缀表达式(将运算符写在操作数之后)

一般的表达式又称 中缀表达式,这种表达式的二元运算符放在两个运算量 之间。而逆波兰表达式又称 后缀表达式,这种表达式把运算符放在运算量 后面

比如如 a+b 的逆波兰式表示为 ab+

注意:逆波兰式是一个无括号表达式;逆波兰式的运算符出现的顺序就是原表达式的运算顺序。

(二)逆波兰式编译原理有什么关系?

逆波兰式,三元式,四元式等是编译原理 - 中间代码生成阶段的常见的中间代码形式。

(三)本篇任务

通过设计,使用 Java 语言编写一个逆波兰式生成程序,测试效果:

(四)Java 源代码

package com.java997.analyzer.rpn;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Stack; /**
* <p>
* 逆波兰式
*
* @author XiaoPengwei
* @since 2019-06-19
*/
public class RpnMain { /**
* 检查算术表达术括号是否匹配, 语法是否正确
*
* @param s 算术表达术
* @return boolean
*/
public boolean isMatch(String s) { //括号符号栈
Stack<Character> charStack = new Stack<>(); //将表达式的字符串转换成数组
char[] charArray = s.toCharArray(); //遍历数组
for (char aChar : charArray) {
if (aChar == '(') {
charStack.push(aChar);
} else if (aChar == ')') {
//如果是 ) , 且栈为空则返回 false
if (charStack.isEmpty()) {
return false;
} else {
//如果是 ) , 且栈不为空则返回 false
//peek() 是返回栈顶的值, 不做其他操作
if (charStack.peek() == '(') {
//把栈顶的值删除
charStack.pop();
}
}
}
}
//走到这里, 栈为空则表达式正确
return charStack.empty();
} /**
* 判断是否为操作符 + - * /
*
* @param charAt
* @return boolean
*/
public boolean isOperator(char charAt) {
return charAt == '+' || charAt == '-' || charAt == '*' || charAt == '/';
} /**
* 根据正确的表达式, 获取逆波兰式
*
* @param input
* @return java.lang.String
*/
public StringBuilder getRpn(String input) {
//结果
StringBuilder sb = new StringBuilder();
sb.append("The RPN is: ");
//运算符栈
Stack<Character> opStack = new Stack(); //运算符优先级
Map<Character, Integer> opMap = new HashMap(5);
opMap.put('(', 0);
opMap.put('+', 1);
opMap.put('-', 1);
opMap.put('*', 2);
opMap.put('/', 2); //处理字符串
for (int i = 0; i < input.length(); i++) {
//如果是'('直接压栈
if (input.charAt(i) == '(') {
opStack.push('(');
} else if (new RpnMain().isOperator(input.charAt(i))) {
//如果是运算符
char curOp = input.charAt(i);
//如果运算符栈是空,就直接压栈
if (opStack.isEmpty()) {
opStack.push(curOp);
} else if (opMap.get(curOp) > opMap.get(opStack.peek())) {
//运算符栈不为空,且当当前运算符的优先级比站内第一个运算符的优先级高的时候,压栈
opStack.push(curOp);
} else {
//栈不为空,且运算符的优先级小于等于栈顶元素
for (int j = 0; j <= opStack.size(); j++) {
//弹出栈内第一个元素
char ch = opStack.pop();
sb.append(ch);
if (opStack.isEmpty()) {
opStack.push(curOp);
break;
} else if (opMap.get(curOp) > opMap.get(opStack.peek())) {
opStack.push(curOp);
break;
}
}
}
} else if (input.charAt(i) == ')') {
//如果是')'就把站内'('上的元素都弹出栈
for (int j = 0; j < opStack.size(); j++) {
char c = opStack.pop();
if (c == '(') {
break;
} else {
sb.append(c);
}
}
} else if ('A'<=input.charAt(i)&&input.charAt(i)<='Z'){
//如果是字母就直接添加
sb.append(input.charAt(i));
}else if ('a'<=input.charAt(i)&&input.charAt(i)<='z'){
//如果是字母就直接添加
sb.append(input.charAt(i));
}else if (Character.isDigit(input.charAt(i))){
//如果是数字
sb.append(input.charAt(i));
}else {
return new StringBuilder("But the expression contains unrecognizable characters");
}
} //把栈内剩余的运算符都弹出站
for (int i = 0; i <= opStack.size(); i++) {
sb.append(opStack.pop());
} return sb;
} public static void main(String[] args) {
RpnMain rpnMain = new RpnMain();
Scanner sc = new Scanner(System.in); while (true) {
System.out.println("==========================\nPlease input an expression:");
String input = sc.nextLine(); if ("q".equals(input)) {
sc.close();
return;
} else {
if (rpnMain.isMatch(input)) {
System.out.println("The expression's brackets are matched");
// 获取逆波兰式
System.out.println(rpnMain.getRpn(input));
} else {
System.out.println("Error: The expression's brackets are not matched! Enter 'q' to exit");
}
}
}
}
}

测试:

Java 实现《编译原理》中间代码生成 -逆波兰式生成与计算 - 程序解析的更多相关文章

  1. javascript:逆波兰式表示法计算表达式结果

    逆波兰式表示法,是由栈做基础的表达式,举个例子: 5 1 2 + 4 * + 3 -  等价于   5 + ((1 + 2) * 4) - 3 原理:依次将5 1 2 压入栈中, 这时遇到了运算符 + ...

  2. c# 逆波兰式实现计算器

    语文不好,不太会组织语言,希望不要太在意. 如题,先简要介绍一下什么是逆波兰式  通常我们在写数学公式的时候  就是a+b+c这样,这种表达式称为中缀表达式,逆波兰式又称为后缀表达式,例如a+b 后缀 ...

  3. Java的编译原理

    概述 java语言的"编译期"分为前端编译和后端编译两个阶段.前端编译是指把*.java文件转变成*.class文件的过程; 后端编译(JIT, Just In Time Comp ...

  4. Haskell解决逆波兰式

    摘自<Haskell趣学指南- Learn You a Haskell for Great Good> {- 逆波兰式(revese polish notation, RPN): 操作符出 ...

  5. HDU1237 简单的计算器 【堆】+【逆波兰式】

    简单的计算器 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Sub ...

  6. HDU1237 简单计算器 【栈】+【逆波兰式】

    简单计算器 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Subm ...

  7. [LeetCode]Evaluate Reverse Polish Notation(逆波兰式的计算)

    原题链接:http://oj.leetcode.com/problems/evaluate-reverse-polish-notation/ 题目描述: Evaluate the value of a ...

  8. python 逆波兰式

    逆波兰式,也叫后缀表达式 技巧:为简化代码,引入一个不存在的运算符#,优先级最低.置于堆栈底部 class Stack(object): '''堆栈''' def __init__(self): se ...

  9. codechef Transform the Expression 转换成逆波兰式

    版权声明:本文作者靖心,靖空间地址:http://blog.csdn.net/kenden23/.未经本作者同意不得转载. https://blog.csdn.net/kenden23/article ...

随机推荐

  1. 【D3D12学习手记】The Swap Chain and Page Flipping

    为了避免动画中的闪烁,最好将整个动画帧绘制到称为后台缓冲区的屏幕外纹理(off-screen texture)中.一旦整个场景被绘制到给定动画帧的后缓冲区,它就作为一个完整的帧呈现给屏幕;以这种方式, ...

  2. React-Native传值方式之 :DeviceEventEmitter添加监听控制并传值到其他页面

    在 native 开发中,我们可以使用广播实现事件的订阅和事件的触发,从而实现不在该页面但是可以调用该页面的方法. 在 React Native 中,我们也可以使用 DeviceEventEmitte ...

  3. 网络实验 02-交换机的Telnet远程登录设置

    交换机的Telnet远程登录设置 一.实验目标 掌握采用telnet方式配置交换机的方法 二.技术原理 1. 配置交换机的管理IP地址(计算机的IP地址与交换机管理IP地址在同一网段) 2. 为tel ...

  4. 微信小程序-T

    微信小程序安装完毕后,真机调试及预览(打开调试模式)有数据,其他类型无数据在微信公众号-开发-域名信息中绑定正确的域名即可 配置微信小程序合法域名微信公众号-开发微擎应用-基础设置微信app-site ...

  5. ASP.NET Core 入门笔记2,建立项目

    1.建立项目 2.项目结构 1.项目结构说明 根目录/文件 说明 .vscode目录 VS Code项目配置目录,相当于.vs..idea文件夹 bin目录 编译输出目录 obj目录 编译配置与中间目 ...

  6. react中处理后台接口返回存在html标签的字符串

    render{ return( <div> <div>接下来是例子</div> <div dangerouslySetInnerHTML={{__html:& ...

  7. Linux后台运行python程序并输出到日志文件

    后台运行python程序并标准输出到文件 现在有test.py程序要后台部署, 里面有输出内容 使用命令: nohup python -u test.py > test.log 2>&am ...

  8. PJzhang:python基础入门的7个疗程-three

    猫宁!!! 参考链接:易灵微课-21天轻松掌握零基础python入门必修课-售价29元人民币 https://www.liaoxuefeng.com/wiki/1016959663602400 第七天 ...

  9. ubuntu 16.04 server 扩容(LVM)磁盘

    因为发现我的本地server出现磁盘满了的情况 所以进行lvm的扩容 1 查看磁盘情况 df -h 原本发现 /dev/mapper/ubuntu1604--vg-root 这个磁盘满了 所以要进行扩 ...

  10. Identification of Encryption Algorithm Using Decision Tree

    本文主要做了两件事,一是提出了一种使用C4.5算法生成的决策树来识别密文所使用的加密算法的方法,二是为这一算法设计了一个特征提取系统提取八个特征作为算法的输入,最终实现了70%~75的准确率. 准备工 ...