目標:爲Stone語言添加簡單的數組功能,下標(index)只能使用整數值。java
elements : expr { "," expr } primary : ( "[" [ elements ] "]" | "(" expr ")" | NUMBER | IDENTIFIER | STRING ) { postfix } postfix : "(" [ args ] ")" | "[" expr "]"
// 代碼清單10.2 ArrayParser.java package stone; import stone.ast.*; import javassist.gluonj.Reviser; import static stone.Parser.rule; @Reviser public class ArrayParser extends FuncParser { Parser elements = rule(ArrayLiteral.class).ast(expr).repeat(rule().sep(",").ast(expr)); public ArrayParser() { reserved.add("]"); primary.insertChoice(rule().sep("[").maybe(elements).sep("]")); postfix.insertChoice(rule(ArrayRef.class).sep("[").ast(expr).sep("]")); } }
package stone.ast; import java.util.List; public class ArrayLiteral extends ASTList { public ArrayLiteral(List<ASTree> c) { super(c); } public int size() { return numChildren(); } }
接下來咱們根據新的語法規則來擴展語法分析器。代碼清單10.2是須要使用的修改器。代碼清單10.3與代碼清單10.4是抽象語法樹中新增的節點類,該修改器須要藉助它們實現。數組
代碼清單10.2的修改器將爲第7章代碼清單7.2中的FuncParser類添加elements字段,並在構造函數中更新相應的處理。ide
修改器首先在構造函數內,經過add方法向由reserved字段表示的哈希表中添加了右中括號 ]。因而,] 不會再被識別爲標識符。若是忘了添加這個符號,解釋器將沒法在語法分析階段把它處理爲一種分隔符,也就沒法順利運行。此外,primary與postfix也都分別經過insertChoice方法添加了對新語法規則的支持。函數
package stone.ast; import java.util.List; public class ArrayRef extends Postfix { public ArrayRef(List<ASTree> c) { super(c); } public ASTree index() { return child(0); } public String toString() { return "[" + index() + "]"; } }
咱們只要爲抽象語法樹中新增的節點類添加eval方法,就能讓Stone語言支持數組。代碼清單10.5是用於添加eval方法的修改器的具體實現。post
ArrayRef類的eval方法將首先對下標表達式調用eval方法,計算下標的值。以後,它將從參數value指向的object類型數組中獲取與該下標對應的元素的值並返回。這裏的eval方法覆蓋了第7章代碼清單7.7中由FuncEvaluator修改器爲Postfix類添加的eva1方法。ui
數組也可能出如今賦值表達式的左側,咱們須要覆蓋BinaryExpr類的computeAssign方法來處理這種狀況。該方法最初在第6章代碼清單6.3中由修改器添加。this
代碼清單10.6是解釋器的啓動程序,它將整合並執行修改後的程序。因爲數組功能徹底由修改器實現,所以此次咱們不須要對解釋器做修改。代碼清單10.6直接使用了上一章代碼清單9.9中的解釋器。lua
package chap10; import java.util.List; import javassist.gluonj.*; import stone.ArrayParser; import stone.StoneException; import stone.ast.*; import chap6.Environment; import chap6.BasicEvaluator; import chap6.BasicEvaluator.ASTreeEx; import chap7.FuncEvaluator; import chap7.FuncEvaluator.PrimaryEx; @Require({FuncEvaluator.class, ArrayParser.class}) @Reviser public class ArrayEvaluator { @Reviser public static class ArrayLitEx extends ArrayLiteral { public ArrayLitEx(List<ASTree> list) { super(list); } public Object eval(Environment env) { int s = numChildren(); Object[] res = new Object[s]; int i = 0; for (ASTree t: this) res[i++] = ((ASTreeEx)t).eval(env); return res; } } @Reviser public static class ArrayRefEx extends ArrayRef { public ArrayRefEx(List<ASTree> c) { super(c); } public Object eval(Environment env, Object value) { if (value instanceof Object[]) { Object index = ((ASTreeEx)index()).eval(env); if (index instanceof Integer) return ((Object[])value)[(Integer)index]; } throw new StoneException("bad array access", this); } } @Reviser public static class AssignEx extends BasicEvaluator.BinaryEx { public AssignEx(List<ASTree> c) { super(c); } @Override protected Object computeAssign(Environment env, Object rvalue) { ASTree le = left(); if (le instanceof PrimaryExpr) { PrimaryEx p = (PrimaryEx)le; if (p.hasPostfix(0) && p.postfix(0) instanceof ArrayRef) { Object a = ((PrimaryEx)le).evalSubExpr(env, 1); if (a instanceof Object[]) { ArrayRef aref = (ArrayRef)p.postfix(0); Object index = ((ASTreeEx)aref.index()).eval(env); if (index instanceof Integer) { ((Object[])a)[(Integer)index] = rvalue; return rvalue; } } throw new StoneException("bad array access", this); } } return super.computeAssign(env, rvalue); } } }
package chap10; import javassist.gluonj.util.Loader; import chap7.ClosureEvaluator; import chap8.NativeEvaluator; import chap9.ClassEvaluator; import chap9.ClassInterpreter; public class ArrayRunner { public static void main(String[] args) throws Throwable { Loader.run(ClassInterpreter.class, args, ClassEvaluator.class, ArrayEvaluator.class, NativeEvaluator.class, ClosureEvaluator.class); } }