从整体上看, 词法分析是编译器前段的第一个部分, 它的任务是完成从字符流到记号流的转换.

字符流 : 其实就是源代码. 那么什么是记号流, 记号其实是一种数据结构, 方便编译器后期对代码进行有效处理而产生的, 比如在java 中, 这就是记号(Token).

1 public class Token {
2     public static enum Type{
3         Id, If, Else, While, For, LP, RP
4     };
5 
6     private Type type;
7     private String value;
8 }

 

实现方案至少有两种

1. 手工编码

2. 词法分析器的生成器

先看手工编码, 为了方便代码生成, 我们可以先生成所需要识别程序代码的状态转移图. 大概就长成这个样子 :

 

图里面有几点需要提一下 : 首先是 0 号状态 (开始状态) : 最明显的标志是有一个箭头从外指向它... 然后是2, 4, 6号状态, 他们由两个同心圆组成, 表示接受状态, 也就是说此时这个Token已经生成并且可以返回了(比如6号状态), 仔细看的话你会发先他们的右上角都有一个 * 的符号,  这说明在这个位置有一个回滚操作, 比如 你从输入流中读入了 i f 进去了状态5, 那么你只有在读入一个字母 (可以是空格), 你才能知道这边是if 而不会是 ifx 但是这个空格你是没有处理过的, 所以你必须把这个空格回滚到输入流中...

另外一点, 其实这个转移图可以改进, 我们会发现 程序的关键字 其实可以认为是标识符的一部分, 都是字母或者下划线开头, 字母下划线数字跟在后面, 所以更好的做法是先统一把关键字识别为标识符, 等到真正构造Token的时候在对它进行细化...

下面是一个小练习 :

我的代码实现...

 

 1 package font;
 2 
 3 public class Token {
 4     public static enum Type{
 5         IF, ID, NUM
 6     };
 7 
 8     private Type type;
 9     private String value;
10     private int line;
11     private int pos;
12 
13     public Token(Type type, String value, int line, int pos) {
14         if(type == Type.ID && value.equals("if")){
15             this.type = Type.IF;
16         }else {
17             this.type = type;
18             this.value = value;
19         }
20 
21         this.line = line;
22         this.pos = pos - value.length();
23     }
24 
25 
26     @Override
27     public String toString() {
28         return type + "("+ value +")" + "("+ line + ", " + pos +")";
29     }
30 }
 1 package font;
 2 
 3 import java.io.IOException;
 4 import java.io.Reader;
 5 import java.util.ArrayList;
 6 
 7 public class Lexer {
 8     Reader reader;
 9     char[] buffer = new char[256];
10     int length;
11     int curPos = 0;
12     StringBuilder sb = new StringBuilder();
13     int line = 1;
14     int pos = 1;
15     ArrayList<Token> tokens = new ArrayList<>();
16     boolean EOF = false;
17 
18     public Lexer(Reader reader) throws IOException {
19         this.reader = reader;
20         length = reader.read(buffer, 0, 256);
21     }
22 
23     public void lex() throws IOException {
24         char ch;
25         while(true) {
26             eatSpace();
27             ch = buffer[curPos];
28             if (isNumber(ch)) {
29                 sb.append(ch);
30                 pos++;
31                 while (isNumber(ch = buffer[++curPos])) {
32                     sb.append(ch);
33                     pos++;
34                 }
35                 tokens.add(new Token(Token.Type.NUM, sb.toString(), line, pos));
36                 sb = new StringBuilder();
37             } else if (isIdentifier(ch)) {
38                 sb.append(ch);
39                 pos++;
40                 while (isIdentifier(ch = buffer[++curPos])) {
41                     sb.append(ch);
42                     pos++;
43                 }
44                 tokens.add(new Token(Token.Type.ID, sb.toString(), line, pos));
45                 sb = new StringBuilder();
46             } else if (EOF) {
47                 return;
48             } else {
49                 System.exit(110);
50             }
51         }
52     }
53 
54     public void eatSpace(){
55         char ch;
56         while(Character.isWhitespace(ch = buffer[curPos++]) || ch == '\0'){
57             if(ch == '\n'){
58                 line++;
59                 pos = 1;
60             }else if(ch == '\0'){
61                 EOF = true;
62                 break;
63             } else{
64                 pos++;
65             }
66         }
67         curPos--;
68     }
69 
70     public boolean isIdentifier(char ch){
71         return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || isNumber(ch);
72     }
73 
74     public boolean isNumber(char ch){
75         return ch >= '0' && ch <= '9';
76     }
77 
78     public void print(){
79         for(Token each : tokens){
80             System.out.println(each);
81         }
82     }
83 }
package font;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        File fh = new File("/Users/zhangzhimin/test.c");
        Lexer lexer = new Lexer(new BufferedReader(new FileReader(fh)));
        lexer.lex();
        lexer.print();

    }
}

输出结果 :

posted on 2016-05-05 10:56  内脏坏了  阅读(296)  评论(0编辑  收藏  举报