UNPKG

@tsukiroku/tiny

Version:
922 lines (921 loc) 44.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UNDEFINED = exports.NULL = void 0; const node_fs_1 = require("node:fs"); const Tiny = __importStar(require("../../index")); exports.NULL = { kind: 309 /* Tiny.ObjectKind.NULL */ }; exports.UNDEFINED = { kind: 310 /* Tiny.ObjectKind.UNDEFINED */ }; class Evaluator { program; enviroment; option; messages; constructor(program, enviroment, option) { this.program = program; this.enviroment = enviroment; this.option = option; this.messages = Tiny.localization(option); } eval() { if (this.program.errors.length > 0) return null; const result = this.evalStatements(this.program.statements, this.enviroment); if (result?.kind === 308 /* Tiny.ObjectKind.ERROR */) { Tiny.printError(result, this.option.filename, this.option.stdio.stderr, this.option); return null; } return result; } evalStatements(statements, enviroment) { let results = []; for (const statement of statements) { const result = this.evalStatement(statement, enviroment); results.push(result); if (result) { if (result.kind === 307 /* Tiny.ObjectKind.RETURN_VALUE */) return result.value; if (result.kind === 308 /* Tiny.ObjectKind.ERROR */) return result; } } if (results.length === 0) return exports.UNDEFINED; return results[results.length - 1]; } evalBlockStatements(statement, enviroment) { const { statements, returnFinal } = statement; let results = []; for (const statement of statements) { const result = this.evalStatement(statement, enviroment); results.push(result); if (result) { if (result.kind === 307 /* Tiny.ObjectKind.RETURN_VALUE */) return result; if (result.kind === 308 /* Tiny.ObjectKind.ERROR */) return result; } } if (results.length === 0) return exports.UNDEFINED; if (returnFinal) return results[results.length - 1]; else return exports.UNDEFINED; } evalStatement(statement, enviroment) { switch (statement.kind) { case Tiny.NodeKind.ExpressionStatement: return this.evalExpression(statement.expression, enviroment); case Tiny.NodeKind.LetStatement: { const value = this.evalExpression(statement.value, enviroment); if (value?.kind === 308 /* Tiny.ObjectKind.ERROR */) return value; const name = statement.ident.value; if (statement.ident && name !== '_') enviroment.set(name, value); return null; } case Tiny.NodeKind.ReturnStatement: { const expression = this.evalExpression(statement.value, enviroment); if (expression) return { value: expression, kind: 307 /* Tiny.ObjectKind.RETURN_VALUE */, }; return exports.NULL; } case Tiny.NodeKind.WhileStatement: { let condition = this.evalExpression(statement.condition, enviroment); if (condition?.kind === 308 /* Tiny.ObjectKind.ERROR */) return condition; const resultExpr = []; while (this.isTruthy(condition)) { const result = this.evalExpression(statement.body, enviroment); if (result?.kind === 308 /* Tiny.ObjectKind.ERROR */) return result; condition = this.evalExpression(statement.condition, enviroment); if (condition?.kind === 308 /* Tiny.ObjectKind.ERROR */) return condition; resultExpr.push(result); } return exports.NULL; } case Tiny.NodeKind.DecoratorStatement: { const decorator = statement; const value = this.evalExpression(decorator.value, enviroment); if (value?.kind === 308 /* Tiny.ObjectKind.ERROR */) return value; const func = this.evalFunction(decorator.function, enviroment, value); if (func?.kind !== 305 /* Tiny.ObjectKind.FUNCTION */) return null; return func; } default: return exports.NULL; } } evalExpression(expression, enviroment) { if (!expression) return null; switch (expression.kind) { case Tiny.ExpressionKind.Literal: return this.evalLiteral(expression); case Tiny.ExpressionKind.Prefix: { return this.evalPrefix(expression.operator, expression.right, enviroment, { line: expression.line, column: expression.column, }); } case Tiny.ExpressionKind.Infix: const infix = expression; switch (infix.operator) { case Tiny.TokenType.ASSIGN: return this.evalIdentInfix(infix.operator, infix.left, infix.right, enviroment, { line: infix.line, column: infix.column, }); case Tiny.TokenType.ELEMENT: return this.evalElementInfix(infix.left, infix.right, enviroment, { line: infix.line, column: infix.column, }); } return this.evalInfix(infix.operator, infix.left, infix.right, enviroment, { line: infix.line, column: infix.column, }); case Tiny.ExpressionKind.Block: return this.evalBlockStatements(expression, enviroment); case Tiny.ExpressionKind.If: { return this.evalIfExpression(expression.condition, expression.consequence, expression.alternative, enviroment); } case Tiny.ExpressionKind.Ident: return this.evalIdent(expression.value, enviroment, { line: expression.line, column: expression.column, }); case Tiny.ExpressionKind.Function: return this.evalFunction(expression, enviroment); case Tiny.ExpressionKind.Call: return this.evalCallExpression(expression, enviroment); case Tiny.ExpressionKind.Array: { const args = this.evalExpressions(expression.elements, enviroment); if (args.length == 1 && args[0]?.kind === 308 /* Tiny.ObjectKind.ERROR */) return args[0]; return { value: args, kind: 303 /* Tiny.ObjectKind.ARRAY */, }; } case Tiny.ExpressionKind.Index: { const resultExpr = this.evalExpression(expression.left, enviroment); if (!resultExpr) return null; if (resultExpr.kind === 308 /* Tiny.ObjectKind.ERROR */) return exports.NULL; const index = this.evalExpression(expression.index, enviroment); if (!index) return null; if (index.kind === 308 /* Tiny.ObjectKind.ERROR */) return exports.NULL; return this.evalIndex(resultExpr, index, { line: expression.line, column: expression.column, }); } case Tiny.ExpressionKind.Object: return this.evalObjectParameters(expression.pairs, enviroment); case Tiny.ExpressionKind.Typeof: { const value = this.evalExpression(expression.value, enviroment); if (value?.kind === 308 /* Tiny.ObjectKind.ERROR */) return value; if (!value) return exports.NULL; return { kind: 301 /* Tiny.ObjectKind.STRING */, value: Tiny.objectKindStringify(value.kind), }; } case Tiny.ExpressionKind.Throw: { const message = this.evalExpression(expression.message, enviroment); if (message?.kind === 308 /* Tiny.ObjectKind.ERROR */) return message; if (!message) return exports.NULL; return Tiny.error(Tiny.objectStringify(message), expression.line, expression.column); } case Tiny.ExpressionKind.Delete: { if (expression.value?.kind !== Tiny.ExpressionKind.Ident) return Tiny.error(this.messages.runtimeError.deleteRequiresIdentifier, expression.line, expression.column); enviroment.delete(expression.value.value); return exports.NULL; } case Tiny.ExpressionKind.Use: { const path = this.evalExpression(expression.path, enviroment); if (path?.kind === 308 /* Tiny.ObjectKind.ERROR */) return path; if (path?.kind !== 301 /* Tiny.ObjectKind.STRING */) return Tiny.error(this.messages.runtimeError.useRequiresString, expression.line, expression.column); return this.importEnv(path.value, enviroment, this, { line: expression.line, column: expression.column, }); } case Tiny.ExpressionKind.Void: { const value = this.evalExpression(expression.value, enviroment); if (value?.kind === 308 /* Tiny.ObjectKind.ERROR */) return value; return exports.UNDEFINED; } case Tiny.ExpressionKind.Expr: { const value = this.evalExpression(expression.value, enviroment); if (value?.kind === 308 /* Tiny.ObjectKind.ERROR */) return { kind: 304 /* Tiny.ObjectKind.OBJECT */, pairs: new Map([ [ { kind: 301 /* Tiny.ObjectKind.STRING */, value: 'message', }, { kind: 301 /* Tiny.ObjectKind.STRING */, value: value.message, }, ], [ { kind: 301 /* Tiny.ObjectKind.STRING */, value: 'line', }, { kind: 300 /* Tiny.ObjectKind.NUMBER */, value: value.line, }, ], [ { kind: 301 /* Tiny.ObjectKind.STRING */, value: 'column', }, { kind: 300 /* Tiny.ObjectKind.NUMBER */, value: value.column, }, ], [ { kind: 301 /* Tiny.ObjectKind.STRING */, value: 'filename', }, { kind: 301 /* Tiny.ObjectKind.STRING */, value: this.option.filename, }, ], [ { kind: 301 /* Tiny.ObjectKind.STRING */, value: 'error', }, { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: true, }, ], ]), }; return value; } default: return null; } } importEnv(path, enviroment, evaluator, position) { try { if (!path.endsWith('.tiny')) path += '.tiny'; const parsed = new Tiny.Parser(new Tiny.Lexer((0, node_fs_1.readFileSync)(`${evaluator.option.root}${path}`, 'utf8'), { ...evaluator.option, stderr: evaluator.option.stdio.stderr, }, path), evaluator.option).parseProgram(); parsed.errors.forEach((error) => Tiny.printError(error, path, evaluator.option.stdio.stderr, { ...evaluator.option, })); return new Tiny.Evaluator(parsed, enviroment, { ...evaluator.option, filename: path, }).eval(); } catch (e) { return { kind: 308 /* Tiny.ObjectKind.ERROR */, message: `Could not import file: ${evaluator.option.root}${path}`, ...position, }; } } evalFunction(expression, enviroment, decorator) { const functionObject = { function: expression.function ?? null, parameters: expression.parameters, body: expression.body, enviroment: enviroment, option: this.option, decorator: decorator, kind: 305 /* Tiny.ObjectKind.FUNCTION */, }; const name = functionObject.function ? functionObject.function.value ?? null : null; if (expression.function && name && name !== '_') enviroment.set(name, functionObject); return functionObject; } evalCallExpression(expression, enviroment) { const functionObject = this.evalExpression(expression.function, enviroment); if (functionObject?.kind === 308 /* Tiny.ObjectKind.ERROR */) return functionObject; const args = this.evalExpressions(expression.parameters, enviroment); if (args.length == 1 && args[0]?.kind === 308 /* Tiny.ObjectKind.ERROR */) return args[0]; return this.applyFunction(functionObject, expression.function.value, args, enviroment, { line: expression.line, column: expression.column, }, { kind: 304 /* Tiny.ObjectKind.OBJECT */, pairs: new Map([ [ { kind: 301 /* Tiny.ObjectKind.STRING */, value: 'arguments', }, { kind: 303 /* Tiny.ObjectKind.ARRAY */, value: args, }, ], [ { kind: 301 /* Tiny.ObjectKind.STRING */, value: 'decorator', }, functionObject.decorator ?? exports.NULL, ], ]), }); } evalObjectParameters(parameters, enviroment) { const object = { kind: 304 /* Tiny.ObjectKind.OBJECT */, pairs: new Map(), }; parameters.forEach((arg) => { const key = this.evalExpression(arg.key, enviroment); if (!key) return; if (key.kind === 308 /* Tiny.ObjectKind.ERROR */) return key; const value = this.evalExpression(arg.value, enviroment); if (!value) return; if (value.kind === 308 /* Tiny.ObjectKind.ERROR */) return key; if (key.kind !== 301 /* Tiny.ObjectKind.STRING */ && key.kind !== 300 /* Tiny.ObjectKind.NUMBER */) return; if (key) object.pairs.set(key, value); }); return object; } evalExpressions(expression, enviroment) { return expression.map((expression) => this.evalExpression(expression, enviroment)); } getDecorator(key, func) { if (!func.decorator) return null; return new Map([...func.decorator.pairs].map(([key, value]) => [key.value, value])).get(key) ?? exports.NULL; } applyFunction(functionObject, name, parameters, enviroment, position, thisObject) { if (functionObject?.kind === 305 /* Tiny.ObjectKind.FUNCTION */) { if (!this.getDecorator('skipCheckArguments', functionObject) && functionObject.parameters.length !== parameters.length) return Tiny.error(Tiny.errorFormatter(this.messages.runtimeError.invalidArgument, name ?? '<Anonymous>', functionObject.parameters.length, parameters.length), position.line, position.column); const result = this.evalExpression(functionObject.body, this.extendFunctionEnv(functionObject, parameters, enviroment, thisObject, this.getDecorator('noCapture', functionObject)?.value ?? false)); if (result?.kind === 307 /* Tiny.ObjectKind.RETURN_VALUE */) return result.value; if (result?.kind === 308 /* Tiny.ObjectKind.ERROR */) return result; return result; } if (functionObject?.kind === 306 /* Tiny.ObjectKind.BUILTIN */) return functionObject.func(parameters, enviroment, this, position); return Tiny.error(Tiny.errorFormatter(this.messages.runtimeError.invalidFunction, name ?? '<unknown>'), position.line, position.column); } extendFunctionEnv(functionObject, parameters, enviroment, thisObject, noCapture) { if (functionObject?.kind === 305 /* Tiny.ObjectKind.FUNCTION */) { let extendEnviroment = new Tiny.Enviroment(enviroment); if (!noCapture) extendEnviroment.outer = functionObject.enviroment; functionObject.parameters.forEach((param, i) => { if (param?.kind === Tiny.ExpressionKind.Ident) extendEnviroment.set(param.value, parameters[i]); }); extendEnviroment.set('this', thisObject); return extendEnviroment; } return new Tiny.Enviroment(); } evalIdent(name, enviroment, position) { if (enviroment.get(name)) return enviroment.get(name); const builtin = Tiny.builtinFunction(name); if (!builtin) return Tiny.error(Tiny.errorFormatter(this.messages.runtimeError.identifierNotDefined_2, name), position.line, position.column); return builtin; } evalLiteral(literal) { switch (literal.value.kind) { case Tiny.LiteralKind.Number: return { kind: 300 /* Tiny.ObjectKind.NUMBER */, value: literal.value.value, }; case Tiny.LiteralKind.String: return { kind: 301 /* Tiny.ObjectKind.STRING */, value: literal.value.value, }; case Tiny.LiteralKind.Boolean: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: literal.value.value, }; default: return exports.NULL; } } evalPrefix(operator, right, enviroment, position) { const expression = this.evalExpression(right, enviroment); if (expression?.kind === 308 /* Tiny.ObjectKind.ERROR */) return expression; switch (operator) { case Tiny.TokenType.MINUS: return this.evalMinus(expression, position); case Tiny.TokenType.BANG: return this.evalBang(expression); default: return exports.NULL; } } evalInfix(operator, leftOperand, rightOperand, enviroment, position) { const left = this.evalExpression(leftOperand, enviroment); if (left?.kind === 308 /* Tiny.ObjectKind.ERROR */) return left; const right = this.evalExpression(rightOperand, enviroment); if (right?.kind === 308 /* Tiny.ObjectKind.ERROR */) return right; if (operator === Tiny.TokenType.NULLISH) return left?.kind === 309 /* Tiny.ObjectKind.NULL */ ? right : left; switch (left?.kind) { case 300 /* Tiny.ObjectKind.NUMBER */: return this.evalNumberInfix(operator, left, right, position); case 301 /* Tiny.ObjectKind.STRING */: return this.evalStringInfix(operator, left, right, position); case 302 /* Tiny.ObjectKind.BOOLEAN */: return this.evalBooleanInfix(operator, left, right, position); case 304 /* Tiny.ObjectKind.OBJECT */: return this.evalObjectInfix(operator, left, right, position); case 303 /* Tiny.ObjectKind.ARRAY */: return this.evalArrayInfix(operator, left, right, position); default: return Tiny.error(Tiny.errorFormatter(this.messages.runtimeError.typeMismatch_2, left?.kind, right?.kind), position.line, position.column); } } typeMissmatch(left, right, position) { return Tiny.error(Tiny.errorFormatter(this.messages.runtimeError.typeMismatch_2, Tiny.objectKindStringify(left?.kind ?? 309 /* Tiny.ObjectKind.NULL */), Tiny.objectKindStringify(right?.kind ?? 309 /* Tiny.ObjectKind.NULL */)), position.line, position.column); } evalNumberInfix(operator, leftOperand, rightOperand, position) { if (operator === Tiny.TokenType.IN) return this.evalInOperator(leftOperand, rightOperand, position); if (leftOperand?.kind !== 300 /* Tiny.ObjectKind.NUMBER */ || rightOperand?.kind !== 300 /* Tiny.ObjectKind.NUMBER */) return this.typeMissmatch(leftOperand, rightOperand, position); switch (operator) { case Tiny.TokenType.PLUS: return { kind: 300 /* Tiny.ObjectKind.NUMBER */, value: leftOperand.value + rightOperand.value, }; case Tiny.TokenType.MINUS: return { kind: 300 /* Tiny.ObjectKind.NUMBER */, value: leftOperand.value - rightOperand.value, }; case Tiny.TokenType.SLASH: return { kind: 300 /* Tiny.ObjectKind.NUMBER */, value: leftOperand.value / rightOperand.value, }; case Tiny.TokenType.ASTERISK: return { kind: 300 /* Tiny.ObjectKind.NUMBER */, value: leftOperand.value * rightOperand.value, }; case Tiny.TokenType.PERCENT: return { kind: 300 /* Tiny.ObjectKind.NUMBER */, value: leftOperand.value % rightOperand.value, }; case Tiny.TokenType.EQUAL: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: leftOperand.value === rightOperand.value, }; case Tiny.TokenType.NOT_EQUAL: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: leftOperand.value !== rightOperand.value, }; case Tiny.TokenType.GT: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: leftOperand.value > rightOperand.value, }; case Tiny.TokenType.LT: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: leftOperand.value < rightOperand.value, }; case Tiny.TokenType.GTE: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: leftOperand.value >= rightOperand.value, }; case Tiny.TokenType.LTE: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: leftOperand.value <= rightOperand.value, }; default: return null; } } evalBooleanInfix(operator, leftOperand, rightOperand, position) { if (operator === Tiny.TokenType.IN) return this.evalInOperator(leftOperand, rightOperand, position); if (leftOperand?.kind !== 302 /* Tiny.ObjectKind.BOOLEAN */ || rightOperand?.kind !== 302 /* Tiny.ObjectKind.BOOLEAN */) return this.typeMissmatch(leftOperand, rightOperand, position); switch (operator) { case Tiny.TokenType.EQUAL: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: leftOperand.value === rightOperand.value, }; case Tiny.TokenType.NOT_EQUAL: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: leftOperand.value !== rightOperand.value, }; case Tiny.TokenType.AND: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: leftOperand.value && rightOperand.value, }; case Tiny.TokenType.OR: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: leftOperand.value || rightOperand.value, }; default: return null; } } evalStringInfix(operator, leftOperand, rightOperand, position) { if (operator === Tiny.TokenType.IN) return this.evalInOperator(leftOperand, rightOperand, position); if (leftOperand?.kind !== 301 /* Tiny.ObjectKind.STRING */ || rightOperand?.kind !== 301 /* Tiny.ObjectKind.STRING */) return this.typeMissmatch(leftOperand, rightOperand, position); switch (operator) { case Tiny.TokenType.PLUS: return { kind: 301 /* Tiny.ObjectKind.STRING */, value: `${leftOperand.value}${rightOperand.value}`, }; case Tiny.TokenType.EQUAL: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: leftOperand.value === rightOperand.value, }; case Tiny.TokenType.NOT_EQUAL: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: leftOperand.value !== rightOperand.value, }; default: return null; } } evalObjectInfix(operator, leftOperand, rightOperand, position) { switch (operator) { case Tiny.TokenType.IN: return this.evalInOperator(leftOperand, rightOperand, position); } if (leftOperand?.kind !== 304 /* Tiny.ObjectKind.OBJECT */ || rightOperand?.kind !== 304 /* Tiny.ObjectKind.OBJECT */) return Tiny.error(Tiny.errorFormatter(this.messages.runtimeError.typeMismatch_2, Tiny.objectKindStringify(leftOperand?.kind ?? 309 /* Tiny.ObjectKind.NULL */), Tiny.objectKindStringify(rightOperand?.kind ?? 309 /* Tiny.ObjectKind.NULL */)), position.line, position.column); switch (operator) { case Tiny.TokenType.EQUAL: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: JSON.stringify(leftOperand.pairs) === JSON.stringify(rightOperand.pairs), }; case Tiny.TokenType.NOT_EQUAL: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: JSON.stringify(leftOperand.pairs) !== JSON.stringify(rightOperand.pairs), }; case Tiny.TokenType.PLUS: return { kind: 304 /* Tiny.ObjectKind.OBJECT */, pairs: new Map([ ...[...leftOperand.pairs.entries()].filter(([k]) => !new Map([...rightOperand.pairs.entries()].map(([k, v]) => [JSON.stringify(k), v])).has(JSON.stringify(k))), ...rightOperand.pairs, ]), }; default: return null; } } evalArrayInfix(operator, leftOperand, rightOperand, position) { switch (operator) { case Tiny.TokenType.IN: return this.evalInOperator(leftOperand, rightOperand, position); } if (leftOperand?.kind !== 303 /* Tiny.ObjectKind.ARRAY */ || rightOperand?.kind !== 303 /* Tiny.ObjectKind.ARRAY */) return Tiny.error(Tiny.errorFormatter(this.messages.runtimeError.typeMismatch_2, Tiny.objectKindStringify(leftOperand?.kind ?? 309 /* Tiny.ObjectKind.NULL */), Tiny.objectKindStringify(rightOperand?.kind ?? 309 /* Tiny.ObjectKind.NULL */)), position.line, position.column); switch (operator) { case Tiny.TokenType.PLUS: return { kind: 303 /* Tiny.ObjectKind.ARRAY */, value: [...leftOperand.value, ...rightOperand.value], }; case Tiny.TokenType.EQUAL: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: JSON.stringify(leftOperand.value) === JSON.stringify(rightOperand.value), }; case Tiny.TokenType.NOT_EQUAL: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: JSON.stringify(leftOperand.value) !== JSON.stringify(rightOperand.value), }; default: return null; } } evalIdentInfix(operator, leftOperand, rightOperand, enviroment, position) { if (operator === Tiny.TokenType.ASSIGN) { const right = this.evalExpression(rightOperand, enviroment); if (right?.kind === 308 /* Tiny.ObjectKind.ERROR */) return right; if (leftOperand?.kind !== Tiny.ExpressionKind.Ident && leftOperand?.kind !== Tiny.ExpressionKind.Index) return Tiny.error(this.messages.runtimeError.typeMismatch_1, position.line, position.column); switch (leftOperand.kind) { case Tiny.ExpressionKind.Ident: { if (!enviroment.get(leftOperand.value)) return Tiny.error(Tiny.errorFormatter(this.messages.runtimeError.identifierNotDefined_1, leftOperand.value), position.line, position.column); enviroment.update(leftOperand.value, right); return right; } case Tiny.ExpressionKind.Index: { const index = leftOperand.index; const left = this.evalExpression(leftOperand.left, enviroment); if (left?.kind === 308 /* Tiny.ObjectKind.ERROR */) return left; if (left?.kind !== 303 /* Tiny.ObjectKind.ARRAY */ && left?.kind !== 304 /* Tiny.ObjectKind.OBJECT */) return Tiny.error(Tiny.errorFormatter(this.messages.runtimeError.typeMismatch_2, Tiny.objectKindStringify(left?.kind ?? 309 /* Tiny.ObjectKind.NULL */), Tiny.objectKindStringify(303 /* Tiny.ObjectKind.ARRAY */)), position.line, position.column); if (left?.kind === 303 /* Tiny.ObjectKind.ARRAY */) { const resultIdx = this.evalExpression(index, enviroment); if (resultIdx?.kind === 308 /* Tiny.ObjectKind.ERROR */) return resultIdx; if (resultIdx?.kind !== 300 /* Tiny.ObjectKind.NUMBER */) return Tiny.error(this.messages.runtimeError.typeMismatch_1, position.line, position.column); const value = this.evalExpression(rightOperand, enviroment); if (value?.kind === 308 /* Tiny.ObjectKind.ERROR */) return value; if (value?.kind !== 300 /* Tiny.ObjectKind.NUMBER */) return Tiny.error(this.messages.runtimeError.typeMismatch_2, position.line, position.column); left.value[resultIdx.value] = value; return value; } else { const resultIdx = this.evalExpression(index, enviroment); if (resultIdx?.kind === 308 /* Tiny.ObjectKind.ERROR */) return resultIdx; if (resultIdx?.kind !== 301 /* Tiny.ObjectKind.STRING */ && resultIdx?.kind !== 300 /* Tiny.ObjectKind.NUMBER */) return Tiny.error(this.messages.runtimeError.typeMismatch_1, position.line, position.column); const value = this.evalExpression(rightOperand, enviroment); if (value?.kind === 308 /* Tiny.ObjectKind.ERROR */) return value; left.pairs = new Map(Array.from(new Map([...new Map([...left.pairs].map(([k, v]) => [k.value, v])), [resultIdx.value, value]]).entries()).map(([k, v]) => [ typeof k === 'string' ? { value: k, kind: 301 /* Tiny.ObjectKind.STRING */, } : { value: k, kind: 300 /* Tiny.ObjectKind.NUMBER */, }, v, ])); return value; } } } } return null; } evalElementInfix(leftOperand, rightOperand, enviroment, position) { const left = this.evalExpression(leftOperand, enviroment); if (left?.kind === 308 /* Tiny.ObjectKind.ERROR */) return left; if (left?.kind !== 304 /* Tiny.ObjectKind.OBJECT */ && left?.kind !== 303 /* Tiny.ObjectKind.ARRAY */) return null; let right = null; if (rightOperand?.kind === Tiny.ExpressionKind.Ident) right = { kind: 301 /* Tiny.ObjectKind.STRING */, value: rightOperand.value, }; else if (rightOperand?.kind === Tiny.ExpressionKind.Call) right = rightOperand; else right = this.evalExpression(rightOperand, enviroment); if (right?.kind === 308 /* Tiny.ObjectKind.ERROR */) return right; if (left.kind === 303 /* Tiny.ObjectKind.ARRAY */) if (right?.kind === 300 /* Tiny.ObjectKind.NUMBER */) return this.evalIndex(left, right, position); else exports.NULL; if (right?.kind === 300 /* Tiny.ObjectKind.NUMBER */ || right?.kind === 301 /* Tiny.ObjectKind.STRING */) { return new Map([...left.pairs].map(([key, value]) => [key.value, value])).get(right.value) ?? exports.UNDEFINED; } else if (right?.kind === Tiny.ExpressionKind.Call) { const expression = new Map([...left.pairs].map(([key, value]) => [key.value, value])).get(right.function.value) ?? exports.UNDEFINED; if (expression?.kind === 308 /* Tiny.ObjectKind.ERROR */) return expression; if (expression?.kind !== 305 /* Tiny.ObjectKind.FUNCTION */) return Tiny.error(Tiny.errorFormatter(this.messages.runtimeError.invalidFunction, right.function.value ?? '<unknown>'), position.line, position.column); const callResult = this.evalCallExpression({ kind: Tiny.ExpressionKind.Call, function: { kind: Tiny.ExpressionKind.Function, function: expression.function, parameters: expression.parameters, body: expression.body, ...position, }, parameters: right.parameters, ...position, }, enviroment); if (callResult?.kind === 308 /* Tiny.ObjectKind.ERROR */) return callResult; return callResult; } else return exports.NULL; } evalInOperator(leftOperand, rightOperand, position) { switch (rightOperand?.kind) { case 303 /* Tiny.ObjectKind.ARRAY */: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: rightOperand.value.map((x) => JSON.stringify(x)).includes(JSON.stringify(leftOperand)), }; case 304 /* Tiny.ObjectKind.OBJECT */: { if (leftOperand?.kind === 301 /* Tiny.ObjectKind.STRING */ || leftOperand?.kind === 300 /* Tiny.ObjectKind.NUMBER */) return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: [...rightOperand.pairs.keys()].map((x) => JSON.stringify(x)).includes(JSON.stringify(leftOperand)), }; else return this.typeMissmatch(leftOperand, rightOperand, position); } case 301 /* Tiny.ObjectKind.STRING */: return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: rightOperand.value.includes(leftOperand.value), }; case 300 /* Tiny.ObjectKind.NUMBER */: if (leftOperand?.kind === 304 /* Tiny.ObjectKind.OBJECT */) return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: [...leftOperand.pairs.values()].map((x) => JSON.stringify(x)).includes(JSON.stringify(rightOperand)), }; else return this.typeMissmatch(leftOperand, rightOperand, position); default: return Tiny.error(Tiny.errorFormatter(this.messages.runtimeError.typeMismatch_2, Tiny.objectKindStringify(leftOperand?.kind ?? 309 /* Tiny.ObjectKind.NULL */), Tiny.objectKindStringify(rightOperand?.kind ?? 309 /* Tiny.ObjectKind.NULL */)), position.line, position.column); } } evalIfExpression(condition, consequence, alternative, enviroment) { const conditionExpression = this.evalExpression(condition, enviroment); if (conditionExpression?.kind === 308 /* Tiny.ObjectKind.ERROR */) return conditionExpression; if (this.isTruthy(conditionExpression)) { const consequenceResult = this.evalExpression(consequence, enviroment); if (consequenceResult?.kind === 308 /* Tiny.ObjectKind.ERROR */) return consequenceResult; return consequenceResult; } else if (alternative) { const alternativeResult = this.evalExpression(alternative, enviroment); if (alternativeResult?.kind === 308 /* Tiny.ObjectKind.ERROR */) return alternativeResult; return alternativeResult; } else return exports.NULL; } evalIndex(left, index, position) { switch (left?.kind) { case 303 /* Tiny.ObjectKind.ARRAY */: { if (index?.kind === 300 /* Tiny.ObjectKind.NUMBER */) return this.evalArrayIndex(left, index, position); return Tiny.error(this.messages.runtimeError.typeMismatch_1, position.line, position.column); } case 304 /* Tiny.ObjectKind.OBJECT */: { let key; switch (index?.kind) { case 301 /* Tiny.ObjectKind.STRING */: case 300 /* Tiny.ObjectKind.NUMBER */: key = index.value; break; default: return Tiny.error(this.messages.runtimeError.typeMismatch_1, position.line, position.column); } return new Map([...left.pairs].map(([key, value]) => [key.value, value])).get(key) ?? exports.UNDEFINED; } default: return exports.NULL; } } evalArrayIndex(left, index, position) { if (index?.kind !== 300 /* Tiny.ObjectKind.NUMBER */ || left?.kind !== 303 /* Tiny.ObjectKind.ARRAY */) return Tiny.error(this.messages.runtimeError.typeMismatch_1, position.line, position.column); if (index.value < 0 || index.value >= left.value.length) return Tiny.error(this.messages.runtimeError.indexOutOfRange, position.line, position.column); return left.value[index.value]; } isTruthy(object) { if (!object) return false; switch (object.kind) { case 302 /* Tiny.ObjectKind.BOOLEAN */: return object.value; case 300 /* Tiny.ObjectKind.NUMBER */: return object.value !== 0; case 309 /* Tiny.ObjectKind.NULL */: case 310 /* Tiny.ObjectKind.UNDEFINED */: return false; default: return true; } } evalBang(object) { return { kind: 302 /* Tiny.ObjectKind.BOOLEAN */, value: !this.isTruthy(object), }; } evalMinus(object, position) { if (object?.kind !== 300 /* Tiny.ObjectKind.NUMBER */) return Tiny.error(this.messages.runtimeError.typeMismatch_1, position.line, position.column); return { kind: 300 /* Tiny.ObjectKind.NUMBER */, value: -object.value, }; } } exports.default = Evaluator;