Skip to content

Commit

Permalink
refactor(interpreter): Refactored interpreter service
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasBoda committed Dec 4, 2023
1 parent f49f8d0 commit 8cef421
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 36 deletions.
36 changes: 25 additions & 11 deletions code.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@

agent person 10 {
const x = random(0, width());
const y = random(0, height());

property close = filter(agents(person) => p => p.x >= 0 and p.x <= width());
property closest = min(close => c => c.x);

property color = true;

property coloured = closest.color otherwise false;
agent person 50 {
const speed = 2;
property angle: random(0, 2 * pi()) = angle + choice(-0.1, 0.1);

property shouldStay = prob(0.5);

property xNew: 0 = (x + speed * cos(angle)) % width();
property yNew: 0 = (y + speed * sin(angle)) % height();

property x: random(50, width() - 50) = if shouldStay then x else xNew;
property y: random(50, height() - 50) = if shouldStay then y else yNew;

const distance = 20;

property people = agents(person);
property closePeople = filter(people => p => sqrt((p.x - x) * (p.x - x) + (p.y - y) * (p.y - y)) <= distance);
property closeInfected = filter(closePeople => c => c.infected == true);

const timespan = 200;
property remaining: timespan = if infected then remaining - 1 else timespan;

property shouldInfect = prob(0.4);
property infected: prob(0.5) = (infected and remaining > 0) or (count(closeInfected) > 0 and shouldInfect);

property coloured: false = infected;
}
105 changes: 80 additions & 25 deletions src/interpreter/interpreter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Observable, interval, map, of, take, takeWhile } from "rxjs";
import { Observable, Subject, Subscription, interval, map, of, take, takeWhile } from "rxjs";
import { Lexer } from "../lexer/lexer";
import { Token } from "../lexer/lexer.types";
import { ParserValue, Program } from "../parser/parser.types";
Expand All @@ -14,42 +14,97 @@ import { ErrorLexer, ErrorModel, ErrorParser, ErrorRuntime } from "../utils/erro

export class Interpreter {

public interpret(sourceCode: string, config: InterpreterConfiguration): Observable<InterpreterOutput> {
const symbolizer: Symbolizer = new Symbolizer(sourceCode);
const symbols: Symbol[] = symbolizer.symbolize();
private sourceCode: string = "";
private config: InterpreterConfiguration = { steps: 10000, delay: 20, width: 500, height: 500 };
private currentStep = 0;
private dataSubject: Subject<InterpreterOutput> = new Subject();
private subscription?: Subscription;
private running = false;

const lexer: Lexer = new Lexer(symbols);
private symbolizer?: Symbolizer;
private lexer?: Lexer;
private parser?: Parser;
private runtime?: Runtime;

get(sourceCode: string, config: InterpreterConfiguration): Observable<InterpreterOutput> {
this.sourceCode = sourceCode;
this.config = config;

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

this.lexer = new Lexer(symbols);
let tokens: Token[];

try { tokens = lexer.tokenize(); } catch (error) {
try { tokens = this.lexer.tokenize(); } catch (error) {
return of(this.getRuntimeError(error as ErrorLexer));
}

const parser: Parser = new Parser(tokens);
this.parser = new Parser(tokens);
let program: ParserValue;

try { program = parser.parse(); } catch (error) {
try { program = this.parser.parse(); } catch (error) {
return of(this.getRuntimeError(error as ErrorParser));
}

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

const runtime: Runtime = new Runtime(program as Program, environment);

return interval(config.delay).pipe(
take(config.steps),
map(step => {
try {
const value: RuntimeValue = runtime.run(step);
return this.getRuntimeOutput(value as RuntimeOutput);
} catch (error) {
return this.getRuntimeError(error as ErrorRuntime)
}
}),
takeWhile(output => output.status.code === 0),
);
environment.declareVariable("width", createGlobalFunction(this.createWidthFunction(this.config.width)));
environment.declareVariable("height", createGlobalFunction(this.createHeightFunction(this.config.height)));

this.runtime = new Runtime(program as Program, environment);

return this.dataSubject.asObservable();
}

start() {
this.currentStep = 0;
this.subscribe();
this.running = true;
}

reset() {
this.unsubscribe();
this.currentStep = 0;
this.running = false;
this.runtime?.reset();
}

pause() {
this.unsubscribe();
this.running = false;
}

resume() {
this.subscribe();
this.running = true;
}

step() {
if (this.currentStep >= this.config.steps + 1) {
return;
}

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

private subscribe(): void {
this.subscription = interval(this.config.delay).pipe(
takeWhile(() => this.currentStep <= this.config.steps),
).subscribe(() => this.dataSubject.next(this.getInterpreterOutput(this.currentStep++)));
}

private unsubscribe(): void {
this.subscription?.unsubscribe();
this.subscription = undefined;
}

private getInterpreterOutput(step: number): InterpreterOutput {
try {
const value: RuntimeValue = this.runtime!.run(step);
return this.getRuntimeOutput(value as RuntimeOutput);
} catch (error) {
return this.getRuntimeError(error as ErrorRuntime)
}
}

private getRuntimeOutput(output: RuntimeOutput): InterpreterOutput {
Expand Down
57 changes: 57 additions & 0 deletions src/interpreter/runner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { EMPTY, Observable, Subject, interval, of, startWith, switchMap, takeUntil } from "rxjs";

class Runner {

private dataSubject: Subject<number> = new Subject();
private stopSubject: Subject<boolean> = new Subject();

private isPaused = false;
private counter = 0;

get(steps: number, delay: number): Observable<number> {
return this.dataSubject.asObservable().pipe(
startWith(null),
takeUntil(this.stopSubject),
switchMap(() => interval(delay).pipe(takeUntil(this.stopSubject))),
takeUntil(this.stopSubject),
switchMap(() => {
if (!this.isPaused) {
this.counter++;
if (this.counter <= steps) {
return of(this.counter);
} else {
this.stop();
return EMPTY;
}
} else {
return EMPTY;
}
})
);
}

start() {
this.stop();
this.dataSubject.next(0);
}

stop() {
this.isPaused = false;
this.counter = 0;
this.stopSubject.next(true);
}

pause() {
this.isPaused = true;
}

resume() {
this.isPaused = false;
}

step() {
if (this.isPaused) {
this.dataSubject.next(++this.counter);
}
}
}
4 changes: 4 additions & 0 deletions src/runtime/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ export class Runtime {
return this.evaluateProgram(this.program);
}

public reset(): void {
this.output = { type: ValueType.Output, step: 0, agents: [] };
}

private evaluateProgram(program: Program): RuntimeValue {
for (const statement of program.body) {
if (statement.type !== NodeType.ObjectDeclaration) {
Expand Down

0 comments on commit 8cef421

Please sign in to comment.