两周自制脚本语言-第10天 无法割舍的数组

泛型沙漏
• 阅读 1618

第10天 无法割舍的数组

目标:为Stone语言添加简单的数组功能,下标(index)只能使用整数值。

10.1扩展语法分析器

代码清单10.1 与数组相关的语法规则

elements : expr { "," expr }
primary : ( "[" [ elements ] "]" | "(" expr ")" | NUMBER | IDENTIFIER | STRING ) { postfix }
postfix : "(" [ args ] ")" | "[" expr "]"

代码清单10.2 ArrayParser.java

// 代码清单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("]"));
    }
}

代码清单10.3 ArrayLiteral.java

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字段,并在构造函数中更新相应的处理。

修改器首先在构造函数内,通过add方法向由reserved字段表示的哈希表中添加了右中括号 ]。于是,] 不会再被识别为标识符。如果忘了添加这个符号,解释器将无法在语法分析阶段把它处理为一种分隔符,也就无法顺利运行。此外,primary与postfix也都分别通过insertChoice方法添加了对新语法规则的支持。

代码清单10.4 ArrayRef.java

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() + "]";
    }
}

10.2仅通过修改器来实现数组

我们只要为抽象语法树中新增的节点类添加eval方法,就能让Stone语言支持数组。代码清单10.5是用于添加eval方法的修改器的具体实现。

ArrayRef类的eval方法将首先对下标表达式调用eval方法,计算下标的值。之后,它将从参数value指向的object类型数组中获取与该下标对应的元素的值并返回。这里的eval方法覆盖了第7章代码清单7.7中由FuncEvaluator修改器为Postfix类添加的eva1方法。

数组也可能出现在赋值表达式的左侧,我们需要覆盖BinaryExpr类的computeAssign方法来处理这种情况。该方法最初在第6章代码清单6.3中由修改器添加。

代码清单10.6是解释器的启动程序,它将整合并执行修改后的程序。由于数组功能完全由修改器实现,因此这次我们不需要对解释器作修改。代码清单10.6直接使用了上一章代码清单9.9中的解释器。

代码清单10.5 ArrayEvaluator.java

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);
        }
    }       
}

代码清单10.6 ArrayRunner.java

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);
    }
}
点赞
收藏
评论区
推荐文章
好买-葡萄 好买-葡萄
3年前
【数据结构与算法】—— 二分查找
1.二分查找的概念二分查找指的是在排好序的数组中,找到目标元素。如果元素存在则返回元素的下标,不存在则返回1.下面以升序为例进行简单描述2.查找过程:取数组中间元素与查找元素target比较。如果target等于中间元素则直接返回中间元素的下标,如果target小于数组中间元素则在数组左边查找,如果target大于数组中间元素则在右边查找。重复以上步骤。
LinMeng LinMeng
4年前
js 的 forEach,map,filter,some,every,find(es6),reduce详解
forEach()定义和用法forEach()方法用于调用数组的每个元素,并将元素传递给回调函数注意:forEach()对于空数组是不会执行回调函数的。语法array.forEach(function(currentValue,index,arr),thisValue)参数function(currentValue,index,a
Wesley13 Wesley13
3年前
java.util.Arrays,java.lang.Math,java.lang.System 类的常用方法汇总
java.util.Arrays类是数组的工具类,一般数组常用的方法包括二分查找:publicstaticint binarySearch(array\\,intkey),返回key的下标index扩容缩容:publicstaticint\\ copyOf(array\\,newLength),返回新数组取部分:publ
22 22
4年前
二叉树创建后,如何使用递归和栈遍历二叉树?
0.前言前文主要介绍了树的相关概念和原理,本文主要内容为二叉树的创建及遍历的代码实现,其中包括递归遍历和栈遍历。1.二叉树的实现思路1.0.顺序存储——数组实现前面介绍了满二叉树和完全二叉树,我们对其进行了编号——从0到n的不中断顺序编号,而恰好,数组也有一个这样的编号——数组下标,只要我们把二者联合起来,数组就能存储二叉树了。那么非满
Wesley13 Wesley13
3年前
AS3 数组
varfoo:Array"one","two","three";trace(foo1);//"tow",数组下标从0开始trace(foo.length);//3Array的length属性是可读可写的,这与Java不同,在Java中,数组一旦定义好长度,就无法再更改
Wesley13 Wesley13
3年前
Java中的数组(Array)
数组对于每一门编程语言来讲都是最重要的数据结构之一,当然不同的编程语言对数组的实现以及处理也不尽相同。数组的概念:把有限个相同类型元素变量放在一个整体,这个整体就叫做数组。数组中的每一个元素被称为数组元素,通常可以通过数组元素的索引(也叫下标,可以理解为一种编号,从0开始)来访问数组元素,包括数组元素的赋值(set)和取值(get)。
Stella981 Stella981
3年前
Python3.x:正则 re.findall()的用法
Python3.x:正则re.findall()的用法概念: 语法:findall(pattern,string,flags0) 说明:返回string中所有与pattern相匹配的全部字串,返回形式为数组)示例:python3impo
Stella981 Stella981
3年前
JavaScript splice() 方法和JavaScript split() 方法
定义和用法splice()方法向/从数组中添加/删除项目,然后返回被删除的项目。注释:该方法会改变原始数组。语法arrayObject.splice(index,howmany,item1,.....,itemX)参数描述index必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位
Stella981 Stella981
3年前
JavaScript基础知识
题目:1.找出数字数组中最大的元素(使用Math.max函数)2.转化一个数字数组为function数组(每个function都弹出相应的数字)3.给object数组进行排序(排序条件是每个元素对象的属性个数)4.利用JavaScript打印出Fibonacci数(不使用全局变量)5.实现如下语法的功能:vara(5).plus(
Wesley13 Wesley13
3年前
MHA选择主库源码解析
导读作者:魏新平知数堂第5期MySQL实战班学员,第10期MySQL优化班学员,现任职助教。MHA在选择新的主库之前,会先把活着的slave分为几个数组,分别为latest(最靠前的slave数组),pref(优先被选择为master的数组),bad(不会被选择成为master的slave),slave
达里尔 达里尔
1年前
给数组添加新数据,判断数据是否重复
多选要进行数组拼接,希望判断往原数组里添的新数据是否重复,封装个简易方法languageconstdataArrayname:'aaa',id:1,name:'bbb',id:2;constnewDataname:'ccc',id:2;//要添加的新数
泛型沙漏
泛型沙漏
Lv1
画图恰似归家梦,千里河山寸许长。
文章
3
粉丝
0
获赞
0