java sign函数_从零开始写个编译器吧 - 符号分析,编写 SignParser.java 文件

上一章留下的那个大大的 TODO 表明,似乎还有什么东西没写完。没错,所以这一章我们来写 Sign 状态下的代码。

所谓 Sign 状态,即是用来处理和生成 Sign 类型的 Token ,即运算符。诸如此类,都是运算符:

+、-、*、/、>=、==、&&、||

相对于 String 字符串啊、Identifier 标示符之类的可以通过字符本身来划分开端和结尾的类型,Sign 运算符运算符类型却只能以既定形式来划分开端和结尾。

例如,

"hello world" +-&& str_hello_wold

这段“源码”可以被分割成如下一组 Token,

["\"hello world\"", " ", "+", "-", "&&", "str_hello_world"]

注意 Sign 运算符类型和 String 字符串类型、Identifier 标示符划分的区别。对于 “+”、“-” 两个字符被各自分在一个 Token 中,而 “&&” 却是由两个字符组合成一个 Token。因为 tao 语言中没有 “&” 运算符(没有与位运算,也没有短路与非短路与之分),却拥有 “&&”。

此外,

>=

对于这个字符串,到底应该划分成

[">", "="]

这样两个运算符呢,还是应该划分成

[">="]

这样一个运算符呢?

因为 “>"、"="、">=" 这三个都是合法的 tao 语言运算符。这里为了消除歧义,我规定必须划分成 ">=" 。另外,如果存在多种划分方式,尽量将短的运算符凑成长运算符。

OK,我现在决定把有关 Sign 运算符类型的代码单独写一个类。即创建一个 SignParser.java 文件,并写下如下内容。

package com.taozeyu.taolan.analysis;

class SignParser {

static boolean inCharSet(char c) {

//TODO

}

static List parse(String str) {

//TODO

}

}

对于,LexicalAnalysis.java ,它只会调用 SignParser.inCharset(...) 来判断当前读入的字符是不是某个既定运算符的某个字符。然后以此缓存一个字符串,然后,通过调用 SignParser.parse(str) 来将这个字符串分割成一个一个运算符,并生成 Token。

现把 LexicalAnalysis.java 中 boolean readChar(char c) 函数中TODO 消灭掉吧。

} else if(state == State.Sign) {

if(SignParser.inCharSet(c)) {

readBuffer.append(c);

} else {

List list = SignParser.parse(readBuffer.toString());

for(String signStr:list) {

createToken(Type.Sign, signStr);

}

createType = null;

state = State.Normal;

moveCursor = false;

}

}

当然,这里需要重载 createToken 一下。

private void createToken(Type type, String value) {

Token token = new Token(type, value);

tokenBuffer.addFirst(token);

readBuffer = null;

}

OK,我们在回过头来写 SignParser.java 吧。首先,定义几个静态常量。

package com.taozeyu.taolan.analysis;

import java.util.ArrayList;

import java.util.HashSet;

import java.util.LinkedList;

import java.util.List;

class SignParser {

private final static List> signSetList;

private final static HashSet signCharSet;

private final static int MaxLength, MinLength;

static {

String[] signArray = new String[] {

"+", "-", "*", "/", "%",

">", "=", "<=", "=", "!=", "==", "=~",

"+=", "-=", "*=", "/=", "%=",

"&&", "||", "!", "^",

"&&=", "||=", "^=",

"<>", "->", "

"?", ":",

".", ",", ";", "..",

"(", ")", "[", "]", "{", "}",

"@", "@@", "$",

};

int maxLength = Integer.MIN_VALUE,

minLength = Integer.MAX_VALUE;

signCharSet = new HashSet<>();

for(String sign:signArray) {

int length = sign.length();

if(length > maxLength) {

maxLength = length;

}

if(length < minLength) {

minLength = length;

}

for(int i=0; i

signCharSet.add(sign.charAt(i));

}

}

signSetList = new ArrayList<>(maxLength - minLength + 1);

for(int i=0; i< maxLength - minLength + 1; ++i) {

signSetList.add(new HashSet<>());

}

for(String sign:signArray) {

int length = sign.length();

HashSet signSet = signSetList.get(length - minLength);

signSet.add(sign);

}

MaxLength = maxLength;

MinLength = minLength;

}

static boolean inCharSet(char c) {

//TODO

}

static List parse(String str) {

//TODO

}

}

static 块中的 signArray 变量定义了 tao 语言所用到的所有运算符。之后的代码解析 signArray 并生成 4 个静态常量。

signCharSet:tao 语言所有运算符中可能出现的字符集合。

signSetList:tao 语言中根据运算符长度分开储存的 List。具体来说,如果运算符对应的字符串最短的 length 是 n,最长的是 m。那么 List 的索引范围从 0 到 (m - n) 分别储存字符 length 从 n 到 m 的所有运算符。

MinLength:运算符中字符串 length 的最小长度。

MaxLength:运算符中字符串 length 的最大长度。

然后是填满那两个写着 TODO 的函数。

static boolean inCharSet(char c) {

return signCharSet.contains(c);

}

static List parse(String str) throws LexicalAnalysisException {

LinkedList rsContainer = new LinkedList<>();

int startIndex = 0;

while(startIndex < str.length()) {

String matchStr = match(startIndex, str);

if(matchStr == null) {

throw new LexicalAnalysisException(str.substring(startIndex));

} else {

rsContainer.add(matchStr);

startIndex += matchStr.length();

}

}

return rsContainer;

}

private static String match(int startIndex, String str) {

String matchStr = null;

int length = str.length() - startIndex;

length = Math.min(length, MaxLength);

if(length >= MinLength) {

for(int i=length - MinLength; i>=0; i--) {

int matchLength = i + MinLength;

HashSet signSet = signSetList.get(i);

matchStr = str.substring(startIndex, startIndex + matchLength);

if(signSet.contains(matchStr)) {

break;

}

matchStr = null;

}

}

return matchStr;

}

OK,搞定。特别的,如果 parse 划分运算符过程中发现留下一个尾巴匹配不了,那么将抛出一个词法错误异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值