// 来自龙书第2章2.5小节-简单表达式的翻译器

笔记

既然是语法制导翻译(Syntax-directed translation),那么最重要的东西当然是描述该语言语法的文法,以下为中缀表达式文法(仅由+-以及0~9的数字构成):

expr -> expr + term | expr - term | term
term -> ~9的数字

接下来考虑如何利用该文法将原语言转化为后缀形式,此时可以脑补一下该文法的语法分析树(parse tree),例如:

严格来说,语法分析树是相对于某特定终结符号串生成的,叶子结点必须是终结符号,但是方便起见就省略了。expr1是expr的某个实体,便于和它的父结点区分。如果有能力构造这棵树的话,只需按照某种遍历求值顺序,很容易可以得到“expr->expr+term”到其后缀形式的语法制导定义(syntax-directed definition):

expr.后缀形式 -> expr1.后缀形式 term.后缀形式 +

龙书里把这种简单形式的语法制导定义称为简单语法制导定义(simple syntax-directed definition)。

一个语法制导定义把①每个文法符号和一个属性集合相关联,例如这里把expr和属性“后缀形式”相关联,并且把②每个产生式和一组语义规则(semantic rule)相关联,例如把“expr->expr+term”和“expr.后缀形式 -> expr1.后缀形式 term.后缀形式 +”相关联。

依据上面的语法制导定义我们可以得到一个语法制导翻译方案( syntax-directed translation scheme),也就是在文法产生式中附加一些程序片段来描述翻译结果的表示方法,被嵌入到产生式体中的程序片段称为语义动作(semantic action):

expr -> expr1 + term {print('+')}
term -> 数字 {打印该数字}

但是这个语法制导翻译方案没法直接写成代码,因为会发生左递归的问题:

        function expr() {
expr() ...
}

消除左递归(该过程需特别小心!详见书)得到:

expr -> term rest
rest -> + term {print('+')} rest

代码有两点简化:

  1. 将rest函数的尾递归替换为迭代过程
  2. 将修改后的rest函数并入expr函数

截图与代码

<!DOCTYPE html>
<html> <head>
<meta charset="UTF-8">
<title></title>
<link href="https://fonts.googleapis.com/css?family=Noto+Serif+SC" rel="stylesheet">
<style>
main {
/*对子元素开启弹性布局*/
display: flex;
/*弹性元素在必要的时候换行*/
flex-wrap: wrap;
/*将弹性元素居中*/
justify-content: center;
} textarea,
button {
font-family: 'Noto Serif SC', STFangSong, serif;
font-size: 17px;
}
</style>
</head> <body>
<main>
<textarea name="input" rows="20" cols="40"></textarea>
<textarea name="output" rows="20" cols="40"></textarea>
<button name="execute">Execute</button>
</main> <script>
let inputBox = document.querySelector("textarea[name=input]");
let outputBox = document.querySelector("textarea[name=output]");
let btnExecute = document.querySelector("button[name=execute]"); btnExecute.addEventListener("click", event => {
startParsing(inputBox.value);
}); function startParsing(s) {
str = s;
cur = 0;
result = "";
expr();
outputBox.value = result;
} function expr() {
term();
while (true) {
if (str[cur] == '+') {
match('+');
term();
result += '+';
} else if (str[cur] == '-') {
match('-');
term();
result += '-';
} else {
return;
}
}
} function term() {
if (/[0-9]/.test(str[cur])) {
result += str[cur];
match(str[cur]);
} else {
report("存在语法错误,字符位置为:" + cur);
}
} function match(ch) {
if (cur < str.length && str[cur] === ch) ++cur;
else report("存在语法错误,字符位置为:" + cur);
} function report(s) {
outputBox.value = s;
throw new Error(s);
}
</script>
</body> </html>

编译原理 #03# 龙书中缀转后缀JS实现版的更多相关文章

  1. 正则表达式引擎的构建——基于编译原理DFA(龙书第三章)——3 计算4个函数

    整个引擎代码在github上,地址为:https://github.com/sun2043430/RegularExpression_Engine.git nullable, firstpos, la ...

  2. 编译原理_P1004

    龙书相关知识点总结 //*************************引论***********************************// 1. 编译器(compiler):从一中语言( ...

  3. Compiler Theory(编译原理)、词法/语法/AST/中间代码优化在Webshell检测上的应用

    catalog . 引论 . 构建一个编译器的相关科学 . 程序设计语言基础 . 一个简单的语法制导翻译器 . 简单表达式的翻译器(源代码示例) . 词法分析 . 生成中间代码 . 词法分析器的实现 ...

  4. 龙书(Dragon book) +鲸书(Whale book)+虎书(Tiger book)

    1.龙书(Dragon book)书名是Compilers: Principles,Techniques,and Tools作者是:Alfred V.Aho,Ravi Sethi,Jeffrey D. ...

  5. Go 编译原理实现计算器(测试驱动讲解)

    本文不需要你掌握任何编译原理的知识. 只需要看懂简单的golang语言即可, 完整的代码示例在GIT, 代码是从writing an interpreter in go这本书抽取了简单的部分出来, 如 ...

  6. 学了编译原理能否用 Java 写一个编译器或解释器?

    16 个回答 默认排序​ RednaxelaFX JavaScript.编译原理.编程 等 7 个话题的优秀回答者 282 人赞同了该回答 能.我一开始学编译原理的时候就是用Java写了好多小编译器和 ...

  7. 编译原理之正则表达式转NFA

    本文转载自http://chriszz.sinaapp.com/?p=257 输入一个正则表达式,输出一个NFA. 我的做法:输入一个字符串表示正则,输出则是把输出到一个.dot文件中并将dot文件编 ...

  8. Stanford公开课《编译原理》学习笔记(1~4课)

    目录 一. 编译的基本流程 二. Lexical Analysis(词法分析阶段) 2.1 Lexical Specification(分词原则) 2.2 Finite Automata (典型分词算 ...

  9. Stanford公开课《编译原理》学习笔记(2)递归下降法

    目录 一. Parse阶段 CFG Recursive Descent(递归下降遍历) 二. 递归下降遍历 2.1 预备知识 2.2 多行语句的处理思路 2.3 简易的文法定义 2.4 文法产生式的代 ...

随机推荐

  1. 内核kmalloc内存越界排查过程(转)

    https://blog.csdn.net/hjkfcz/article/details/84500026 内核为了效率,memcpy完全是有汇编实现,加入c代码很困难.可以采用jprobe技术,动态 ...

  2. fpga xilink 电平

  3. nodejs实时的检测系统文件的变化(无需重启服务)

    1.安装superior npm -g install supervisor 注意 superior必须全局安装,否则错误命令会提示安装到全局 2.修改启动 现在我们需要使用 supervisor a ...

  4. css table之合并单元格

    colspan 是合并列,rowspan是合并行,合并行的时候,比如rowspan="2",它的下一行tr会少一列:合并列的时候 colspan="2",此行的 ...

  5. VUE-007-通过路由 router.push 传递 query 参数(路由 name 识别,请求链接显示参数传递)

    在前端页面表单列表修改时,经常需要在页面切换的时候,传递需要修改的表单内容,通常可通过路由进行表单参数的传递. 首先,配置页面跳转路由.在 router/index.js 中配置相应的页面跳转路由,如 ...

  6. 什么是一致性Hash算法?

    一.Redis集群的使用 我们在使用Redis的时候,为了保证Redis的高可用,提高Redis的读写性能,最简单的方式我们会做主从复制,组成Master-Master或者Master-Slave的形 ...

  7. 关于autofac的一些具体的用法

    简介:Autofac是一个.net下非常优秀,性能非常好的IOC容器(.net下效率最高的容器) 1.nuget 引用 2.创建两个类库项目,IService (用于编写接口),ServiceImpl ...

  8. ASM检查RAC是否成功

    [grid@asm ~]$ crsctl status resourceNAME=ora.DATA.dgTYPE=ora.diskgroup.typeTARGET=ONLINESTATE=ONLINE ...

  9. jq中get()和eq()的区别

    一直弄混淆的获取元素的方法,现整理一下: :eq(index) 选择器选取带有指定 index 值的元素. index 值从 0 开始,所有第一个元素的 index 值是 0(不是 1). 如:$(& ...

  10. js 获取鼠标的手势方向角度

    需要获取鼠标的移动角度 1.mousedown 确定起始点 2.mousemove 确立相关点 3.先计算两点的斜率,然后根据三角函数和反三角函数.转换为角度 <!DOCTYPE html> ...