1、前序

真是书到用时方恨少啊,在大学的时候,虽然学过编译原理,但当时真是不懂啊,只是为了应付考试,死记硬背了一点点。现在呢,由于工作上的需要,不得不弥补一下啊。 这两天把编译原理的书又看了一遍,其实也就是主要看了文法,词法分析,语法分析而已,为了备忘,赶紧先记一下吧。

2、定义

词法分析,就是把源码中的一行行代码按照事先规定好的格式分隔成一个个单词符号(token),比如数字,变量名称,函数等等。
      语法分析呢,主要就是分析词法分析后的一个个token,是否能够拼装,组成事先规定好的语法中的一个。
      文法呢,就是对上面语法的规定的集合(程序里面用到的主要是上下文无关文法)。

3、实现

3.1词法分析

主要用到的技术有正则表达式,状态转换图,DFA(确定有限自动机),NFA(非确定有限自动机)以及超前搜索。

3.2语法分析

主要分自上而下分析和子下而上分析。

3.2.1自上而下分析

主要面临的问题有左递归和左因子。

3.2.1.1左递归的消除

可以参考一下公式
  P->Pα|β可以转换成一下语句
  P->βP'
  P'->αP'|ε   (ε为空子)

3.2.1.2消除回溯,提取公因子

可以参考一下公式
  A->δβ1|δβ2|δβ3|γ1|γ2|γ3
  转换成
  A->δA'|γ1|γ2|γ3
  A'->β1|β2|β3

3.2.1.3 LL(1)文法

意思就是从左向右扫描输入串,最左推导,每一步只需向前看一个符号。

3.2.1.4实现方法

当一个文法满足 LL(1)文法条件时,我们就可以为它构造一个不带回溯的自上而下分析程序,这个分析程序是由一组递归过程组成的,每个过程对应文法的一个非终结符。这样的一个分析程序称为递归下降分析器。
      另外一种实现方法为预测分析程序。就是使用一张分析表和一个栈进行联合控制。

3.2.2自下而上分析

从输入串开始,逐步进行规约,直至规约到文法的开始符号。
      由于与我下文说的javacc没有关系,因此我就不再叙述了。

4、javacc概述

JavaCC是一个词法分析器和语法分析器的生成器,用于java领域,根据指定格式的输入文件生成java源码的词法、语法分析程序(比lex和yacc要人性化)。
    是一个免费的开源的工具,可以从网上下载。
    javacc采用的是自顶向下的分析方法,而且没有回溯功能,因此如何解决冲突的问题,是程序员的责任。

必须要解决两个问题:左递归和公因子
      关于左递归的问题,一般采用经典的修改文法的方法解决。
      关于公因子的问题,可以改写算法(提取公因子),也可以通过展望(look ahead)更多符号的方法来解决。 但是因为javacc扩展了经典的BNF,因此它面临更多的问题。总之,当在编译时碰到冲突问题时,就说到了一个choice point。

可以将javacc中choice point归结为以下几类:
 l . 由选择算子 | 引入的冲突,
 2. 由可省略算子 [] 或 ?引入的冲突
 3. 由重复算子 * 引入的冲突
 4. 由重复算子 + 引入的冲突

当在javacc中碰到冲突时,可以采用下面的方法之一解决问题
 1.修改语法,使之成为LL(1)语法。
 2.只要将LOOKAHEAD=k写到Options块中即可,javacc产生的分析器就可以分析LL(K)语法生成的语言
 
 采用第一种方法的好处是效率非常高,易于维护。采用第二种方法的好处是语法更加直观,但是却不易维护。有时候采用第一种方法是无法解决冲突的,第二种方法是唯一的选择。

5、javacc入门

首先先从官网上面下载相应的javacc文件,我这里下载的是javacc-5.0.zip。里面包含一些入门文档(英文)和例子。

addr.jj

expression: Start->NUMBER(PLUS Primary)*

options {
STATIC = false ; //生成非静态类
LOOKAHEAD=2;//向前看2个字母,消除歧义用的
DEBUG_PARSER = true;//以debug形式生成,便于调试
}
PARSER_BEGIN(Adder)
class Adder {
public  static void main( String[] args ) throws ParseException, TokenMgrError
{
Adder parser = new Adder( System.in ) ;
parser.Start() ;
}
}
PARSER_END(Adder)
SKIP : { " " }
SKIP : { "\n" | "\r" | "\r\n" }
TOKEN : { < PLUS : "+" > }
TOKEN : { < NUMBER : <DIGITS> |<DIGITS> "." <DIGITS> |<DIGITS> "." | "." <DIGITS>> }
TOKEN : { < #DIGITS : (["0"-"9"])+ > }
void Start() :
{}
{
<NUMBER>
(
<PLUS>
<NUMBER>
)*
<EOF>
}

把上面代码保存到javacc bin目录下,执行以下语句。

相信能够看懂怎么使用吧。

6、javacc与eclipse整合

从网上下载easy-javacc-1.5.7-eclipse plugin.exe,并安装到eclipse的安装目录里面。然后重启eclipse,看下图。

编译adder.jj,生成java源文件(词法语法分析程序)

7、扩展语法分析器

在上面的例子中的start函数中,我们仅仅通过语法分析器来判断输入的语句是否正确
我们可以扩展BNF表达式,加入Java代码,使得经过语法分析后,得到我们想要的结果或者对象。
我们将start函数改写为:

注意:t.kind表示单词的类别,t.image表示单词值。

8、javacc实例一(计算器)

expression:

Start -> ( Expression EOL )* <EOF>
Expression -> Term(PLUS Term|MINUS Term)*
Term -> Primary(TIMES Primary | DIVIDE Primary)*
Primary -> NUMBER

Calculator.jj

options
{
static = false;
}
PARSER_BEGIN(Calculator)
import java.io.PrintStream ;
public class Calculator {
double previousValue = 0.0 ;
public static void main( String[] args )throws ParseException, TokenMgrError, NumberFormatException {
Calculator parser = new Calculator( System.in ) ;
parser.Start( System.out ) ;
}
}
PARSER_END(Calculator)
SKIP:{ " " }
TOKEN:{< EOL : "\n"|"\r"|"\r\n" >}
TOKEN:{< PLUS : "+">}
TOKEN :{ < MINUS : "-" > }
TOKEN:{< TIMES : "*" > }
TOKEN:{< DIVIDE : "/" > }
TOKEN:{< NUMBER : <DIGITS>| <DIGITS>"." <DIGITS> | <DIGITS>"."|"." <DIGITS> >}
TOKEN : {< #DIGITS : (["0"-"9"])+ >}
void Start(PrintStream printStream) throws NumberFormatException :
{}
{
(
previousValue = Expression()
<EOL>
{ printStream.println(previousValue); }
)*
<EOF>
}
double Expression() throws NumberFormatException :
{
double i;
double value;
}
{
value = Term()
(
<PLUS>
i = Term()
{ value += i;}
|
<MINUS>
i = Term()
{ value -= i; }
)*
{ return value; }
}
double Term() throws NumberFormatException :
{
double i;
double value;
}
{
value = Primary()
(
<TIMES>
i = Primary ()
{ value *= i;}
|
<DIVIDE>
i = Primary ()
{ value /= i; }
)*
{ return value; }
}
double Primary() throws NumberFormatException :
{
Token t;
double d;
}
{
t = <NUMBER>
{ return Double.parseDouble( t.image ); }
}

9、javacc实例一(计算器扩展)

在8的基础上,增加括号支持和负数计算

expression:

Start -> ( Expression EOL )* <EOF>
Expression -> Term(PLUS Term|MINUS Term)*
Term -> Primary(TIMES Primary | DIVIDE Primary)*
Primary -> NUMBER | PREVIOUS | MINUS Primary | OPEN_PAR Expression CLOSE_PAR

Calculator.jj

options
{
static = false;
}
PARSER_BEGIN(Calculator)
import java.io.PrintStream ;
public class Calculator {
double previousValue = 0.0 ;
public static void main( String[] args )throws ParseException, TokenMgrError, NumberFormatException {
Calculator parser = new Calculator( System.in ) ;
parser.Start( System.out ) ;
}
}
PARSER_END(Calculator)
SKIP:{ " " }
TOKEN:{< EOL : "\n"|"\r"|"\r\n" >}
TOKEN:{< PLUS : "+">}
TOKEN :{ < MINUS : "-" > }
TOKEN:{< TIMES : "*" > }
TOKEN:{< DIVIDE : "/" > }
TOKEN:{< OPEN_PAR : "(" > }
TOKEN:{< CLOSE_PAR : ")" > }
TOKEN:{< PREVIOUS : "$" > }
TOKEN:{< NUMBER : <DIGITS>| <DIGITS>"." <DIGITS> | <DIGITS>"."|"." <DIGITS> >}
TOKEN : {< #DIGITS : (["0"-"9"])+ >}
void Start(PrintStream printStream) throws NumberFormatException :
{}
{
(
previousValue = Expression()
<EOL>
{ printStream.println(previousValue); }
)*
<EOF>
}
double Expression() throws NumberFormatException :
{
double i;
double value;
}
{
value = Term()
(
<PLUS>
i = Term()
{ value += i;}
|
<MINUS>
i = Term()
{ value -= i; }
)*
{ return value; }
}
double Term() throws NumberFormatException :
{
double i;
double value;
}
{
value = Primary()
(
<TIMES>
i = Primary ()
{ value *= i;}
|
<DIVIDE>
i = Primary ()
{ value /= i; }
)*
{ return value; }
}
double Primary() throws NumberFormatException :
{
Token t;
double d;
}
{
t = <NUMBER>
{ return Double.parseDouble( t.image ); }
|
<PREVIOUS>
{ return previousValue; }
|
<OPEN_PAR> d = Expression() <CLOSE_PAR>
{ return d; }
|
<MINUS> d = Primary()
{ return -d; }
}

编译原理与javacc初探相关推荐

  1. javacc编译原理实训报告

    一.综合训练目的与要求 本综合训练是软件工程专业重要的实践性环节之一,是在学生学习完<编译原理>课程后进行的综合练习. 本课综合训练的目的: 巩固和加深学生对编译原理课程基本知识的理解和掌 ...

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

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

  3. 国内外编译原理课程实践教学现状分析

    <自己动手写编译器.链接器> 冯向萍 (新疆农业大学计算机与信息工程学院)   摘 要:本文主要从教材的选择,实践项目的设置以及实践课程占总评成绩的比例等方面分析和比较了国内外多所高校编译 ...

  4. 国防科学技术大学 编译原理国家级精品课程

    国防科学技术大学 编译原理国家级精品课程          王挺 五十多年以来,随着编译技术的发展,有关编译原理和技术的内容被逐步引入到了计算机专业本科教学中.从早期各阶段 ACM 和 IEEE 的计 ...

  5. 对学习编译原理的看法

    我认为编译原理这本书是一门与代码做斗争的课程,学习编译原理能够追寻程序设计语言的本质,了解计算机各种语言编译的原理.学习了编译原理能够更加深入的了解计算机各种高级语言使用的原理,能使自己更加容易更加好 ...

  6. 《OpenMP编译原理及实现技术》摘录

    内容摘自<OpenMP编译原理及实现技术>第2章 代码测试环境:Windows7 64bit, VS2010, 4核机. 可以说OpenMP制导指令将C语言扩展为一个并行语言,但OpenM ...

  7. 编译原理(六)自底向上分析之LR分析法

    自底向上分析之LR分析法 说明:以老师PPT为标准,借鉴部分教材内容,AlvinZH学习笔记. 基本概念 1. LR分析:从左到右扫描(L)自底向上进行规约(R),是规范规约,也即最右推导(规范推导) ...

  8. 编译原理上机实习c语言小子集编译程序的实现报告,编译原理上机实习指导书(2015-11修改).pdf...

    <编译原理上机实习>指导书 一.上机实习目的 理解编译程序的构造原理,掌握编译程序的构造方法与技术.通过实习,使学生既加深对 编译原理基础理论的理解,又提高动手能力,特别是提高软件设计能力 ...

  9. 我看过的编译原理方面的好文章

    本文不定期更新,最后更新于2019-7-6 编译原理 编译原理三大经典书籍(龙书 虎书 鲸书) 前端为什么要会正则表达式 - 知乎 一次性搞懂JavaScript正则表达式之引擎 - 掘金 没有AST ...

最新文章

  1. 自定义控件的构建(5)
  2. React开发(169):ant design Popconfirm 使用
  3. Linux Tun/Tap网口(/dev/net/tun)的读写方法
  4. thinkphp5.0 session驱动方式问题汇总
  5. JAVA- Jersey使用示例
  6. word自带公式编辑_如何在word自带的公式编辑器中设置字体格式
  7. 计算机术语hook的理解
  8. go语法 — 多路选择操作符 select的用法
  9. Kyligence 完成 2500 万美元 C 轮融资,Coatue 领投
  10. 国内首台商用人形双足机器人发展史
  11. python的PIL库部分模块函数
  12. 浅谈屏幕拍摄泄密跟踪的检测技术
  13. NULL 指针在不同平台下的表现引发程序报错(C 语言)
  14. 计算机管理服务修复,电脑提示“部署映像服务和管理工具错误87”的修复步骤...
  15. 网友推荐2010年央视春晚节目单 恶搞社会热点
  16. 递归实现20的阶乘(Java实现)
  17. 软件新产品开发失败原因分析
  18. 2021年危险化学品生产单位安全生产管理人员考试及危险化学品生产单位安全生产管理人员考试资料
  19. 用Mac电脑看txt小说,哪些阅读器软件更好用?
  20. PTA 7 龙舌兰酒吧

热门文章

  1. mysql分析之profile详解(过去用法,但常用),一篇博客帮你理解 profile 的用法
  2. 差分方程matlab实验报告,实验二微分方程与差分方程模型Matlab求解
  3. 系统调用之sys_adjtimex
  4. 2023最新ChatGPT网站源码+支持ChatGPT4.0+支持Midjourney绘画+用户会员套餐+后台管理+一键更新版本
  5. vue + iview 项目实践总结 【完】
  6. NFT 内卷战争与创造正外部性
  7. 几种常用的PID控制方式(入门推荐)
  8. mysql数据库latin1转utf8_mysql数据库latin1转utf8
  9. CF1060E Sergey and Subway
  10. 百度2009年度搜索风云榜发布