热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

编译原理与javacc初探[转]

2019独角兽企业重金招聘Python工程师标准1、前序真是书到用时方恨少啊,在大学的时候,虽然学过编译原理,但当时真是不懂啊&#x

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

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)*

[java] view plain copy

 print?在CODE上查看代码片派生到我的代码片

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

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:

[java] view plain copy

 print?在CODE上查看代码片派生到我的代码片

  1. Start -> ( Expression EOL )*   
  2. Expression -> Term(PLUS Term|MINUS Term)*  
  3. Term -> Primary(TIMES Primary | DIVIDE Primary)*  
  4. Primary -> NUMBER  


Calculator.jj

[java] view plain copy

 print?在CODE上查看代码片派生到我的代码片

  1. options  
  2. {  
  3.   static = false;  
  4. }  
  5.   
  6. PARSER_BEGIN(Calculator)  
  7. import java.io.PrintStream ;  
  8. public class Calculator {  
  9.     double previousValue = 0.0 ;  
  10.     public static void main( String[] args )throws ParseException, TokenMgrError, NumberFormatException {  
  11.         Calculator parser = new Calculator( System.in ) ;  
  12.         parser.Start( System.out ) ;  
  13.     }  
  14. }  
  15. PARSER_END(Calculator)  
  16.   
  17. SKIP:{ " " }  
  18.   
  19. TOKEN:{< EOL : "\n"|"\r"|"\r\n" >}  
  20. TOKEN:{< PLUS : "&#43;">}  
  21. TOKEN :{ < MINUS : "-" > }  
  22. TOKEN:{< TIMES : "*" > }  
  23. TOKEN:{< DIVIDE : "/" > }  
  24. TOKEN:{< NUMBER : "."  | "."|"."  >}  
  25. TOKEN : {< #DIGITS : (["0"-"9"])&#43; >}  
  26.   
  27. void Start(PrintStream printStream) throws NumberFormatException :  
  28. {}  
  29. {  
  30.     (  
  31.         previousValue &#61; Expression()  
  32.           
  33.         { printStream.println(previousValue); }  
  34.     )*  
  35.       
  36. }  
  37.   
  38. double Expression() throws NumberFormatException :  
  39. {  
  40.     double i;  
  41.     double value;  
  42. }  
  43. {  
  44.     value &#61; Term()  
  45.     (  
  46.           
  47.         i &#61; Term()  
  48.         { value &#43;&#61; i;}  
  49.         |  
  50.           
  51.         i &#61; Term()  
  52.         { value -&#61; i; }  
  53.     )*  
  54.     { return value; }  
  55. }   
  56.   
  57. double Term() throws NumberFormatException :  
  58. {  
  59.     double i;  
  60.     double value;  
  61. }  
  62. {  
  63.     value &#61; Primary()  
  64.     (  
  65.           
  66.         i &#61; Primary ()  
  67.         { value *&#61; i;}  
  68.         |  
  69.           
  70.         i &#61; Primary ()  
  71.         { value /&#61; i; }  
  72.     )*  
  73.     { return value; }  
  74. }  
  75.   
  76. double Primary() throws NumberFormatException :  
  77. {  
  78.     Token t;  
  79.     double d;  
  80. }  
  81. {  
  82.     t &#61;   
  83.     { return Double.parseDouble( t.image ); }  
  84. }  

9、javacc实例一&#xff08;计算器扩展&#xff09;

   在8的基础上&#xff0c;增加括号支持和负数计算

expression&#xff1a;

[java] view plain copy

 print?在CODE上查看代码片派生到我的代码片

  1. Start -> ( Expression EOL )*   
  2. Expression -> Term(PLUS Term|MINUS Term)*  
  3. Term -> Primary(TIMES Primary | DIVIDE Primary)*  
  4. Primary -> NUMBER | PREVIOUS | MINUS Primary | OPEN_PAR Expression CLOSE_PAR  


Calculator.jj

 

[java] view plain copy

 print?在CODE上查看代码片派生到我的代码片

  1. options  
  2. {  
  3.   static &#61; false;  
  4. }  
  5.   
  6. PARSER_BEGIN(Calculator)  
  7. import java.io.PrintStream ;  
  8. public class Calculator {  
  9.     double previousValue &#61; 0.0 ;  
  10.     public static void main( String[] args )throws ParseException, TokenMgrError, NumberFormatException {  
  11.         Calculator parser &#61; new Calculator( System.in ) ;  
  12.         parser.Start( System.out ) ;  
  13.     }  
  14. }  
  15. PARSER_END(Calculator)  
  16.   
  17. SKIP:{ " " }  
  18.   
  19. TOKEN:{< EOL : "\n"|"\r"|"\r\n" >}  
  20. TOKEN:{< PLUS : "&#43;">}  
  21. TOKEN :{ < MINUS : "-" > }  
  22. TOKEN:{< TIMES : "*" > }  
  23. TOKEN:{< DIVIDE : "/" > }  
  24. TOKEN:{< OPEN_PAR : "(" > }  
  25. TOKEN:{< CLOSE_PAR : ")" > }  
  26. TOKEN:{< PREVIOUS : "$" > }  
  27. TOKEN:{< NUMBER : "."  | "."|"."  >}  
  28. TOKEN : {< #DIGITS : (["0"-"9"])&#43; >}  
  29.   
  30. void Start(PrintStream printStream) throws NumberFormatException :  
  31. {}  
  32. {  
  33.     (  
  34.         previousValue &#61; Expression()  
  35.           
  36.         { printStream.println(previousValue); }  
  37.     )*  
  38.       
  39. }  
  40.   
  41. double Expression() throws NumberFormatException :  
  42. {  
  43.     double i;  
  44.     double value;  
  45. }  
  46. {  
  47.     value &#61; Term()  
  48.     (  
  49.           
  50.         i &#61; Term()  
  51.         { value &#43;&#61; i;}  
  52.         |  
  53.           
  54.         i &#61; Term()  
  55.         { value -&#61; i; }  
  56.     )*  
  57.     { return value; }  
  58. }   
  59.   
  60. double Term() throws NumberFormatException :  
  61. {  
  62.     double i;  
  63.     double value;  
  64. }  
  65. {  
  66.     value &#61; Primary()  
  67.     (  
  68.           
  69.         i &#61; Primary ()  
  70.         { value *&#61; i;}  
  71.         |  
  72.           
  73.         i &#61; Primary ()  
  74.         { value /&#61; i; }  
  75.     )*  
  76.     { return value; }  
  77. }  
  78.   
  79. double Primary() throws NumberFormatException :  
  80. {  
  81.     Token t;  
  82.     double d;  
  83. }  
  84. {  
  85.     t &#61;   
  86.     { return Double.parseDouble( t.image ); }  
  87.     |  
  88.           
  89.     { return previousValue; }     
  90.     |     
  91.      d &#61; Expression()    
  92.     { return d; }     
  93.     |     
  94.      d &#61; Primary()   
  95.     { return -d; }  
  96. }  

转:https://my.oschina.net/qiangzigege/blog/862120



推荐阅读
  • 本文回顾了作者初次接触Unicode编码时的经历,并详细探讨了ASCII、ANSI、GB2312、UNICODE以及UTF-8和UTF-16编码的区别和应用场景。通过实例分析,帮助读者更好地理解和使用这些编码。 ... [详细]
  • Android 构建基础流程详解
    Android 构建基础流程详解 ... [详细]
  • 数字图书馆近期展出了一批精选的Linux经典著作,这些书籍虽然部分较为陈旧,但依然具有重要的参考价值。如需转载相关内容,请务必注明来源:小文论坛(http://www.xiaowenbbs.com)。 ... [详细]
  • 本指南从零开始介绍Scala编程语言的基础知识,重点讲解了Scala解释器REPL(读取-求值-打印-循环)的使用方法。REPL是Scala开发中的重要工具,能够帮助初学者快速理解和实践Scala的基本语法和特性。通过详细的示例和练习,读者将能够熟练掌握Scala的基础概念和编程技巧。 ... [详细]
  • Ihavetwomethodsofgeneratingmdistinctrandomnumbersintherange[0..n-1]我有两种方法在范围[0.n-1]中生 ... [详细]
  • Visual Studio Code (VSCode) 是一款功能强大的源代码编辑器,支持多种编程语言,具备丰富的扩展生态。本文将详细介绍如何在 macOS 上安装、配置并使用 VSCode。 ... [详细]
  • 字节流(InputStream和OutputStream),字节流读写文件,字节流的缓冲区,字节缓冲流
    字节流抽象类InputStream和OutputStream是字节流的顶级父类所有的字节输入流都继承自InputStream,所有的输出流都继承子OutputStreamInput ... [详细]
  • Python 3 Scrapy 框架执行流程详解
    本文详细介绍了如何在 Python 3 环境下安装和使用 Scrapy 框架,包括常用命令和执行流程。Scrapy 是一个强大的 Web 抓取框架,适用于数据挖掘、监控和自动化测试等多种场景。 ... [详细]
  • 在Windows系统中安装TensorFlow GPU版的详细指南与常见问题解决
    在Windows系统中安装TensorFlow GPU版是许多深度学习初学者面临的挑战。本文详细介绍了安装过程中的每一个步骤,并针对常见的问题提供了有效的解决方案。通过本文的指导,读者可以顺利地完成安装并避免常见的陷阱。 ... [详细]
  • 在软件开发过程中,经常需要将多个项目或模块进行集成和调试,尤其是当项目依赖于第三方开源库(如Cordova、CocoaPods)时。本文介绍了如何在Xcode中高效地进行多项目联合调试,分享了一些实用的技巧和最佳实践,帮助开发者解决常见的调试难题,提高开发效率。 ... [详细]
  • PHP预处理常量详解:如何定义与使用常量 ... [详细]
  • 本文深入探讨了Java多线程环境下的同步机制及其应用,重点介绍了`synchronized`关键字的使用方法和原理。`synchronized`关键字主要用于确保多个线程在访问共享资源时的互斥性和原子性。通过具体示例,如在一个类中使用`synchronized`修饰方法,展示了如何实现线程安全的代码块。此外,文章还讨论了`ReentrantLock`等其他同步工具的优缺点,并提供了实际应用场景中的最佳实践。 ... [详细]
  • 在当前的软件开发领域,Lua 作为一种轻量级脚本语言,在 .NET 生态系统中的应用逐渐受到关注。本文探讨了 Lua 在 .NET 环境下的集成方法及其面临的挑战,包括性能优化、互操作性和生态支持等方面。尽管存在一定的技术障碍,但通过不断的学习和实践,开发者能够克服这些困难,拓展 Lua 在 .NET 中的应用场景。 ... [详细]
  • 每年,意甲、德甲、英超和西甲等各大足球联赛的赛程表都是球迷们关注的焦点。本文通过 Python 编程实现了一种生成赛程表的方法,该方法基于蛇形环算法。具体而言,将所有球队排列成两列的环形结构,左侧球队对阵右侧球队,首支队伍固定不动,其余队伍按顺时针方向循环移动,从而确保每场比赛不重复。此算法不仅高效,而且易于实现,为赛程安排提供了可靠的解决方案。 ... [详细]
  • Android中将独立SO库封装进JAR包并实现SO库的加载与调用
    在Android开发中,将独立的SO库封装进JAR包并实现其加载与调用是一个常见的需求。本文详细介绍了如何将SO库嵌入到JAR包中,并确保在外部应用调用该JAR包时能够正确加载和使用这些SO库。通过这种方式,开发者可以更方便地管理和分发包含原生代码的库文件,提高开发效率和代码复用性。文章还探讨了常见的问题及其解决方案,帮助开发者避免在实际应用中遇到的坑。 ... [详细]
author-avatar
黄于諭春琪
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有