basic: fixed tty column count, added hex/octal

This commit is contained in:
Steven Hugg 2020-08-12 10:51:18 -05:00
parent 6c7e852748
commit 0fd04658cb
3 changed files with 24 additions and 14 deletions

View File

@ -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

View File

@ -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

View File

@ -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();