UNPKG

bds.js

Version:

A simple interpreter written to simulate and run BDScript Language in JavaScript

176 lines (175 loc) 7.15 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Runtime = void 0; const path_1 = __importDefault(require("path")); const Context_1 = require("./Context"); const Environment_1 = require("./Environment"); const Evaluator_1 = require("./Evaluator"); const Lexer_1 = require("./Lexer"); const Parser_1 = require("./Parser"); const RuntimeBag_1 = require("./RuntimeBag"); class Runtime { constructor() { this.global = new Environment_1.Environment(); this.contexts = new Map(); this.evaluator = Evaluator_1.Evaluator.singleton; this.options = { alwaysStrict: false, trimOutput: true }; this._prepareGlobal(); } runInput(fileName, input) { const ast = new Parser_1.Parser().parseToAst( /* Tokens */ new Lexer_1.Lexer(input).main(), this); const ctx = this.prepareContext(fileName); return this.evaluator.evaluate(ast, ctx); } prepareContext(fileName) { let env = new Environment_1.Environment(this.global); let bag = new RuntimeBag_1.RuntimeBag(); let ctx = new Context_1.Context(fileName, bag, env, this); this.contexts.set(fileName, ctx); return ctx; } getContext(fileName) { return this.contexts.get(fileName); } _prepareGlobal() { this.global.set("$print", async (ctx) => { console.log(...(await ctx.evaluateArgs(ctx.getArgs()))); return ""; }); this.global.set("$async", (ctx) => { new Promise((res, rej) => { ctx.evaluateArgs(ctx.getArgs()).then(res).catch(rej); }); return ""; }); this.global.set("$wait", async (ctx) => { const [time = 1] = await ctx.evaluateArgs(ctx.getArgs(0, 1)); return new Promise((res) => setTimeout(() => res(""), time * 1000)); }); this.global.set("$filename", (ctx) => ctx.fileName); this.global.set("$dirname", (ctx) => path_1.default.join(ctx.fileName, "..")); this.global.set("$random", (ctx) => Math.random()); this.global.set("$if", async (ctx) => { ctx.argsCheck(2); // Requires 2 arguments const [condition, ifTrue, ifFalse] = ctx.getArgs(0, 3); const op_idx = condition.child.findIndex((v) => v.type === "operator"); const op = condition.child[op_idx]; // if (!op) return Boolean(await ctx.evaluateArgs([condition])) ? ctx.evaluateArgs([ifTrue]) : ctx.evaluateArgs([ifFalse]); const [cond_a, cond_b] = await ctx.evaluateArgs([ { type: "argument", child: condition.child.slice(0, op_idx) }, { type: "argument", child: condition.child.slice(op_idx + 1) }, ]); let res; switch (op.value) { case "==": res = cond_a === cond_b; break; case "!=": res = cond_a !== cond_b; break; case ">=": res = cond_a >= cond_b; break; case ">": res = cond_a > cond_b; break; case "<=": res = cond_a <= cond_b; break; case "<": res = cond_a < cond_b; break; } if (res === true) return ctx.evaluateArgs([ifTrue]); if (res === false) return ctx.evaluateArgs([ifFalse]); throw new Error("Invalid operator!"); }); this.global.set('$safejs', async (ctx) => { const evalCode = await ctx.evaluateArgs(ctx.getArgs(0)); const mask = { ctx, runtime: this, evaluator: this.evaluator, global }; const res = (new Function("with(this) {'use strict';\n" + evalCode + "}").call(mask)); return res; }); this.global.set("$def", async (ctx) => { ctx.argsCheck(3); const [fnarg, mapReturn, body] = ctx.getArgs(); let fnName; if (fnarg.child[0]?.type === "call" || fnarg.child[0]?.type === "string") { fnName = (fnarg.child[0].type === "string" ? "$" : "") + fnarg.child[0].value; } else throw new Error("FnName must be typeof string or call!"); ctx.env.set(fnName, (ctx) => { return this.evaluator.visitArgument(body, ctx, mapReturn.child[0]?.type === "string" && ["yes", "true"].includes(mapReturn.child[0]?.value?.toLowerCase?.())); }); return ''; }); this.global.set("$max", async (ctx) => { const nums = await ctx.evaluateArgs(ctx.getArgs()); return Math.max(...nums); }); this.global.set("$min", async (ctx) => { const nums = await ctx.evaluateArgs(ctx.getArgs()); return Math.min(...nums); }); this.global.set("$round", async (ctx) => { return Math.round(await ctx.evaluateArgs(ctx.getArgs(0, 1))[0]); }); this.global.set("$floor", async (ctx) => { return Math.floor(await ctx.evaluateArgs(ctx.getArgs(0, 1))[0]); }); this.global.set("$trunc", async (ctx) => { return Math.trunc(await ctx.evaluateArgs(ctx.getArgs(0, 1))[0]); }); this.global.set("$sum", async (ctx) => { const nums = await ctx.evaluateArgs(ctx.getArgs()); return nums.reduce((prev, v) => { prev += v; return prev; }, 0); }); this.global.set("$sub", async (ctx) => { const nums = await ctx.evaluateArgs(ctx.getArgs()); return nums.reduce((prev, v) => { prev -= v; return prev; }, 0); }); this.global.set("$multi", async (ctx) => { const nums = await ctx.evaluateArgs(ctx.getArgs()); return nums.reduce((prev, v) => { prev *= v; return prev; }, 0); }); this.global.set("$div", async (ctx) => { const nums = await ctx.evaluateArgs(ctx.getArgs()); return nums.reduce((prev, v) => { prev /= v; return prev; }, 0); }); this.global.set("$modulo", async (ctx) => { const nums = await ctx.evaluateArgs(ctx.getArgs(0, 2)); return nums.shift() % nums.shift(); }); } } exports.Runtime = Runtime;