我是靠谱客的博主 敏感柠檬,最近开发中收集的这篇文章主要介绍编译器-2A:分词器,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

问候,

今天我们有工作要做:我们将设计和实施第一部分

我们的小编译器。

代币

我们从标记化部分开始; 我们的孩子认可的代币

语言有点像C或Java令牌:我们想识别以下内容

二进制运算符:+-* / ^(最后一个代表“提升为幂”),

比较运算符:<<= ==!= =>>以及一些一元运算符:

! +-++-

我们还要识别更多字符:=(和)

最重要的是,我们想识别标识符(或“名称”),就像在

Java或C,我们想识别浮点数。

所有其他字符实际上并没有“被识别”,而只是按原样传递

解析器。 有一个例外:我们要认识到以下事实:

文本已被读取并标记为:文本条件的结尾。

用我们的简单语言会忽略空格。 令牌生成器还必须

跟踪当前行号和列号,因为我们要

就像javac编译器一样,综合漂亮的错误消息。

基本上,分词器通过以下方式对令牌进行分组:


// different types of tokens:
static final int T_ENDT= -1; // end of stream reached
static final int T_CHAR=  0; // an ordinary character
static final int T_NUMB=  1; // a number
static final int T_TEXT=  2; // a recognized token
static final int T_NAME=  3; // an identifier 
我想到的第一个想法是使用Scanner进行令牌识别。

,我们无法使用它,因为扫描程序基于定界符(即令牌)工作

用分度符分隔(您可以根据需要进行设置)。 我们的

语言没有分界符,即我们不想写:'a + b'

一直以来,我们只想写“ a + b”。 我们的代币由

更改字符组,我们事先不知道我们的标记器

将要从输入流中读取,因此我们不知道要使用哪个分度。

我们采用另一种方法:我们只需逐行读取输入并使用

正则表达式以找出第一个可能的标记是什么。 如果

我们找到了一个,我们将其从行中切下并返回令牌。 如果有

不再需要阅读令牌,我们只需阅读下一行并重复该过程即可。

这里有一个小陷阱:假设流中的第一个字符是'+'

或'>'或'<'或'='符号? 我们能否确定那个单一角色

标记,还是我们也应该检查下一个字符? 如果流包含

++或> =或<=或==,那么我们确实应该检查下一个字符。

以下简单规则避免了这个小陷阱:

我们按以下顺序检查令牌:


check for an end of stream state; if found we return a T_ENDT token;
check for spaces, if found we ignore them and continue;
check for a number, if found return a T_NUMB token;
check for an identifier, if found return a T_NAME token;
check for two character tokens, if found return a T_TEXT token;
check for single character tokens, if found return a T_TEXT token;
all else fails return a single character T_CHAR token;  
每次找到长度为L的令牌时,我们都会从

当前行并将列数增加L。我们保留了

当前行,以防需要显示错误。 请注意,如果我们已经

只需读取空格(请参阅第2步)即可增加列数

复杂:空格可能是制表符,一个制表符在视觉上代表了另一个

字符位置的数量,取决于制表位的大小。

标记器由解析器调用。 令牌生成器读取令牌后

尚未由解析器处理的令牌生成器将返回

一遍又一遍地使用相同的令牌,即解析器必须明确告知

令牌生成器,表示已处理了最后一个令牌,应将新令牌

从字符输入流中读取。

令牌类

首先,让我们构建一个简单的Token类; 返回此类的实例

由令牌生成器; 这里是:


package compiler; 
public class Token { 
    public static final Token ONE= new Token(1); 
    private double dbl; // filled if token is a number
    private String str; // String representation of a token
    private int    typ; // the type of this token 
    // ctor for a double token:
    public Token(double dbl) { this(dbl, ""+dbl, TokenTable.T_NUMB); } 
    // ctor for a non-double token:
    public Token(String str, int type) { this(0.0, str, type); } 
    // general ctor:
    public Token(double dbl, String str, int typ) {
        this.dbl= dbl;
        this.str= str;
        this.typ= typ;
    } 
    // mark this token as 'special':
    public Token mark() { 
        str+= "@";
        return this;
    } 
    // getters:
    public double getDbl() { return dbl; }
    public String getStr() { return str; }
    public int getTyp() { return typ; } 
    // token string and type as string:
    public String toString() { return str+"["+typ+"]"; }
} 
Token类只是一个简单的数据类:它存储的double值

由令牌化程序(如果有)读取,令牌的字符串表示形式

以及令牌的类型。 字符串值是该行的第一部分

由令牌生成器切掉。 Token类实现简单

访问器(“获取器”),它可以自行打印。 这也影响了一些

方便的构造函数。 这是一个简单干净的小类。 注意

第一个构造函数引用“ TokenTable”类。 那堂课

包含令牌生成器和小令牌类所需的静态数据。

令牌类具有一个可用的公共静态成员:常量ONE。

有一种方法需要更多说明,即“标记”方法。

解析器使用它来指示当前令牌有点“特殊”

并将其标记为令牌。 解析器将其用于重载的+和

-运算符,因为它们可以表示二进制加法或减法为

以及一元加减运算符。 稍后我们将回到棘手的问题

当我们深入研究此简单编译器的解析器部分时。

正则表达式资源

稍后我们将讨论TokenTable(和其他表类)。 桌子

类包含该项目更有趣的部分所需的数据。

一个值得注意的细节是表类是使用外部变量填充的

属性文件。 这样可以轻松播放各个部分

(令牌生成器和解析器)并随意进行扩充。

属性文件之一是“ tokens.properties”文件,其中包含

分词器需要的正则表达式; 这里是:


space         =        ^\s*
number        =        ^\d+(\.\d*)?([eE][+-]?\d+)?
word        =        ^[A-Za-z_]\w*
symbol2        =        ^(==|<=|>=|!=|^=|\+=|-=|\*=|/=|\+\+|--)
symbol1        =        ^[=!<>+*/()^-]
char        =        ^\S 
双\字符表示单个字符。 当一个属性

读取对象后,它将双反斜杠转换为单反斜杠。

API文档中详细说明了这些乱码

模式类的。 这是一个简短的概述:

第一行显示:^ \ s *,这意味着:在行的开头,我们希望为零

或更多 s字符。 s字符是Pattern类的特殊字符,

表示:任何空格字符(包括制表符)。

第二行比较复杂; 它显示为:一个数字是一个或多个数字

( d +部分)(可选)后接文字点和零个或多个数字

((.. d *)?部分。接下来可选的是小写或大写的“ e”或“ E”

读取(可选)后跟一个+或-号,后跟一个或多个数字

(([[eE] [+-]? d +)?部分。

第三行代表名称:任何字母(大写或小写)或

下划线后跟零或多个相同的数字(包括数字)。

第四行只是一个选择列表,其中每个选择都是我们的选择之一

两个字符标记。

第五行是单字符标记的列表,最后一行是^ S

表示:行首的任何非空格字符。

将这六行与上面列出顺序的段落进行比较

考虑哪些可能的令牌。

因为我们切断了该行的开头,所以剩下的

该行再次有新的起点。 常规字符中的'^'字符

表达式指定一行的开始,大大加快了匹配速度

一行文本的正则表达式。

本周文章的下一部分显示了最终的Tokenizer类。

到时候那里见。

亲切的问候,

乔斯

From: https://bytes.com/topic/java/insights/653378-compilers-2a-tokenizers

最后

以上就是敏感柠檬为你收集整理的编译器-2A:分词器的全部内容,希望文章能够帮你解决编译器-2A:分词器所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(49)

评论列表共有 0 条评论

立即
投稿
返回
顶部