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

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

package com.taozeyu.taolan.analysis;

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

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

从零开始写个编译器吧 - 词法分析器是一个状态机_第1张图片

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

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

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

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 这段。当这个异常被抛出,说明源代码写错了。这不是编译器的错,而是程序员的错,编译器只管把这个错报出来,让程序员去改代码。

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

你可能感兴趣的:(编译器,java)