Skip to content

Commit

Permalink
refactor(structure): Refactored code structure
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasBoda committed Jan 4, 2024
1 parent 7605074 commit 88f8ee6
Show file tree
Hide file tree
Showing 14 changed files with 351 additions and 360 deletions.
3 changes: 2 additions & 1 deletion src/interpreter/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
3 changes: 2 additions & 1 deletion src/runtime/environment.ts
Original file line number Diff line number Diff line change
@@ -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 {

Expand Down
265 changes: 265 additions & 0 deletions src/runtime/functions/functions.ts
Original file line number Diff line number Diff line change
@@ -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);
}
2 changes: 2 additions & 0 deletions src/runtime/functions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./functions";
export * from "./utils";
43 changes: 43 additions & 0 deletions src/runtime/functions/utils.ts
Original file line number Diff line number Diff line change
@@ -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;
}
3 changes: 2 additions & 1 deletion src/runtime/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./runtime";
export * from "./model";
export * from "./environment";
export * from "./environment";
export * from "./functions";
3 changes: 2 additions & 1 deletion src/runtime/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
9 changes: 9 additions & 0 deletions src/utils/error/error-lexer.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
9 changes: 9 additions & 0 deletions src/utils/error/error-parser.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
9 changes: 9 additions & 0 deletions src/utils/error/error-runtime.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading

0 comments on commit 88f8ee6

Please sign in to comment.