转载地址 : http://blog.csdn.net/tyler_download/article/details/50668983/

视频地址 : http://study.163.com/course/courseLearn.htm?courseId=1002830012#/learn/video?lessonId=1003210101&courseId=1002830012

正文内容

本文对应代码下载地址为:

http://download.csdn.net/detail/tyler_download/9435103

视频地址:

http://v.youku.com/v_show/id_XMTQ3NTQwMDkxMg==.html?from=s1.8-1-1.2

技术的发展可谓是日新月异,层出不穷,但无论是炙手可热的大数据,还是火烧鸟了的人工智能,所有这些高大上的尖端科技无不建立在基础技术的根基之上。编译原理,计算机网络,操作系统,便是所有软件技术的基石。在这三根支柱中,维编译原理最为难懂,特别是大学课本那种晦涩难通,不讲人话的言语,更是让人觉得这门基础技术就像九十多岁的老妪,皮肤干巴,老态龙钟,让人提不起一点欲望。除了国内教材,就算是被广为称赞的一千多页的”龙书“,也是满篇理论,让人望而生畏。

味道怎样,咬一口就知道,手感如何,摸一把就晓得。编译原理缺的不是理论概念,而是能够动手实践的流程,代码,很多原理用话语怎么讲都难以明了,但跑一遍代码,基本就水落石出。本文本着动手实操(念第一声)的原则,用java实现一个简单的编译器,让读者朋友能一感编译原理的实质,我秉持一个原则,没有代码可实践的计算机理论,都是耍流氓。

编译器作用就是将一种计算机无法理解的文本,转译成计算机能执行的语句,我们要做的编译器如下,将带有加法和乘法的算术式子,转译成机器能执行的汇编语句,例如语句:

1+2*3+4, 经过编译后转换成:

t0 = 1

t1 = 2

t2 = 3

t1 *= t2

t0 += t1

t1 = 4

t0 += t1

t0, t1 是对寄存器的模拟,上述语句基本上就类似计算机能执行的汇编语句了。

本章首先专注于词法解析的探讨。

编译原理由两部分组成,一是词法分析,一是语义分析。先说词法分析,词法分析就是将一个语句分割成若干个有意义的字符串的组合,然后给分割的字符串打标签。例如语句:

1+2*3+4; 可以分割成 1+, 2*, 3+, 4; 但这些子字符串没有实质意义,有意义的分割是1, +, 2, * , 3, +, 4, ;. 接着就是给这些分割后的字符串打标签,例如给1, 2, 3, 4 打上的标签是NUM_OR_ID,  + 打的标签是PLUS, *的标签是TIMES, ;的标签是SEMI, 好了,看看词法分析的代码,大家可能更容易理解:

Lexer.java:

import java.util.Scanner;public class Lexer {public static final int  EOI = 0;public static final int  SEMI = 1;public static final int  PLUS = 2;public static final int  TIMES = 3;public static final int  LP = 4;public static final int  RP = 5;public static final int  NUM_OR_ID = 6;private int lookAhead = -1;public String yytext = "";public int yyleng = 0;public int yylineno = 0;private String input_buffer = "";private String current = "";private boolean isAlnum(char c) {if (Character.isAlphabetic(c) == true ||Character.isDigit(c) == true) {return true;}return false;}private int lex() {while (true) {while (current == "") {Scanner s = new Scanner(System.in);while (true) {String line = s.nextLine();if (line.equals("end")) {break;}input_buffer += line;}s.close();if (input_buffer.length() == 0) {current = "";return EOI;}current = input_buffer;++yylineno;current.trim();}//while (current != "")for (int i = 0; i < current.length(); i++) {yyleng = 0;yytext = current.substring(0, 1);switch (current.charAt(i)) {case ';': current = current.substring(1); return SEMI;case '+': current = current.substring(1); return PLUS;case '*': current = current.substring(1);return TIMES;case '(': current = current.substring(1);return LP;case ')': current = current.substring(1);return RP;case '\n':case '\t':case ' ': current = current.substring(1); break;default:if (isAlnum(current.charAt(i)) == false) {System.out.println("Ignoring illegal input: " + current.charAt(i));}else {while (isAlnum(current.charAt(i))) {i++;yyleng++;} // while (isAlnum(current.charAt(i)))yytext = current.substring(0, yyleng);current = current.substring(yyleng); return NUM_OR_ID;}break;} //switch (current.charAt(i))}//  for (int i = 0; i < current.length(); i++) }//while (true)   }//lex()public boolean match(int token) {if (lookAhead == -1) {lookAhead = lex();}return token == lookAhead;}public void advance() {lookAhead = lex();}public void runLexer() {while (!match(EOI)) {System.out.println("Token: " + token() + " ,Symbol: " + yytext );advance();}}private String token() {String token = "";switch (lookAhead) {case EOI:token = "EOI";break;case PLUS:token = "PLUS";break;case TIMES:token = "TIMES";break;case NUM_OR_ID:token = "NUM_OR_ID";break;case SEMI:token = "SEMI";break;case LP:token = "LP";break;case RP:token = "RP";break;}return token;}
}

代码中2到6行是对标签的定义,其中LP 代表左括号(,  RP代表右括号), EOI 表示语句末尾, 第10行的lookAhead 变量用于表明当前分割的字符串指向的标签值,yytext用于存储当前正在分析的字符串,yyleng是当前分析的字符串的长度,yylineno是当前分析的字符串所在的行号。input_buffer 用于存储要分析的语句例如: 1+2*3+4;  isAlNum 用于判断输入的字符是否是数字或字母。lex() 函数开始了词法分析的流程,31到40行从控制台读入语句,语句以"end"表明结束,例如在控制台输入:

1+2*3+4;

end

回车后,从52行开始执行词法解析流程。以上面的输入为例,input_buffer 存储语句 1+2*3+4, 由于第一个字符是 1, 在for 循环中,落入switch 的default 部分,isAlNum 返回为真,yyleng 自加后值为1, yytext 存储的字符串就是 "1", current前进一个字符变为+2*3+4, 再次执行lex(), 则解析的字符是+, 在for 循环中,落入switch的case '+' 分支,于是yytext为"+", 返回的标签就是PLUS依次类推, advance 调用一次, lex()就执行一次词法分析,当lex执行若干次后,语句1+2*3+4;会被分解成1, +, 2, *, 3, +, 4, ; 。字符串1, 2, 3, 4具有的标签是NUM_OR_ID, + 具有的标签是PLUS, *的标签是TIMES, ;的标签是SEMI.

runLexer() 将驱动词法解析器,执行解析流程,如果解析到的当前字符串,其标签不是EOI(end of input), 也就是没有达到输入末尾,那么就打印出当前分割的字符串和它所属的标签,接着调用advance() 进行下一次解析。

match, advance 会被稍后我们将看到的语法解析器调用。

接下来我们在main函数中,跑起Lexer, 看看词法解析过程:

Compiler.java

public class Compiler {public static void main(String[] args) {Lexer lexer = new Lexer();//Parser parser = new Parser(lexer);//parser.statements();lexer.runLexer();}
}

在eclipse 中运行给定代码,然后在控制台中输入如下:

1+2*3+4;

end

程序运行后输出:

Token: NUM_OR_ID ,Symbol: 1

Token: PLUS ,Symbol: +

Token: NUM_OR_ID ,Symbol: 2

Token: TIMES ,Symbol: *

Token: NUM_OR_ID ,Symbol: 3

Token: PLUS ,Symbol: +

Token: NUM_OR_ID ,Symbol: 4

Token: SEMI ,Symbol: ;

后记:

该篇叙述的只是一个简单的词法解析入门,希望通过可运行的代码,让大家能体会一下词法分析的流程,从感性上获得直接的认识,为后续理解完整专业的词法解析打下基础。

完整的代码我会上传到csdn, 大家可以获得代码后,自己运行尝试一下。我将在后续的文章中,继续与大家一起探讨一个完整编译器的开发。

另外,我希望将此教程制作成视频模式,大家通过观看视频,可以更直观的看到代码调试,解析,运行等流程,更容易学习和加深理解,如果哪位朋友有兴趣,留个邮箱,我把

制作好的视频发给你们,并虚心的向诸位朋友求教。

(2) 用java实现一个简易编译器1-词法解析入门相关推荐

  1. 用java实现一个简易自动提款机

    用java实现一个简易自动提款机,且有以下要求 如何实现呢?首先,我们定义一个用户类User,同时根据要求设计好属性(本人部分命名没有使用驼峰命名法,不够规范).因为一个人可能有多个卡,卡号又不能重复 ...

  2. 使用Java制作一个简易的远控终端

    使用Java制作一个简易的远控终端 远控终端的本质 1.服务端(攻击者)传输消息 ----> socket连接 ----> 客户端(被攻击者)接收消息 2.客户端执行消息内容(即执行服务端 ...

  3. java简易计算器考察什么_练习:用java写一个简易计算器

    初学java中的选择,判断,循环和方法的概念,写一个简易的两位数计算器 思路推荐: 写4个方法:加减乘除 利用循环+switch进行用户交互 传递需要操作的两个数 输出结果 package com.j ...

  4. 用Java实现一个简易的植物大战僵尸游戏

    今天给大家分享一个简易的植物大战僵尸游戏,用Java编写,还是挺好玩的. 一.设计思路 1.植物大战僵尸运用Java基础实现,通过滚轮上选择植物,随机生成各种类型僵尸,以及植物. 2.玩家可以安放各种 ...

  5. 用java制作一个简易抽签器

    最近闲来无事,想用Java编写一个随机抽签器,最终编写后的运行结果是这样的 下面说一下怎么具体实现,非常简单. 首先,要想出现这个滚动标签,首先必须要有一个窗口来承载它,所以要在main()方法中创建 ...

  6. 使用java创建一个简易的视屏播放器

    最近有个多媒体的作业,要求使用visualC++和opencv编写一个简易的视屏播放器,对于C/C++残疾者而言是不可能的,于是萌生了用java编写的想法.具体经验分享一下. 目标:制作简易视屏播放器 ...

  7. 用java写一个简易日历

    写一个简易日历 记录学习之路,用java语言,用的IDEA编辑器. 我觉得写日历一定要有两个首要方法,所以我首先先写了两个方法,一个是判断是否是闰年,方法代码如下 static boolean jud ...

  8. java 生成media_使用java创建一个简易的视屏播放器

    最近有个多媒体的作业,要求使用visualC++和opencv编写一个简易的视屏播放器,对于C/C++残疾者而言是不可能的,于是萌生了用java编写的想法.具体经验分享一下. 目标:制作简易视屏播放器 ...

  9. Java实现一个简易的区块链(三)

    继前两篇文章主要完成对区块链总体框架结构的设计.引入hash值验证前后节点数据是否被篡改等等,这里引入数字签名,进一步对交易进行合法性验证.在数字签名的部分没有使用secp256k1的数字签名算法,而 ...

最新文章

  1. ADMT3.2域迁移之Server2003至Server2012系列(八)生成密钥文件及安装密码迁移工具...
  2. OCP12C题库,63数据库管理( Administration Workshop- 63)(新增)
  3. 人工智能选go还是python_深圳人工智能学Python还是go,真实经历分享
  4. CUDA算法——Stream and Event
  5. mysql配置utf8_mb4
  6. QT For Android 运行的时候找不到手机怎么办?
  7. linux的mongo主从异常,解决Linux系统下MongoDB数据库异常退出的问题
  8. 自学-Linux-老男孩Linux77期-day4
  9. 这位985名校的教授火了!当了10年博导,竟无一博士毕业?
  10. 概率论 —— 条件数学期望
  11. 企业员工管理系统 一:项目介绍
  12. 牛客刷题记录之语法入门选择结构篇
  13. 现代化 Android Pie: 安全与隐私
  14. C++笔试笔记1(4399 西山居 深信服 剑心互娱 快手)
  15. java 庖丁解牛中文分词_庖丁解牛中文分词包
  16. 我的世界java版z_我的世界Java版1.16 20w12a 发布 添加下界重生锚
  17. 通过session实现用户的登录与登出功能
  18. 线程池作用及C语言实现
  19. 简单的文本挖掘-用于QQ聊天记录(R)
  20. 贵阳python培训价格

热门文章

  1. 《重构:改善既有代码的设计》阅读笔记
  2. mlx rdma网卡指标参数简介
  3. Hadoop推测执行(以空间换取时间)
  4. CentOS 编译Hadoop 2.6 32位
  5. python多维数据post给php_使用Python中的POST将数据发送到PHP
  6. C++string详解
  7. 小程序同时设置点击跳转与自动跳转导致重复跳转的问题及解决
  8. 传统公司部署OpenStack(t版)简易介绍(六)——neutron模块部署
  9. verilog 给数组集体赋值_Verilog中如何对数组赋值(存储器memory详解)
  10. union和union all有什么区别_Django基础(29):select_related和prefetch_related的用法与区别...