Skip to content

Commit

Permalink
READ data, RESTORE corrected; multi dimensional DIM as code snippet
Browse files Browse the repository at this point in the history
  • Loading branch information
benchmarko committed Dec 9, 2024
1 parent 51f9745 commit 05f5378
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 52 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
- numbers with exponential notation
- command line tool should output a stand alone running JS file for node, so include dimArray() on-demand in the compiled source?
- TIME: *300/1000 ?
- mid$ as assing? a$="abcde":mid$(a$,3,2)="w":?a$ ?
- mid$ as assign? a$="abcde":mid$(a$,3,2)="w":?a$ ?
- Do we want keywords all uppercase? And variables all lowercase?
And maybe new features with capital letter? E.g. If...Then...Else...Endif on multiple lines?
Expand Down
4 changes: 2 additions & 2 deletions dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="locobasic.css">
<title>LocoBasic v0.1.6</title>
<title>LocoBasic v0.1.12</title>
</head>

<body>
Expand Down Expand Up @@ -32,7 +32,7 @@
<div id="outputArea" class="flexBox">
<label for="outputText" title="output">Output</label>
<br />
<textarea id="outputText" rows="20" cols="80" spellcheck="false" autocomplete="off" autocapitalize="off" tabindex="0"></textarea>
<textarea id="outputText" rows="25" cols="80" spellcheck="false" autocomplete="off" autocapitalize="off" tabindex="0"></textarea>
</div>

<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js"></script>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "locobasic",
"version": "0.1.11",
"version": "0.1.12",
"description": "# LocoBasic - Loco BASIC",
"type": "commonjs",
"scripts": {
Expand Down
19 changes: 0 additions & 19 deletions src/Core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,9 @@ import { Parser } from "./Parser";
import { arithmetic } from "./arithmetic";
import { Semantics } from "./Semantics";

type VariableValue = string | number | [] | VariableValue[];

const vm = {
_output: "",
_fnOnCls: (() => undefined) as () => void,
dimArray: (dims: number[], initVal: string | number = 0) => {
const createRecursiveArray = function (depth: number) {
const length = dims[depth] + 1, // +1 because of 0-based index
array: VariableValue[] = new Array(length);

depth += 1;
if (depth < dims.length) { // more dimensions?
for (let i = 0; i < length; i += 1) {
array[i] = createRecursiveArray(depth); // recursive call
}
} else { // one dimension
array.fill(initVal);
}
return array;
};
return createRecursiveArray(0);
},
cls: () => {
vm._output = "";
vm._fnOnCls();
Expand Down
111 changes: 88 additions & 23 deletions src/Semantics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@ type GosubLabelEntryType = {
}

interface SemanticsHelper {
addDataIndex(count: number): void,
addDefinedLabel(label: string, line: number): void,
addGosubLabel(label: string): void,
addIndent(num: number): number,
addInstr(name: string): number,
addRestoreLabel(label: string): void,
applyNextIndent(): void,
getDataIndex(): number,
getDataList(): (string | number)[],
getDefinedLabels(): DefinedLabelEntryType[],
getGosubLabels(): Record<string, GosubLabelEntryType>,
getIndent(): number,
getIndentStr(): string,
getInstr(name: string): number,
getInstrKeys(): string[],
getRestoreMap(): Record<string, number>,
getVariable(name: string): string,
getVariables(): string[],
Expand All @@ -34,6 +36,42 @@ interface SemanticsHelper {
setIndent(indent: number): void
}

function getCodeSnippets() {
let _data: (string | number)[] = [];
let _dataPtr = 0;
let _restoreMap: Record<string, number> = {};
//let dataList: (string|number)[] = []; // eslint-disable-line prefer-const

const codeSnippets: Record<string, Function> = {
_dataDefine: function _dataDefine() { // not really used
_data = [ ];
_dataPtr = 0;
_restoreMap = {};
},
_dim: function _dim(dims: number[], initVal: string | number = 0): any[] {
const createRecursiveArray = (depth: number): any[] => {
const length = dims[depth] + 1; // +1 because of 0-based index
const array = Array.from({ length }, () =>
depth + 1 < dims.length ? createRecursiveArray(depth + 1) : initVal
);
return array;
};
return createRecursiveArray(0);
},
_input: function _input(msg: string, isNum: boolean) {
return new Promise(resolve => setTimeout(() => resolve(isNum ? Number(prompt(msg)) : prompt(msg)), 0));
},
_read: function _read() {
return _data[_dataPtr++];
},
_restore: function _restore(label: string) {
_dataPtr = _restoreMap[label];
}
};
return codeSnippets;
}


function evalChildren(children: Node[]) {
return children.map(c => c.eval());
}
Expand Down Expand Up @@ -80,21 +118,31 @@ function getSemantics(semanticsHelper: SemanticsHelper) {

const dataList = semanticsHelper.getDataList();
if (dataList.length) {
lineList.unshift(`const _data = _getData();\nlet _dataPrt = 0;`);
lineList.push(`function _getData() {\n return [\n${dataList.join(",\n")}\n ];\n}`);
//let startIdx = 0;
for (const key of Object.keys(restoreMap)) {
let index = restoreMap[key];
if (index < 0) {
index = 0;
restoreMap[key] = index; //TODO
}
}
lineList.unshift(`const {_data, _restoreMap} = _defineData();\nlet _dataPrt = 0;`);
lineList.push(`function _defineData() {\n const _data = [\n${dataList.join(",\n")}\n ];\nconst _restoreMap =\n ${JSON.stringify(restoreMap)};\nreturn {_data, _restoreMap}\n}`);
}
if (Object.keys(restoreMap).length) {
lineList.unshift(`const _restoreMap = _getRestore();`);
lineList.push(`function _getRestore() {\n return [\n ${JSON.stringify(restoreMap)}\n ];\n}`);

lineList.push("// library");

const instrKeys = semanticsHelper.getInstrKeys();
const codeSnippets = getCodeSnippets();
for (const key of instrKeys) {
lineList.push(String(codeSnippets[key]));
}

if (varStr) {
lineList.unshift(varStr);
}

if (semanticsHelper.getInstr("input")) {
lineList.push(`async function _input(msg, isNum) {\n return new Promise(resolve => setTimeout(() => resolve(isNum ? Number(prompt(msg)) : prompt(msg)), 0));\n}`);

if (instrKeys.includes("_input")) {
lineList.unshift(`return async function() {`);
lineList.push('}();');
}
Expand Down Expand Up @@ -133,7 +181,7 @@ function getSemantics(semanticsHelper: SemanticsHelper) {

Statements(stmt: Node, _stmtSep: Node, stmts: Node) {
// separate statements, use ";", if the last stmt does not end with "{"
return [stmt.eval(), ...evalChildren(stmts.children)].reduce((str, st) => str.endsWith("{") ? `${str} ${st}`: `${str}; ${st}` );
return [stmt.eval(), ...evalChildren(stmts.children)].reduce((str, st) => str.endsWith("{") ? `${str} ${st}` : `${str}; ${st}`);
},

ArrayAssign(ident: Node, _op: Node, e: Node): string {
Expand Down Expand Up @@ -180,17 +228,17 @@ function getSemantics(semanticsHelper: SemanticsHelper) {
Data(_datalit: Node, args: Node) {
const argList = args.asIteration().children.map(c => c.eval());

const dataList = semanticsHelper.getDataList();
const definedLabels = semanticsHelper.getDefinedLabels();

const dataIndex = dataList.length;

if (definedLabels.length) {
const dataIndex = semanticsHelper.getDataIndex();
const currentLabel = definedLabels[definedLabels.length - 1];
currentLabel.dataIndex = dataIndex;
}

const dataList = semanticsHelper.getDataList();
dataList.push(argList.join(", "));
semanticsHelper.addDataIndex(argList.length);
return "";
},

Expand All @@ -203,7 +251,8 @@ function getSemantics(semanticsHelper: SemanticsHelper) {
let createArrStr: string;
if (indices.length > 1) { // multi-dimensional?
const initValStr = ident.endsWith("$") ? ', ""' : '';
createArrStr = `_o.dimArray([${indices}]${initValStr})`; // automatically joined with comma
createArrStr = `_dim([${indices}]${initValStr})`; // indices are automatically joined with comma
semanticsHelper.addInstr("_dim");
} else {
const fillStr = ident.endsWith("$") ? `""` : "0";
createArrStr = `new Array(${indices[0]} + 1).fill(${fillStr})`; // +1 because of 0-based index
Expand Down Expand Up @@ -248,7 +297,7 @@ function getSemantics(semanticsHelper: SemanticsHelper) {
Erase(_eraseLit: Node, arrayIdents: Node) { // erase not really needed
const argList = arrayIdents.asIteration().children.map(c => c.eval());
const results: string[] = [];

for (const ident of argList) {
const initValStr = ident.endsWith("$") ? '""' : '0';
results.push(`${ident} = ${initValStr}`);
Expand Down Expand Up @@ -300,7 +349,7 @@ function getSemantics(semanticsHelper: SemanticsHelper) {
},

Input(_inputLit: Node, message: Node, _semi: Node, e: Node) {
semanticsHelper.addInstr("input");
semanticsHelper.addInstr("_input");

const msgStr = message.sourceString.replace(/\s*[;,]$/, "");
const ident = e.eval();
Expand Down Expand Up @@ -391,10 +440,12 @@ function getSemantics(semanticsHelper: SemanticsHelper) {
},

Read(_readlit: Node, args: Node) {
semanticsHelper.addInstr("_read");
const argList = args.asIteration().children.map(c => c.eval());
const results: string[] = [];
for (const ident of argList) {
results.push(`${ident} = _data[_dataPrt++]`);
//results.push(`${ident} = _data[_dataPrt++]`);
results.push(`${ident} = _read()`);
}
return results.join("; ");
},
Expand All @@ -407,7 +458,8 @@ function getSemantics(semanticsHelper: SemanticsHelper) {
const labelStr = e.sourceString || "0";
semanticsHelper.addRestoreLabel(labelStr);

return `_dataPtr = _restoreMap[${labelStr}]`;
semanticsHelper.addInstr("_restore");
return `_restore(${labelStr})`;
},

Return(_returnLit: Node) { // eslint-disable-line @typescript-eslint/no-unused-vars
Expand Down Expand Up @@ -650,6 +702,7 @@ export class Semantics {
private readonly gosubLabels: Record<string, GosubLabelEntryType> = {};

private readonly dataList: (string | number)[] = [];
private dataIndex = 0;
private readonly restoreMap: Record<string, number> = {};

private static readonly reJsKeyword = /^(arguments|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|eval|export|extends|false|finally|for|function|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)$/;
Expand Down Expand Up @@ -689,6 +742,14 @@ export class Semantics {
this.indentAdd += num;
}

private addDataIndex(count: number) {
return this.dataIndex += count;
}

private getDataIndex() {
return this.dataIndex;
}

private addDefinedLabel(label: string, line: number) {
this.definedLabels.push({
label,
Expand All @@ -697,7 +758,7 @@ export class Semantics {
dataIndex: -1
});
}

private getDefinedLabels() {
return this.definedLabels;
}
Expand All @@ -709,12 +770,12 @@ export class Semantics {
this.gosubLabels[label].count = (this.gosubLabels[label].count || 0) + 1;
}

private getGosubLabels() {
private getGosubLabels() {
return this.gosubLabels;
}

private getInstr(name: string) {
return this.instrMap[name] || 0;
private getInstrKeys() {
return Object.keys(this.instrMap);
}

private addInstr(name: string) {
Expand Down Expand Up @@ -767,24 +828,28 @@ export class Semantics {
this.definedLabels.length = 0;
Semantics.deleteAllItems(this.gosubLabels);
this.dataList.length = 0;
this.dataIndex = 0;
Semantics.deleteAllItems(this.restoreMap);
Semantics.deleteAllItems(this.instrMap);
}

public getSemantics() {
const semanticsHelper: SemanticsHelper = {
addDataIndex: (count: number) => this.addDataIndex(count),
addDefinedLabel: (label: string, line: number) => this.addDefinedLabel(label, line),
addGosubLabel: (label: string) => this.addGosubLabel(label),
addIndent: (num: number) => this.addIndent(num),
addInstr: (name: string) => this.addInstr(name),
addRestoreLabel: (label: string) => this.addRestoreLabel(label),
applyNextIndent: () => this.applyNextIndent(),
getDataIndex: () => this.getDataIndex(),
getDataList: () => this.getDataList(),
getDefinedLabels: () => this.getDefinedLabels(),
getGosubLabels: () => this.getGosubLabels(),
getIndent: () => this.getIndent(),
getIndentStr: () => this.getIndentStr(),
getInstr: (name: string) => this.getInstr(name),
//getInstr: (name: string) => this.getInstr(name),
getInstrKeys: () => this.getInstrKeys(),
getRestoreMap: () => this.getRestoreMap(),
getVariable: (name: string) => this.getVariable(name),
getVariables: () => this.getVariables(),
Expand Down
13 changes: 7 additions & 6 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,24 @@ function addItem(key: string, input: string | (() => void)) {


interface NodeFs {
//readFile: (name: string, encoding: string, fn: (res: any) => void) => any
promises: any;
promises: {
readFile(name: string, encoding: string): Promise<string>
};
}

let fs: NodeFs;
let modulePath: string;

declare function require(name: string): any;
declare function require(name: string): NodeModule | NodeFs;

async function nodeReadFile(name: string): Promise<string> {
if (!fs) {
fs = require("fs");
fs = require("fs") as NodeFs;
}

if (!module) {
module = require("module");
modulePath = (module as any).path || "";
module = require("module") as NodeModule;
modulePath = module.path || "";

if (!modulePath) {
console.warn("nodeReadFile: Cannot determine module path");
Expand Down

0 comments on commit 05f5378

Please sign in to comment.