mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-08-09 09:25:27 +00:00
basic: help, dialect info, fixed advance() issue, slice = bug, GOTO ref fix, RESTORE fix
This commit is contained in:
@@ -321,9 +321,12 @@ export class BASICParser {
|
|||||||
this.listings = {};
|
this.listings = {};
|
||||||
this.refs = {};
|
this.refs = {};
|
||||||
}
|
}
|
||||||
compileError(msg: string, loc?: SourceLocation) {
|
addError(msg: string, loc?: SourceLocation) {
|
||||||
if (!loc) loc = this.peekToken().$loc;
|
if (!loc) loc = this.peekToken().$loc;
|
||||||
this.errors.push({path:loc.path, line:loc.line, label:this.curlabel, start:loc.start, end:loc.end, msg:msg});
|
this.errors.push({path:loc.path, line:loc.line, label:this.curlabel, start:loc.start, end:loc.end, msg:msg});
|
||||||
|
}
|
||||||
|
compileError(msg: string, loc?: SourceLocation) {
|
||||||
|
this.addError(msg, loc);
|
||||||
throw new CompileError(msg, loc);
|
throw new CompileError(msg, loc);
|
||||||
}
|
}
|
||||||
dialectError(what: string, loc?: SourceLocation) {
|
dialectError(what: string, loc?: SourceLocation) {
|
||||||
@@ -576,8 +579,14 @@ export class BASICParser {
|
|||||||
}
|
}
|
||||||
parseLabel() : Expr {
|
parseLabel() : Expr {
|
||||||
// parse full expr?
|
// parse full expr?
|
||||||
if (this.opts.computedGoto)
|
if (this.opts.computedGoto) {
|
||||||
return this.parseExpr();
|
// parse expression, but still add to list of label targets if constant
|
||||||
|
var expr = this.parseExpr();
|
||||||
|
if ((expr as Literal).value != null) {
|
||||||
|
this.targets[(expr as Literal).value] = this.lasttoken.$loc;
|
||||||
|
}
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
// parse a single number or ident label
|
// parse a single number or ident label
|
||||||
var tok = this.consumeToken();
|
var tok = this.consumeToken();
|
||||||
switch (tok.type) {
|
switch (tok.type) {
|
||||||
@@ -991,7 +1000,7 @@ export class BASICParser {
|
|||||||
checkLabels() {
|
checkLabels() {
|
||||||
for (let targ in this.targets) {
|
for (let targ in this.targets) {
|
||||||
if (this.labels[targ] == null) {
|
if (this.labels[targ] == null) {
|
||||||
this.compileError(`There isn't a line number ${targ}.`, this.targets[targ]);
|
this.addError(`There isn't a line number ${targ}.`, this.targets[targ]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1081,7 +1090,7 @@ export const TINY_BASIC : BASICOptions = {
|
|||||||
multipleNextVars : false,
|
multipleNextVars : false,
|
||||||
bitwiseLogic : false,
|
bitwiseLogic : false,
|
||||||
checkOnGotoIndex : false,
|
checkOnGotoIndex : false,
|
||||||
computedGoto : true,
|
computedGoto : true, // TODO: is it really though?
|
||||||
restoreWithLabel : false,
|
restoreWithLabel : false,
|
||||||
squareBrackets : false,
|
squareBrackets : false,
|
||||||
arraysContainChars : false,
|
arraysContainChars : false,
|
||||||
@@ -1090,7 +1099,7 @@ export const TINY_BASIC : BASICOptions = {
|
|||||||
|
|
||||||
|
|
||||||
export const HP_TIMESHARED_BASIC : BASICOptions = {
|
export const HP_TIMESHARED_BASIC : BASICOptions = {
|
||||||
dialectName: "HPTSB",
|
dialectName: "HP2000",
|
||||||
asciiOnly : true,
|
asciiOnly : true,
|
||||||
uppercaseOnly : false,
|
uppercaseOnly : false,
|
||||||
optionalLabels : false,
|
optionalLabels : false,
|
||||||
@@ -1400,8 +1409,9 @@ export const DIALECTS = {
|
|||||||
"ALTAIR41": ALTAIR_BASIC41,
|
"ALTAIR41": ALTAIR_BASIC41,
|
||||||
"ECMA55": ECMA55_MINIMAL,
|
"ECMA55": ECMA55_MINIMAL,
|
||||||
"MINIMAL": ECMA55_MINIMAL,
|
"MINIMAL": ECMA55_MINIMAL,
|
||||||
"HPTSB": HP_TIMESHARED_BASIC,
|
"HP": HP_TIMESHARED_BASIC,
|
||||||
"HPB": HP_TIMESHARED_BASIC,
|
"HPB": HP_TIMESHARED_BASIC,
|
||||||
|
"HPTSB": HP_TIMESHARED_BASIC,
|
||||||
"HP2000": HP_TIMESHARED_BASIC,
|
"HP2000": HP_TIMESHARED_BASIC,
|
||||||
"HPBASIC": HP_TIMESHARED_BASIC,
|
"HPBASIC": HP_TIMESHARED_BASIC,
|
||||||
"HPACCESS": HP_TIMESHARED_BASIC,
|
"HPACCESS": HP_TIMESHARED_BASIC,
|
||||||
|
@@ -1,6 +1,33 @@
|
|||||||
|
|
||||||
import { BASICParser, DIALECTS } from "./compiler";
|
import { BASICParser, DIALECTS, BASICOptions } from "./compiler";
|
||||||
import { BASICRuntime } from "./runtime";
|
import { BASICRuntime } from "./runtime";
|
||||||
|
import { lpad, rpad } from "../util";
|
||||||
|
|
||||||
|
function dumpDialectInfo() {
|
||||||
|
var dialects = new Set<BASICOptions>();
|
||||||
|
var array = {};
|
||||||
|
var SELECTED_DIALECTS = ['TINY','ECMA55','HP','ALTAIR41','BASIC80','MODERN'];
|
||||||
|
SELECTED_DIALECTS.forEach((dkey) => {
|
||||||
|
dialects.add(DIALECTS[dkey]);
|
||||||
|
});
|
||||||
|
dialects.forEach((dialect) => {
|
||||||
|
Object.entries(dialect).forEach(([key, value]) => {
|
||||||
|
if (value === null) value = "all";
|
||||||
|
else if (value === true) value = "Y";
|
||||||
|
else if (value === false) value = "-";
|
||||||
|
else if (Array.isArray(value))
|
||||||
|
value = value.length;
|
||||||
|
if (!array[key]) array[key] = [];
|
||||||
|
array[key].push(value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Object.entries(array).forEach(([key, arr]) => {
|
||||||
|
var s = rpad(key, 30) + "|";
|
||||||
|
s += (arr as []).map((val) => rpad(val, 9)).join('|');
|
||||||
|
console.log(s);
|
||||||
|
});
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
var readline = require('readline');
|
var readline = require('readline');
|
||||||
var rl = readline.createInterface({
|
var rl = readline.createInterface({
|
||||||
@@ -28,6 +55,8 @@ for (var i=0; i<args.length; i++) {
|
|||||||
runtime.trace = true;
|
runtime.trace = true;
|
||||||
else if (args[i] == '-d')
|
else if (args[i] == '-d')
|
||||||
parser.opts = DIALECTS[args[++i]] || Error('no such dialect');
|
parser.opts = DIALECTS[args[++i]] || Error('no such dialect');
|
||||||
|
else if (args[i] == '--dialects')
|
||||||
|
dumpDialectInfo();
|
||||||
else
|
else
|
||||||
filename = args[i];
|
filename = args[i];
|
||||||
}
|
}
|
||||||
|
@@ -121,9 +121,9 @@ export class BASICRuntime {
|
|||||||
});
|
});
|
||||||
// parse DATA literals
|
// parse DATA literals
|
||||||
this.allstmts.filter((stmt) => stmt.command == 'DATA').forEach((datastmt) => {
|
this.allstmts.filter((stmt) => stmt.command == 'DATA').forEach((datastmt) => {
|
||||||
|
this.label2dataptr[datastmt.$loc.label] = this.datums.length;
|
||||||
(datastmt as basic.DATA_Statement).datums.forEach(datum => {
|
(datastmt as basic.DATA_Statement).datums.forEach(datum => {
|
||||||
this.curpc = datastmt.$loc.offset; // for error reporting
|
this.curpc = datastmt.$loc.offset; // for error reporting
|
||||||
this.label2dataptr[datastmt.$loc.label] = this.datums.length;
|
|
||||||
this.datums.push(datum);
|
this.datums.push(datum);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -409,6 +409,7 @@ export class BASICRuntime {
|
|||||||
this.checkFuncArgs(expr, this.builtins[expr.name]);
|
this.checkFuncArgs(expr, this.builtins[expr.name]);
|
||||||
s += `this.builtins.${expr.name}(${jsargs})`;
|
s += `this.builtins.${expr.name}(${jsargs})`;
|
||||||
} else if (expr.args) {
|
} else if (expr.args) {
|
||||||
|
// get array slice (HP BASIC)
|
||||||
if (this.opts.arraysContainChars && expr.name.endsWith('$'))
|
if (this.opts.arraysContainChars && expr.name.endsWith('$'))
|
||||||
s += `this.MID$(this.vars.${expr.name}, ${jsargs})`;
|
s += `this.MID$(this.vars.${expr.name}, ${jsargs})`;
|
||||||
else
|
else
|
||||||
@@ -436,9 +437,14 @@ export class BASICRuntime {
|
|||||||
if (expr.name.startsWith("FN") || this.builtins[expr.name]) this.runtimeError(`I can't call a function here.`);
|
if (expr.name.startsWith("FN") || this.builtins[expr.name]) this.runtimeError(`I can't call a function here.`);
|
||||||
// is it a subscript?
|
// is it a subscript?
|
||||||
if (expr.args) {
|
if (expr.args) {
|
||||||
s += this.expr2js(expr, {novalid:true}); // check array bounds
|
// set array slice (HP BASIC)
|
||||||
s += `;this.getArray(${qname}, ${expr.args.length})`;
|
if (this.opts.arraysContainChars && expr.name.endsWith('$')) {
|
||||||
s += expr.args.map((arg) => '[this.ROUND('+this.expr2js(arg, opts)+')]').join('');
|
this.runtimeError(`I can't set array slices via this command yet.`);
|
||||||
|
} else {
|
||||||
|
s += this.expr2js(expr, {novalid:true}); // check array bounds
|
||||||
|
s += `;this.getArray(${qname}, ${expr.args.length})`;
|
||||||
|
s += expr.args.map((arg) => '[this.ROUND('+this.expr2js(arg, opts)+')]').join('');
|
||||||
|
}
|
||||||
} else { // just a variable
|
} else { // just a variable
|
||||||
s = `this.vars.${expr.name}`;
|
s = `this.vars.${expr.name}`;
|
||||||
}
|
}
|
||||||
@@ -664,7 +670,6 @@ export class BASICRuntime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
do__LET(stmt : basic.LET_Statement) {
|
do__LET(stmt : basic.LET_Statement) {
|
||||||
var lexpr = this.assign2js(stmt.lexpr);
|
|
||||||
var right = this.expr2js(stmt.right);
|
var right = this.expr2js(stmt.right);
|
||||||
// HP BASIC string-slice syntax?
|
// HP BASIC string-slice syntax?
|
||||||
if (this.opts.arraysContainChars && stmt.lexpr.args && stmt.lexpr.name.endsWith('$')) {
|
if (this.opts.arraysContainChars && stmt.lexpr.args && stmt.lexpr.name.endsWith('$')) {
|
||||||
@@ -674,6 +679,7 @@ export class BASICRuntime {
|
|||||||
console.log(s);
|
console.log(s);
|
||||||
return s;
|
return s;
|
||||||
} else {
|
} else {
|
||||||
|
var lexpr = this.assign2js(stmt.lexpr);
|
||||||
return `${lexpr} = this.assign(${JSON.stringify(stmt.lexpr.name)}, ${right});`;
|
return `${lexpr} = this.assign(${JSON.stringify(stmt.lexpr.name)}, ${right});`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -83,8 +83,9 @@ class BASICPlatform implements Platform {
|
|||||||
if (this.tty.isBusy()) return;
|
if (this.tty.isBusy()) return;
|
||||||
var ips = this.program.opts.commandsPerSec || 1000;
|
var ips = this.program.opts.commandsPerSec || 1000;
|
||||||
this.animcount += ips / 60;
|
this.animcount += ips / 60;
|
||||||
while (!this.runtime.exited && this.animcount-- > 0) {
|
while (this.runtime.running && this.animcount-- > 0) {
|
||||||
this.advance();
|
if (!this.advance())
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,6 +195,9 @@ class BASICPlatform implements Platform {
|
|||||||
return o.toString();
|
return o.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
showHelp(tool:string, ident:string) {
|
||||||
|
window.open("https://8bitworkshop.com/blog/platforms/basic/", "_help");
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: debugging (get running state, etc)
|
// TODO: debugging (get running state, etc)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user