@gobstones/gobstones-lang
Version:
226 lines (189 loc) • 7.59 kB
text/typescript
/* eslint-disable no-underscore-dangle */
import { ASTMain, Input, Parser } from '@gobstones/gobstones-parser';
import { Linter } from '@gobstones/gobstones-parser';
import { SymbolTable } from '@gobstones/gobstones-parser';
import { Compiler } from './compiler';
import { RuntimePrimitives, RuntimeState } from './runtime';
import { VirtualMachine } from './vm';
import { UnknownPosition } from '@gobstones/gobstones-parser';
import { T_UPPERID, T_LOWERID, Token } from '@gobstones/gobstones-parser';
import {
ASTDefProcedure,
ASTDefFunction,
ASTDefType,
ASTStmtBlock,
ASTConstructorDeclaration
} from '@gobstones/gobstones-parser';
import { Code } from './instruction';
import { Value } from './value';
/* This module is a façade for all the combined functionality of the
* parser/compiler/vm
*/
const tok = (tag: symbol, value: string): Token =>
new Token(tag, value, UnknownPosition, UnknownPosition);
export interface RunnerResult {
result: Value;
state: RuntimeState;
}
export class Runner {
private _ast: ASTMain;
private _primitives: RuntimePrimitives;
private _symtable: SymbolTable;
private _linter: Linter;
private _code: Code;
private _vm: VirtualMachine;
private _result: any;
public constructor() {
this.initialize();
}
public initialize(): void {
this._ast = undefined;
this._primitives = new RuntimePrimitives();
this._symtable = this._newSymtableWithPrimitives();
this._linter = new Linter(this._symtable);
this._code = undefined;
this._vm = undefined;
this._result = undefined;
}
/* Parse, compile, and run a program in the default global state
* (typically an empty 9x9 board in Gobstones).
* Return the return value of the program, ignoring the final state.
* A GbsInterpreterException may be thrown.
*/
public run(input: Input): Value {
return this.runState(input, new RuntimeState()).result;
}
/* Parse, compile, and run a program in the given initial state.
* Return an object of the form
* {'result': r, 'state': s]
* where r is the result of the program and s is the final state.
* A GbsInterpreterException may be thrown.
*/
public runState(input: Input, initialState: RuntimeState): RunnerResult {
this.parse(input);
this.lint();
this.compile();
this.execute(initialState);
return { result: this._result, state: this._vm.globalState() };
}
public parse(input: Input): void {
const parser = new Parser(input);
this._ast = parser.parse();
for (const option of parser.getLanguageOptions()) {
this._setLanguageOption(option);
}
}
public enableLintCheck(linterCheckId: string, enabled: boolean): void {
this._linter.enableCheck(linterCheckId, enabled);
}
public lint(): void {
this._symtable = this._linter.lint(this._ast);
}
public compile(): void {
this._code = new Compiler(this._symtable).compile(this._ast);
}
public initializeVirtualMachine(initialState: RuntimeState): void {
this._vm = new VirtualMachine(this._code, initialState);
}
public execute(initialState: RuntimeState): void {
this.executeWithTimeout(initialState, 0);
}
public executeWithTimeout(initialState: RuntimeState, millisecs: number): void {
this.executeWithTimeoutTakingSnapshots(initialState, millisecs, undefined);
}
public executeWithTimeoutTakingSnapshots(
initialState: RuntimeState,
millisecs: number,
// eslint-disable-next-line @typescript-eslint/ban-types
snapshotCallback: Function
): void {
this.initializeVirtualMachine(initialState);
this._result = this._vm.runWithTimeoutTakingSnapshots(millisecs, snapshotCallback);
}
public executeEventWithTimeout(eventValue: Value, millisecs: number): void {
this._result = this._vm.runEventWithTimeout(eventValue, millisecs);
}
public get abstractSyntaxTree(): ASTMain {
return this._ast;
}
public get primitives(): RuntimePrimitives {
return this._primitives;
}
public get symbolTable(): SymbolTable {
return this._symtable;
}
public get virtualMachineCode(): Code {
return this._code;
}
public get result(): Value {
return this._result;
}
public get globalState(): RuntimeState {
return this._vm.globalState();
}
/* Evaluate language options set by the LANGUAGE pragma */
public _setLanguageOption(option: string): void {
if (option === 'DestructuringForeach') {
this.enableLintCheck('forbidden-extension-destructuring-foreach', false);
} else if (option === 'AllowRecursion') {
this.enableLintCheck('forbidden-extension-allow-recursion', false);
} else {
throw Error('Unknown language option: ' + option);
}
}
/* Dynamic stack of regions */
public regionStack(): string[] {
return this._vm.regionStack();
}
/* Create a new symbol table, including definitions for all the primitive
* types and operations (which come from RuntimePrimitives) */
public _newSymtableWithPrimitives(): SymbolTable {
const symtable = new SymbolTable();
/* Populate symbol table with primitive types */
for (const type of this._primitives.types()) {
symtable.defType(this._astDefType(type));
}
/* Populate symbol table with primitive procedures */
for (const procedureName of this._primitives.procedures()) {
symtable.defProcedure(this._astDefProcedure(procedureName));
}
/* Populate symbol table with primitive functions */
for (const functionName of this._primitives.functions()) {
symtable.defFunction(this._astDefFunction(functionName));
}
return symtable;
}
public _astDefType(type: string): ASTDefType {
const constructorDeclarations = [];
for (const constructor of this._primitives.typeConstructors(type)) {
constructorDeclarations.push(this._astConstructorDeclaration(type, constructor));
}
return new ASTDefType(tok(T_UPPERID, type), constructorDeclarations);
}
public _astDefProcedure(procedureName: string): ASTDefProcedure {
const nargs = this._primitives.getOperation(procedureName).nargs();
const parameters = [];
for (let i = 1; i <= nargs; i++) {
parameters.push(tok(T_LOWERID, 'x' + i.toString()));
}
return new ASTDefProcedure(tok(T_LOWERID, procedureName), parameters, new ASTStmtBlock([]));
}
public _astDefFunction(functionName: string): ASTDefFunction {
const nargs = this._primitives.getOperation(functionName).nargs();
const parameters = [];
for (let i = 1; i <= nargs; i++) {
parameters.push(tok(T_LOWERID, 'x' + i.toString()));
}
return new ASTDefFunction(tok(T_LOWERID, functionName), parameters, new ASTStmtBlock([]));
}
public _astConstructorDeclaration(
type: string,
constructor: string
): ASTConstructorDeclaration {
const fields = [];
for (const field of this._primitives.constructorFields(type, constructor)) {
fields.push(tok(T_LOWERID, field));
}
return new ASTConstructorDeclaration(tok(T_UPPERID, constructor), fields);
}
}