UNPKG

@gyro-lang/core

Version:

Fast, Performant and scalable programming language designed for string manipulation and deep recursion.

177 lines (158 loc) 4.05 kB
import { Environment } from "./environment.js"; function evaluate(exp, env: Environment) { switch (exp.type) { case "number": case "string": case "boolean": return exp.value; case "identifier": return env.get(exp.value); case "AssignmentExpression": if (exp.left.type != "identifier") { // We're not reassigning a value, check if a type has been annotated. if (exp.left.type == "TypeExpression") { // Expect a type to be assigned to the variable. => (varName: type) = value; return env.def( exp.left.left.value, evaluate(exp.right, env) ); } else { // We can't assign a value to someone whose type we don't know. throw new Error( "Cannot assign to " + JSON.stringify(exp.left) + "with missing type" ); } } // Expect that the user wishes the `any` type to be assigned automatically. return env.def(exp.left.value, evaluate(exp.right, env)); case "TypeExpression": return exp.value; case "BinaryExpression": return applyOperation( exp.operator, evaluate(exp.left, env), evaluate(exp.right, env) ); case "ForInStatement": return makeLoop(env, exp); case "FunctionDeclaration": return makeFunction(env, exp); case "if": var cond = evaluate(exp.cond, env); if (cond !== false) return evaluate(exp.then, env); return exp.else ? evaluate(exp.else, env) : false; case "Program": var val = false; exp.body.forEach(function (subExpression: {}) { val = evaluate(subExpression, env); }); return val; case "CallExpression": var func = evaluate(exp.callee, env); return func.apply( null, exp.args.map(function (arg) { return evaluate(arg, env); }) ); case "ObjectAccessor": return evaluate(exp.left, env)[evaluate(exp.right, env)]; case "ArrayExpression": // We allow deeply nested arrays, we must recurse to parse them return exp.elements.map(function (e) { return evaluate(e, env); }); default: throw new Error("I don't know how to evaluate " + exp.type); } } function applyOperation(op: string, a: any, b: any) { function num(x: any) { if (typeof x != "number") throw new Error("Expected number but got " + x); return x; } function div(x: number) { if (num(x) == 0) throw new Error("Divide by zero"); return x; } switch (op) { case "+": return num(a) + num(b); case "-": return num(a) - num(b); case "*": return num(a) * num(b); case "/": return num(a) / div(b); case "%": return num(a) % div(b); case "&&": return a !== false && b; case "||": return a !== false ? a : b; case "<": return num(a) < num(b); case ">": return num(a) > num(b); case "<=": return num(a) <= num(b); case ">=": return num(a) >= num(b); case "==": return a === b; case "!=": return a !== b; case "**": return Math.pow(a, b); } throw new Error("Can't apply operator " + op); } function makeLoop( env: Environment, exp: { type: string; left: any; right: any; body: any } ) { let localEnv = env.extend(); let control = exp.left.value; let array = evaluate(exp.right, env); if (typeof array === "number") { for (let i = 0; i < array; i++) { localEnv.def(control, i); evaluate(exp.body, localEnv); } } else if (Array.isArray(array)) { for (const x of array) { localEnv.def(control, x); evaluate(exp.body, localEnv); } } else { for (const key in array) { localEnv.def(control, key); evaluate(exp.body, localEnv); } } } function makeFunction(env: Environment, exp) { function lambda() { var names = exp.vars; var scope = env.extend(); for (var i = 0; i < names.length; ++i) { if (names[i].type == "TypeExpression") { scope.def( names[i].left.value, i < arguments.length ? arguments[i] : false ); } else { scope.def( names[i].value, i < arguments.length ? arguments[i] : false ); } } return evaluate(exp.body, scope); } return lambda; } export { evaluate, Environment };