资讯专栏INFORMATION COLUMN

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

calx / 2601人阅读

摘要:词法分析器本身就是一个状态机,生成这个状态机有很多种方法,而我打算采取手写的方式。状态机不断从源代码即一个字符串中读入一个一个字符,读到不同的字符将使状态机的状态从一个状态变化到另外一个状态。

词法分析器 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种状态吧。

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

例如,在 Normal 状态下读到了“#”将使状态变为 Annotation ,反过来如果继续读到一个“ "即换行符号,则会从 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)阶段抛出的,因此我们将其称之为词法错误吧,以便区分。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64212.html

相关文章

  • 从零开始写个译器系列

    摘要:是的,这个系列将呈现一个完整的编译器从无到有的过程。但在写这个编译器的过程中,我可不会偷工减料,该有的一定会写上的。该语言的虚拟机将运行于之上,同时编译器将使用实现。我早有写编译器的想法之前没写过,故希望一边写编译器一边完成这个系列。 是的,这个系列将呈现一个完整的编译器从无到有的过程。当然,为了保证该系列内容的简洁(也为了降低难度),仅仅保证编译器的最低要求,即仅能用。但在写这个编译...

    genedna 评论0 收藏0
  • 从零开始写个译器 - 开始词法析器(1)

    摘要:上一章提到我要手写词法分析器这个状态机,嗯,那就让我们开始吧。实际上,在状态机不断接受字符的过程中,会先调用将其缓存,并在适当的时机调用生成。一个典型的状态机,处于不同状态,对于接受的参数进行不同的操作。 上一章提到我要手写词法分析器这个状态机,嗯,那就让我们开始吧。 public class LexicalAnalysis { private...

    littleGrow 评论0 收藏0
  • 从零开始写个译器 - 开始词法析器(3)

    摘要:在之前的章节第章从零开始写个编译器吧开始写词法分析器中我有说,我将函数设计成主动调用的形式,而则是被动调用的形式。接下来本系列将进入编写语法分析器的阶段,不过在此之前,我将抽出一点时间介绍一下语言本身。 上周周末旅游去了,就没更新了,虽然回到海拔0m的地区,不过目前似乎还在缺氧,所以本次就少更点吧。 这章将结束词法分析的部分。 在之前的章节(第7章从零开始写个编译器吧 - 开始写词...

    Barrior 评论0 收藏0
  • 从零开始写个译器 - 开始词法析器(2)

    摘要:读到一个非数字非英文字母非下划线字符。此时立即跳转回状态。以一个双引号开始,并以一个双引号结束。另外,在读和时源代码不许结束,即读到符号,若结束,则判定为词法错误。对于而言,也有一些其他的词法错误判定,如,不能换行。 对于非 Normal 状态,我只需要关心两个过程: 何时从 Normal 跳转到该状态; 何时从该状态跳回 Normal 状态。 在上一章中,我已经写好了从 Nor...

    MarvinZhang 评论0 收藏0
  • 从零开始写个译器 - tao语言的词法析器(Tokenizer)的类型定义

    摘要:要为语言设计词法分析器,首先得知道语言是一种什么样的语言。,不过首先我们得把词法分析器能生成的单词类型定义好了。 要为 tao 语言设计词法分析器,首先得知道 tao 语言是一种什么样的语言。不过呢,我脑海里还没有 tao 语言具体形象。我还是先贴一段 tao 语言的代码,大概展示下这是怎么回事吧。 def say_hello_world(who) print hello ...

    qpal 评论0 收藏0

发表评论

0条评论

calx

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<