上一篇提到用樹狀結構分析運算式並計算求值。但對程式來說,輸入運算式只是字串。要表示成樹狀結構,必須先將運算式分解成節點,再由節點組成樹。這裡將樹上每個節點稱為記號物件 TokenRecord。
根據分析,記號物件需求:擁有儲存自身值的變數、特定計算方法、知道下層節點值。由此得出 TokenRecord 基本資訊(略去非關鍵資訊):
Index:運算式中的列號(int),出錯時指示錯誤位置,從 1 開始。Priority:優先級(int),分析運算式時需要。TokenValue:記號值(object)。ChildList:下層清單(List<TokenRecord>),儲存下層元素,實現樹結構。Execute:執行該元素操作(abstract)。SetChildCount:設定下層數量(virtual),建構子中調用,配合 m_ChildCount 欄位檢查下層數量合法性。SetPriority:設定優先級(virtual),建構子中調用。CheckChildCount:檢查下層數量,在 Execute 中調用,保證運算式合法。TokenValue 用 object 是因為支援字串、數值、邏輯值運算。早期曾用 string 和 double 兩欄位,邏輯值用數值表示,後改用 object 更簡單。
public abstract class TokenRecord
{
#region 屬性和欄位
protected int m_ChildCount;
private int m_Index;
public int Index { get { return m_Index; } }
protected int m_Priority;
public int Priority { get { return m_Priority; } }
private int m_Length;
public int Length { get { return m_Length; } }
private Type m_TokenValueType;
public Type TokenValueType { get { return m_TokenValueType; } set { m_TokenValueType = value; } }
private object m_TokenValue;
public object TokenValue { get { return m_TokenValue; } set { m_TokenValue = value; } }
private List<TokenRecord> m_ChildList = new List<TokenRecord>();
public List<TokenRecord> ChildList { get { return m_ChildList; } }
#endregion
public TokenRecord(int Index, int Length)
{ this.m_Index = Index; this.m_Length = Length; this.SetPriority(); this.SetChildCount(); }
#region 方法
public override string ToString() { return this.GetType().Name + "_" + GetValueString() + "_" + TokenValueType.ToString(); }
public string GetValueString() { return this.TokenValue.ToString(); }
internal void CheckChildCount(string ErrorInformation)
{ if (this.m_ChildList.Count != this.m_ChildCount) throw new SyntaxException(this.m_Index, this.m_Length, ErrorInformation); }
#region 必須覆寫的方法
public abstract void Execute();
protected abstract void SetChildCount();
protected abstract void SetPriority();
#endregion
#endregion
#region 轉換記號值類型
internal string ChangeTokenToString() { string strValue = (string)(this.TokenValue = this.TokenValue.ToString()); this.TokenValueType = typeof(string); return strValue; }
internal double ChangeTokenToDouble(string ErrorInformation)
{
double dblValue;
if (this.TokenValueType != typeof(double))
{
if (double.TryParse(this.TokenValue.ToString(), out dblValue)) this.TokenValueType = typeof(double);
else throw new SyntaxException(this.m_Index, this.m_Length, ErrorInformation);
}
else dblValue = (double)this.TokenValue;
return dblValue;
}
internal bool ChangeTokenToBoolean()
{
bool blnValue = false;
if (this.TokenValueType == typeof(string))
{
switch (this.TokenValue.ToString().Trim().ToLower())
{ case "true": blnValue = (bool)(this.TokenValue = true); break; case "false": case "": default: blnValue = (bool)(this.TokenValue = false); break; }
this.TokenValueType = typeof(bool);
}
else if (this.TokenValueType == typeof(double))
{ blnValue = (bool)((Convert.ToInt32(this.TokenValue) != 0) ? (this.TokenValue = true) : (this.TokenValue = false)); this.TokenValueType = typeof(bool); }
else if (this.TokenValueType == typeof(bool)) blnValue = (bool)this.TokenValue;
return blnValue;
}
#endregion
}