Skip to content

Commit

Permalink
chore(docs): Added docstrings to runtime and interpreter modules
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasBoda committed Feb 20, 2024
1 parent 5fe5231 commit 1c8238b
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 138 deletions.
55 changes: 27 additions & 28 deletions src/interpreter/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,100 +24,97 @@ export class Interpreter {

private program?: Program;

// subscribes to interpreter output
/**
* Generates an observable used for retrieving the interpreter's output
*
* @param sourceCode - source code of the simulation
* @param config - configuration of the interpreter
* @returns observable holding the interpreter's output
*/
public get(sourceCode: string, config: InterpreterConfiguration): Observable<InterpreterOutput> {
this.build(sourceCode, config);
return this.dataSubject.asObservable();
}

// returns current program
public getProgram(): Program {
return this.program!;
}

// sets new program
public setProgram(program: Program): void {
this.program = program;
}

public updateAgentValue(agentIndex: number, propertyIdentifier: string, value: number): void {
this.runtime?.updateAgentValue(agentIndex, propertyIdentifier, value);
}

/**
* Builds the interpreter and initializes the simulation
*
* @param sourceCode - source code to be initialized and built upon
* @param config - configuration of the interpreter
*/
public build(sourceCode: string, config: InterpreterConfiguration): void {
this.sourceCode = sourceCode;
this.config = config;

// generate source code symbols
this.symbolizer = new Symbolizer(this.sourceCode);
const symbols: Symbol[] = this.symbolizer.symbolize();

// generate source code tokens
this.lexer = new Lexer(symbols);
let tokens: Token[] = this.lexer.tokenize();

// generate source code abstract syntax tree
this.parser = new Parser(tokens);
let program: Program = this.parser.parse();

Validation.validate(program);

// sort program topologically
this.topology = new Topology();
let sortedProgram: Program = this.topology.getSortedProgram(program);

// save abstract syntax tree
this.program = sortedProgram;

// initialize default global environment
const environment: Environment = Environment.createGlobalEnvironment();
environment.declareVariable("width", createGlobalFunction(this.createWidthFunction(this.config.width)));
environment.declareVariable("height", createGlobalFunction(this.createHeightFunction(this.config.height)));

// save runtime
this.runtime = new Runtime(this.program, environment);

this.reset();
}

// rebuilds current interpreter step
public rebuild(): void {
this.runtime?.setProgram(this.program!);
this.currentStep--;
this.step();
}

// starts the interpreter
public start() {
this.currentStep = 0;
this.subscribe();
}

// resets the interpreter
public reset() {
this.unsubscribe();
this.currentStep = 0;
this.runtime?.reset();
}

// pauses the interpreter
public pause() {
this.unsubscribe();
}

// resumes the interpreter
public resume() {
this.subscribe();
}

// steps manually through the interpreter
public step() {
if (this.currentStep >= this.config.steps + 1) {
return;
}

this.dataSubject.next(this.getInterpreterOutput(this.currentStep++));
}

public getProgram(): Program {
return this.program!;
}

public setProgram(program: Program): void {
this.program = program;
}

public updateAgentValue(agentIndex: number, propertyIdentifier: string, value: number): void {
this.runtime?.updateAgentValue(agentIndex, propertyIdentifier, value);
}

private subscribe(): void {
this.subscription = interval(this.config.delay).pipe(
Expand Down Expand Up @@ -175,6 +172,8 @@ export class Interpreter {
return { identifier: agent.identifier, variables } as Agent;
}

// interpreter's functions initialization

private createWidthFunction(width: number): FunctionCall {
function widthFunction(args: RuntimeValue[]): RuntimeValue {
if (args.length !== 0) {
Expand Down
34 changes: 22 additions & 12 deletions src/parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Position } from "../symbolizer";
import { Token, TokenType } from "../lexer";
import { BinaryExpression, BooleanLiteral, CallExpression, ConditionalExpression, DefineDeclaration, Expression, Identifier, LambdaExpression, LogicalExpression, MemberExpression, NodeType, NumericLiteral, ObjectDeclaration, OtherwiseExpression, ParserValue, Program, Statement, UnaryExpression, VariableDeclaration, VariableType } from "./model";
import { ErrorParser } from "../utils";
import { writeFileSync } from "fs";

export class Parser {

Expand All @@ -12,6 +11,11 @@ export class Parser {
this.tokens = tokens;
}

/**
* Parses the array of tokens into an AST structure representing the program
*
* @returns program structure in form of AST
*/
public parse(): Program {
const program = this.createEmptyProgram();

Expand Down Expand Up @@ -192,7 +196,7 @@ export class Parser {
left,
right,
position
}
};

return otherwiseExpression;
}
Expand Down Expand Up @@ -399,13 +403,7 @@ export class Parser {

this.next();

let args: Expression[];

if (this.at().type === TokenType.CloseParen) {
args = [];
} else {
args = this.parseCallExpressionArgumentsList();
}
const args: Expression[] = this.at().type === TokenType.CloseParen ? [] : this.parseCallExpressionArgumentsList();

if (this.isNotOf(TokenType.CloseParen)) {
throw new ErrorParser("Expected a closing parenthesis after function arguments in call expression", this.position());
Expand Down Expand Up @@ -474,8 +472,8 @@ export class Parser {
private parseNegativeNumericLiteral(): UnaryExpression {
const { value, position } = this.at();

if (value !== "+" && value !== "-") {
throw new ErrorParser("Unary expression requires operator + or -", this.position());
if (value !== "-") {
throw new ErrorParser("Unary expression requires the - operator", this.position());
}

if (this.getNext().type !== TokenType.Number && this.getNext().type !== TokenType.Identifier) {
Expand Down Expand Up @@ -510,7 +508,7 @@ export class Parser {
const { value, position } = this.at();

if (value !== "!") {
throw new ErrorParser("Unary expression requires operator !", this.position());
throw new ErrorParser("Unary expression requires the ! operator", this.position());
}

if (this.getNext().type !== TokenType.Boolean && this.getNext().type !== TokenType.Identifier) {
Expand Down Expand Up @@ -570,6 +568,13 @@ export class Parser {
return this.at().type !== TokenType.EOF;
}

/**
* Converts the specified token type to a variable type
* Throws an exception if the token type is not convertible to a variable type
*
* @param tokenType - token type to convert
* @returns variable type
*/
private getVariableType(tokenType: TokenType): VariableType {
switch (tokenType) {
case TokenType.Property:
Expand All @@ -581,6 +586,11 @@ export class Parser {
}
}

/**
* Creates an empty program node
*
* @returns program with empty body
*/
private createEmptyProgram(): Program {
return {
type: NodeType.Program,
Expand Down
1 change: 0 additions & 1 deletion src/parser/validation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { writeFileSync } from "fs";
import { ErrorParser } from "../utils";
import { DefineDeclaration, NodeType, ObjectDeclaration, Program, VariableDeclaration } from "./model";

Expand Down
87 changes: 59 additions & 28 deletions src/runtime/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,13 @@ export class Environment {
this.variables = new Map();
}

public static createGlobalEnvironment(): Environment {
const environment = new Environment();

environment.declareVariable("random", createGlobalFunction(RANDOM));
environment.declareVariable("choice", createGlobalFunction(CHOICE));
environment.declareVariable("sqrt", createGlobalFunction(SQRT));
environment.declareVariable("abs", createGlobalFunction(ABS));
environment.declareVariable("floor", createGlobalFunction(FLOOR));
environment.declareVariable("ceil", createGlobalFunction(CEIL));
environment.declareVariable("round", createGlobalFunction(ROUND));
environment.declareVariable("sin", createGlobalFunction(SIN));
environment.declareVariable("cos", createGlobalFunction(COS));
environment.declareVariable("tan", createGlobalFunction(TAN));
environment.declareVariable("atan", createGlobalFunction(ATAN));
environment.declareVariable("pi", createGlobalFunction(PI));
environment.declareVariable("prob", createGlobalFunction(PROB));
environment.declareVariable("dist", createGlobalFunction(DIST));

environment.declareVariable("count", createGlobalFunction(COUNT));
environment.declareVariable("filter", createGlobalFunction(FILTER));
environment.declareVariable("empty", createGlobalFunction(EMPTY));
environment.declareVariable("min", createGlobalFunction(MIN));
environment.declareVariable("max", createGlobalFunction(MAX));
environment.declareVariable("sum", createGlobalFunction(SUM));

return environment;
}

/**
* Declares a new variable in the current environment
*
* @param identifier - identifier of the new variable
* @param value - value of the new variable
* @returns value that has been declared
*/
public declareVariable(identifier: string, value: RuntimeValue): RuntimeValue {
if (this.variables?.has(identifier)) {
this.assignVariable(identifier, value);
Expand All @@ -50,6 +29,13 @@ export class Environment {
return value;
}

/**
* Assing an existing variable in the current environment a new value
*
* @param identifier - identifier of the existing variable
* @param value - value to be reassigned to the existing variable
* @returns value that has been reassigned
*/
public assignVariable(identifier: string, value: RuntimeValue): RuntimeValue {
const env = this.resolve(identifier);

Expand All @@ -61,6 +47,12 @@ export class Environment {
return value;
}

/**
* Retrieves the value of a variable from the current or any of the parent environments by its identifier
*
* @param identifier - identifier of the variable
* @returns variable value or undefined if not found
*/
public lookupVariable(identifier: string): RuntimeValue | undefined {
const env = this.resolve(identifier);

Expand All @@ -71,6 +63,12 @@ export class Environment {
return env.variables.get(identifier) as RuntimeValue;
}

/**
* Searches the current and all parent environments for a variable by its identifier and returns this environment if found
*
* @param identifier - identifier of the searched variable
* @returns environment where the variable has been declared
*/
public resolve(identifier: string): Environment | undefined {
if (this.variables.has(identifier)) {
return this;
Expand All @@ -82,4 +80,37 @@ export class Environment {

return this.parent.resolve(identifier);
}

/**
* Initializes a default global environment with all built-in functions
*
* @returns environment with all built-in functions defined
*/
public static createGlobalEnvironment(): Environment {
const environment = new Environment();

environment.declareVariable("random", createGlobalFunction(RANDOM));
environment.declareVariable("choice", createGlobalFunction(CHOICE));
environment.declareVariable("sqrt", createGlobalFunction(SQRT));
environment.declareVariable("abs", createGlobalFunction(ABS));
environment.declareVariable("floor", createGlobalFunction(FLOOR));
environment.declareVariable("ceil", createGlobalFunction(CEIL));
environment.declareVariable("round", createGlobalFunction(ROUND));
environment.declareVariable("sin", createGlobalFunction(SIN));
environment.declareVariable("cos", createGlobalFunction(COS));
environment.declareVariable("tan", createGlobalFunction(TAN));
environment.declareVariable("atan", createGlobalFunction(ATAN));
environment.declareVariable("pi", createGlobalFunction(PI));
environment.declareVariable("prob", createGlobalFunction(PROB));
environment.declareVariable("dist", createGlobalFunction(DIST));

environment.declareVariable("count", createGlobalFunction(COUNT));
environment.declareVariable("filter", createGlobalFunction(FILTER));
environment.declareVariable("empty", createGlobalFunction(EMPTY));
environment.declareVariable("min", createGlobalFunction(MIN));
environment.declareVariable("max", createGlobalFunction(MAX));
environment.declareVariable("sum", createGlobalFunction(SUM));

return environment;
}
}
Loading

0 comments on commit 1c8238b

Please sign in to comment.