diff --git a/README.md b/README.md index d5a53dd..117f173 100644 --- a/README.md +++ b/README.md @@ -13,14 +13,21 @@ A simple language server based VSCode Extension for the (Branflakes?) (BrainFuck ### Execution -Use the command to execute the code. -Issue is, because BF is a **turing complete** language, there is no way to know if the program will terminate or not. Hence for now, the command may lead to infinite execution. +Use the BF execute task to execute the code. +Because JS is single threaded+. If the program requires input, it will be requested as a prompt. TODO: Implement a timeout. ### Changelog +#### 0.3.0 + +- Added a task for execution + - Press Control C to halt it while its waiting for input + - Close task to abort execution + + #### 0.2.1 - Change category diff --git a/client/src/exec/AbortRequestor.ts b/client/src/exec/AbortRequestor.ts new file mode 100644 index 0000000..fb448b2 --- /dev/null +++ b/client/src/exec/AbortRequestor.ts @@ -0,0 +1,11 @@ + +export class AbortClassRequestor{ + private abortRequest=false; + + requestAbort(){ + this.abortRequest=true; + } + isAborted(){ + return this.abortRequest; + } +} \ No newline at end of file diff --git a/client/src/exec/BranFlakesExecutorVisitor.ts b/client/src/exec/BranFlakesExecutorVisitor.ts index 6bbc43e..0072261 100644 --- a/client/src/exec/BranFlakesExecutorVisitor.ts +++ b/client/src/exec/BranFlakesExecutorVisitor.ts @@ -5,20 +5,21 @@ import { DiagnosticSeverity } from 'vscode-languageclient'; import { getTree } from '../BranFlakesParseRunner'; import { RuleNode } from 'antlr4ts/tree/RuleNode'; import type InputStrategy from '../input/InputStrategy'; +import { AbortClassRequestor } from './AbortRequestor'; export class BranFlakesExecutorVisitor extends AbstractParseTreeVisitor> - implements bfVisitor> -{ + implements bfVisitor> { /** - * - * @param input Input string - * @param inputPtr Input pointer to start from - */ + * + * @param input Input string + * @param inputPtr Input pointer to start from + */ constructor( - private inputStrategy: InputStrategy, - private logger: (val: string) => Thenable, - private inputPtr: number = 0 + private inputStrategy: InputStrategy, + private logger: (val: string) => Thenable, + private abortRequestor?: AbortClassRequestor, + private inputPtr: number = 0 ) { super(); } @@ -27,116 +28,120 @@ export class BranFlakesExecutorVisitor // */ // private cells: Map = new Map(); - private byteArraySize: number = 30000; - private byteArray: Int8Array = new Int8Array(this.byteArraySize); - /** - * Pointer - */ - private ptr: number = 0; - /** Output string */ - private outputStr: string = ''; + private byteArraySize: number = 30000; + private byteArray: Int8Array = new Int8Array(this.byteArraySize); + /** + * Pointer + */ + private ptr: number = 0; + /** Output string */ + private outputStr: string = ''; - - defaultResult() { - return Promise.resolve(); - } - /** - * Run a file - * @param text - * @param fn - * @param inputStrategy - * @returns - */ - static async run( - text: string, - fn: string, - inputStrategy: InputStrategy, - logger: (str: string) => Thenable - ) { - //get tree and issues - const { tree, issues } = getTree(text, fn); + defaultResult() { + return Promise.resolve(); + } + /** + * Run a file + * @param text + * @param fn + * @param inputStrategy + * @returns + */ + static async run( + text: string, + fn: string, + inputStrategy: InputStrategy, + logger: (str: string) => Thenable, + aborter?: AbortClassRequestor + ) { + //get tree and issues + const { tree, issues } = getTree(text, fn); - //get only errors - const x = issues.filter(e => e.type === DiagnosticSeverity.Error); - //if any error, drop - if (x.length > 0) { - throw Error('Errors exist'); - } - // make visitor - const vis = new BranFlakesExecutorVisitor(inputStrategy, logger); - //visit the tree - await vis.visit(tree); + //get only errors + const x = issues.filter(e => e.type === DiagnosticSeverity.Error); + //if any error, drop + if (x.length > 0) { + throw Error('Errors exist'); + } + // make visitor + const vis = new BranFlakesExecutorVisitor(inputStrategy, logger, aborter); + //visit the tree + await vis.visit(tree); - //get output - return vis.outputStr; - } + //get output + return vis.outputStr; + } - getCell(pointerIndex: number) { - return this.byteArray[pointerIndex]; - } - setCell(pointerIndex: number, value: number): void { - this.byteArray[pointerIndex] = value; - } + getCell(pointerIndex: number) { + return this.byteArray[pointerIndex]; + } + setCell(pointerIndex: number, value: number): void { + this.byteArray[pointerIndex] = value; + } - async visitLoopStmt(ctx: LoopStmtContext) { - while ((this.getCell(this.ptr) ?? 0) !== 0) { - await this.visitChildren(ctx); - } - } - async visitPtrLeft() { - this.ptr = (this.ptr + this.byteArraySize - 1) % this.byteArraySize; - } - async visitPtrRight() { - this.ptr = (this.ptr + this.byteArraySize + 1) % this.byteArraySize; - } - async visitPtrIncr() { - const val = this.getCell(this.ptr); - this.setCell(this.ptr, (val + 1) % 256); - } - async visitPtrDecr() { - const val = this.getCell(this.ptr); - this.setCell(this.ptr, (val + 255) % 256); - } - async visitOutputStmt() { - const val = this.getCell(this.ptr) ?? 0; - const str = String.fromCharCode(val); + async visitLoopStmt(ctx: LoopStmtContext) { + while ((this.getCell(this.ptr) ?? 0) !== 0) { + await this.visitChildren(ctx); + } + } + async visitPtrLeft() { + this.ptr = (this.ptr + this.byteArraySize - 1) % this.byteArraySize; + } + async visitPtrRight() { + this.ptr = (this.ptr + this.byteArraySize + 1) % this.byteArraySize; + } + async visitPtrIncr() { + const val = this.getCell(this.ptr); + this.setCell(this.ptr, (val + 1) % 256); + } + async visitPtrDecr() { + const val = this.getCell(this.ptr); + this.setCell(this.ptr, (val + 255) % 256); + } + async visitOutputStmt() { + const val = this.getCell(this.ptr) ?? 0; + const str = String.fromCharCode(val); - this.outputStr += str; - await this.logger(str); - } + this.outputStr += str; + await this.logger(str); + } - async visitInputStmt() { - //get char - const char = (await this.inputStrategy.getInput()) ?? 0; - if(char===3) {throw Error('Halt input wait');} - //increment the input pointer after this - this.inputPtr++; - this.setCell(this.ptr, char); - } + async visitInputStmt() { + //get char + const char = (await this.inputStrategy.getInput()) ?? 0; + if (char === 3) { throw Error('Halt input wait'); } + //increment the input pointer after this + this.inputPtr++; + this.setCell(this.ptr, char); + } - // override for maintaining async - async visitChildren(node: RuleNode): Promise { - let result = this.defaultResult(); - await result; - let n = node.childCount; - for (let i = 0; i < n; i++) { - if (!this.shouldVisitNextChild(node, result)) { - break; - } - let c = node.getChild(i); - let childResult = c.accept(this); - result = this.aggregateResult(result, childResult); - await result; - } - return Promise.resolve(); - } - // override for maintaining async - protected async aggregateResult( - aggregate: Promise, - nextResult: Promise - ): Promise { - await aggregate; - return nextResult; - } + // override for maintaining async + async visitChildren(node: RuleNode): Promise { + let result = this.defaultResult(); + await result; + let n = node.childCount; + for (let i = 0; i < n; i++) { + if (!this.shouldVisitNextChild(node, result)) { + break; + } + let c = node.getChild(i); + let childResult = c.accept(this); + result = this.aggregateResult(result, childResult); + await result; + } + // break for any close requests + return new Promise((res, rej) => { + + setTimeout(() => { if (this.abortRequestor?.isAborted()??false) {rej('aborted');} else {res(undefined);} }, 0); + }); + } + // override for maintaining async + protected async aggregateResult( + aggregate: Promise, + nextResult: Promise + ): Promise { + await aggregate; + return nextResult; + } } diff --git a/client/src/task/CustomExecutionTerminal.ts b/client/src/task/CustomExecutionTerminal.ts index 7258c8f..fc54cac 100644 --- a/client/src/task/CustomExecutionTerminal.ts +++ b/client/src/task/CustomExecutionTerminal.ts @@ -1,6 +1,7 @@ import * as vscode from 'vscode'; import * as path from 'path'; import { BranFlakesExecutorVisitor } from '../exec/BranFlakesExecutorVisitor'; +import { AbortClassRequestor } from '../exec/AbortRequestor'; interface BFRunTaskDefinition extends vscode.TaskDefinition { file?: string; @@ -63,7 +64,7 @@ class CustomBuildTaskTerminal implements vscode.Pseudoterminal { private openDocument: vscode.TextDocument | undefined; onDidWrite: vscode.Event = this.writeEmitter.event; onDidClose?: vscode.Event = this.closeEmitter.event; - + abortRequestor = new AbortClassRequestor(); handleInput(data: string): void { // this.writeEmitter.fire(`Echo(${data.length})` + data); @@ -106,6 +107,8 @@ class CustomBuildTaskTerminal implements vscode.Pseudoterminal { close(): void { // The terminal has been closed. Shutdown the build. + console.log('Forced close'); + this.abortRequestor.requestAbort(); } private async doExecution(): Promise { @@ -120,13 +123,11 @@ class CustomBuildTaskTerminal implements vscode.Pseudoterminal { return new Promise((res, rej) => { if (cus.inputQueue.length > 0) { const char = cus.inputQueue.shift(); - console.log('consumed input', char); res(char); } else { const dispose: vscode.Disposable[] = []; cus.readEmitter.event(e => { const char = cus.inputQueue.shift(); - console.log('consumed input async', char); //clear the earliest disposable dispose.shift()?.dispose(); res(char); @@ -137,9 +138,8 @@ class CustomBuildTaskTerminal implements vscode.Pseudoterminal { }, }, async (data) => { - // console.log('output', data); this.writeEmitter.fire(replaceLFWithCRLF(data)); - } + },this.abortRequestor ); this.closeEmitter.fire(0); } catch (e) { diff --git a/package-lock.json b/package-lock.json index b6e359d..cf85ac1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bfc-server", - "version": "0.2.1", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bfc-server", - "version": "0.2.1", + "version": "0.3.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 800ed99..9c49fb1 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "author": "Atreya Bain", "license": "MIT", "publisher": "atreyabain", - "version": "0.2.1", + "version": "0.3.0", "icon": "assets/128.png", "categories": ["Programming Languages","Linters"], "keywords": [