avg-storyscript
Version:
Build-in Story Script System for AVG.js.
157 lines (149 loc) • 3.95 kB
JavaScript
/**
* Copyright 2016 Icemic Jia <bingfeng.web@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
let GLOBAL = {},
SAVE = {},
SCOPES = [];
let CURRENTSCOPE = {};
function calculate(exp, node=0) {
switch (exp.type) {
case 'expression':
const value = exp.value;
return calc_expression(calculate(value.left), value.operator, calculate(value.right));
case 'variable':
return calc_variable(exp.value, exp.prefix, node);
case 'value':
return exp.value;
default: throw `Unrecognized type ${type}`;
}
}
function calc_expression(left, operator, right) {
switch (operator) {
case '&&': return left && right; break;
case '||': return left || right; break;
case '==': return left == right; break;
case '>=': return left >= right; break;
case '<=': return left <= right; break;
case '>': return left > right; break;
case '<': return left < right; break;
case '+': return left + right; break;
case '-': return left - right; break;
case '*': return left * right; break;
case '/': return left / right; break;
case '^': return Math.pow(left, right); break;
case '%': return left % right; break;
default: throw `Unrecognized operator ${operator}`;
}
}
function calc_variable(name, prefix, node) {
switch (prefix) {
case null: return findVariableValue(name, node); break;
case '$': return GLOBAL[name]; break;
case '%': return SAVE[name]; break;
default: throw `Unrecognized prefix ${prefix}`;
}
}
function findVariableValue(name, node=0) {
let ret = null;
const _SCOPES = [...SCOPES, CURRENTSCOPE];
for (let i = _SCOPES.length - 1 - node; i > -1; i--) {
const scope = _SCOPES[i];
ret = scope[name] || null;
if (ret) {
break;
}
}
return ret;
}
function assign(name, prefix, value, explicit) {
if (prefix) {
if (prefix === '$') {
GLOBAL[name] = value;
} else if (prefix === '%') {
SAVE[name] = value;
}
} else if (explicit) {
const scope = CURRENTSCOPE;
if (scope[name]) {
throw `Identifier '${name}' has already been declared`;
} else {
scope[name] = value;
}
} else {
let defined = false;
let scope = null;
const _SCOPES = [...SCOPES, CURRENTSCOPE];
for (let i = _SCOPES.length - 1; i > -1; i--) {
scope = _SCOPES[i];
if (scope.hasOwnProperty(name)) {
defined = true;
break;
}
}
if (defined) {
scope[name] = value;
} else {
throw `${name} is not defined`;
}
}
}
module.exports = {
load() {
GLOBAL = {};
SAVE = {};
SCOPES = [];
CURRENTSCOPE = {};
},
dump() {
return {
GLOBAL: GLOBAL,
SAVE: SAVE,
SCOPES: SCOPES,
CURRENTSCOPE: CURRENTSCOPE
}
},
getGlobalScope() {
return GLOBAL;
},
getSaveScope() {
return SAVE;
},
getScope(node) {
return [...SCOPES, CURRENTSCOPE][SCOPES.length - node]
},
setGlobalScope(scope) {
GLOBAL = scope;
},
setSaveScope(scope) {
SAVE = scope;
},
setScopes(scopes) {
SCOPES = SCOPES;
this.popScope();
},
pushScope(scope={}) {
SCOPES.push(CURRENTSCOPE);
CURRENTSCOPE = scope;
},
popScope() {
CURRENTSCOPE = SCOPES.pop();
},
calc(exp, node) {
return calculate(exp);
},
assign(name, prefix, right, explicit) {
return assign(name, prefix, calculate(right), explicit);
}
}