资讯专栏INFORMATION COLUMN

从零开始写个编译器吧 - Token.java 文件的编写

Eirunye / 2492人阅读

摘要:现在,让我们来动手写编译器的第一个个文件吧。如其名字所示,这个类实例化的对象用于表示词法分析器的产物。我希望词法分析器从源代码中提取出语素,并根据上下文推测出单词类型,从而构造出对象。只需要构造出类型即可,进一步细分将在的构造函数中进行。

现在,让我们来动手写编译器的第一个个java文件吧。本章要写的类,是Token类。如其名字所示,这个类实例化的对象用于表示词法分析器 Tokenizer 的产物。同时,也作为下一阶段的语法分析器 Parser 的原料。

让我们开始吧!先新建一个Token.java 于 src/com/taozeyu/taolan/analysis之中。

package com.taozeyu.taolan.analysis;

public class Token {
    public static enum Type {
        Keyword, Number, Identifier, Sign, Annotation,
        String, RegEx, Space, NewLine, EndSymbol;
    }
    final Type type;
    final String value;

    Token(Type type, String value) {
        //TODO
    }
}

如之前章节讨论的一样,Token对象应该包含类型和语素两个属性。注意这个 Type 枚举类型,其内容就是我在上一章所说的 tao 语言应该具备的10种单词类型。

我希望词法分析器从源代码中提取出语素,并根据上下文推测出单词类型,从而构造出Token对象。但实际上,请注意Type这个枚举类的三个类型:

Keyword, Number, Identifier

这三个类型不同之处?实际上这三个类型的形式极其类似(甚至 Keyword 和 Identifier 的形式是完全相同的),并且可以仅通过语素准确判定其类型。因此,我希望对词法分析器 Tokenizer 隐藏着三种类型的区别,将这三种类型统称 Identifier,以简化编码。

Token(Type type, String value) {
    if(type == Type.Identifier) {
        char firstChar = value.charAt(0);
        if(firstChar >= "0" & firstChar < "9") {
            type = Type.Number;
        } else if(keywordsSet.contains(value)){
            type = Type.Keyword;
        }
    }
    this.type = type;
    this.value = value;
}

于是,Token 对 Tokenizer 隐藏了 Number、Keyword 类型。Tokenizer 只需要构造出 Identifier 类型即可,进一步细分将在 Token 的构造函数中进行。

特别的,构造函数中引用了一个 keywordsSet 变量。实际上这个变量应该包含所有 tao 语言的关键字。此处稍稍定义一下。

private static final HashSet keywordsSet = new HashSet<>();

static {
    keywordsSet.add("if");
    keywordsSet.add("when");
    keywordsSet.add("elsif");
    keywordsSet.add("else");
    keywordsSet.add("while");
    keywordsSet.add("begin");
    keywordsSet.add("until");
    keywordsSet.add("for");
    keywordsSet.add("do");
    keywordsSet.add("try");
    keywordsSet.add("catch");
    keywordsSet.add("finally");
    keywordsSet.add("end");
    keywordsSet.add("def");
    keywordsSet.add("var");
    keywordsSet.add("this");
    keywordsSet.add("null");
    keywordsSet.add("throw");
    keywordsSet.add("break");
    keywordsSet.add("continue");
    keywordsSet.add("return");
    keywordsSet.add("operator");
}

好吧,tao 语言我能想出的可能有的关键字都在这里了。如果有遗漏或者多余,其实以后再回过头来改也没问题。

特别的,对于 Annotation、String、RegEx ,它们在源代码中存在的形式和具体的语素并不完全等同。

#我是注释(回车)

"我是一个字符串"

^s+d+$

对于 Tokenizer 而言,它倾向于读出如上一整行信息。但是仅仅只加下划线的文字是Token的语素。因此,我还需要再构造函数中对构造参数value进行进一步提取,以得到正确的语素。

另外,EndSymbol 的语素必须为空,不管 Tokenizer 传入什么参数都必须如此。

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

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

相关文章

  • 从零开始写个译器系列

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

    genedna 评论0 收藏0
  • 从零开始写个译器 - TerminalSymbol.java 与 NonTerminalSymb

    摘要:对于而言,终结符与的是对应的。这些内容,我将其称之为终结符的值。对于一个非终结符的产生式对于非终结符,其对象的字段则会表现成如下形式。对于里面的数组,其元素可能为终结符对象非终结符对象或表达式枚举对象。 首先是 TerminalSymbol.java 即终结符。 package com.taozeyu.taolan.analysis; import java.util.HashSet...

    darry 评论0 收藏0
  • 从零开始写个译器 - 符号分析,编写 SignParser.java 文件

    摘要:即创建一个文件,并写下如下内容。然后以此缓存一个字符串,然后,通过调用来将这个字符串分割成一个一个运算符,并生成。语言中根据运算符长度分开储存的。具体来说,如果运算符对应的字符串最短的是,最长的是。 上一章留下的那个大大的 TODO 表明,似乎还有什么东西没写完。没错,所以这一章我们来写 Sign 状态下的代码。 所谓 Sign 状态,即是用来处理和生成 Sign 类型的 Token...

    joyqi 评论0 收藏0
  • 从零开始写个译器 - Parser 语法分析器

    摘要:这样的程序或称工具有很多现成的可供选择包括在平台上可用的,但既然我这个系列叫做从零开始写个编译器吧,那显然如果我用现成的工具,那是犯规行为。 Parser(语法分析器)的编写相对于 Tokenizer (词法分析器)要复杂得多,因此,在编写之前可能也会铺垫得更多一些。当然,本系列旨在写出一个编译器,所以理论方面只会简单介绍 tao 语言所涉及的部分。 之前的几章中,我纯手写了tao 语...

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

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

    Barrior 评论0 收藏0

发表评论

0条评论

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