[add] branflakes execution
This commit is contained in:
20
client/src/BranFlakesExecutorGen.ts
Normal file
20
client/src/BranFlakesExecutorGen.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { EventEmitter } from 'vscode';
|
||||
import InputStrategy from './input/InputStrategy';
|
||||
import {BranFlakesExecutorVisitor} from './exec/BranFlakesExecutorVisitor';
|
||||
|
||||
export class BranFlakesStreamingExecutor {
|
||||
|
||||
constructor(private fileData: string, private fileName: string = 'fileName:dummy', private emitter: EventEmitter<string>,
|
||||
private inputStrategy: InputStrategy
|
||||
) { }
|
||||
|
||||
|
||||
async run() {
|
||||
const finalOutput = await BranFlakesExecutorVisitor.run(
|
||||
this.fileData,
|
||||
this.fileName,
|
||||
this.inputStrategy,
|
||||
async (str) => { this.emitter.fire(str); }
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,140 +0,0 @@
|
||||
import { AbstractParseTreeVisitor } from 'antlr4ts/tree/AbstractParseTreeVisitor';
|
||||
import { LoopStmtContext } from './generated/bfParser';
|
||||
import { bfVisitor } from './generated/bfVisitor';
|
||||
import { DiagnosticSeverity } from 'vscode-languageclient';
|
||||
import { getTree } from './BranFlakesParseRunner';
|
||||
import { RuleNode } from 'antlr4ts/tree/RuleNode';
|
||||
import type InputStrategy from './input/InputStrategy';
|
||||
|
||||
export class BranFlakesExecutorVisitor
|
||||
extends AbstractParseTreeVisitor<Promise<void>>
|
||||
implements bfVisitor<Promise<void>>
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @param input Input string
|
||||
* @param inputPtr Input pointer to start from
|
||||
*/
|
||||
constructor(
|
||||
private inputStrategy: InputStrategy,
|
||||
private logger: (val: string) => Thenable<string>,
|
||||
private inputPtr: number = 0
|
||||
) {
|
||||
super();
|
||||
}
|
||||
// /**
|
||||
// * The memory cells (Can work with negative cells this way)
|
||||
// */
|
||||
// private cells: Map<number, number> = new Map();
|
||||
|
||||
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<string>
|
||||
) {
|
||||
//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 output
|
||||
return vis.outputStr;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
this.outputStr += str;
|
||||
}
|
||||
|
||||
async visitInputStmt() {
|
||||
//get char
|
||||
const char = (await this.inputStrategy.getInput()) ?? 0;
|
||||
//increment the input pointer after this
|
||||
this.inputPtr++;
|
||||
this.setCell(this.ptr, char);
|
||||
}
|
||||
|
||||
// override for maintaining async
|
||||
async visitChildren(node: RuleNode): Promise<void> {
|
||||
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<void>,
|
||||
nextResult: Promise<void>
|
||||
): Promise<void> {
|
||||
await aggregate;
|
||||
return nextResult;
|
||||
}
|
||||
}
|
@@ -4,7 +4,7 @@ import { VSCodePromptInputStrategy } from '../input/VSCodePromptInputStrategy';
|
||||
|
||||
export class CompileBranFlakesCommand implements BranFlakesCommand {
|
||||
getCommandName() {
|
||||
return 'bf.execute';
|
||||
return 'bf.execute.old';
|
||||
}
|
||||
getCommandHandler() {
|
||||
return async () => {
|
||||
@@ -14,7 +14,7 @@ export class CompileBranFlakesCommand implements BranFlakesCommand {
|
||||
window.showInputBox
|
||||
);
|
||||
const { BranFlakesExecutorVisitor } = await import(
|
||||
'../BranFlakesExecutorVisitor'
|
||||
'../exec/BranFlakesExecutorVisitor'
|
||||
);
|
||||
|
||||
const output = await BranFlakesExecutorVisitor.run(
|
||||
|
142
client/src/exec/BranFlakesExecutorVisitor.ts
Normal file
142
client/src/exec/BranFlakesExecutorVisitor.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import { AbstractParseTreeVisitor } from 'antlr4ts/tree/AbstractParseTreeVisitor';
|
||||
import { LoopStmtContext } from '../generated/bfParser';
|
||||
import { bfVisitor } from '../generated/bfVisitor';
|
||||
import { DiagnosticSeverity } from 'vscode-languageclient';
|
||||
import { getTree } from '../BranFlakesParseRunner';
|
||||
import { RuleNode } from 'antlr4ts/tree/RuleNode';
|
||||
import type InputStrategy from '../input/InputStrategy';
|
||||
|
||||
export class BranFlakesExecutorVisitor
|
||||
extends AbstractParseTreeVisitor<Promise<void>>
|
||||
implements bfVisitor<Promise<void>>
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @param input Input string
|
||||
* @param inputPtr Input pointer to start from
|
||||
*/
|
||||
constructor(
|
||||
private inputStrategy: InputStrategy,
|
||||
private logger: (val: string) => Thenable<void>,
|
||||
private inputPtr: number = 0
|
||||
) {
|
||||
super();
|
||||
}
|
||||
// /**
|
||||
// * The memory cells (Can work with negative cells this way)
|
||||
// */
|
||||
// private cells: Map<number, number> = new Map();
|
||||
|
||||
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<void>
|
||||
) {
|
||||
//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 output
|
||||
return vis.outputStr;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// override for maintaining async
|
||||
async visitChildren(node: RuleNode): Promise<void> {
|
||||
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<void>,
|
||||
nextResult: Promise<void>
|
||||
): Promise<void> {
|
||||
await aggregate;
|
||||
return nextResult;
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import * as path from 'path';
|
||||
import { BranFlakesExecutorVisitor } from '../exec/BranFlakesExecutorVisitor';
|
||||
|
||||
interface BFRunTaskDefinition extends vscode.TaskDefinition {
|
||||
file?: string;
|
||||
@@ -9,7 +10,7 @@ export class CustomExecutionTaskProvider implements vscode.TaskProvider {
|
||||
static type: string = 'bf-run';
|
||||
tasks: vscode.Task[] | undefined;
|
||||
|
||||
constructor(private workspaceRoot: string|undefined){
|
||||
constructor(private workspaceRoot: string | undefined) {
|
||||
|
||||
}
|
||||
|
||||
@@ -21,25 +22,25 @@ export class CustomExecutionTaskProvider implements vscode.TaskProvider {
|
||||
}
|
||||
|
||||
|
||||
getTaskFromDefinition(fileName: string|undefined): vscode.Task {
|
||||
const definition:BFRunTaskDefinition = {
|
||||
getTaskFromDefinition(fileName: string | undefined): vscode.Task {
|
||||
const definition: BFRunTaskDefinition = {
|
||||
type: CustomExecutionTaskProvider.type,
|
||||
file: fileName
|
||||
};
|
||||
return new vscode.Task(definition, vscode.TaskScope.Workspace,`bf: run: current file`,CustomExecutionTaskProvider.type,
|
||||
new vscode.CustomExecution(async ()=>{
|
||||
return new vscode.Task(definition, vscode.TaskScope.Workspace, `bf: run: current file`, CustomExecutionTaskProvider.type,
|
||||
new vscode.CustomExecution(async () => {
|
||||
return new CustomBuildTaskTerminal(definition.file);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
resolveTask(_task: vscode.Task, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.Task> {
|
||||
const definition:BFRunTaskDefinition = <any>_task.definition;
|
||||
const definition: BFRunTaskDefinition = <any>_task.definition;
|
||||
|
||||
const fileNameRecovered = definition.file;
|
||||
const taskName = `bf: run: `+ ( fileNameRecovered??'current file');
|
||||
return new vscode.Task(definition,vscode.TaskScope.Workspace, taskName, CustomExecutionTaskProvider.type,
|
||||
new vscode.CustomExecution(async ()=>{
|
||||
const taskName = `bf: run: ` + (fileNameRecovered ?? 'current file');
|
||||
return new vscode.Task(definition, vscode.TaskScope.Workspace, taskName, CustomExecutionTaskProvider.type,
|
||||
new vscode.CustomExecution(async () => {
|
||||
return new CustomBuildTaskTerminal(definition.file);
|
||||
})
|
||||
);
|
||||
@@ -47,31 +48,60 @@ export class CustomExecutionTaskProvider implements vscode.TaskProvider {
|
||||
|
||||
}
|
||||
|
||||
function replaceLFWithCRLF(data: string) {
|
||||
return data.replace(/(?<!\r)\n/gm, '\r\n');
|
||||
}
|
||||
|
||||
|
||||
class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
|
||||
private writeEmitter = new vscode.EventEmitter<string>();
|
||||
private closeEmitter = new vscode.EventEmitter<number>();
|
||||
|
||||
private readEmitter = new vscode.EventEmitter<void>();
|
||||
inputQueue: number[] = [];
|
||||
|
||||
private openDocument:vscode.TextDocument|undefined;
|
||||
private openDocument: vscode.TextDocument | undefined;
|
||||
onDidWrite: vscode.Event<string> = this.writeEmitter.event;
|
||||
onDidClose?: vscode.Event<number> = this.closeEmitter.event;
|
||||
|
||||
|
||||
handleInput(data: string): void {
|
||||
this.writeEmitter.fire(`Echo`+data);
|
||||
// this.writeEmitter.fire(`Echo(${data.length})` + data);
|
||||
const newData = [...data].map(e => e.charCodeAt(0));
|
||||
console.log('new input', newData);
|
||||
this.inputQueue.push(...newData);
|
||||
this.readEmitter.fire();
|
||||
}
|
||||
|
||||
|
||||
constructor(private fileName?:string) {
|
||||
constructor(private fileName?: string) {
|
||||
}
|
||||
|
||||
open(initialDimensions: vscode.TerminalDimensions | undefined): void {
|
||||
open(_initialDimensions: vscode.TerminalDimensions | undefined): void {
|
||||
// At this point we can start using the terminal.
|
||||
this.openDocumentForTask().then(this.doExecution.bind(this));
|
||||
}
|
||||
|
||||
const openDocument = vscode.window.activeTextEditor.document;
|
||||
getPath(fileLocationString: string | undefined, fileName: string) {
|
||||
if (fileLocationString === undefined) { return vscode.Uri.file(fileName); }
|
||||
return vscode.Uri.file(path.resolve(fileLocationString, fileName));
|
||||
}
|
||||
|
||||
private async openDocumentForTask() {
|
||||
let openDocument: vscode.TextDocument;
|
||||
if (this.fileName !== undefined) {
|
||||
try {
|
||||
const fileLocationPathString = vscode.workspace.workspaceFolders?.[0]?.uri?.fsPath;
|
||||
const finalPath = this.getPath(fileLocationPathString, this.fileName);
|
||||
openDocument = await vscode.workspace.openTextDocument(finalPath);
|
||||
} catch (e) {
|
||||
vscode.window.showErrorMessage('Failed to open file ' + this.fileName);
|
||||
this.closeEmitter.fire(2);
|
||||
}
|
||||
} else {
|
||||
openDocument = vscode.window.activeTextEditor.document;
|
||||
}
|
||||
this.openDocument = openDocument;
|
||||
console.log(openDocument.languageId);
|
||||
this.doExecution();
|
||||
}
|
||||
|
||||
close(): void {
|
||||
@@ -79,9 +109,43 @@ class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
|
||||
}
|
||||
|
||||
private async doExecution(): Promise<void> {
|
||||
this.writeEmitter.fire('Build complete.\r\n\r\n');
|
||||
this.writeEmitter.fire('Requested build of '+this.fileName);
|
||||
this.writeEmitter.fire(this.openDocument.getText());
|
||||
this.closeEmitter.fire(0);
|
||||
this.writeEmitter.fire('[bf] Requested execution of ' + this.fileName + '\r\n');
|
||||
const cus = this;
|
||||
try {
|
||||
|
||||
await BranFlakesExecutorVisitor.run(this.openDocument.getText(),
|
||||
this.openDocument.uri.fsPath,
|
||||
{
|
||||
getInput() {
|
||||
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);
|
||||
}, null, dispose);
|
||||
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
async (data) => {
|
||||
// console.log('output', data);
|
||||
this.writeEmitter.fire(replaceLFWithCRLF(data));
|
||||
}
|
||||
);
|
||||
this.closeEmitter.fire(0);
|
||||
} catch (e) {
|
||||
this.closeEmitter.fire(1);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -16,7 +16,8 @@ const config = {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: '[name].js',
|
||||
libraryTarget: 'commonjs2',
|
||||
devtoolModuleFilenameTemplate: '../[resource-path]'
|
||||
sourceMapFilename: '[name].js.map',
|
||||
devtoolModuleFilenameTemplate: '../[resource-path]',
|
||||
},
|
||||
// devtool: 'source-map',
|
||||
externals: {
|
||||
@@ -27,6 +28,7 @@ const config = {
|
||||
innerGraph:true,
|
||||
usedExports:true
|
||||
},
|
||||
|
||||
resolve: {
|
||||
// support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
|
||||
extensions: ['.ts', '.js'],
|
||||
|
@@ -57,7 +57,7 @@
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"command": "bf.execute",
|
||||
"command": "bf.execute.old",
|
||||
"title": "BF: Execute",
|
||||
"when": "editorLangId == bf",
|
||||
"enablement": "editorLangId == bf"
|
||||
|
Reference in New Issue
Block a user