[refactor] commands

This commit is contained in:
2024-01-02 17:15:19 +05:30
parent 6e143cd4d0
commit 0a2a6c4db6
8 changed files with 127 additions and 90 deletions

View File

@@ -1,31 +1,39 @@
# BF extension
A simple language server based VSCode Extension for the ~~Branflakes~~ BF language. You can also execute your code and see its output.
A simple language server based VSCode Extension for the (Branflakes?) (BrainFuck?) BF language. You can also execute your code and see its output.
![BF](./assets/screenshot.png)
![BF](https://kekvrose.me/static/projects/screenshots/bf-server.png)
## Functionality
- [X] Syntax
- [X] Bracket matching
- [X] Autocomplete suggestions
- [X] Extension icon
- [X] Execution
- [ ] Timeout
- Syntax Highlighting
- Execution
- Autocomplete suggestions
### 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.
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.
If the program requires input, it will be requested as a prompt.
TODO: Implement a timeout.
### Changelog
#### 0.2.0
- Cycle input pointer on overflow/underflow
- Refactoring code
#### 0.1.0
- Request input as required during execution
- Using array-based indexing. This implies that only positive indices upto 30k are supported.
### Building it
1. `npm i` - Install all dependencies
2. `npm i -g @vscode/vsce` - Install VSCode Command line CLI
3. `vsce package` - Package to VSIX

BIN
assets/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -16,32 +16,27 @@ export default class BranFlakesExecutorVisitor
* @param inputPtr Input pointer to start from
*/
constructor(
protected inputStrategy: InputStrategy,
protected logger: (val: string) => Thenable<string>,
protected inputPtr: number = 0
private inputStrategy: InputStrategy,
private logger: (val: string) => Thenable<string>,
private inputPtr: number = 0
) {
super();
}
// /**
// * The memory cells (Can work with negative cells this way)
// */
// protected cells: Map<number, number> = new Map();
// private cells: Map<number, number> = new Map();
protected byteArraySize: number = 30000;
protected byteArray: Int8Array = new Int8Array(this.byteArraySize);
private byteArraySize: number = 30000;
private byteArray: Int8Array = new Int8Array(this.byteArraySize);
/**
* Pointer
*/
protected ptr: number = 0;
private ptr: number = 0;
/** Output string */
protected outputStrArray: string[] = [];
private outputStr: string = '';
/**
* Output string (Available only after visiting)
*/
public get outputStr() {
return this.outputStrArray.join('');
}
defaultResult() {
return Promise.resolve();
@@ -57,7 +52,7 @@ export default class BranFlakesExecutorVisitor
text: string,
fn: string,
inputStrategy: InputStrategy,
logger: (str:string) => Thenable<string>
logger: (str: string) => Thenable<string>
) {
//get tree and issues
const { tree, issues } = getTree(text, fn);
@@ -90,10 +85,10 @@ export default class BranFlakesExecutorVisitor
}
}
async visitPtrLeft() {
--this.ptr;
this.ptr = (this.ptr + this.byteArraySize - 1) % this.byteArraySize;
}
async visitPtrRight() {
++this.ptr;
this.ptr = (this.ptr + this.byteArraySize + 1) % this.byteArraySize;
}
async visitPtrIncr() {
const val = this.getCell(this.ptr);
@@ -107,12 +102,12 @@ export default class BranFlakesExecutorVisitor
const val = this.getCell(this.ptr) ?? 0;
const str = String.fromCharCode(val);
this.outputStrArray.push(str);
this.outputStr += str;
}
async visitInputStmt() {
//get char
const char = await this.inputStrategy.getInput() ?? 0;
const char = (await this.inputStrategy.getInput()) ?? 0;
//increment the input pointer after this
this.inputPtr++;
this.setCell(this.ptr, char);
@@ -120,7 +115,6 @@ export default class BranFlakesExecutorVisitor
// override for maintaining async
async visitChildren(node: RuleNode): Promise<void> {
// await this.logger("checking "+node.constructor.name)
let result = this.defaultResult();
await result;
let n = node.childCount;

View File

@@ -0,0 +1,5 @@
export interface Command{
getCommandName():string;
getCommandHandler():(...args:any)=>Promise<any>;
}

View File

@@ -0,0 +1,26 @@
import { window } from 'vscode';
import { Command as BranFlakesCommand } from './Command';
import { VSCodePromptInputStrategy } from '../input/VSCodePromptInputStrategy';
import BranFlakesExecutorVisitor from '../BranFlakesExecutorVisitor';
export class CompileBranFlakesCommand implements BranFlakesCommand {
getCommandName() {
return 'bf.execute';
}
getCommandHandler() {
return async () => {
const text = window.activeTextEditor.document.getText();
const fn = window.activeTextEditor.document.fileName;
const inputStrategy = new VSCodePromptInputStrategy(
window.showInputBox
);
const output = await BranFlakesExecutorVisitor.run(
text,
fn,
inputStrategy,
window.showInformationMessage
);
await window.showInformationMessage(`Output: ${output}`);
};
}
}

View File

@@ -4,16 +4,14 @@
* ------------------------------------------------------------------------------------------ */
import * as path from 'path';
import { ExtensionContext,commands, window } from 'vscode';
import { ExtensionContext, commands, window } from 'vscode';
import {
LanguageClient,
LanguageClientOptions,
ServerOptions,
TransportKind
TransportKind,
} from 'vscode-languageclient';
import BranFlakesExecutorVisitor from './BranFlakesExecutorVisitor';
import { VSCodePromptInputStrategy } from './input/VSCodePromptInputStrategy';
import { CompileBranFlakesCommand } from './command/CompileCommand';
let client: LanguageClient;
@@ -33,26 +31,25 @@ export function activate(context: ExtensionContext) {
debug: {
module: serverModule,
transport: TransportKind.ipc,
options: debugOptions
}
options: debugOptions,
},
};
// Options to control the language client
let clientOptions: LanguageClientOptions = {
// Register the server for plain text documents
documentSelector: [{ scheme: 'file', language: 'bf' }]
documentSelector: [{ scheme: 'file', language: 'bf' }],
};
const command = 'bf.execute';
const commandHandler = async()=>{
const text= window.activeTextEditor.document.getText();
const fn = window.activeTextEditor.document.fileName;
const inputStrategy = new VSCodePromptInputStrategy(window.showInputBox);
const output = await BranFlakesExecutorVisitor.run(text,fn,inputStrategy,window.showInformationMessage);
await window.showInformationMessage(`Output: ${output}`);
};
context.subscriptions.push(commands.registerCommand(command,commandHandler));
const branFlakesCommands = [new CompileBranFlakesCommand()];
for (let branFlakesCommand of branFlakesCommands) {
context.subscriptions.push(
commands.registerCommand(
branFlakesCommand.getCommandName(),
branFlakesCommand.getCommandHandler()
)
);
}
// Create the language client and start the client.
client = new LanguageClient(

View File

@@ -2,8 +2,13 @@ import { CancellationToken, InputBoxOptions } from 'vscode';
import InputStrategy from './InputStrategy';
export class VSCodePromptInputStrategy implements InputStrategy {
private inputQueue:string;
constructor(private requestor:(promptOptions?:InputBoxOptions,cancelToken?:CancellationToken)=>Thenable<string>) {}
private inputQueue: string = '';
constructor(
private requestor: (
promptOptions?: InputBoxOptions,
cancelToken?: CancellationToken
) => Thenable<string>
) {}
async getInput(): Promise<number> {
while (this.inputQueue.length == 0) {
@@ -15,7 +20,9 @@ export class VSCodePromptInputStrategy implements InputStrategy {
return character;
}
private async requestInputFromPrompt() {
const inputPrompt = await this.requestor({prompt:"More input is required. Please provide input:"});
const inputPrompt = await this.requestor({
prompt: 'More input is required. Please provide input:',
});
this.inputQueue += inputPrompt;
}
}

View File

@@ -5,7 +5,7 @@
"author": "Atreya Bain",
"license": "MIT",
"publisher": "atreyabain",
"version": "0.1.0",
"version": "0.2.0",
"icon": "assets/128.png",
"categories": [],
"keywords": [