From 0fd04658cb8fc7193377d11ba22ad72951d4afca Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 12 Aug 2020 10:51:18 -0500 Subject: [PATCH] basic: fixed tty column count, added hex/octal --- presets/basic/hello.bas | 2 +- src/common/basic/compiler.ts | 24 ++++++++++++++++++------ src/common/teletype.ts | 12 +++++------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/presets/basic/hello.bas b/presets/basic/hello.bas index e0787527..de4c8fb8 100644 --- a/presets/basic/hello.bas +++ b/presets/basic/hello.bas @@ -2,7 +2,7 @@ 15 PRINT 20 INPUT "WOULD YOU MIND TYPING IN YOUR NAME";A$ 25 PRINT -30 PRINT "THANKS, ";A$;"! THIS WILL BE FUN!" +30 PRINT "THANKS, ";A$;"! THIS WILL BE FUN!";CHR$(7) 35 PRINT 40 INPUT "NOW TELL ME YOUR FAVORITE NUMBER";N 45 PRINT diff --git a/src/common/basic/compiler.ts b/src/common/basic/compiler.ts index 59efacf3..0de545ac 100644 --- a/src/common/basic/compiler.ts +++ b/src/common/basic/compiler.ts @@ -9,6 +9,7 @@ export interface BASICOptions { optionalWhitespace : boolean; // can "crunch" keywords? varNaming : 'A1'|'AA'|'*'; // only allow A0-9 for numerics, single letter for arrays/strings tickComments : boolean; // support 'comments? + hexOctalConsts : boolean; // support &H and &O integer constants? validKeywords : string[]; // valid keywords (or null for accept all) validFunctions : string[]; // valid functions (or null for accept all) validOperators : string[]; // valid operators (or null for accept all) @@ -50,13 +51,14 @@ export class CompileError extends Error { // Lexer regular expression -- each (capture group) handles a different token type -const re_toks = /([0-9.]+[E][+-]?\d+)|(\d*[.]\d*[E0-9]*)|[0]*(\d+)|(['].*)|(\w+[$]?)|(".*?")|([<>]?[=<>])|([-+*/^,;:()?\\])|(\S+)/gi; +const re_toks = /([0-9.]+[E][+-]?\d+)|(\d*[.]\d*[E0-9]*)|[0]*(\d+)|&([OH][0-9A-F]+)|(['].*)|(\w+[$]?)|(".*?")|([<>]?[=<>])|([-+*/^,;:()?\\])|(\S+)/gi; export enum TokenType { EOL = 0, Float1, Float2, Int, + HexOctalInt, Remark, Ident, String, @@ -313,7 +315,7 @@ export class BASICParser { var tok = this.consumeToken(); var tokstr = tok.str; if (strlist.indexOf(tokstr) < 0) { - this.compileError(msg || `There should be one of "${strlist}" here.`); + this.compileError(msg || `There should be a ${strlist.map((s) => `"${s}"`).join(' or ')} here.`); } return tok; } @@ -343,6 +345,7 @@ export class BASICParser { line.label = tok.str; this.curlabel = tok.str; break; + case TokenType.HexOctalInt: case TokenType.Float1: case TokenType.Float2: this.compileError(`Line numbers must be positive integers.`); @@ -384,7 +387,7 @@ export class BASICParser { if (this.opts.asciiOnly && !/^[\x00-\x7F]*$/.test(s)) this.dialectError(`non-ASCII characters`); // uppercase all identifiers, and maybe more - if (i == TokenType.Ident || this.opts.uppercaseOnly) + if (i == TokenType.Ident || i == TokenType.HexOctalInt || this.opts.uppercaseOnly) s = s.toUpperCase(); // un-crunch tokens? if (splitre) { @@ -396,7 +399,7 @@ export class BASICParser { } else break; } - if (/^[0-9]+$/.test(s)) i = TokenType.Int; + if (/^[0-9]+$/.test(s)) i = TokenType.Int; // leftover might be integer } // add token to list this.tokens.push({str: s, type: i, $loc:loc}); @@ -543,12 +546,16 @@ export class BASICParser { parsePrimary(): Expr { let tok = this.consumeToken(); switch (tok.type) { + case TokenType.HexOctalInt: + if (!this.opts.hexOctalConsts) this.dialectError(`hex/octal constants`); + let base = tok.str.startsWith('H') ? 16 : 8; + return { value: parseInt(tok.str.substr(1), base) }; case TokenType.Int: case TokenType.Float1: case TokenType.Float2: - return { value: this.parseNumber(tok.str)/*, $loc: tok.$loc*/ }; + return { value: this.parseNumber(tok.str) }; case TokenType.String: - return { value: stripQuotes(tok.str)/*, $loc: tok.$loc*/ }; + return { value: stripQuotes(tok.str) }; case TokenType.Ident: if (tok.str == 'NOT') { let expr = this.parsePrimary(); @@ -882,6 +889,7 @@ export const ECMA55_MINIMAL : BASICOptions = { maxDefArgs : 255, maxStringLength : 255, tickComments : false, + hexOctalConsts : false, validKeywords : ['BASE','DATA','DEF','DIM','END', 'FOR','GO','GOSUB','GOTO','IF','INPUT','LET','NEXT','ON','OPTION','PRINT', 'RANDOMIZE','READ','REM','RESTORE','RETURN','STEP','STOP','SUB','THEN','TO' @@ -915,6 +923,7 @@ export const BASICODE : BASICOptions = { maxDefArgs : 255, maxStringLength : 255, tickComments : false, + hexOctalConsts : false, validKeywords : ['BASE','DATA','DEF','DIM','END', 'FOR','GO','GOSUB','GOTO','IF','INPUT','LET','NEXT','ON','OPTION','PRINT', 'READ','REM','RESTORE','RETURN','STEP','STOP','SUB','THEN','TO' @@ -949,6 +958,7 @@ export const ALTAIR_BASIC41 : BASICOptions = { maxDefArgs : 255, maxStringLength : 255, tickComments : false, + hexOctalConsts : false, validKeywords : [ 'OPTION', 'CONSOLE','DATA','DEF','DEFUSR','DIM','END','ERASE','ERROR', @@ -986,6 +996,7 @@ export const APPLESOFT_BASIC : BASICOptions = { maxDefArgs : 1, // TODO: no string FNs maxStringLength : 255, tickComments : false, + hexOctalConsts : false, validKeywords : [ 'OPTION', 'CLEAR','LET','DIM','DEF','GOTO','GOSUB','RETURN','ON','POP', @@ -1028,6 +1039,7 @@ export const MODERN_BASIC : BASICOptions = { maxDefArgs : 255, maxStringLength : 2048, // TODO? tickComments : true, + hexOctalConsts : true, validKeywords : null, // all validFunctions : null, // all validOperators : null, // all diff --git a/src/common/teletype.ts b/src/common/teletype.ts index 5139186f..766ea2e2 100644 --- a/src/common/teletype.ts +++ b/src/common/teletype.ts @@ -43,8 +43,9 @@ export class TeleType { } flushline() { this.curline = null; + this.col = 0; + this.movePrintHead(false); } - // TODO: support fixed-width window (use CSS grid?) addtext(line: string, style: number) { this.ensureline(); if (line.length) { @@ -80,17 +81,15 @@ export class TeleType { } this.col += line.length; // wrap @ 80 columns (TODO: const) - if (this.fixed && this.col >= this.ncols) this.newline(); + if (this.fixed && this.col >= this.ncols) this.flushline(); this.ncharsout += line.length; this.movePrintHead(true); } } newline() { this.flushline(); - this.col = 0; - this.movePrintHead(false); + this.ensureline(); } - // TODO: bug in interpreter where it tracks cursor position but maybe doesn't do newlines? print(val: string) { // split by newlines var lines = val.split("\n"); @@ -239,7 +238,7 @@ export class TeleTypeWithKeyboard extends TeleType { s = s.toUpperCase(); this.addtext(s, 4); this.flushline(); - this.resolveInput(s.split(',')); // TODO: should parse quotes, etc + this.resolveInput(s.split(',')); this.resolveInput = null; } this.clearinput(); @@ -253,7 +252,6 @@ export class TeleTypeWithKeyboard extends TeleType { super.ensureline(); } scrollToBottom() { - // TODO: fails when lots of lines are scrolled if (this.scrolldiv) { this.scrolling++; var top = $(this.page).height() + $(this.input).height();