diff --git a/src/common/basic/compiler.ts b/src/common/basic/compiler.ts index f1da3642..59efacf3 100644 --- a/src/common/basic/compiler.ts +++ b/src/common/basic/compiler.ts @@ -31,6 +31,7 @@ export interface BASICOptions { numericPadding : boolean; // " " or "-" before and " " after numbers? // CONTROL FLOW testInitialFor : boolean; // can we skip a NEXT statement? (can't interleave tho) + optionalNextVar : boolean; // can do NEXT without variable ifElse : boolean; // IF...ELSE construct // MISC commandsPerSec? : number; // how many commands per second? @@ -290,7 +291,7 @@ export class BASICParser { } compileError(msg: string, loc?: SourceLocation) { if (!loc) loc = this.peekToken().$loc; - this.errors.push({path:loc.path, line:loc.line, label:loc.label, start:loc.start, end:loc.end, msg:msg}); + this.errors.push({path:loc.path, line:loc.line, label:this.curlabel, start:loc.start, end:loc.end, msg:msg}); throw new CompileError(`${msg} (line ${loc.line})`); // TODO: label too? } dialectError(what: string, loc?: SourceLocation) { @@ -308,6 +309,14 @@ export class BASICParser { } return tok; } + expectTokens(strlist: string[], msg?: string) : Token { + var tok = this.consumeToken(); + var tokstr = tok.str; + if (strlist.indexOf(tokstr) < 0) { + this.compileError(msg || `There should be one of "${strlist}" here.`); + } + return tok; + } peekToken(): Token { var tok = this.tokens[0]; return tok ? tok : this.eol; @@ -654,11 +663,10 @@ export class BASICParser { var cond = this.parseExpr(); var iftrue: Statement[]; // we accept GOTO or THEN if line number provided - if (this.peekToken().str == 'GOTO') this.consumeToken(); - else this.expectToken('THEN'); + var thengoto = this.expectTokens(['THEN','GOTO']); var lineno = this.peekToken(); // assume GOTO if number given after THEN - if (lineno.type == TokenType.Int) { + if (lineno.type == TokenType.Int && thengoto.str == 'THEN') { this.pushbackToken({type:TokenType.Ident, str:'GOTO', $loc:lineno.$loc}); } // add fake ":" @@ -709,7 +717,7 @@ export class BASICParser { var prompt = this.consumeToken(); var promptstr; if (prompt.type == TokenType.String) { - this.expectToken(';'); + this.expectTokens([';', ',']); promptstr = stripQuotes(prompt.str); } else { this.pushbackToken(prompt); @@ -884,6 +892,7 @@ export const ECMA55_MINIMAL : BASICOptions = { numericPadding : true, checkOverflow : true, testInitialFor : true, + optionalNextVar : false, ifElse : false, bitwiseLogic : false, } @@ -917,6 +926,7 @@ export const BASICODE : BASICOptions = { numericPadding : true, checkOverflow : true, testInitialFor : true, + optionalNextVar : false, ifElse : false, bitwiseLogic : false, } @@ -952,6 +962,7 @@ export const ALTAIR_BASIC41 : BASICOptions = { numericPadding : true, checkOverflow : true, testInitialFor : false, + optionalNextVar : true, //multipleNextVars : true, // TODO: not supported ifElse : true, bitwiseLogic : true, @@ -994,6 +1005,7 @@ export const APPLESOFT_BASIC : BASICOptions = { numericPadding : false, checkOverflow : true, testInitialFor : false, + optionalNextVar : true, ifElse : false, bitwiseLogic : false, } @@ -1023,6 +1035,7 @@ export const MODERN_BASIC : BASICOptions = { numericPadding : false, checkOverflow : true, testInitialFor : true, + optionalNextVar : true, ifElse : true, bitwiseLogic : true, } diff --git a/src/common/basic/runtime.ts b/src/common/basic/runtime.ts index 0aa7491e..f66476c9 100644 --- a/src/common/basic/runtime.ts +++ b/src/common/basic/runtime.ts @@ -423,7 +423,7 @@ export class BASICRuntime { } nextForLoop(name) { - var fl = this.forLoops[name]; + var fl = this.forLoops[name || (this.opts.optionalNextVar && this.topForLoopName)]; if (!fl) this.runtimeError(`I couldn't find a FOR for this NEXT.`) fl.$next(name); } diff --git a/src/platform/basic.ts b/src/platform/basic.ts index fcb661a7..f4548adb 100644 --- a/src/platform/basic.ts +++ b/src/platform/basic.ts @@ -179,7 +179,7 @@ class BASICPlatform implements Platform { ForLoops: this.runtime.forLoops, ReturnStack: this.runtime.returnStack, NextDatum: this.runtime.datums[this.runtime.dataptr], - Dialect: this.runtime.opts, + Options: this.runtime.opts, Internals: this.runtime, } }