From 88f8ee655ef8b07942bec53b11695a0b4b87ca69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Bo=C4=8Fa?= Date: Thu, 4 Jan 2024 14:00:21 +0100 Subject: [PATCH] refactor(structure): Refactored code structure --- src/interpreter/interpreter.ts | 3 +- src/runtime/environment.ts | 3 +- src/runtime/functions/functions.ts | 265 +++++++++++++++++++ src/runtime/functions/index.ts | 2 + src/runtime/functions/utils.ts | 43 +++ src/runtime/index.ts | 3 +- src/runtime/runtime.ts | 3 +- src/utils/error/error-lexer.ts | 9 + src/utils/error/error-parser.ts | 9 + src/utils/error/error-runtime.ts | 9 + src/utils/{errors.ts => error/error.ts} | 23 +- src/utils/error/index.ts | 4 + src/utils/functions.ts | 332 ------------------------ src/utils/index.ts | 3 +- 14 files changed, 351 insertions(+), 360 deletions(-) create mode 100644 src/runtime/functions/functions.ts create mode 100644 src/runtime/functions/index.ts create mode 100644 src/runtime/functions/utils.ts create mode 100644 src/utils/error/error-lexer.ts create mode 100644 src/utils/error/error-parser.ts create mode 100644 src/utils/error/error-runtime.ts rename src/utils/{errors.ts => error/error.ts} (58%) create mode 100644 src/utils/error/index.ts delete mode 100644 src/utils/functions.ts diff --git a/src/interpreter/interpreter.ts b/src/interpreter/interpreter.ts index 9291986..b85e2b6 100644 --- a/src/interpreter/interpreter.ts +++ b/src/interpreter/interpreter.ts @@ -4,7 +4,8 @@ import { Lexer, Token } from "../lexer"; import { Parser, ParserValue, Program } from "../parser"; import { Runtime, Environment, FunctionCall, NumberValue, RuntimeAgent, RuntimeOutput, RuntimeValue, ValueType } from "../runtime"; import { Agent, InterpreterConfiguration, InterpreterOutput } from "./model"; -import { ErrorModel, ErrorRuntime, createGlobalFunction } from "../utils"; +import { ErrorModel, ErrorRuntime } from "../utils"; +import { createGlobalFunction } from "../runtime/functions/utils"; export class Interpreter { diff --git a/src/runtime/environment.ts b/src/runtime/environment.ts index 4e48eb0..5772c40 100644 --- a/src/runtime/environment.ts +++ b/src/runtime/environment.ts @@ -1,5 +1,6 @@ import { RuntimeValue } from "./model/values"; -import { ErrorRuntime, ABS, CEIL, CHOICE, COS, COUNT, EMPTY, FILTER, FLOOR, PI, PROB, RANDOM, ROUND, SIN, SQRT, TAN, createGlobalFunction, MIN, MAX, DIST } from "../utils"; +import { ErrorRuntime } from "../utils"; +import { ABS, CEIL, CHOICE, COS, COUNT, EMPTY, FILTER, FLOOR, PI, PROB, RANDOM, ROUND, SIN, SQRT, TAN, createGlobalFunction, MIN, MAX, DIST } from "./functions"; export class Environment { diff --git a/src/runtime/functions/functions.ts b/src/runtime/functions/functions.ts new file mode 100644 index 0000000..6daf84e --- /dev/null +++ b/src/runtime/functions/functions.ts @@ -0,0 +1,265 @@ +import { ErrorRuntime } from "../../utils"; +import { AgentsValue, BooleanValue, LambdaValue, NumberValue, RuntimeAgent, RuntimeValue, ValueType } from "../model"; +import { createAgentValue, createAgentsValue, createBooleanValue, createNullValue, createNumberValue, expectArgumentCount, expectArgumentType } from "./utils"; + +export function DIST(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("dist", 4, args.length); + expectArgumentType("dist", args[0], ValueType.Number); + expectArgumentType("dist", args[1], ValueType.Number); + expectArgumentType("dist", args[2], ValueType.Number); + expectArgumentType("dist", args[3], ValueType.Number); + + const x1: NumberValue = args[0] as NumberValue; + const y1: NumberValue = args[1] as NumberValue; + const x2: NumberValue = args[2] as NumberValue; + const y2: NumberValue = args[3] as NumberValue; + + const result = Math.sqrt((x1.value - x2.value) * (x1.value - x2.value) + (y1.value - y2.value) * (y1.value - y2.value)); + + return createNumberValue(result); +} + +export function PROB(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("prob", 1, args.length); + expectArgumentType("prob", args[0], ValueType.Number); + + const probability: NumberValue = args[0] as NumberValue; + + if (probability.value < 0 || probability.value > 1) { + throw new ErrorRuntime(`Function 'prob' expected a number between 0 and 1, ${probability.value} provided`); + } + + const result = Math.random() < probability.value; + + return createBooleanValue(result); +} + +export function PI(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("pi", 0, args.length); + + return createNumberValue(Math.PI); +} + +export function EMPTY(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("empty", 0, args.length); + + return createAgentsValue([]); +} + +export function MIN(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("min", 1, args.length); + expectArgumentType("min", args[0], ValueType.Lambda); + + const lambda: LambdaValue = args[0] as LambdaValue; + + if (lambda.agents.length !== lambda.results.length) { + throw new ErrorRuntime(`Number of agents does not equal the number of results in 'min' function.`); + } + + for (let i = 0; i < lambda.results.length; i++) { + const result: RuntimeValue = lambda.results[i]; + + if (result.type !== ValueType.Number) { + throw new ErrorRuntime(`Function 'min' requires a lambda expression that returns numeric values`); + } + } + + const results = lambda.results.map((result: RuntimeValue) => (result as NumberValue).value); + const minValue = Math.min(...results); + + for (let i = 0; i < results.length; i++) { + if (results[i] === minValue) { + const agent: RuntimeAgent = lambda.agents[i]; + return createAgentValue(agent); + } + } + + return createNullValue(); +} + +export function MAX(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("max", 1, args.length); + expectArgumentType("max", args[0], ValueType.Lambda); + + const lambda: LambdaValue = args[0] as LambdaValue; + + if (lambda.agents.length !== lambda.results.length) { + throw new ErrorRuntime(`Number of agents does not equal the number of results in 'max' function.`); + } + + for (let i = 0; i < lambda.results.length; i++) { + const result: RuntimeValue = lambda.results[i]; + + if (result.type !== ValueType.Number) { + throw new ErrorRuntime(`Function 'max' requires a lambda expression that returns numeric values`); + } + } + + const results = lambda.results.map((result: RuntimeValue) => (result as NumberValue).value); + const maxValue = Math.max(...results); + + for (let i = 0; i < results.length; i++) { + if (results[i] === maxValue) { + const agent: RuntimeAgent = lambda.agents[i]; + return createAgentValue(agent); + } + } + + return createNullValue(); +} + +export function FILTER(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("filter", 1, args.length); + expectArgumentType("filter", args[0], ValueType.Lambda); + + const lambda: LambdaValue = args[0] as LambdaValue; + + if (lambda.agents.length !== lambda.results.length) { + throw new ErrorRuntime(`Number of agents does not equal the number of results in 'filter' function.`); + } + + const agents: RuntimeAgent[] = []; + + for (let i = 0; i < lambda.agents.length; i++) { + if (lambda.results[i].type !== ValueType.Boolean) { + throw new ErrorRuntime(`Function 'filter' requires lambda expression with boolean return value`); + } + + const result: BooleanValue = lambda.results[i] as BooleanValue; + + if (result.value) { + agents.push(lambda.agents[i] as RuntimeAgent); + } + } + + return createAgentsValue(agents); +} + +export function COUNT(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("count", 1, args.length); + expectArgumentType("count", args[0], ValueType.Agents); + + const agents: AgentsValue = args[0] as AgentsValue; + const result = agents.value.length; + + return createNumberValue(result); +} + +export function RANDOM(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("random", 2, args.length); + expectArgumentType("random", args[0], ValueType.Number); + expectArgumentType("random", args[1], ValueType.Number); + + const min: NumberValue = args[0] as NumberValue; + const max: NumberValue = args[1] as NumberValue; + + if (min.value >= max.value) { + throw new ErrorRuntime("In function call RANDOM the first argument must be less than the second argument"); + } + + const result = Math.random() * (max.value - min.value) + min.value; + + return createNumberValue(result); +} + +export function CHOICE(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("choice", 2, args.length); + + if (args[0].type === ValueType.Number && args[1].type === ValueType.Number) { + const first: NumberValue = args[0] as NumberValue; + const second: NumberValue = args[1] as NumberValue; + + const result = Math.random() >= 0.5 ? first.value : second.value; + return createNumberValue(result); + } + + if (args[0].type === ValueType.Boolean && args[1].type === ValueType.Boolean) { + const first: BooleanValue = args[0] as BooleanValue; + const second: BooleanValue = args[1] as BooleanValue; + + const result = Math.random() >= 0.5 ? first.value : second.value; + return createBooleanValue(result); + } + + throw new ErrorRuntime("Function 'choice' requires arguments of type 'number' or 'boolean'"); +} + +export function SQRT(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("sqrt", 1, args.length); + expectArgumentType("sqrt", args[0], ValueType.Number); + + const number: NumberValue = args[0] as NumberValue; + const result = Math.sqrt(number.value); + + return createNumberValue(result); +} + +export function ABS(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("abs", 1, args.length); + expectArgumentType("abs", args[0], ValueType.Number); + + const number: NumberValue = args[0] as NumberValue; + const result = Math.abs(number.value); + + return createNumberValue(result); +} + +export function FLOOR(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("floor", 1, args.length); + expectArgumentType("floor", args[0], ValueType.Number); + + const number: NumberValue = args[0] as NumberValue; + const result = Math.floor(number.value); + + return createNumberValue(result); +} + +export function CEIL(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("ceil", 1, args.length); + expectArgumentType("ceil", args[0], ValueType.Number); + + const number: NumberValue = args[0] as NumberValue; + const result = Math.ceil(number.value); + + return createNumberValue(result); +} + +export function ROUND(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("round", 1, args.length); + expectArgumentType("round", args[0], ValueType.Number); + + const number: NumberValue = args[0] as NumberValue; + const result = Math.round(number.value); + + return createNumberValue(result); +} + +export function SIN(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("sin", 1, args.length); + expectArgumentType("sin", args[0], ValueType.Number); + + const number: NumberValue = args[0] as NumberValue; + const result = Math.sin(number.value); + + return createNumberValue(result); +} + +export function COS(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("cos", 1, args.length); + expectArgumentType("cos", args[0], ValueType.Number); + + const number: NumberValue = args[0] as NumberValue; + const result = Math.cos(number.value); + + return createNumberValue(result); +} + +export function TAN(args: RuntimeValue[]): RuntimeValue { + expectArgumentCount("tan", 1, args.length); + expectArgumentType("tan", args[0], ValueType.Number); + + const number: NumberValue = args[0] as NumberValue; + const result = Math.tan(number.value); + + return createNumberValue(result); +} \ No newline at end of file diff --git a/src/runtime/functions/index.ts b/src/runtime/functions/index.ts new file mode 100644 index 0000000..2cb8340 --- /dev/null +++ b/src/runtime/functions/index.ts @@ -0,0 +1,2 @@ +export * from "./functions"; +export * from "./utils"; \ No newline at end of file diff --git a/src/runtime/functions/utils.ts b/src/runtime/functions/utils.ts new file mode 100644 index 0000000..8a95527 --- /dev/null +++ b/src/runtime/functions/utils.ts @@ -0,0 +1,43 @@ +import { ErrorRuntime } from "../../utils"; +import { AgentValue, AgentsValue, BooleanValue, FunctionCall, FunctionValue, NullValue, NumberValue, RuntimeAgent, RuntimeValue, ValueType } from "../model"; + +export function createGlobalFunction(call: FunctionCall): FunctionValue { + return { type: ValueType.Function, call } as FunctionValue; +} + +export function expectArgumentCount(identifier: string, expected: number, provided: number): void { + if (expected !== provided) { + throw new ErrorRuntime(`Function '${identifier}' expected ${expected} arguments, ${provided} provided`); + } +} + +export function expectArgumentType(identifier: string, argument: RuntimeValue, type: ValueType): void { + if (argument.type !== type) { + throw new ErrorRuntime(`Function '${identifier}' expected argument of type '${type}', '${argument.type}' provided`); + } +} + +export function createNumberValue(value: number): NumberValue { + return { type: ValueType.Number, value: normalizeNumber(value) }; +} + +export function createBooleanValue(value: boolean): BooleanValue { + return { type: ValueType.Boolean, value }; +} + +export function createAgentValue(value: RuntimeAgent): AgentValue { + return { type: ValueType.Agent, value }; +} + +export function createAgentsValue(value: RuntimeAgent[]): AgentsValue { + return { type: ValueType.Agents, value }; +} + +export function createNullValue(): NullValue { + return { type: ValueType.Null }; +} + +export function normalizeNumber(value: number, digits: number = 2): number { + const pow = Math.pow(10, digits); + return Math.round(value * pow) / pow; +} \ No newline at end of file diff --git a/src/runtime/index.ts b/src/runtime/index.ts index ea0a42f..4995478 100644 --- a/src/runtime/index.ts +++ b/src/runtime/index.ts @@ -1,3 +1,4 @@ export * from "./runtime"; export * from "./model"; -export * from "./environment"; \ No newline at end of file +export * from "./environment"; +export * from "./functions"; \ No newline at end of file diff --git a/src/runtime/runtime.ts b/src/runtime/runtime.ts index 6082988..58e3cec 100644 --- a/src/runtime/runtime.ts +++ b/src/runtime/runtime.ts @@ -2,7 +2,8 @@ import { Position } from "../symbolizer"; import { BinaryExpression, BooleanLiteral, CallExpression, ConditionalExpression, Identifier, LambdaExpression, LogicalExpression, MemberExpression, NodeType, NumericLiteral, ObjectDeclaration, OtherwiseExpression, ParserValue, Program, Statement, UnaryExpression, VariableDeclaration, VariableType } from "../parser"; import { AgentsValue, AgentValue, BooleanValue, FunctionCall, FunctionValue, IdentifierValue, LambdaValue, NumberValue, RuntimeAgent, RuntimeOutput, RuntimeValue, ValueType } from "./model"; import { Environment } from "./environment"; -import { ErrorRuntime, createGlobalFunction, normalizeNumber } from "../utils"; +import { ErrorRuntime } from "../utils"; +import { createGlobalFunction, normalizeNumber } from "./functions"; export class Runtime { diff --git a/src/utils/error/error-lexer.ts b/src/utils/error/error-lexer.ts new file mode 100644 index 0000000..60239ce --- /dev/null +++ b/src/utils/error/error-lexer.ts @@ -0,0 +1,9 @@ +import { ErrorModel } from "./error"; +import { Position } from "../../symbolizer"; + +export class ErrorLexer extends ErrorModel { + + constructor(about: string, position?: Position) { + super("Lexer", about, position); + } +} \ No newline at end of file diff --git a/src/utils/error/error-parser.ts b/src/utils/error/error-parser.ts new file mode 100644 index 0000000..6dc2458 --- /dev/null +++ b/src/utils/error/error-parser.ts @@ -0,0 +1,9 @@ +import { ErrorModel } from "./error"; +import { Position } from "../../symbolizer"; + +export class ErrorParser extends ErrorModel { + + constructor(about: string, position?: Position) { + super("Parser", about, position); + } +} \ No newline at end of file diff --git a/src/utils/error/error-runtime.ts b/src/utils/error/error-runtime.ts new file mode 100644 index 0000000..e721d13 --- /dev/null +++ b/src/utils/error/error-runtime.ts @@ -0,0 +1,9 @@ +import { ErrorModel } from "./error"; +import { Position } from "../../symbolizer"; + +export class ErrorRuntime extends ErrorModel { + + constructor(about: string, position?: Position) { + super("Runtime", about, position); + } +} \ No newline at end of file diff --git a/src/utils/errors.ts b/src/utils/error/error.ts similarity index 58% rename from src/utils/errors.ts rename to src/utils/error/error.ts index b46a363..e810ef4 100644 --- a/src/utils/errors.ts +++ b/src/utils/error/error.ts @@ -1,4 +1,4 @@ -import { Position } from "../symbolizer"; +import { Position } from "../../symbolizer"; type ErrorType = "Lexer" | "Parser" | "Runtime"; @@ -27,25 +27,4 @@ export class ErrorModel extends Error { public throw(): void { console.log(this.toString()); } -} - -export class ErrorLexer extends ErrorModel { - - constructor(about: string, position?: Position) { - super("Lexer", about, position); - } -} - -export class ErrorParser extends ErrorModel { - - constructor(about: string, position?: Position) { - super("Parser", about, position); - } -} - -export class ErrorRuntime extends ErrorModel { - - constructor(about: string, position?: Position) { - super("Runtime", about, position); - } } \ No newline at end of file diff --git a/src/utils/error/index.ts b/src/utils/error/index.ts new file mode 100644 index 0000000..b9e14d9 --- /dev/null +++ b/src/utils/error/index.ts @@ -0,0 +1,4 @@ +export * from "./error"; +export * from "./error-lexer"; +export * from "./error-parser"; +export * from "./error-runtime"; \ No newline at end of file diff --git a/src/utils/functions.ts b/src/utils/functions.ts deleted file mode 100644 index 9132ae1..0000000 --- a/src/utils/functions.ts +++ /dev/null @@ -1,332 +0,0 @@ -import { AgentsValue, AgentValue, BooleanValue, FunctionCall, FunctionValue, LambdaValue, NullValue, NumberValue, RuntimeAgent, RuntimeValue, ValueType } from "../runtime"; -import { ErrorRuntime } from "./errors"; - -export function createGlobalFunction(call: FunctionCall): FunctionValue { - return { type: ValueType.Function, call } as FunctionValue; -} - -export function normalizeNumber(value: number, digits: number = 2): number { - const pow = Math.pow(10, digits); - return Math.round(value * pow) / pow; -} - -function expectNumericArgs(args: RuntimeValue[], count: number): RuntimeValue[] { - if (args.length !== count) { - throw new ErrorRuntime("Number of arguments do not match, expected " + count + ", provided " + args.length + " in a function call"); - } - - const returnedArguments: RuntimeValue[] = []; - - for (const arg of args) { - if (arg.type !== ValueType.Number) { - throw new ErrorRuntime("Expected a numeric argument, did not get a number"); - } - - returnedArguments.push(arg as NumberValue); - } - - return returnedArguments; -} - -// GLOBAL FUNCTIONS - -export function DIST(args: RuntimeValue[]): RuntimeValue { - if (args.length !== 4) { - throw new ErrorRuntime(`Function 'dist' expected 4 arguments, ${args.length} provided`); - } - - if (args[0].type !== ValueType.Number) { - throw new ErrorRuntime(`Function 'dist' expected 1. argument of type 'number', type '${args[0].type}' provided`); - } - - if (args[1].type !== ValueType.Number) { - throw new ErrorRuntime(`Function 'dist' expected 2. argument of type 'number', type '${args[1].type}' provided`); - } - - if (args[2].type !== ValueType.Number) { - throw new ErrorRuntime(`Function 'dist' expected 3. argument of type 'number', type '${args[2].type}' provided`); - } - - if (args[3].type !== ValueType.Number) { - throw new ErrorRuntime(`Function 'dist' expected 4. argument of type 'number', type '${args[3].type}' provided`); - } - - const x1: NumberValue = args[0] as NumberValue; - const y1: NumberValue = args[1] as NumberValue; - const x2: NumberValue = args[2] as NumberValue; - const y2: NumberValue = args[3] as NumberValue; - - const result = Math.sqrt((x1.value - x2.value) * (x1.value - x2.value) + (y1.value - y2.value) * (y1.value - y2.value)); - - return { type: ValueType.Number, value: result } as NumberValue; -} - -export function PROB(args: RuntimeValue[]): RuntimeValue { - if (args.length !== 1) { - throw new ErrorRuntime(`Function 'prob' expected 1 argument, ${args.length} provided`); - } - - if (args[0].type !== ValueType.Number) { - throw new ErrorRuntime(`Function 'prob' expected argument of type 'number', type '${args[0].type}' provided`); - } - - const probability: NumberValue = args[0] as NumberValue; - - if (probability.value < 0 || probability.value > 1) { - throw new ErrorRuntime(`Function 'prob' expected number between 0 and 1, ${probability.value} provided`); - } - - const result = Math.random() < probability.value; - - return { type: ValueType.Boolean, value: result } as BooleanValue; -} - -export function PI(args: RuntimeValue[]): RuntimeValue { - if (args.length !== 0) { - throw new ErrorRuntime(`Function 'pi' expected 0 arguments, ${args.length} provided`); - } - - return { type: ValueType.Number, value: Math.PI } as NumberValue; -} - -export function EMPTY(args: RuntimeValue[]): RuntimeValue { - if (args.length !== 0) { - throw new ErrorRuntime(`Function 'empty' expected 0 arguments, ${args.length} provided`); - } - - return { type: ValueType.Agents, value: [] } as AgentsValue; -} - -export function MIN(args: RuntimeValue[]): RuntimeValue { - if (args.length !== 1) { - throw new ErrorRuntime(`Function 'min' expected 1 argument, ${args.length} provided`); - } - - if (args[0].type !== ValueType.Lambda) { - throw new ErrorRuntime(`Function 'min' expected arguments of type 'lambda', ${args[0].type} provided`); - } - - const lambda: LambdaValue = args[0] as LambdaValue; - - if (lambda.agents.length !== lambda.results.length) { - throw new ErrorRuntime(`Number of agents does not equal the number of results in 'min' function.`); - } - - for (let i = 0; i < lambda.results.length; i++) { - const result: RuntimeValue = lambda.results[i]; - - if (result.type !== ValueType.Number) { - throw new ErrorRuntime(`Function 'min' requires a lambda expression that returns numeric values`); - } - } - - const results = lambda.results.map((result: RuntimeValue) => (result as NumberValue).value); - const minValue = Math.min(...results); - - for (let i = 0; i < results.length; i++) { - if (results[i] === minValue) { - const agent: RuntimeAgent = lambda.agents[i]; - - return { type: ValueType.Agent, value: agent } as AgentValue; - } - } - - return { type: ValueType.Null, value: {} } as NullValue; -} - -export function MAX(args: RuntimeValue[]): RuntimeValue { - if (args.length !== 1) { - throw new ErrorRuntime(`Function 'max' expected 1 argument, ${args.length} provided`); - } - - if (args[0].type !== ValueType.Lambda) { - throw new ErrorRuntime(`Function 'max' expected arguments of type 'lambda', ${args[0].type} provided`); - } - - const lambda: LambdaValue = args[0] as LambdaValue; - - if (lambda.agents.length !== lambda.results.length) { - throw new ErrorRuntime(`Number of agents does not equal the number of results in 'max' function.`); - } - - for (let i = 0; i < lambda.results.length; i++) { - const result: RuntimeValue = lambda.results[i]; - - if (result.type !== ValueType.Number) { - throw new ErrorRuntime(`Function 'max' requires a lambda expression that returns numeric values`); - } - } - - const results = lambda.results.map((result: RuntimeValue) => (result as NumberValue).value); - const maxValue = Math.max(...results); - - for (let i = 0; i < results.length; i++) { - if (results[i] === maxValue) { - const agent: RuntimeAgent = lambda.agents[i]; - - return { type: ValueType.Agent, value: agent } as AgentValue; - } - } - - return { type: ValueType.Null, value: {} } as NullValue; -} - -export function FILTER(args: RuntimeValue[]): RuntimeValue { - if (args.length !== 1) { - throw new ErrorRuntime(`Function 'filter' expected 1 argument, ${args.length} provided`); - } - - if (args[0].type !== ValueType.Lambda) { - throw new ErrorRuntime(`Function 'filter' expected arguments of type 'lambda', ${args[0].type} provided`); - } - - const lambda: LambdaValue = args[0] as LambdaValue; - - if (lambda.agents.length !== lambda.results.length) { - throw new ErrorRuntime(`Number of agents does not equal the number of results in 'filter' function.`); - } - - const agents: RuntimeAgent[] = []; - - for (let i = 0; i < lambda.agents.length; i++) { - if (lambda.results[i].type !== ValueType.Boolean) { - throw new ErrorRuntime(`Function 'filter' requires lambda expression with boolean return value`); - } - - const result: BooleanValue = lambda.results[i] as BooleanValue; - - if (result.value) { - agents.push(lambda.agents[i] as RuntimeAgent); - } - } - - return { type: ValueType.Agents, value: agents } as AgentsValue; -} - -export function COUNT(args: RuntimeValue[]): RuntimeValue { - if (args.length !== 1) { - throw new ErrorRuntime(`Function 'count' expected 1 argument, ${args.length} provided`); - } - - if (args[0].type !== ValueType.Agents) { - throw new ErrorRuntime(`Function 'count' expected arguments of type 'agents', ${args[0].type} provided`); - } - - const agents: AgentsValue = args[0] as AgentsValue; - const length = agents.value.length; - - return { type: ValueType.Number, value: length } as NumberValue; -} - -export function RANDOM(args: RuntimeValue[]): RuntimeValue { - const numericArgs: RuntimeValue[] = expectNumericArgs(args, 2); - - const min: NumberValue = numericArgs[0] as NumberValue; - const max: NumberValue = numericArgs[1] as NumberValue; - - if (min.value >= max.value) { - throw new ErrorRuntime("In function call RANDOM the first argument must be less than the second argument"); - } - - const result = Math.random() * (max.value - min.value) + min.value; - - return { type: ValueType.Number, value: normalizeNumber(result) } as NumberValue; -} - -export function CHOICE(args: RuntimeValue[]): RuntimeValue { - if (args.length !== 2) { - throw new ErrorRuntime(`Function 'choice' requires 2 arguments, ${args.length} provided`); - } - - if (args[0].type === ValueType.Number && args[1].type === ValueType.Number) { - const first: NumberValue = args[0] as NumberValue; - const second: NumberValue = args[1] as NumberValue; - - const result = Math.random() >= 0.5 ? first.value : second.value; - - return { type: ValueType.Number, value: normalizeNumber(result) } as NumberValue; - } - - if (args[0].type === ValueType.Boolean && args[1].type === ValueType.Boolean) { - const first: BooleanValue = args[0] as BooleanValue; - const second: BooleanValue = args[1] as BooleanValue; - - const result = Math.random() >= 0.5 ? first.value : second.value; - - return { type: ValueType.Boolean, value: result } as BooleanValue; - } - - throw new ErrorRuntime("Function 'choice' requires arguments of type 'number' or 'boolean'"); -} - -export function SQRT(args: RuntimeValue[]): RuntimeValue { - const numericArgs: RuntimeValue[] = expectNumericArgs(args, 1); - - const number: NumberValue = numericArgs[0] as NumberValue; - const result = Math.sqrt(number.value); - - return { type: ValueType.Number, value: normalizeNumber(result) } as NumberValue; -} - -export function ABS(args: RuntimeValue[]): RuntimeValue { - const numericArgs: RuntimeValue[] = expectNumericArgs(args, 1); - - const number: NumberValue = numericArgs[0] as NumberValue; - const result = Math.abs(number.value); - - return { type: ValueType.Number, value: normalizeNumber(result) } as NumberValue; -} - -export function FLOOR(args: RuntimeValue[]): RuntimeValue { - const numericArgs: RuntimeValue[] = expectNumericArgs(args, 1); - - const number: NumberValue = numericArgs[0] as NumberValue; - const result = Math.floor(number.value); - - return { type: ValueType.Number, value: normalizeNumber(result) } as NumberValue; -} - -export function CEIL(args: RuntimeValue[]): RuntimeValue { - const numericArgs: RuntimeValue[] = expectNumericArgs(args, 1); - - const number: NumberValue = numericArgs[0] as NumberValue; - const result = Math.ceil(number.value); - - return { type: ValueType.Number, value: normalizeNumber(result) } as NumberValue; -} - -export function ROUND(args: RuntimeValue[]): RuntimeValue { - const numericArgs: RuntimeValue[] = expectNumericArgs(args, 1); - - const number: NumberValue = numericArgs[0] as NumberValue; - const result = Math.round(number.value); - - return { type: ValueType.Number, value: normalizeNumber(result) } as NumberValue; -} - -export function SIN(args: RuntimeValue[]): RuntimeValue { - const numericArgs: RuntimeValue[] = expectNumericArgs(args, 1); - - const number: NumberValue = numericArgs[0] as NumberValue; - const result = Math.sin(number.value); - - return { type: ValueType.Number, value: normalizeNumber(result) } as NumberValue; -} - -export function COS(args: RuntimeValue[]): RuntimeValue { - const numericArgs: RuntimeValue[] = expectNumericArgs(args, 1); - - const number: NumberValue = numericArgs[0] as NumberValue; - const result = Math.cos(number.value); - - return { type: ValueType.Number, value: normalizeNumber(result) } as NumberValue; -} - -export function TAN(args: RuntimeValue[]): RuntimeValue { - const numericArgs: RuntimeValue[] = expectNumericArgs(args, 1); - - const number: NumberValue = numericArgs[0] as NumberValue; - const result = Math.tan(number.value); - - return { type: ValueType.Number, value: normalizeNumber(result) } as NumberValue; -} \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts index 1747ab3..0c54ada 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,4 +1,3 @@ -export * from "./errors"; +export * from "./error"; export * from "./formatter"; -export * from "./functions"; export * from "./logger"; \ No newline at end of file