1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-11-29 14:51:17 +00:00

basic: added ENTER elapsed time

This commit is contained in:
Steven Hugg 2020-08-20 22:20:53 -05:00
parent 7d806850ee
commit eeae662e6b
7 changed files with 53 additions and 22 deletions

View File

@ -10,7 +10,7 @@ export interface BASICOptions {
optionalLabels : boolean; // can omit line numbers and use labels? optionalLabels : boolean; // can omit line numbers and use labels?
optionalWhitespace : boolean; // can "crunch" keywords? also, eat extra ":" delims optionalWhitespace : boolean; // can "crunch" keywords? also, eat extra ":" delims
multipleStmtsPerLine : boolean; // multiple statements separated by ":" multipleStmtsPerLine : boolean; // multiple statements separated by ":"
varNaming : 'A'|'A1'|'AA'|'*'; // only allow A0-9 for numerics, single letter for arrays/strings varNaming : 'A'|'A1'|'A1$'|'AA'|'*'; // only allow A0-9 for numerics, single letter for arrays/strings
squareBrackets : boolean; // "[" and "]" interchangable with "(" and ")"? squareBrackets : boolean; // "[" and "]" interchangable with "(" and ")"?
tickComments : boolean; // support 'comments? tickComments : boolean; // support 'comments?
hexOctalConsts : boolean; // support &H and &O integer constants? hexOctalConsts : boolean; // support &H and &O integer constants?
@ -198,6 +198,13 @@ export interface INPUT_Statement extends Statement {
command: "INPUT"; command: "INPUT";
prompt: Expr; prompt: Expr;
args: IndOp[]; args: IndOp[];
timeout?: Expr;
elapsed?: IndOp;
}
export interface ENTER_Statement extends INPUT_Statement {
timeout: Expr;
elapsed: IndOp;
} }
export interface DATA_Statement extends Statement { export interface DATA_Statement extends Statement {
@ -852,6 +859,10 @@ export class BASICParser {
if (lexpr.args != null && !/^[A-Z]?[$]?$/i.test(lexpr.name)) if (lexpr.args != null && !/^[A-Z]?[$]?$/i.test(lexpr.name))
this.dialectErrorNoSupport(`array names other than a single letter`); this.dialectErrorNoSupport(`array names other than a single letter`);
break; break;
case 'A1$':
if (!/^[A-Z][0-9]?[$]?$/i.test(lexpr.name))
this.dialectErrorNoSupport(`variable names other than a letter followed by an optional digit`);
break;
case 'AA': case 'AA':
if (lexpr.args == null && !/^[A-Z][A-Z0-9]?[$]?$/i.test(lexpr.name)) if (lexpr.args == null && !/^[A-Z][A-Z0-9]?[$]?$/i.test(lexpr.name))
this.dialectErrorNoSupport(`variable names other than a letter followed by an optional letter or digit`); this.dialectErrorNoSupport(`variable names other than a letter followed by an optional letter or digit`);
@ -1057,11 +1068,11 @@ export class BASICParser {
} }
/* for HP BASIC only */ /* for HP BASIC only */
stmt__ENTER() : INPUT_Statement { stmt__ENTER() : INPUT_Statement {
var secs = this.parseExpr(); var timeout = this.parseExpr();
this.expectToken(','); this.expectToken(',');
var result = this.parseLexpr(); // TODO: this has to go somewheres var elapsed = this.parseLexpr(); // TODO: this has to go somewheres
this.expectToken(','); this.expectToken(',');
return this.stmt__INPUT(); return { command:'INPUT', prompt:null, args:this.parseLexprList(), timeout:timeout, elapsed:elapsed };
} }
// TODO: DATA statement doesn't read unquoted strings // TODO: DATA statement doesn't read unquoted strings
stmt__DATA() : DATA_Statement { stmt__DATA() : DATA_Statement {
@ -1406,7 +1417,7 @@ export const HP_TIMESHARED_BASIC : BASICOptions = {
optionalLabels : false, optionalLabels : false,
optionalWhitespace : false, optionalWhitespace : false,
multipleStmtsPerLine : true, multipleStmtsPerLine : true,
varNaming : "A1", varNaming : "A1$",
staticArrays : true, staticArrays : true,
sharedArrayNamespace : false, sharedArrayNamespace : false,
defaultArrayBase : 1, defaultArrayBase : 1,
@ -1874,6 +1885,7 @@ const BUILTIN_DEFS : BuiltinFunctionDef[] = [
['OCT$', ['number'], 'string' ], ['OCT$', ['number'], 'string' ],
['PI', [], 'number'], ['PI', [], 'number'],
['POS', ['number'], 'number' ], // arg ignored ['POS', ['number'], 'number' ], // arg ignored
['POS', ['string','string'], 'number' ], // HP POS
['LEFT$', ['string', 'number'], 'string' ], ['LEFT$', ['string', 'number'], 'string' ],
['RND', [], 'number' ], ['RND', [], 'number' ],
['RND', ['number'], 'number' ], ['RND', ['number'], 'number' ],

View File

@ -1,6 +1,6 @@
import { BASICParser, DIALECTS, BASICOptions, CompileError } from "./compiler"; import { BASICParser, DIALECTS, BASICOptions, CompileError } from "./compiler";
import { BASICRuntime } from "./runtime"; import { BASICRuntime, InputResponse } from "./runtime";
import { EmuHalt } from "../emu"; import { EmuHalt } from "../emu";
process.on('unhandledRejection', (reason, promise) => { process.on('unhandledRejection', (reason, promise) => {
@ -20,12 +20,12 @@ export function fuzz(buf) {
runtime.print = (s) => { runtime.print = (s) => {
if (s == null) throw new Error("PRINT null string"); if (s == null) throw new Error("PRINT null string");
} }
runtime.input = function(prompt: string, nargs: number) : Promise<string[]> { runtime.input = function(prompt: string, nargs: number) : Promise<InputResponse> {
var p = new Promise<string[]>( (resolve, reject) => { var p = new Promise<InputResponse>( (resolve, reject) => {
var arr = []; var arr = [];
for (var i=0; i<Math.random()*10; i++) for (var i=0; i<Math.random()*10; i++)
arr.push(i+""); arr.push(i+"");
resolve(arr); resolve({vals:arr, line:arr.join(' ')});
}); });
return p; return p;
} }

View File

@ -69,9 +69,10 @@ runtime.print = (s:string) => {
runtime.input = async (prompt:string) => { runtime.input = async (prompt:string) => {
return new Promise( (resolve, reject) => { return new Promise( (resolve, reject) => {
function answered(answer) { function answered(answer) {
var vals = answer.toUpperCase().split(','); var line = answer.toUpperCase();
var vals = line.split(',');
//console.log(">>>",vals); //console.log(">>>",vals);
resolve(vals); resolve({line:line, vals:vals});
} }
prompt += ' ?'; prompt += ' ?';
if (inputlines.length) { if (inputlines.length) {

View File

@ -16,6 +16,12 @@ function isUnOp(arg: basic.Expr): arg is basic.UnOp {
return (arg as any).op != null && (arg as any).expr != null; return (arg as any).op != null && (arg as any).expr != null;
} }
export interface InputResponse {
line: string;
vals: string[];
elapsed?: number;
}
// expr2js() options // expr2js() options
class ExprOptions { class ExprOptions {
isconst?: boolean; // only allow constant operations isconst?: boolean; // only allow constant operations
@ -408,8 +414,8 @@ export class BASICRuntime {
} }
// override this // override this
async input(prompt: string, nargs: number) : Promise<string[]> { async input(prompt: string, nargs: number) : Promise<InputResponse> {
return []; return {line:"", vals:[]};
} }
// override this // override this
@ -730,22 +736,24 @@ export class BASICRuntime {
} }
do__INPUT(stmt : basic.INPUT_Statement) { do__INPUT(stmt : basic.INPUT_Statement) {
var prompt = this.expr2js(stmt.prompt); var prompt = stmt.prompt != null ? this.expr2js(stmt.prompt) : '""';
var elapsed = stmt.elapsed != null ? this.assign2js(stmt.elapsed) : "let ___";
var setvals = ''; var setvals = '';
stmt.args.forEach((arg, index) => { stmt.args.forEach((arg, index) => {
var lexpr = this.assign2js(arg); var lexpr = this.assign2js(arg);
setvals += ` setvals += `
var value = this.convert(${JSON.stringify(arg.name)}, vals[${index}]); var value = this.convert(${JSON.stringify(arg.name)}, response.vals[${index}]);
valid &= this.isValid(value); valid &= this.isValid(value);
${lexpr} = value; ${lexpr} = value;
` `
}); });
return `this.preInput(); return `this.preInput();
this.input(${prompt}, ${stmt.args.length}).then((vals) => { this.input(${prompt}, ${stmt.args.length}).then((response) => {
let valid = 1; let valid = 1;
${setvals} ${setvals}
this.postInput(valid); this.postInput(valid);
this.column = 0; // assume linefeed this.column = 0; // assume linefeed
${elapsed} = response.elapsed;
})`; })`;
} }
@ -1165,8 +1173,11 @@ export class BASICRuntime {
return Math.PI; return Math.PI;
} }
// TODO: POS(haystack, needle, start) // TODO: POS(haystack, needle, start)
POS(arg : number) : number { // arg ignored POS(arg1, arg2) { // arg ignored
return this.column + 1; if (typeof arg1 == 'string' && typeof arg2 == 'string')
return arg1.indexOf(arg2) >= 0 + 1;
else
return this.column + 1;
} }
RIGHT$(arg : string, count : number) : string { RIGHT$(arg : string, count : number) : string {
arg = this.checkString(arg); arg = this.checkString(arg);

View File

@ -1,4 +1,6 @@
import { InputResponse } from "./basic/runtime";
export class TeleType { export class TeleType {
page: HTMLElement; page: HTMLElement;
fixed: boolean; fixed: boolean;
@ -166,11 +168,12 @@ export class TeleTypeWithKeyboard extends TeleType {
keephandler : boolean = true; keephandler : boolean = true;
uppercaseOnly : boolean = false; uppercaseOnly : boolean = false;
splitInput : boolean = false; splitInput : boolean = false;
resolveInput : (inp) => void; resolveInput : (InputResponse) => void;
focused : boolean = true; focused : boolean = true;
scrolling : number = 0; scrolling : number = 0;
waitingfor : string; waitingfor : string;
lastInputRequestTime : number;
constructor(page: HTMLElement, fixed: boolean, input: HTMLInputElement) { constructor(page: HTMLElement, fixed: boolean, input: HTMLInputElement) {
super(page, fixed); super(page, fixed);
@ -214,6 +217,7 @@ export class TeleTypeWithKeyboard extends TeleType {
$(this.input).addClass('transcript-input-char') $(this.input).addClass('transcript-input-char')
else else
$(this.input).removeClass('transcript-input-char') $(this.input).removeClass('transcript-input-char')
this.lastInputRequestTime = Date.now();
} }
hideinput() { hideinput() {
this.showPrintHead(true); this.showPrintHead(true);
@ -238,12 +242,14 @@ export class TeleTypeWithKeyboard extends TeleType {
} }
sendinput(s: string) { sendinput(s: string) {
if (this.resolveInput) { if (this.resolveInput) {
var elapsed = Date.now() - this.lastInputRequestTime;
if (this.uppercaseOnly) s = s.toUpperCase(); // TODO: always uppercase? if (this.uppercaseOnly) s = s.toUpperCase(); // TODO: always uppercase?
this.addtext(s, 4); this.addtext(s, 4);
this.flushline(); this.flushline();
this.clearinput(); this.clearinput();
this.hideinput(); // keep from losing input handlers this.hideinput(); // keep from losing input handlers
this.resolveInput(this.splitInput ? s.split(',') : s); var vals = this.splitInput ? s.split(',') : null;
this.resolveInput({line:s, vals:vals, elapsed:elapsed/1000});
if (!this.keephandler) this.resolveInput = null; if (!this.keephandler) this.resolveInput = null;
} }
} }

View File

@ -262,7 +262,6 @@ export class SourceEditor implements ProjectView {
} }
clearErrors() { clearErrors() {
this.refreshDebugState(false); // TODO: why?
this.dirtylisting = true; this.dirtylisting = true;
// clear line widgets // clear line widgets
this.editor.clearGutter("gutter-info"); this.editor.clearGutter("gutter-info");

View File

@ -3,6 +3,7 @@ import { Platform, BasePlatform, BaseDebugPlatform, Preset, EmuState, inspectSym
import { PLATFORMS, EmuHalt } from "../common/emu"; import { PLATFORMS, EmuHalt } from "../common/emu";
import { loadScript } from "../ide/ui"; import { loadScript } from "../ide/ui";
import { TeleType, TeleTypeWithKeyboard } from "../common/teletype"; import { TeleType, TeleTypeWithKeyboard } from "../common/teletype";
import { InputResponse } from "../common/basic/runtime";
const ZMACHINE_PRESETS = [ const ZMACHINE_PRESETS = [
{ id: 'hello.inf', name: 'Hello World' }, { id: 'hello.inf', name: 'Hello World' },
@ -71,7 +72,8 @@ class GlkImpl {
3: new TeleType(null, true), // fake window for resizing 3: new TeleType(null, true), // fake window for resizing
}; };
this.input = input; this.input = input;
this.mainwnd.resolveInput = (s:string) => { this.mainwnd.resolveInput = (resp:InputResponse) => {
var s = resp.line;
if (this.vm.read_data.buffer) { if (this.vm.read_data.buffer) {
for (var i = 0; i < s.length; i++) { for (var i = 0; i < s.length; i++) {
this.vm.read_data.buffer[i] = s.charCodeAt(i) & 0xff; this.vm.read_data.buffer[i] = s.charCodeAt(i) & 0xff;