【用JS自制表格软件玩数据】10. 为表格脚本设计一个语法解析器

概述

在EXCEL表格中,有个标配的VBA脚本语言,近年,也有人开始将 Python 也植入里面。
在该自制的脚本语言中,目标是办公自动化。语法特性是尽量贴近 其他开发语言的使用习惯。但更倾向于简单易用为主。不排除后面进行简化。
本章为脚本解析器,为下一章的虚拟机指令集与CPU,内存等虚拟硬件做铺垫。

脚本源码语法预览

var a = 1+6*(2-1)/3; // 一条普通的对 变量a 进行赋值,计算结果为整数
var b = 1+a*2/1/3; // 对 变量b赋值的公式中,包含了变量a,计算结果为整数
var c = 1+(2-1)/3; // 一条普通的对 变量c 进行赋值,计算结果为浮点数

运行效果如下图

在这里插入图片描述

设计计算符号的优先级

下列的运算符,越底部,优先级越高。

window.calculatelevel = [
  [",", ";"], // 多个计算	按优先级计算,然后从右向左
  ["=", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<", "<=", ">", ">=", ">>="], // 混合赋值运算符	从右向左
  ["?", ":"], // 条件运算符	从右向左
  ["||"], // 短路或(逻辑“或”)	从左向右
  ["&&"], // 短路与(逻辑“与”)	从左向右
  ["|"], // 按位“或”	从左向右
  ["^"], // 按位“异或”	从左向右
  ["&"], // 按位“与”	从左向右
  ["==", "!=", "===", "!=="], // 相等、不相等、全等,不全等	从左向右
  ["<", "<=", ">", ">="], // 小于、小于或等于、大于、大于或等于、是否为特定类的实例	从左向右
  ["<<", ">>", ">>>"], // 左位移、右位移、无符号右移	从左向右
  ["+", "-"], // 相加、相减、字符串串联	从左向右
  ["*", "/", "%"], // 相乘、相除、求余数	从左向右
  ["++", "--", "~", "!"], // 	一元运算符、返回数据类型、对象创建、未定义的值	从右向左
  ["[", "]", "(", ")", "{", "}", ".", "\"", "'"] // 字段访问、数组索引、函数调用和表达式分组	从左向右
];

定义一些关键词

因为脚本计划借鉴JS的语法结构,所以直接把它的关键词都直接复用,等后面看哪些用不上的再去除。

window.jskeyword = [
  "abstract",
  "arguments",
  "boolean",
  "break",
  "case",
  "continue",
  "char",
  "catch",
  "class*",
  "delete",
  "default",
  "double",
  "debugger",
  "extends*",
  "for",
  "import*",
  "let",
  "package",
  "short",
  "this",
  "try",
  "while",
  "else",
  "FALSE",
  "function",
  "in",
  "long",
  "private",
  "static",
  "throw",
  "typeof",
  "with",
  "enum*",
  "final",
  "goto",
  "instanceof",
  "native",
  "protected",
  "super*",
  "throws",
  "var",
  "yield",
  "eval",
  "finally",
  "if",
  "int",
  "new",
  "public",
  "switch",
  "transient",
  "void",
  "byte",
  "const",
  "do",
  "export*",
  "float",
  "implements",
  "interface",
  "null",
  "return",
  "synchronized",
  "TRUE",
  "volatile",
  "Array",
  "Infinity",
  "Math",
  "prototype",
  "Date",
  "isFinite",
  "NaN",
  "String",
  "eval",
  "isNaN",
  "name",
  "toString",
  "function",
  "isPrototypeOf",
  "Number",
  "undefined",
  "hasOwnProperty",
  "length",
  "Object",
  "valueOf",
  "alert",
  "assign",
  "clearTimeout",
  "constructor",
  "document",
  "encodeURI",
  "focus",
  "innerWidth",
  "mimeTypes",
  "hidden",
  "open",
  "packages",
  "parseInt",
  "propertyIsEnum",
  "scroll",
  "setTimeout",
  "textarea",
  "all",
  "blur",
  "clientInformation",
  "crypto",
  "element",
  "encodeURIComponent",
  "form",
  "layer",
  "navigate",
  "history",
  "opener",
  "pageXOffset",
  "password",
  "radio",
  "secure",
  "status",
  "top",
  "anchor",
  "button",
  "close",
  "decodeURI",
  "elements",
  "escape",
  "forms",
  "layers",
  "navigator",
  "image",
  "option",
  "pageYOffset",
  "pkcs11",
  "reset",
  "select",
  "submit",
  "unescape",
  "anchors",
  "checkbox",
  "closed",
  "decodeURIComponent",
  "embed",
  "event",
  "frame",
  "link",
  "frames",
  "images",
  "outerHeight",
  "parent",
  "plugin",
  "screenX",
  "self",
  "taint",
  "untaint",
  "area",
  "clearInterval",
  "confirm",
  "defaultStatus",
  "embeds",
  "fileUpload",
  "innerHeight",
  "location",
  "frameRate",
  "offscreenBuffering",
  "outerWidth",
  "parseFloat",
  "prompt",
  "screenY",
  "setInterval",
  "text",
  "window"
];

生成一份关键词的map方便引用

const Keywords = new Set(window.jskeyword) // window.jskeyword

枚举关键词的类型


class Enum {
    constructor(type, value) {
        this.type = type;
        this.value = value;
    }
}

var TokenType = {
    KEYWORD: new Enum("KEYWORD", 1), // 关键词
    VARIABLE: new Enum("VARIABLE", 2), // 值
    STRING: new Enum("STRING", 3), // 字符串
    OPERATOR: new Enum("OPERATOR", 4), // 操作符
    BRACKET: new Enum("BRACKET", 5), // 各类括号以及闭包符号,分段
    INTEGER: new Enum("INTEGER", 6), // 整数
    FLOAT: new Enum("FLOAT", 7), // 浮点数
    BOOLEAN: new Enum("BOOLEAN", 8), // 布尔数(只有两种): true , false
    OBJECT: new Enum("OBJECT", 9), // 类,对象
    FUNCTION: new Enum("FUNCTION", 10), // 函数
    ARRAY: new Enum("ARRAY", 11) // 数组
}

总共有11种类型

  1. KEYWORD 关键词
  2. VARIABLE 变量
  3. STRING 字符串
  4. OPERATOR 操作符
  5. BRACKET 分段符
  6. INTEGER 整数
  7. FLOAT 浮点数
  8. BOOLEAN 布尔值
  9. OBJECT 对象
  10. FUNCTION 函数
  11. ARRAY 数组

错误异常的捕获

脚本的异常捕获,它继承自Error 类。这样比较省事儿


// 异常处理
class ScriptException extends Error {
    constructor(msg) {
        super(msg)
    }

    // 词汇异常
    static fromChar(c) {
        return new ScriptException(`unexpected char ${c}`);
    }
    
    // 语法异常
    static fromToken(token) {
        return new ScriptException(`Syntax Error, unexpected token ${token.getValue()}`)
    }
}

字符匹配

// 字符匹配
class AlphabetMatch {
    static ptnLetter = /^[a-zA-Z]$/
    static ptnNumber = /^[0-9]$/
    static ptnLiteral = /^[_a-zA-Z0-9]$/
    static operator = /^[+-\\*/><=!&|?:^%]$/

    static isLetter(c) { //判断是否字母
        return AlphabetMatch.ptnLetter.test(c)
    }

    static isNumber(c) { //判断是否数字
        return AlphabetMatch.ptnNumber.test(c)
    }

    static isLiteral(c) { // 判断是否字符
        return AlphabetMatch.ptnLiteral.test(c)
    }

    static isOperator(c) { // 判断是否操作符
        return AlphabetMatch.operator.test(c)
    }
}

代码的字符转化成迭代器

function* arrayToGenerator(array) {
    for (let i = 0; i < array.length; i++) {
        yield array[i];
    }
} // 构建数组迭代器

关键词标记器

class Token { // 标记器
    constructor(type, value) {
        this._type = type;
        this._value = value;
        this._level = null;
    }

    getType() {
        return this._type;
    } // 获取类型

    getValue() {
        return this._value;
    } // 获取值

    getLevel() {
        return this._level;
    } // 获取优先级

    isVariable() { // 判断是否变量
        return this._type == TokenType.VARIABLE;
    }

    isFunction() { // 判断是否函数
        return this._type == TokenType.FUNCTION;
    }

    isValue() {
        return this.isScalar() || this.isVariable();
    } // 判断是否数值

    isType() {
        return (
            this._value === "bool" ||
            this._value === "int" ||
            this._value === "float" ||
            this._value === "void" ||
            this._value === "string"
        );
    } // 返回格式

    isScalar() {
        return (
            this._type == TokenType.INTEGER ||
            this._type == TokenType.FLOAT ||
            this._type == TokenType.STRING ||
            this._type == TokenType.BOOLEAN
        );
    }

    toString() {
        return `type ${this._type.type}, value ${this._value}`;
    } // 对格式与值进行序列化

    static makeVarOrKeyword(it) { // 返回关键词
        let s = ""; // 初始化一个临时的字符串

        while (it.hasNext()) { // 判断迭代器是否有下一个字符
            const c = it.peek();

            if (AlphabetMatch.isLiteral(c)) { // 判断字符串
                s += c; // 如果是字符串,就对字符进行拼接
            } else {
                break; // 否则跳出 while 循环
            }

            it.next(); // 迭代器一直往前推进
        }

        if (Keywords.has(s)) { // 是否关键词
            return new Token(TokenType.KEYWORD, s);
        }

        if (s == "true" || s == "false") { // 是否布尔
            return new Token(TokenType.BOOLEAN, s);
        }

        return new Token(TokenType.VARIABLE, s); // 返回变量
    }

    static makeString(it) { // 返回字符串
        let s = ""; // 初始化一个临时的字符串

        let state = 0; // 初始化状态

        while (it.hasNext()) { // 判断迭代器是否有下一个字符
            let c = it.next();

            switch (state) {
                case 0:
                    if (c == '"') { // 检测出是前双引号
                        state = 1;
                    } else {
                        state = 2;
                    }
                    s += c;
                    break;
                case 1:
                    if (c == '"') { // 检测出是后双引号
                        return new Token(TokenType.STRING, s + c);
                    } else {
                        s += c; // 否则就链接字符
                    }
                    break;
                case 2:
                    if (c == "'") { // 判断单引号
                        return new Token(TokenType.STRING, s + c);
                    } else {
                        s += c;
                    }
                    break;
            }
        }
        throw new ScriptException("Unexpected error"); // 抛出异常
    }

    static makeOp(it) { // 返回操作符
        let state = 0; // 设置状态
        while (it.hasNext()) {
            let lookahead = it.next();
            switch (state) {
                case 0:
                    switch (lookahead) {
                        case "+": state = 1; break;
                        case "-": state = 2; break;
                        case "*": state = 3; break;
                        case "/": state = 4; break;
                        case ">": state = 5; break;
                        case "<": state = 6; break;
                        case "=": state = 7; break;
                        case "!": state = 8; break;
                        case "&": state = 9; break;
                        case "|": state = 10; break;
                        case "^": state = 11; break;
                        case "%": state = 12; break;
                        case ".": state = 13; break;
                        case "?": return new Token(TokenType.OPERATOR, "?");
                        case ":": return new Token(TokenType.OPERATOR, ":");
                        case ",": return new Token(TokenType.OPERATOR, ",");
                        case ";": return new Token(TokenType.OPERATOR, ";");
                    }
                    break;
                case 1: {
                    if (lookahead == "+") { return new Token(TokenType.OPERATOR, "++"); }
                    else if (lookahead == "=") { return new Token(TokenType.OPERATOR, "+="); }
                    else { it.putBack(); return new Token(TokenType.OPERATOR, "+"); }
                }
                case 2: {
                    if (lookahead == "-") { return new Token(TokenType.OPERATOR, "--"); }
                    else if (lookahead == "=") { return new Token(TokenType.OPERATOR, "-="); }
                    else { it.putBack(); return new Token(TokenType.OPERATOR, "-"); }
                }
                case 3: {
                    if (lookahead == "=") { return new Token(TokenType.OPERATOR, "*="); }
                    else { it.putBack(); return new Token(TokenType.OPERATOR, "*"); }
                }
                case 4: {
                    if (lookahead == "=") { return new Token(TokenType.OPERATOR, "/="); }
                    else { it.putBack(); return new Token(TokenType.OPERATOR, "/"); }
                }
                case 5: {
                    if ((lookahead == ">") && (">" == it.peek())) { it.next(); return new Token(TokenType.OPERATOR, ">>>"); }
                    else if ((lookahead == ">") && ("=" == it.peek())) { it.next(); return new Token(TokenType.OPERATOR, ">>="); }
                    else if (lookahead == "=") { return new Token(TokenType.OPERATOR, ">="); }
                    else if (lookahead == ">") { return new Token(TokenType.OPERATOR, ">>"); }
                    else { it.putBack(); return new Token(TokenType.OPERATOR, ">"); }
                }
                case 6: {
                    if (lookahead == "=") { return new Token(TokenType.OPERATOR, "<="); }
                    else if (lookahead == "<") { return new Token(TokenType.OPERATOR, "<<"); }
                    else { it.putBack(); return new Token(TokenType.OPERATOR, "<"); }
                }
                case 7: {
                    if ((lookahead == "=") && ("=" == it.peek())) { it.next(); return new Token(TokenType.OPERATOR, "==="); }
                    else if (lookahead == "=") { return new Token(TokenType.OPERATOR, "=="); }
                    else if (lookahead == ">") { return new Token(TokenType.OPERATOR, "=>"); }
                    else { it.putBack(); return new Token(TokenType.OPERATOR, "="); }
                }
                case 8: {
                    if ((lookahead == "=") && ("=" == it.peek())) { it.next(); return new Token(TokenType.OPERATOR, "!=="); }
                    else if (lookahead == "=") { return new Token(TokenType.OPERATOR, "!="); }
                    else { it.putBack(); return new Token(TokenType.OPERATOR, "!"); }
                }
                case 9: {
                    if (lookahead == "&") { return new Token(TokenType.OPERATOR, "&&"); }
                    else if (lookahead == "=") { return new Token(TokenType.OPERATOR, "&="); }
                    else { it.putBack(); return new Token(TokenType.OPERATOR, "&"); }
                }
                case 10: {
                    if (lookahead == "|") { return new Token(TokenType.OPERATOR, "||"); }
                    else if (lookahead == "=") { return new Token(TokenType.OPERATOR, "|="); }
                    else { it.putBack(); return new Token(TokenType.OPERATOR, "|"); }
                }
                case 11: {
                    if (lookahead == "^") { return new Token(TokenType.OPERATOR, "^^"); }
                    else if (lookahead == "=") { return new Token(TokenType.OPERATOR, "^="); }
                    else { it.putBack(); return new Token(TokenType.OPERATOR, "^"); }
                }
                case 12: {
                    if (lookahead == "=") { return new Token(TokenType.OPERATOR, "%="); }
                    else { it.putBack(); return new Token(TokenType.OPERATOR, "%"); }
                }
                case 13: {
                    if ((lookahead == ".") && ("." == it.peek())) { it.next(); return new Token(TokenType.OPERATOR, "..."); }
                    else { return new Token(TokenType.OPERATOR, "."); }
                }
            }
        } // 这里结束 while

        throw new ScriptException("Unexpected error"); // 抛出异常
    }

    static makeNumber(it) { // 返回数字
        let state = 0;
        let s = "";

        while (it.hasNext()) {
            let lookahead = it.peek();

            switch (state) {
                case 0:
                    if (lookahead == "0") {
                        state = 1;
                    } else if (AlphabetMatch.isNumber(lookahead)) {
                        state = 2;
                    } else if (lookahead == "+" || lookahead == "-") {
                        state = 3;
                    } else if (lookahead == ".") {
                        state = 5;
                    }
                    break;
                case 1:
                    if (lookahead == "0") {
                        state = 1;
                    } else if (lookahead == ".") {
                        state = 4;
                    } else if (AlphabetMatch.isNumber(lookahead)) {
                        state = 2;
                    } else {
                        return new Token(TokenType.INTEGER, s);
                    }
                    break;
                case 2:
                    if (AlphabetMatch.isNumber(lookahead)) {
                        state = 2;
                    } else if (lookahead == ".") {
                        state = 4;
                    } else {
                        return new Token(TokenType.INTEGER, s);
                    }
                    break;
                case 3:
                    if (AlphabetMatch.isNumber(lookahead)) {
                        state = 2;
                    } else if (lookahead == ".") {
                        state = 5;
                    } else {
                        throw ScriptException.fromChar(lookahead);
                    }
                    break;
                case 4:
                    if (lookahead == ".") {
                        throw ScriptException.fromChar(lookahead);
                    } else if (AlphabetMatch.isNumber(lookahead)) {
                        state = 4;
                    } else {
                        return new Token(TokenType.FLOAT, s);
                    }
                    break;
                case 5:
                    if (AlphabetMatch.isNumber(lookahead)) {
                        state = 4;
                    } else {
                        throw ScriptException.fromChar(lookahead);
                    }
                    break;
            }
            s += lookahead;
            it.next();
        } // 这里结束 while
        throw new ScriptException("Unexpected error"); // 抛出异常
    }
}

词法分析器

class Lexer { // 词法分析器
    analyse(source) { // 这里是源代码的入口
        const tokens = [];
        const it = new characterIterator(source, "\0");

        while (it.hasNext()) {
            let c = it.next();
            // 如果遇到终结符 \0 ,就停止
            if (c == "\0") {
                break;
            }

            // 获取当前的指针指向
            let lookahead = it.peek();

            // 跳过空格,换行符
            if (c == " " || c == "\n" || c == "\r") {
                continue;
            }

            // 提取注释的程序
            if (c == "/") { //如果前一个字符是 /
                if (lookahead == "/") { // 如果当前字符也是 /
                    // 一直循环,直至遇到 \n 换行符,最后跳出循环
                    while (it.hasNext() && (c = it.next()) != "\n");
                    continue;
                } else if (lookahead == "*") { // 如果当前字符是 *
                    let valid = false; // 设置注释符的开关设置为不可用的状态
                    while (it.hasNext()) { // 开始一直读取字符
                        const p = it.next(); // 先读取一个字符
                        if (p == "*" && it.peek() == "/") { //如果读取的字符和当前指向的字符为 */ ,那就意味着注释的终结
                            valid = true; // 将开关置为 true 可用状态
                            it.next(); // 由于当前指向的字符为 / , 所以,直接 next 跳过这个字符
                            break; // 跳出循环
                        }
                    }

                    if (!valid) { // 注释没有终结符,抛出异常
                        throw new ScriptException("comment not matched");
                    }
                    continue;
                }
            }

            if (c == "{" || c == "}" || c == "(" || c == ")" || c == "[" || c == "]") { // 如果遇到了括号
                tokens.push(new Token(TokenType.BRACKET, c)); // 将括号压入队列
                continue;
            }

            if (c == '"' || c == "'") {// 如果遇到了单引号或者双引号
                it.putBack(); // 将c退回流中
                tokens.push(Token.makeString(it)); // 将it数据流,放到makeString中提取字符串
                continue;
            }

            if (AlphabetMatch.isLetter(c)) {// 判断是否字符串
                it.putBack(); // 将c退回流中
                tokens.push(Token.makeVarOrKeyword(it));
                continue;
            }

            if (AlphabetMatch.isNumber(c)) {// 判断是否数字
                it.putBack(); // 将c退回流中
                tokens.push(Token.makeNumber(it));
                continue;
            }

            // + -
            if ((c == "+" || c == "-") && AlphabetMatch.isNumber(lookahead)) {
                // 跳过:a+1, 1+1
                // +5, 3*-5
                const lastToken = tokens[tokens.length - 1] || null;

                if (lastToken == null || !lastToken.isValue()) {
                    it.putBack();
                    tokens.push(Token.makeNumber(it));
                    continue;
                }
            }

            if (AlphabetMatch.isOperator(c)) { // 判断是否操作符
                it.putBack();
                tokens.push(Token.makeOp(it));
                continue;
            }

            throw ScriptException.fromChar(c);
        }
        return tokens;
    }

    static fromFile(src) { // 从远程文件中加载源代码
        // 此处代码未完善
    }
}

设计一个队列处理器

该队列处理器为了方便后面的迭代器的操作


/**
 * 队列模块
 * 该队列为一个数组,可以设置长度,可以从头部压入,尾部弹出;也可以从尾部压入,头部弹出。
 */
class Queue {
  /**
   * @param {number} items_count 设置队列的长度
   */
  constructor(items_count) {
    this.items_count = items_count || 2000; // 队列尺寸
    this.items = []; // 队列实体
  }

  /**
   * @property {Function} enqueue_to_head 头部入队
   * @param {*} input 元素输入
   * @returns {boolean|Object} 如果储存的长度溢出,则将最先进入的元素从尾部弹出,否则,返回false
   */
  enqueue_to_head(input) {
    this.items.unshift(input);
    if (this.items_count < this.items.length) {
      // 若数据入队,则将旧数据挤出
      return this.dequeue_from_tail();
    } else {
      return false;
    }
  }

  /**
   * @property {Function} dequeue_from_head 头部出队
   * @returns {boolean|Object} 头部出队,如果没有内容,则返回false
   */
  dequeue_from_head() {
    var result = this.items.shift();
    return typeof result != "undefined" ? result : false;
  }

  /**
   * @property {Function} enqueue_to_tail 尾部入队
   * @param {input} 元素输入
   * @returns {boolean|Object} 如果储存的长度溢出,则将最先进入的元素从头部弹出,否则,返回false
   */
  enqueue_to_tail(input) {
    this.items.push(input);
    if (this.items_count < this.items.length) {
      return this.dequeue_from_head();
    } else {
      return false;
    }
  }

  /**
   * @property {Function} dequeue_from_tail 尾部出队
   * @returns {boolean|Object} 尾部出队,如果没有内容,则返回false
   */
  dequeue_from_tail() {
    var result = this.items.pop();
    return typeof result != "undefined" ? result : false;
  }

  /**
   * @property {Function} isEmpty 队列是否空
   * @returns {boolean} 如果为空,返回True
   */
  isEmpty() {
    return this.items.length == 0;
  }

  /**
   * @property {Function} clearQueue 清空队列
   * @returns {void}
   */
  clearQueue() {
    this.items = [];
  }

  /**
   * @property {Function} showQueue 打印队列
   * @returns {void}
   */
  showQueue() {
    console.log("show Queue:");
    for (let index = 0; index < this.items.length; index++) {
      console.log(index + ":", this.items[index]);
    }
  }
}

源代码字符串迭代器

/**
 * @class
 * @param {Iterable} it 迭代的数组
 * @param {String} endToken 终结符
 * @method peek 获取即将读取的值
 * @method putBack 回退
 */
class characterIterator {
    constructor(it, endToken = null) {
        this.it = it;

        // 需要putback的元素
        this.stackPutBacks = new Queue();

        // 基于时间窗口的缓存
        this.queueCache = new Queue(10);

        // this.stackPutBacks.items = it;

        this.endToken = endToken;
    }


    peek() {
        if (!this.stackPutBacks.isEmpty()) { // 如果不空
            let tail = this.stackPutBacks.items.length - 1;
            var tmp = this.stackPutBacks.items[tail];
            return tmp;
        }

        const val = this.next()
        this.putBack()
        return val
    }

    putBack() {
        if (!this.queueCache.isEmpty()) { // 判断缓存是不空了
            var val = this.queueCache.dequeue_from_tail() // 缓存尾部出队
            this.stackPutBacks.enqueue_to_tail(val); // 尾部入队
        }
    }

    hasNext() {
        // 该语句会将任意类型转为bool类型
        return this.endToken || !!this.peek();
    }

    next() {
        let val = null;

        if (!this.stackPutBacks.isEmpty()) { // 判断是否空了
            val = this.stackPutBacks.dequeue_from_tail() // 尾部出队

        } else {
            val = this.it.next().value
            if (val === undefined) {
                const tmp = this.endToken
                this.endToken = null
                val = tmp
            }
        }

        this.queueCache.enqueue_to_tail(val) // 尾部入队
        return val;
    }
}

代码的块级运行环境

我们的脚本要能运行一下变量的计算,因此就要设计一个块级的运行环境,设计变量的作用域

脚本源码

var a = 1+6*(2-1)/3; // 1621-*3/+
var b = 1+a*2/1/3; // 162*1/3/+
var c = 1+(2-1)/3; // 121-3/+

运行效果如下图

在这里插入图片描述

上代码


class Block { // 代码块的运行环境
    constructor(){
        this.mem = { // 抽象语法树
            "parent" : null
        };
        this.mempoint = this.mem // 语法树指针

        this.level = {}; // 符号等级树
        this.levelStep = 0; // 当前符号等级

        //建立运算符级别表
        for (let index = 0; index < window.calculatelevel.length; index++) {
            for (let _index = 0; _index < window.calculatelevel[index].length; _index++) {
                this.level[window.calculatelevel[index][_index]] = index;
            }
        }

    }

    /**
     * @property {Function} getLevel 获取关键词的级别
     * @param {Object} operator 输入关键词
     * @returns {Number} 返回级别编号,数字越大,级别越高
     */
    getLevel(operator) {
        return this.level[operator.getValue()] + this.levelStep*window.calculatelevel.length;
    }

    /**
     * @property {Function} changeLevel 切换级别
     * @param {Object} bracket 终结符
     */
    changeLevel(bracket){
        if (bracket.getValue() == "(") {
            ++this.levelStep;
        } else if (bracket.getValue() == ")"){
            --this.levelStep;
        }
    }

    /**
     * @property {Function} calculate 计算
     * @param {Object} s 计算符号
     * @param {Object} b 值
     * @param {Object} a 值
     * @returns {Object}
     */
    calculate(s,b,a){
        var aa,bb;

        if (a.getType() == TokenType.INTEGER) {
            aa = parseInt(a.getValue())
        }else{
            aa = parseFloat(a.getValue())
        }

        if (b.getType() == TokenType.INTEGER) {
            bb = parseInt(b.getValue())
        }else{
            bb = parseFloat(b.getValue())
        }

		var calculatemap = {
			"+" : function(a,b){return a+b;},
			"-" : function(a,b){return a-b;},
			"*" : function(a,b){return a*b;},
			"/" : function(a,b){return a/b;}
		}
        var t = calculatemap[s.getValue()](aa,bb);
		if(t%1 === 0){
            return new Token(TokenType.INTEGER, ""+t);
        }else{
            return new Token(TokenType.FLOAT, ""+t);
        }
	}

    /**
     * @property {Function} ReversePoland 逆波兰式
     * @param {String} it 迭代器
     * @returns {Object} 返回结果
     */
    ReversePoland(it){
        var sop = [];
		var L = [];

        while (it.hasNext()) {
            var step = it.next();
            // 如果遇到终结符,换行符,分号,则退出循环
            if ((step.getValue() == "\0")||(step.getValue() == "\n")||(step.getValue() == "\r")||(step.getValue() == ";")) {
                while (sop.length != 0) {
                    L.push(sop.pop());
                }
                break;
            }
            switch (step.getType()) {
                case TokenType.VARIABLE: // 如果是变量
                    // 则直接追加到 L中
                    if (!this.mempoint.hasOwnProperty(step.getValue())) { throw new Error("变量未定义"); } // 如果没有找到已经定义的变量

                    if ((this.mempoint[step.getValue()].getType() == TokenType.INTEGER)||
                        (this.mempoint[step.getValue()].getType() == TokenType.FLOAT)) {
                        
                        L.push(this.mempoint[step.getValue()]);
                    } else { throw new Error("变量非数字"); }
                    break;
                case TokenType.BRACKET: // 如果是分界符
                    this.changeLevel(step); // 切换级别
                    break;
                case TokenType.INTEGER: // 整数
                case TokenType.FLOAT: // 浮点数
                    L.push(step);
                    if((L[L.length-1].getType() == TokenType.OPERATOR)&&
                       (L[L.length-1].getLevel() >= sop[sop.length-1].getLevel())
                        ){
                        L.push(sop.pop());
                    }
                    break;
                case TokenType.STRING: // 字符串
                case TokenType.BOOLEAN: // 布尔
                    return step;
                case TokenType.OPERATOR: // 如果是运算符
                    step._level = this.getLevel(step) // 获取优先级
                    if (sop.length == 0) { // 如果栈为空
                        sop.push(step);
                    } else if(step.getLevel() > sop[sop.length-1].getLevel()){
                        sop.push(step);// 如果当前符号级别比栈顶级别高
                    } else {
                        // 如果当前符号级别比栈顶级别 等于或低于
                        while ((step.getLevel() <= sop[sop.length-1].getLevel())&&(sop.length != 0)) {
                            L.push(sop.pop());
                        }
                        sop.push(step);
                    }
                    break;
                default:
                    throw new Error("赋值失败");
            }
        }
        for (let index = 0; index < L.length; index++) {
            switch (L[index].getValue()) {
                case "+":
                case "-":
                case "*":
                case "/":
                    sop.push(this.calculate(L[index],sop.pop(),sop.pop()))
                    break;
                default:
                    sop.push(L[index]);
            }
        }
        return sop.pop();
    }

    setvariable(it){ // 设置变量
        while (it.hasNext()) {
            var step = it.next();
            if ((step.getValue() == "\0")||(step.getValue() == "\n")||(step.getValue() == "\r")||(step.getValue() == ";")) { break; }

            if (this.mempoint.hasOwnProperty(step.getValue())) { throw new Error("重复定义变量"); }
            if ((step.getType() == TokenType.VARIABLE)&&(it.next().getValue() == "=")) {
                this.mempoint[step.getValue()] = this.ReversePoland(it); // 在此处设置变量
                break;
            }
        }
    }
}

最后封装成简单的脚本解析器

class SimpleParser extends Block{
    constructor(){
        super();
        var that = this;
        this._dokeyword = {
            "var" : function (it) {
                that.setvariable(it)
            }
        };
    }

    parse(tokens) {
        const it = new characterIterator(tokens);
        while (it.hasNext()) {
            var step = it.next();
            if (step.getValue() == "\0") { break; }
            switch (step.getType()){
                case TokenType.KEYWORD:
                    this._dokeyword[step.getValue()](it);
                    break;
                case TokenType.VARIABLE:
                    // if(!this.mempoint.hasOwnProperty(step.getValue())) { throw new Error("未定义的变量或函数"); } // 判断变量是否重复定义
                    // if(this.mempoint[step.getValue()].isFunction()){
                    //     this.mempoint = this.mempoint[step.getValue()];
                    // } // 如果是函数
                    break;
            }
        }
        console.log(this.mem);
    }
}

暂时这个解析器还比较粗糙,接下来要设计虚拟机来执行这些脚本,所以暂时做到这个阶段,
下一篇会开始介绍虚拟机的开发。包括虚拟机的机器指令集,内存作用域。当打好这些基础做好之后,再继续回过头来完善这个脚本解析器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妇男主任

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值