java編譯錯誤需要標識符,Java終結符_從零開始寫個編譯器吧 - TerminalSymbol.java 與 NonTerminalSymbol.java

 2023-11-18 阅读 24 评论 0

摘要:首先是 TerminalSymbol.java 即終結符。package com.taozeyu.taolan.analysis;import java.util.HashSet;import com.taozeyu.taolan.analysis.Token.Type;public class TerminalSymbol {@SuppressWarnings("serial")private final static HashSet careValueTypeS

首先是 TerminalSymbol.java 即終結符。

package com.taozeyu.taolan.analysis;

import java.util.HashSet;

import com.taozeyu.taolan.analysis.Token.Type;

public class TerminalSymbol {

@SuppressWarnings("serial")

private final static HashSet careValueTypeSet = new HashSet() {{

add(Type.Keyword);

add(Type.Sign);

}};

static final TerminalSymbol Empty = new TerminalSymbol(null, null);

public final Type type;

public final String value;

final boolean careValue;

TerminalSymbol(Type type, String value) {

this.type = type;

this.value = value;

this.careValue = careValueTypeSet.contains(type);

}

boolean isEmpty() {

return this.type == null;

}

@Override

public boolean equals(Object obj) {

boolean isEquals = false;

if(obj instanceof TerminalSymbol) {

TerminalSymbol other = (TerminalSymbol) obj;

isEquals = isEquals(this.type, other.type);

if(isEquals & careValue) {

isEquals = isEquals(this.value, other.value);

}

}

return isEquals;

}

private boolean isEquals(Object o1, Object o2) {

boolean isEquals = false;

if(o1 == null & o2 == null) {

isEquals = true;

} else if(o1 != null & o2 != null) {

isEquals = o1.equals(o2);

}

return isEquals;

}

@Override

public int hashCode() {

int hashCode = getHashCode(this.type);

if(careValue) {

hashCode ^= getHashCode(this.value);

}

return hashCode;

}

private int getHashCode(Object obj) {

int hashCode = 0;

if(obj != null) {

hashCode = obj.hashCode();

}

return hashCode;

}

@Override

public String toString() {

String str;

if(this.value != null) {

str = " “" + this.value + "”";

} else {

if(this.type != null) {

str = this.type.toString();

} else {

str = "ε";

}

}

return str;

}

}

對于 Parser 而言,終結符 Terminal Symbol 與 Tokenizer 的 Token 是對應的。特別的,對于以上代碼:

import com.taozeyu.taolan.analysis.Token.Type;

這里非終結符的類型(Type)我就直接用了 Token.java 中定義的內部枚舉類啦。

特別的,

@SuppressWarnings("serial")

private final static HashSet careValueTypeSet = new HashSet() {{

add(Type.Keyword);

add(Type.Sign);

}};

這里將 Keyword、Sign 這兩種類型歸于 careValueType。這是什么意思呢?容我稍微說明一下。

對于 Parser 中的終結符,即便它無法繼續展開,但該終結符也并非就只能表示唯一的內容。例如,對于一個終結符,已知它是 Identifier,那么它實際上對應的字符串可能是 "hello_world" 或者 "accountBuilder" 之類的。這些內容,我將其稱之為終結符的值 value。

顯然,Parser 分析語法樹的時候根本就不關心 Identifier 的值是什么,因為 Tokenizer 的工作就是將 Token 提取出來并識別其類型,以便讓 Parser 無需關心瑣碎的內容。

因此,我們就知道了,每一個終結符都對應一類 Token。

但是,對于 Keyword 和 Sign 而言,它的值卻影響 Parser 分析語法樹。例如 Sign 的值取 “+” 還是 “*” 會讓生成的語法樹完全不一樣。

換句話說,在定義終結符的時候,我應該為每一個 Sign 單獨歸于一個類型,而不是將它們統稱為一個類型。同樣的道理適用于 Keyword 類型。

但是,就我個人而言,將 Keyword 和 Sign 拆分成多個類型未免有些繁瑣,而且不好維護。于是我在這里做了一點取巧,將終結符分為 careValueType 型和非 careValueType 型。前者(目前有且僅有 Keyword、Sign) Parser 在識別它們的時候,不僅要考慮它們的 type,還要考慮它們的 value。而后者, Parser 僅考慮它們的 type,而 value 會被忽視。

具體請參考前面代碼中的如下函數:

equals

hashCode

最后,我定義了一個特殊的非終結符:

static final TerminalSymbol Empty = new TerminalSymbol(null, null);

用來描述 ε。但是這個符號僅僅用于 Parser 初始化階段,在 Parser 編譯源代碼的時候這個符號是用不上的。

然后是 NonTerminalSymbol.java 即非終結符。

package com.taozeyu.taolan.analysis;

import java.util.ArrayList;

import java.util.HashSet;

class NonTerminalSymbol {

static enum Exp {

//TODO

}

final Exp exp;

Character sign = null;

final ArrayList expansionList = new ArrayList<>();

final ArrayList banList = new ArrayList<>();

final ArrayList> firstSetList = new ArrayList<>();

final HashSet firstSet = new HashSet<>();

NonTerminalSymbol(Exp exp) {

this.exp = exp;

}

NonTerminalSymbol ban(TerminalSymbol...args) {

for(TerminalSymbol node:args) {

banList.add(node);

}

return this;

}

NonTerminalSymbol or(Object...args) {

expansionList.add(args);

return this;

}

NonTerminalSymbol sign(char sign) {

this.sign = sign;

return this;

}

@Override

public String toString() {

String str = String.valueOf(exp);

if(sign != null) {

str += "(" + sign + ")";

}

return str;

}

}

這個類實際上更多的是用來描述非終結符的產生式的。

對于一個非終結符的產生式:

A → abc | de | fg

對于非終結符 A,其對象的 expansionList 字段則會表現成如下形式。

對于里面的 Object 數組,其元素可能為 終結符對象(TerminalSymbol)、非終結符對象(NonTerminalSymbol)、或表達式枚舉對象(Exp)。

expansionList = new ArrayList<>() {{

add(new Object[] { a, b, c});

add(new Object[] { d, e});

add(new Object[] { f, g});

}};

其中表達式枚舉對象,就是代碼中 //TODO 要填寫的部分。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/4/177887.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息