1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-11-25 18:33:11 +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?
optionalWhitespace : boolean; // can "crunch" keywords? also, eat extra ":" delims
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 ")"?
tickComments : boolean; // support 'comments?
hexOctalConsts : boolean; // support &H and &O integer constants?
@ -198,6 +198,13 @@ export interface INPUT_Statement extends Statement {
command: "INPUT";
prompt: Expr;
args: IndOp[];
timeout?: Expr;
elapsed?: IndOp;
}
export interface ENTER_Statement extends INPUT_Statement {
timeout: Expr;
elapsed: IndOp;
}
export interface DATA_Statement extends Statement {
@ -852,6 +859,10 @@ export class BASICParser {
if (lexpr.args != null && !/^[A-Z]?[$]?$/i.test(lexpr.name))
this.dialectErrorNoSupport(`array names other than a single letter`);
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':
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`);
@ -1057,11 +1068,11 @@ export class BASICParser {
}
/* for HP BASIC only */
stmt__ENTER() : INPUT_Statement {
var secs = this.parseExpr();
var timeout = this.parseExpr();
this.expectToken(',');
var result = this.parseLexpr(); // TODO: this has to go somewheres
var elapsed = this.parseLexpr(); // TODO: this has to go somewheres
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
stmt__DATA() : DATA_Statement {
@ -1406,7 +1417,7 @@ export const HP_TIMESHARED_BASIC : BASICOptions = {
optionalLabels : false,
optionalWhitespace : false,
multipleStmtsPerLine : true,
varNaming : "A1",
varNaming : "A1$",
staticArrays : true,
sharedArrayNamespace : false,
defaultArrayBase : 1,
@ -1874,6 +1885,7 @@ const BUILTIN_DEFS : BuiltinFunctionDef[] = [
['OCT$', ['number'], 'string' ],
['PI', [], 'number'],
['POS', ['number'], 'number' ], // arg ignored
['POS', ['string','string'], 'number' ], // HP POS
['LEFT$', ['string', 'number'], 'string' ],
['RND', [], 'number' ],
['RND', ['number'], 'number' ],

View File

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

View File

@ -69,9 +69,10 @@ runtime.print = (s:string) => {
runtime.input = async (prompt:string) => {
return new Promise( (resolve, reject) => {
function answered(answer) {
var vals = answer.toUpperCase().split(',');
var line = answer.toUpperCase();
var vals = line.split(',');
//console.log(">>>",vals);
resolve(vals);
resolve({line:line, vals:vals});
}
prompt += ' ?';
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;
}
export interface InputResponse {
line: string;
vals: string[];
elapsed?: number;
}
// expr2js() options
class ExprOptions {
isconst?: boolean; // only allow constant operations
@ -408,8 +414,8 @@ export class BASICRuntime {
}
// override this
async input(prompt: string, nargs: number) : Promise<string[]> {
return [];
async input(prompt: string, nargs: number) : Promise<InputResponse> {
return {line:"", vals:[]};
}
// override this
@ -730,22 +736,24 @@ export class BASICRuntime {
}
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 = '';
stmt.args.forEach((arg, index) => {
var lexpr = this.assign2js(arg);
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);
${lexpr} = value;
`
});
return `this.preInput();
this.input(${prompt}, ${stmt.args.length}).then((vals) => {
this.input(${prompt}, ${stmt.args.length}).then((response) => {
let valid = 1;
${setvals}
this.postInput(valid);
this.column = 0; // assume linefeed
${elapsed} = response.elapsed;
})`;
}
@ -1165,8 +1173,11 @@ export class BASICRuntime {
return Math.PI;
}
// TODO: POS(haystack, needle, start)
POS(arg : number) : number { // arg ignored
return this.column + 1;
POS(arg1, arg2) { // arg ignored
if (typeof arg1 == 'string' && typeof arg2 == 'string')
return arg1.indexOf(arg2) >= 0 + 1;
else
return this.column + 1;
}
RIGHT$(arg : string, count : number) : string {
arg = this.checkString(arg);

View File

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

View File

@ -262,7 +262,6 @@ export class SourceEditor implements ProjectView {
}
clearErrors() {
this.refreshDebugState(false); // TODO: why?
this.dirtylisting = true;
// clear line widgets
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 { loadScript } from "../ide/ui";
import { TeleType, TeleTypeWithKeyboard } from "../common/teletype";
import { InputResponse } from "../common/basic/runtime";
const ZMACHINE_PRESETS = [
{ id: 'hello.inf', name: 'Hello World' },
@ -71,7 +72,8 @@ class GlkImpl {
3: new TeleType(null, true), // fake window for resizing
};
this.input = input;
this.mainwnd.resolveInput = (s:string) => {
this.mainwnd.resolveInput = (resp:InputResponse) => {
var s = resp.line;
if (this.vm.read_data.buffer) {
for (var i = 0; i < s.length; i++) {
this.vm.read_data.buffer[i] = s.charCodeAt(i) & 0xff;