从零开始写个编译器吧 - 词法分析器是一个状态机

词法分析器 Tokenizer 自己就是一个状态机,生成这个状态机有不少种方法,而我打算采起手写的方式。由于 tao 语言的词法仍是相对比较简单的,手写不成问题。
先新建一个LexicalAnalysis.java 于 src/com/taozeyu/taolan/analysis之中。java

package com.taozeyu.taolan.analysis;

public class LexicalAnalysis {
    private static enum State {
        Normal,
        Identifier, Sign, Annotation,
        String, RegEx, Space;
    }
}

看看其中定义的 State 枚举类型,其中有6种类型与 Token 的类型对应。特别的,Normal 类型表示状态能够转化成任何一种单词类型的状态。我仍是贴一张图来描述着7种状态吧。程序员

c9719a1388b2b27b96f700440bc20618_b.jpg

如图所示,Normal 状态做为状态机的初始状态,也是各个其余状态的中转状态。状态机不断从源代码(即一个字符串)中读入一个一个字符,读到不一样的字符将使状态机的状态从一个状态变化到另一个状态。函数

例如,在 Normal 状态下读到了“#”将使状态变为 Annotation ,反过来若是继续读到一个“\n"即换行符号,则会从 Annotation 状态回到 Normal 状态。固然,对于 Identifier、 Sign、Space 的状态变化更为复杂一点,但仅凭当前读入的那一个字符就能够变化到正确的状态(图中没有表现)。spa

此外,当源代码读完了,若是状态机处于Normal状态,此时应该生成一个EndSymbol。但若是此时不处于 Normal 状态,那就有问题了,必须抛出一个异常。(这种状况是程序员把源代码自己写错了,例如最后一个字符串少右边的"之类的。)
至此,我就能够知道 LexicalAnalysis 类应该有那些函数可供(Parser)调用啦。code

package com.taozeyu.taolan.analysis;

public class LexicalAnalysis {
    private static enum State {
        Normal,
        Identifier, Sign, Annotation,
        String, RegEx, Space;
    }

    public LexicalAnalysis(Reader reader) {
        //TODO
    }

    Token read() throws IOException, LexicalAnalysisException {
        //TODO
    }
}

至此,语法分析器 Parser 能够不断调用 read() 函数来得到 Token 对象,直到读到 EndSymbol 或抛出异常为止。注意 read() 函数的声明中 throws LexicalAnalysisException 这段。当这个异常被抛出,说明源代码写错了。这不是编译器的错,而是程序员的错,编译器只管把这个错报出来,让程序员去改代码。orm

固然对于程序员而言,这是个语法错误。可是既然我是在写编译器,我可能要把这些错误分得更细一点。由于这个错误是在单词化(Tokenization)阶段抛出的,所以咱们将其称之为词法错误吧,以便区分。对象

相关文章
相关标签/搜索