首先是 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 要填寫的部分。
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态