UNPKG

@gobstones/gobstones-lang

Version:
226 lines (189 loc) 7.59 kB
/* 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); } }