词法分析 java_[Java] 尝试词法分析器的总结

写在前面

由于我对写解析器只有 阅读了几篇文章 的知识量,因此水平并不是很高,此文权当一次个人总结,无法保证所涉及的知识点、思路完全无误,如有错误,还请各位大佬指正。

从一个正整数表达式开始

这篇文章围绕的仅仅是一个 正整数表达式,而且它很简单,不会出现括号嵌套等情况,我们的目标只是把

10 * 5 + 1

解析为一个 Token 序列,如下:

[

{ type: NUMBER, value: `10` },

{ type: OPERATOR, value: `*` },

{ type: NUMBER, value: `5` },

{ type: OPERATOR, value: `+` },

{ type: NUMBER, value: `1` }

]

我习惯从简单的开始,那么我们先从一个最简单的、只有个位数、没有空格的式子开始:

1+1

最简单的思路

其实词法分析器要做的事本质上很简单:对输入的字符串进行遍历,分割成有意义的 Token。

因此,最简单的思路就是一个 for 循环:

String expression = "1+1";

for (char ch : expression.toCharArray()) {

// 在这里进行处理

}

所以我们定义一个 Scanner,为了后续方便,顺手实现个简单的单例吧:

public class Scanner {

private static volatile Scanner instance;

public static Scanner getInstance() {

if (Scanner.instance == null) {

synchronized ( Scanner.class ) {

if (Scanner.instance == null) {

Scanner.instance = new Scanner();

}

}

}

return Scanner.instance;

}

private String expression;

public Scanner from(String expression) {

this.expression = expression;

return this;

}

public void process() {

for (char ch : expression.toCharArray()) {

// 在这里进行处理

}

}

public static void main(String ... args) {

Scanner scanner = Scanner.getInstance().from("1+1");

scanner.process();

}

}

定义 Token 类型

在当前的 1+1 表达式中,涉及到的 Token 不多,只有数字、操作符,因此用一个枚举类即可表述:

public enum Type {

INIT,

NUMBER,

OPERATOR,

UNKNOWN;

public static Type of(char ch) {

if ('0' <= ch && ch <= '9') {

return NUMBER;

}

if ("+-*/".indexOf(ch) != -1) {

return OPERATOR;

}

return UNKNOWN;

}

}

同时该枚举类承担辨识字符类型的工作:

Type.of('1') // NUMBER

Type.of('+') // OPERATOR

Type.of('a') // UNKNOWN

定义 Token

public class Token {

// 一个 Token 的类型一旦确定,就不可能再改变。

private final Type type;

// 用以存储 Token 的值。

private final StringBuffer value;

public Token(Type type) {

this.type = type;

this.value = new StringBuffer();

}

public void appendValue(char ch) {

this.value.append(ch);

}

public String getValue() {

return this.value.toString();

}

public Type getType() {

return this.type;

}

@Override

public String toString() {

return String.format("{type: %s, value: `%s`}",

this.getType().name(),

this.getValue());

}

}

处理 1+1

public class Scanner {

// 省略...

public void process() {

for (char ch : expression.toCharArray()) {

Type type = Type.of(ch);

if (Type.UNKNOWN.equals(type)) {

throw new RuntimeException(String.format("`%c` 并不属于期望的字符类型", ch));

}

Token token = new Token(type);

token.appendValue(ch);

System.out.println(token);

}

}

public static void main(String ... args) {

Scanner scanner = new Scanner("1+1");

scanner.process();

}

}

/** 输出

* {type: NUMBER, value: `1`}

* {type: OPERATOR, value: `+`}

* {type: NUMBER, value: `1`}

*/

现在来加点难度: 10+1

现在一个数字可能不止一位了,那么我们该怎么办呢?

使用状态图:

┌-[ 0-9 ]-┐ ┌-[ +|-|*|/ ]-┐ ┌-[ 0-9 ]-┐

---( NUMBER )--- ( OPERATOR )---( NUMBER )---

具体的理论这里就不赘述了,有兴趣可以自行查阅相关资料,这里简单说一下怎么用:

现在我们来列个表,看一下对于 10+1,在状态上有什么变化:

字符

状态

Token

NULL

INIT

NULL

1

NUMBER

{id: 0, type: NUMBER, value: 1}

0

NUMBER

{id: 0, type: NUMBER, value: 10}

+

OPERATOR

{id: 1, type: OPERATOR, value: +}

1

NUMBER

{id: 2, type: NUMBER, value: 1}

可以看到,在读到字符 1 和 0 时,状态没有发生变化,也就是说它们是一个整体(或是一个整体的一部分)。

如果在 0 后面还有其他数字,那么直到引起状态改变的字符出现之前,这些字符就组成了整个 Token。

同时,我们还发现引入状态图后,有个有意思的事:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值