这学期开始上计算机专业最难(听说是公认的)的一门课程——编译原理。还好,老师颇有经验而且教得很用心。用的教材是《编译原理及实践》,寒假时上了豆瓣看了一下评论,听说是英文原版比中译版读起来还要通顺易懂,因而就借来英文版《Compiler Construction:Principle and Practice》,还真的不是那么难读懂,给我入门刚恰好。=) java
但是,第一节课老师才跟你们说“这本教材翻译得有点难懂,有能力的同窗尽可能读英文版。”噢,其余同窗们其实早就买了中译版,老师这才如此建议...
---------------------------------------正文---------------------------------- ide
按照第二章“Scanning”其实就是“词法分析 lexical analysis”的样例,须要将如下程序做为样例输入,进行词法分析得到一个个的Token及其可能的对应类别,例如保留字、特殊符号、数值、标识符等等。 学习
样例输入 sample.tny (.tny是教材中使用的称为Tiny语言的后缀):ui
- { Sample program
- in TINY language -
- computes factorial
- }
- read x; { input an integer }
- if 0 < x then { don't compute if x <= 0 }
- fact := 1;
- repeat
- fact := fact * x;
- x := x - 1
- until x = 0;
- write fact { output factorial of x }
- end
教材中要求的输出结果以下:this
当时还没认真学习到用有限自动机(DFA)等等概念,本身先照着教材的样例输出用本身的傻瓜化办法实现一下,“追求”的目标是样例输出要和教材的同样(固然不能打印每个句子啦)。下面是个人作法。 spa
★ 如下是在程序实现代码中使用Java的正则表达式功能,并对Tiny源程序代码作了必定的前提条件所完成的 Tiny 语言,其实就是模仿教材中的最终输出文件解析出每个 Token。 翻译
思路:
默认每一个Token之间都以空格“ ”隔开,所以可用Java中的正则表达式将每一行依此规律拆分为一个个Token,而后再对每个Token进行类别匹配(也用到正则表达式),最后按类别打印输出。如下实现可是不可以处理多行注释的问题。blog
sample1.tny
样例输出以下:
代码实现:
- package lexical_analysis;
- import java.io.BufferedReader;
- import java.io.FileReader;
- import java.util.regex.Pattern;
- /**
- * 假设 Tiny 源程序每一个 Token 都用“空格”分开,所以能够简单地实现:
- * 每次读取源程序的一整行,使用Java中的正则表达式以“空格”提取出来,
- * 再对比“词汇表”以肯定每个 Token 的类别、属性(Attribute)。
- *
- * @author liguihao
- */
- public class SimpleLexicalAnalyser {
- // 保留字
- private String [] reservedWords = new String[] {"read", "if",
- "then", "repeat",
- "until", "write", "end"};
- // 数学运算符
- private String [] arithmeticSymbols = new String[] {"+", "-", "*", "/",
- "%", ":=", "=", "<",
- ">", "<=", ">="};
- // 源程序文件输入流
- private BufferedReader sourceFile;
- // 代码行数
- private int lineCount = 0;
- public SimpleLexicalAnalyser(String sourceFilePath) throws Exception {
- // 建立并加载源程序文件输入流
- this.sourceFile = new BufferedReader(new FileReader(sourceFilePath));
- }
- /**
- * 逐行扫描源程序代码
- * @throws Exception
- */
- public void scan() throws Exception {
- String sourceLine = "";
- String[] tokens;
- while((sourceLine = this.sourceFile.readLine()) != null) {
- lineCount++;
- System.out.printf("%d: %s\n", lineCount, sourceLine);
- // 获取每一行的Token集合
- tokens = this.getTokens(sourceLine);
- int size = tokens.length;
- for(int i = 0; i < size; i++) {
- if(isArithmeticSymbol(tokens[i])) { // 数学运算符
- System.out.println(" " + lineCount + ": " + tokens[i]);
- } else if(isReservedWord(tokens[i])) { // 保留字
- System.out.println(" " + lineCount + ": " + "reserved word: " + tokens[i]);
- } else if(";".equals(tokens[i])) { // 行结束符,即分号
- System.out.println(" " + lineCount + ": " + tokens[i]);
- } else if(isID(tokens[i])) { // 自定义标识符ID
- System.out.println(" " + lineCount + ": " + "ID, name= " + tokens[i]);
- } else if(isNum(tokens[i])) { // 数值NUM
- System.out.println(" " + lineCount + ": " + "NUM, val= " + tokens[i]);
- } else if("{".equals(tokens[i])) { // 行注释符,即左大括号{
- break; // 直接跳过行注释
- }
- // 源程序文件结束符
- if("end".equals(tokens[i])) {
- lineCount++;
- System.out.printf("%2d: %s\n", lineCount, "EOF");
- break;
- }
- }
- }
- }
- /**
- * 用“空格Space”正则表达式将每一行源代码拆分红单个Token的集合
- */
- public String[] getTokens(String sourceLine) {
- Pattern pattern = Pattern.compile(" ");
- return pattern.split(sourceLine);
- }
- /**
- * 判断是否为“保留字”
- * @param token
- * @return
- */
- private boolean isReservedWord(String token) {
- int size = this.reservedWords.length;
- for(int i = 0; i < size; i++) {
- if(token.equals(reservedWords[i])) {
- return true;
- }
- }
- return false;
- }
- /**
- * 判断是否为“数学运算符”
- * @param token
- * @return
- */
- private boolean isArithmeticSymbol(String token) {
- int size = this.arithmeticSymbols.length;
- for(int i = 0; i < size; i++) {
- if(token.equals(arithmeticSymbols[i])) {
- return true;
- }
- }
- return false;
- }
- /**
- * 判断是否为“数值NUM”
- * @param token
- * @return
- */
- private boolean isNum(String token) {
- boolean flag = Pattern.matches("[a-zA-Z]+?", token);
- return flag;
- }
- /**
- * 判断是否为“ID”
- * @param token
- * @return
- */
- private boolean isID(String token) {
- boolean flag = Pattern.matches("\\d+?", token);
- return flag;
- }
- /**
- * “词法分析程序”的启动入口
- * @param args
- */
- public static void main(String[] args) throws Exception {
- String sourceFilePath = "sample1.tny";
- SimpleLexicalAnalyser lexicalAnalyser = new SimpleLexicalAnalyser(sourceFilePath);
- lexicalAnalyser.scan();
- }
- }