设计脚本语言的语法解析器
- 概述
- 脚本源码语法预览
- 运行效果如下图
- 设计计算符号的优先级
- 定义一些关键词
- 生成一份关键词的map方便引用
- 枚举关键词的类型
- 错误异常的捕获
- 字符匹配
- 代码的字符转化成迭代器
- 关键词标记器
- 词法分析器
- 设计一个队列处理器
- 源代码字符串迭代器
- 代码的块级运行环境
- 脚本源码
- 运行效果如下图
- 上代码
- 最后封装成简单的脚本解析器
概述
在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种类型
- KEYWORD 关键词
- VARIABLE 变量
- STRING 字符串
- OPERATOR 操作符
- BRACKET 分段符
- INTEGER 整数
- FLOAT 浮点数
- BOOLEAN 布尔值
- OBJECT 对象
- FUNCTION 函数
- 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);
}
}
暂时这个解析器还比较粗糙,接下来要设计虚拟机来执行这些脚本,所以暂时做到这个阶段,
下一篇会开始介绍虚拟机的开发。包括虚拟机的机器指令集,内存作用域。当打好这些基础做好之后,再继续回过头来完善这个脚本解析器。