目标:为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); } }