onelang
Version:
OneLang transpiler framework core
248 lines • 10.3 kB
JavaScript
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class ExprLangError extends Error {
}
class JSModelHandler {
static methodCall(method, args, thisObj, model) {
if (typeof method !== "function")
throw new ExprLangError(`Tried to call a non-method value: '${method}' (${typeof method})`);
const result = method.apply(thisObj, args);
return result;
}
static memberAccess(obj, memberName, isProperty) {
if (typeof obj !== "object")
throw new ExprLangError(`Expected object for accessing member: (${typeof obj})`);
if (!(memberName in obj))
//throw new ExprLangError(`Member '${memberName}' not found in object '${obj.constructor.name}' (${typeof obj})`);
return null;
return obj[memberName];
}
methodCall(method, args, thisObj, model) {
return JSModelHandler.methodCall(method, args, thisObj, model);
}
memberAccess(obj, memberName, isProperty) {
return JSModelHandler.memberAccess(obj, memberName, isProperty);
}
}
exports.JSModelHandler = JSModelHandler;
class VariableSource {
constructor(name) {
this.name = name;
this.vars = {};
this.callbacks = {};
}
checkUnique(varName, allowOverwrite = false) {
if (!varName)
throw new ExprLangError("Variable name is missing!");
if (typeof varName !== "string")
throw new ExprLangError(`Expected string as variable name!`);
if (varName in this.callbacks)
throw new ExprLangError(`Callback was already set for variable '${varName}'`);
if (!allowOverwrite && varName in this.vars)
throw new ExprLangError(`Variable '${varName}' was already set`);
}
addCallback(varName, callback) {
this.checkUnique(varName);
this.callbacks[varName] = callback;
}
setVariable(varName, value, allowOverwrite = false) {
this.checkUnique(varName, allowOverwrite);
this.vars[varName] = value;
}
getVariable(varName) {
let result = null;
if (varName in this.vars) {
result = this.vars[varName];
}
else if (varName in this.callbacks) {
result = this.callbacks[varName]();
}
return result;
}
printAll() {
const result = Object.keys(this.vars).map(varName => `${varName}: ${this.vars[varName]}`)
.concat(Object.keys(this.callbacks).map(varName => `${varName}: ${this.callbacks[varName]()}`));
return result.map(x => `${x}\n`).join("");
}
static createSingle(varName, value, sourceName) {
const source = new VariableSource(sourceName || `var: ${varName}`);
source.setVariable(varName, value);
return source;
}
static fromObject(obj, sourceName) {
const source = new VariableSource(sourceName || `object`);
for (const key in obj)
source.setVariable(key, obj[key]);
return source;
}
}
exports.VariableSource = VariableSource;
class VariableContext {
constructor(sources = []) {
this.sources = sources;
}
inherit(...newSources) {
return new VariableContext([...newSources, ...this.sources]);
}
getVariable(varName) {
for (const source of this.sources) {
const result = source.getVariable(varName);
if (result !== null)
return result;
}
throw new ExprLangError(`Variable '${varName}' was not found in contexts: `
+ this.sources.map(x => `'${x.name}'`).join(", "));
}
printAll() {
const result = this.sources.map(src => `Source['${src.name}']:\n ${src.printAll().replace(/\n/g, "\n ")}`).join("\n");
return result;
}
}
exports.VariableContext = VariableContext;
class ExprLangVM {
constructor(modelHandler) {
this.modelHandler = modelHandler || new JSModelHandler();
}
evaluate(expr, vars) {
if (expr.kind === "literal") {
const litExpr = expr;
return litExpr.value;
}
else if (expr.kind === "identifier") {
const identifier = expr;
if (identifier.text === "true") {
return true;
}
else if (identifier.text === "false") {
return false;
}
else if (identifier.text === "null") {
return null;
}
else {
if (!vars)
throw new ExprLangError(`Variable context was not set!`);
const result = vars.getVariable(identifier.text);
return result;
}
}
else if (expr.kind === "unary") {
const unaryExpr = expr;
const exprValue = this.evaluate(unaryExpr.expr, vars);
if (unaryExpr.op === "!") {
return !exprValue;
}
else if (unaryExpr.op === "+") {
return +exprValue;
}
else if (unaryExpr.op === "-") {
return -exprValue;
}
else
throw new ExprLangError(`Unexpected unary operator: '${unaryExpr.op}'`);
}
else if (expr.kind === "binary") {
const binaryExpr = expr;
const leftValue = this.evaluate(binaryExpr.left, vars);
const rightValue = this.evaluate(binaryExpr.right, vars);
if (binaryExpr.op === "+") {
return leftValue + rightValue;
}
else if (binaryExpr.op === "-") {
return leftValue - rightValue;
}
else if (binaryExpr.op === "*") {
return leftValue * rightValue;
}
else if (binaryExpr.op === "/") {
return leftValue / rightValue;
}
else if (binaryExpr.op === "<<") {
return leftValue << rightValue;
}
else if (binaryExpr.op === ">>") {
return leftValue >> rightValue;
}
else if (binaryExpr.op === "==") {
return leftValue === rightValue;
}
else if (binaryExpr.op === "!=") {
return leftValue !== rightValue;
}
else if (binaryExpr.op === ">=") {
return leftValue >= rightValue;
}
else if (binaryExpr.op === "<=") {
return leftValue <= rightValue;
}
else if (binaryExpr.op === "<") {
return leftValue < rightValue;
}
else if (binaryExpr.op === ">") {
return leftValue > rightValue;
}
else if (binaryExpr.op === "&&") {
return leftValue && rightValue;
}
else if (binaryExpr.op === "||") {
return leftValue || rightValue;
}
else
throw new ExprLangError(`Unexpected binary operator: '${binaryExpr.op}'`);
}
else if (expr.kind === "parenthesized") {
const parenExpr = expr;
const exprValue = this.evaluate(parenExpr, vars);
return exprValue;
}
else if (expr.kind === "conditional") {
const condExpr = expr;
const condValue = this.evaluate(condExpr.condition, vars);
const result = this.evaluate(condValue ? condExpr.whenTrue : condExpr.whenFalse, vars);
return result;
}
else if (expr.kind === "call") {
const callExpr = expr;
const method = this.evaluate(callExpr.method, vars);
let thisObj;
if (callExpr.method.kind === "propertyAccess") {
const thisObjExpr = callExpr.method.object;
thisObj = this.evaluate(thisObjExpr, vars);
}
else {
thisObj = null;
}
const args = callExpr.arguments.map(arg => this.evaluate(arg, vars));
const result = this.modelHandler.methodCall(method, args, thisObj, vars);
return result;
}
else if (expr.kind === "propertyAccess") {
const propAccExpr = expr;
const object = this.evaluate(propAccExpr.object, vars);
const result = this.modelHandler.memberAccess(object, propAccExpr.propertyName, true);
return result;
}
else if (expr.kind === "elementAccess") {
const elemAccExpr = expr;
const object = this.evaluate(elemAccExpr.object, vars);
const memberName = this.evaluate(elemAccExpr.elementExpr, vars);
const result = this.modelHandler.memberAccess(object, memberName, false);
return result;
}
else {
throw new ExprLangError(`Unknown expression kind: '${expr.kind}'`);
}
}
}
exports.ExprLangVM = ExprLangVM;
});
//# sourceMappingURL=ExprLangVM.js.map