1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-06-26 21:29:33 +00:00

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 15 PRINT
20 INPUT "WOULD YOU MIND TYPING IN YOUR NAME";A$ 20 INPUT "WOULD YOU MIND TYPING IN YOUR NAME";A$
25 PRINT 25 PRINT
30 PRINT "THANKS, ";A$;"! THIS WILL BE FUN!" 30 PRINT "THANKS, ";A$;"! THIS WILL BE FUN!";CHR$(7)
35 PRINT 35 PRINT
40 INPUT "NOW TELL ME YOUR FAVORITE NUMBER";N 40 INPUT "NOW TELL ME YOUR FAVORITE NUMBER";N
45 PRINT 45 PRINT

View File

@ -9,6 +9,7 @@ export interface BASICOptions {
optionalWhitespace : boolean; // can "crunch" keywords? optionalWhitespace : boolean; // can "crunch" keywords?
varNaming : 'A1'|'AA'|'*'; // only allow A0-9 for numerics, single letter for arrays/strings varNaming : 'A1'|'AA'|'*'; // only allow A0-9 for numerics, single letter for arrays/strings
tickComments : boolean; // support 'comments? tickComments : boolean; // support 'comments?
hexOctalConsts : boolean; // support &H and &O integer constants?
validKeywords : string[]; // valid keywords (or null for accept all) validKeywords : string[]; // valid keywords (or null for accept all)
validFunctions : string[]; // valid functions (or null for accept all) validFunctions : string[]; // valid functions (or null for accept all)
validOperators : string[]; // valid operators (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 // 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 { export enum TokenType {
EOL = 0, EOL = 0,
Float1, Float1,
Float2, Float2,
Int, Int,
HexOctalInt,
Remark, Remark,
Ident, Ident,
String, String,
@ -313,7 +315,7 @@ export class BASICParser {
var tok = this.consumeToken(); var tok = this.consumeToken();
var tokstr = tok.str; var tokstr = tok.str;
if (strlist.indexOf(tokstr) < 0) { 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; return tok;
} }
@ -343,6 +345,7 @@ export class BASICParser {
line.label = tok.str; line.label = tok.str;
this.curlabel = tok.str; this.curlabel = tok.str;
break; break;
case TokenType.HexOctalInt:
case TokenType.Float1: case TokenType.Float1:
case TokenType.Float2: case TokenType.Float2:
this.compileError(`Line numbers must be positive integers.`); this.compileError(`Line numbers must be positive integers.`);
@ -384,7 +387,7 @@ export class BASICParser {
if (this.opts.asciiOnly && !/^[\x00-\x7F]*$/.test(s)) if (this.opts.asciiOnly && !/^[\x00-\x7F]*$/.test(s))
this.dialectError(`non-ASCII characters`); this.dialectError(`non-ASCII characters`);
// uppercase all identifiers, and maybe more // 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(); s = s.toUpperCase();
// un-crunch tokens? // un-crunch tokens?
if (splitre) { if (splitre) {
@ -396,7 +399,7 @@ export class BASICParser {
} else } else
break; 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 // add token to list
this.tokens.push({str: s, type: i, $loc:loc}); this.tokens.push({str: s, type: i, $loc:loc});
@ -543,12 +546,16 @@ export class BASICParser {
parsePrimary(): Expr { parsePrimary(): Expr {
let tok = this.consumeToken(); let tok = this.consumeToken();
switch (tok.type) { 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.Int:
case TokenType.Float1: case TokenType.Float1:
case TokenType.Float2: case TokenType.Float2:
return { value: this.parseNumber(tok.str)/*, $loc: tok.$loc*/ }; return { value: this.parseNumber(tok.str) };
case TokenType.String: case TokenType.String:
return { value: stripQuotes(tok.str)/*, $loc: tok.$loc*/ }; return { value: stripQuotes(tok.str) };
case TokenType.Ident: case TokenType.Ident:
if (tok.str == 'NOT') { if (tok.str == 'NOT') {
let expr = this.parsePrimary(); let expr = this.parsePrimary();
@ -882,6 +889,7 @@ export const ECMA55_MINIMAL : BASICOptions = {
maxDefArgs : 255, maxDefArgs : 255,
maxStringLength : 255, maxStringLength : 255,
tickComments : false, tickComments : false,
hexOctalConsts : false,
validKeywords : ['BASE','DATA','DEF','DIM','END', validKeywords : ['BASE','DATA','DEF','DIM','END',
'FOR','GO','GOSUB','GOTO','IF','INPUT','LET','NEXT','ON','OPTION','PRINT', 'FOR','GO','GOSUB','GOTO','IF','INPUT','LET','NEXT','ON','OPTION','PRINT',
'RANDOMIZE','READ','REM','RESTORE','RETURN','STEP','STOP','SUB','THEN','TO' 'RANDOMIZE','READ','REM','RESTORE','RETURN','STEP','STOP','SUB','THEN','TO'
@ -915,6 +923,7 @@ export const BASICODE : BASICOptions = {
maxDefArgs : 255, maxDefArgs : 255,
maxStringLength : 255, maxStringLength : 255,
tickComments : false, tickComments : false,
hexOctalConsts : false,
validKeywords : ['BASE','DATA','DEF','DIM','END', validKeywords : ['BASE','DATA','DEF','DIM','END',
'FOR','GO','GOSUB','GOTO','IF','INPUT','LET','NEXT','ON','OPTION','PRINT', 'FOR','GO','GOSUB','GOTO','IF','INPUT','LET','NEXT','ON','OPTION','PRINT',
'READ','REM','RESTORE','RETURN','STEP','STOP','SUB','THEN','TO' 'READ','REM','RESTORE','RETURN','STEP','STOP','SUB','THEN','TO'
@ -949,6 +958,7 @@ export const ALTAIR_BASIC41 : BASICOptions = {
maxDefArgs : 255, maxDefArgs : 255,
maxStringLength : 255, maxStringLength : 255,
tickComments : false, tickComments : false,
hexOctalConsts : false,
validKeywords : [ validKeywords : [
'OPTION', 'OPTION',
'CONSOLE','DATA','DEF','DEFUSR','DIM','END','ERASE','ERROR', 'CONSOLE','DATA','DEF','DEFUSR','DIM','END','ERASE','ERROR',
@ -986,6 +996,7 @@ export const APPLESOFT_BASIC : BASICOptions = {
maxDefArgs : 1, // TODO: no string FNs maxDefArgs : 1, // TODO: no string FNs
maxStringLength : 255, maxStringLength : 255,
tickComments : false, tickComments : false,
hexOctalConsts : false,
validKeywords : [ validKeywords : [
'OPTION', 'OPTION',
'CLEAR','LET','DIM','DEF','GOTO','GOSUB','RETURN','ON','POP', 'CLEAR','LET','DIM','DEF','GOTO','GOSUB','RETURN','ON','POP',
@ -1028,6 +1039,7 @@ export const MODERN_BASIC : BASICOptions = {
maxDefArgs : 255, maxDefArgs : 255,
maxStringLength : 2048, // TODO? maxStringLength : 2048, // TODO?
tickComments : true, tickComments : true,
hexOctalConsts : true,
validKeywords : null, // all validKeywords : null, // all
validFunctions : null, // all validFunctions : null, // all
validOperators : null, // all validOperators : null, // all

View File

@ -43,8 +43,9 @@ export class TeleType {
} }
flushline() { flushline() {
this.curline = null; this.curline = null;
this.col = 0;
this.movePrintHead(false);
} }
// TODO: support fixed-width window (use CSS grid?)
addtext(line: string, style: number) { addtext(line: string, style: number) {
this.ensureline(); this.ensureline();
if (line.length) { if (line.length) {
@ -80,17 +81,15 @@ export class TeleType {
} }
this.col += line.length; this.col += line.length;
// wrap @ 80 columns (TODO: const) // 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.ncharsout += line.length;
this.movePrintHead(true); this.movePrintHead(true);
} }
} }
newline() { newline() {
this.flushline(); this.flushline();
this.col = 0; this.ensureline();
this.movePrintHead(false);
} }
// TODO: bug in interpreter where it tracks cursor position but maybe doesn't do newlines?
print(val: string) { print(val: string) {
// split by newlines // split by newlines
var lines = val.split("\n"); var lines = val.split("\n");
@ -239,7 +238,7 @@ export class TeleTypeWithKeyboard extends TeleType {
s = s.toUpperCase(); s = s.toUpperCase();
this.addtext(s, 4); this.addtext(s, 4);
this.flushline(); this.flushline();
this.resolveInput(s.split(',')); // TODO: should parse quotes, etc this.resolveInput(s.split(','));
this.resolveInput = null; this.resolveInput = null;
} }
this.clearinput(); this.clearinput();
@ -253,7 +252,6 @@ export class TeleTypeWithKeyboard extends TeleType {
super.ensureline(); super.ensureline();
} }
scrollToBottom() { scrollToBottom() {
// TODO: fails when lots of lines are scrolled
if (this.scrolldiv) { if (this.scrolldiv) {
this.scrolling++; this.scrolling++;
var top = $(this.page).height() + $(this.input).height(); var top = $(this.page).height() + $(this.input).height();