diff --git a/README.md b/README.md
index d6217ea..95f9643 100644
--- a/README.md
+++ b/README.md
@@ -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?
diff --git a/dist/index.html b/dist/index.html
index cc09c14..7911c69 100644
--- a/dist/index.html
+++ b/dist/index.html
@@ -4,7 +4,7 @@
-
LocoBasic v0.1.6
+ LocoBasic v0.1.12
@@ -32,7 +32,7 @@
Output
-
+
diff --git a/package.json b/package.json
index d779e87..11a0991 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "locobasic",
- "version": "0.1.11",
+ "version": "0.1.12",
"description": "# LocoBasic - Loco BASIC",
"type": "commonjs",
"scripts": {
diff --git a/src/Core.ts b/src/Core.ts
index 2af1a35..796e00d 100644
--- a/src/Core.ts
+++ b/src/Core.ts
@@ -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();
diff --git a/src/Semantics.ts b/src/Semantics.ts
index b2eafd5..785b867 100644
--- a/src/Semantics.ts
+++ b/src/Semantics.ts
@@ -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,
getIndent(): number,
getIndentStr(): string,
- getInstr(name: string): number,
+ getInstrKeys(): string[],
getRestoreMap(): Record,
getVariable(name: string): string,
getVariables(): string[],
@@ -34,6 +36,42 @@ interface SemanticsHelper {
setIndent(indent: number): void
}
+function getCodeSnippets() {
+ let _data: (string | number)[] = [];
+ let _dataPtr = 0;
+ let _restoreMap: Record = {};
+ //let dataList: (string|number)[] = []; // eslint-disable-line prefer-const
+
+ const codeSnippets: Record = {
+ _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());
}
@@ -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('}();');
}
@@ -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 {
@@ -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 "";
},
@@ -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
@@ -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}`);
@@ -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();
@@ -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("; ");
},
@@ -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
@@ -650,6 +702,7 @@ export class Semantics {
private readonly gosubLabels: Record = {};
private readonly dataList: (string | number)[] = [];
+ private dataIndex = 0;
private readonly restoreMap: Record = {};
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)$/;
@@ -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,
@@ -697,7 +758,7 @@ export class Semantics {
dataIndex: -1
});
}
-
+
private getDefinedLabels() {
return this.definedLabels;
}
@@ -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) {
@@ -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(),
diff --git a/src/main.ts b/src/main.ts
index c2207b2..0e751e3 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -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
+ };
}
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 {
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");