Files
bf-server/client/src/exec/BranFlakesExecutorVisitor.ts

148 lines
3.8 KiB
TypeScript
Raw Normal View History

2025-07-20 03:36:43 +01:00
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';
2025-07-20 04:12:13 +01:00
import { AbortClassRequestor } from './AbortRequestor';
2025-07-20 03:36:43 +01:00
export class BranFlakesExecutorVisitor
extends AbstractParseTreeVisitor<Promise<void>>
2025-07-20 04:12:13 +01:00
implements bfVisitor<Promise<void>> {
2025-07-20 03:36:43 +01:00
/**
2025-07-20 04:12:13 +01:00
*
* @param input Input string
* @param inputPtr Input pointer to start from
*/
2025-07-20 03:36:43 +01:00
constructor(
2025-07-20 04:12:13 +01:00
private inputStrategy: InputStrategy,
private logger: (val: string) => Thenable<void>,
private abortRequestor?: AbortClassRequestor,
private inputPtr: number = 0
2025-07-20 03:36:43 +01:00
) {
super();
}
// /**
// * The memory cells (Can work with negative cells this way)
// */
// private cells: Map<number, number> = new Map();
2025-07-20 04:12:13 +01:00
private byteArraySize: number = 30000;
private byteArray: Int8Array = new Int8Array(this.byteArraySize);
/**
* Pointer
*/
private ptr: number = 0;
/** Output string */
private outputStr: string = '';
2025-07-20 03:36:43 +01:00
2025-07-20 04:12:13 +01:00
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>,
aborter?: AbortClassRequestor
) {
//get tree and issues
const { tree, issues } = getTree(text, fn);
2025-07-20 03:36:43 +01:00
2025-07-20 04:12:13 +01:00
//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);
2025-07-20 03:36:43 +01:00
2025-07-20 04:12:13 +01:00
//get output
return vis.outputStr;
}
2025-07-20 03:36:43 +01:00
2025-07-20 04:12:13 +01:00
getCell(pointerIndex: number) {
return this.byteArray[pointerIndex];
}
setCell(pointerIndex: number, value: number): void {
this.byteArray[pointerIndex] = value;
}
2025-07-20 03:36:43 +01:00
2025-07-20 04:12:13 +01:00
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);
}
2025-07-20 03:36:43 +01:00
2025-07-20 04:12:13 +01:00
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);
}
2025-07-20 03:36:43 +01:00
2025-07-20 04:12:13 +01:00
// 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;
}
// break for any close requests
return new Promise((res, rej) => {
2025-07-20 03:36:43 +01:00
2025-07-20 04:12:13 +01:00
setTimeout(() => { if (this.abortRequestor?.isAborted()??false) {rej('aborted');} else {res(undefined);} }, 0);
});
}
// override for maintaining async
protected async aggregateResult(
aggregate: Promise<void>,
nextResult: Promise<void>
): Promise<void> {
await aggregate;
return nextResult;
}
2025-07-20 03:36:43 +01:00
}