Added keyword table to support language localization

This commit is contained in:
Joshua Bell 2012-06-20 11:54:32 -04:00
parent e7248bc09e
commit 783b85a8db
1 changed files with 253 additions and 147 deletions

400
basic.js
View File

@ -112,6 +112,111 @@ var basic = (function() {
INTERRUPT: [255, "Break"]
};
// Keyword table - these can be altered (e.g. localized) here
var kws = {
ABS: "ABS",
AND: "AND",
ASC: "ASC",
ATN: "ATN",
AT: "AT",
CALL: "CALL",
CHR$: "CHR$",
CLEAR: "CLEAR",
COLOR: "COLOR=",
CONT: "CONT",
COS: "COS",
DATA: "DATA",
DEF: "DEF",
DEL: "DEL",
DIM: "DIM",
DRAW: "DRAW",
END: "END",
EXP: "EXP",
FLASH: "FLASH",
FN: "FN",
FOR: "FOR",
FRE: "FRE",
GET: "GET",
GOSUB: "GOSUB",
GOTO: "GOTO",
GR: "GR",
HCOLOR: "HCOLOR=",
HGR2: "HGR2",
HGR: "HGR",
HIMEM: "HIMEM:",
HLIN: "HLIN",
HOME: "HOME",
HPLOT: "HPLOT",
HTAB: "HTAB",
IF: "IF",
IN: "IN#",
INPUT: "INPUT",
INT: "INT",
INVERSE: "INVERSE",
LEFT$: "LEFT$",
LEN: "LEN",
LET: "LET",
LIST: "LIST",
LOAD: "LOAD",
LOG: "LOG",
LOMEM: "LOMEM:",
MID$: "MID$",
NEW: "NEW",
NEXT: "NEXT",
NORMAL: "NORMAL",
NOTRACE: "NOTRACE",
NOT: "NOT",
ONERR: "ONERR",
ON: "ON",
OR: "OR",
PDL: "PDL",
PEEK: "PEEK",
PLOT: "PLOT",
POKE: "POKE",
POP: "POP",
POS: "POS",
PRINT: "PRINT",
PR: "PR#",
READ: "READ",
RECALL: "RECALL",
REM: "REM",
RESTORE: "RESTORE",
RESUME: "RESUME",
RETURN: "RETURN",
RIGHT$: "RIGHT$",
RND: "RND",
ROT: "ROT=",
RUN: "RUN",
SAVE: "SAVE",
SCALE: "SCALE=",
SCRN: "SCRN",
SGN: "SGN",
SHLOAD: "SHLOAD",
SIN: "SIN",
SPC: "SPC",
SPEED: "SPEED=",
SQR: "SQR",
STEP: "STEP",
STOP: "STOP",
STORE: "STORE",
STR$: "STR$",
TAB: "TAB",
TAN: "TAN",
TEXT: "TEXT",
THEN: "THEN",
TO: "TO",
TRACE: "TRACE",
USR: "USR",
VAL: "VAL",
VLIN: "VLIN",
VTAB: "VTAB",
WAIT: "WAIT",
XDRAW: "XDRAW",
AMPERSAND: "&",
QUESTION: "?",
HSCRN: "HSCRN"
};
//
// Runtime flow control
//
@ -123,8 +228,6 @@ var basic = (function() {
this.callback = callback;
}
// Adapted from:
// http://stackoverflow.com/questions/424292/how-to-create-my-own-javascript-random-number-generator-that-i-can-also-set-the-s
function PRNG() {
@ -974,7 +1077,7 @@ var basic = (function() {
return func;
}
funlib = {
funlib = {};
//////////////////////////////////////////////////////////////////////
//
@ -985,29 +1088,29 @@ var basic = (function() {
//////////////////////////////////////////////////////////////////////
"ABS": funcsign(Math.abs, 'number', 'number'),
"ASC": funcsign(function(s) {
funlib[kws.ABS] = funcsign(Math.abs, 'number', 'number');
funlib[kws.ASC] = funcsign(function(s) {
if (s.length < 1) { runtime_error(ERRORS.ILLEGAL_QUANTITY); }
return s.charCodeAt(0);
}, 'number', 'string'),
"ATN": funcsign(Math.atan, 'number', 'number'),
"CHR$": funcsign(String.fromCharCode, 'string', 'number'),
"COS": funcsign(Math.cos, 'number', 'number'),
"EXP": funcsign(Math.exp, 'number', 'number'),
"INT": funcsign(Math.floor, 'number', 'number'),
"LEN": funcsign(function LEN(s) { return s.length; }, 'number', 'string'),
"LOG": funcsign(Math.log, 'number', 'number'),
"SGN": funcsign(function SGN(n) { return n > 0 ? 1 : n < 0 ? -1 : 0; }, 'number', 'number'),
"SIN": funcsign(Math.sin, 'number', 'number'),
"SQR": funcsign(Math.sqrt, 'number', 'number'),
"STR$": funcsign(function STR$(n) { return n.toString(); }, 'string', 'number'),
"TAN": funcsign(Math.tan, 'number', 'number'),
"VAL": funcsign(function VAL(s) {
}, 'number', 'string');
funlib[kws.ATN] = funcsign(Math.atan, 'number', 'number');
funlib[kws.CHR$] = funcsign(String.fromCharCode, 'string', 'number');
funlib[kws.COS] = funcsign(Math.cos, 'number', 'number');
funlib[kws.EXP] = funcsign(Math.exp, 'number', 'number');
funlib[kws.INT] = funcsign(Math.floor, 'number', 'number');
funlib[kws.LEN] = funcsign(function LEN(s) { return s.length; }, 'number', 'string');
funlib[kws.LOG] = funcsign(Math.log, 'number', 'number');
funlib[kws.SGN] = funcsign(function SGN(n) { return n > 0 ? 1 : n < 0 ? -1 : 0; }, 'number', 'number');
funlib[kws.SIN] = funcsign(Math.sin, 'number', 'number');
funlib[kws.SQR] = funcsign(Math.sqrt, 'number', 'number');
funlib[kws.STR$] = funcsign(function STR$(n) { return n.toString(); }, 'string', 'number');
funlib[kws.TAN] = funcsign(Math.tan, 'number', 'number');
funlib[kws.VAL] = funcsign(function VAL(s) {
var n = parseFloat(s);
return isFinite(n) ? n : 0;
}, 'number', 'string'),
}, 'number', 'string');
"RND": funcsign(function RND(n) {
funlib[kws.RND] = funcsign(function RND(n) {
if (n > 0) {
// Next in PRNG sequence
return state.prng.next();
@ -1017,14 +1120,14 @@ var basic = (function() {
return state.prng.next();
}
return state.prng.last;
}, 'number', 'number'),
}, 'number', 'number');
"LEFT$": funcsign(function LEFT$(s, n) { return s.substring(0, n); }, 'string', 'string', 'number'),
"MID$": funcsign(function MID$(s, n, n2) { return n2 === (void 0) ? s.substring(n - 1) : s.substring(n - 1, n + n2 - 1); }, 'string', 'string', 'number', 'number?'),
"RIGHT$": funcsign(function RIGHT$(s, n) { return s.length < n ? s : s.substring(s.length - n); }, 'string', 'string', 'number'),
funlib[kws.LEFT$] = funcsign(function LEFT$(s, n) { return s.substring(0, n); }, 'string', 'string', 'number');
funlib[kws.MID$] = funcsign(function MID$(s, n, n2) { return n2 === (void 0) ? s.substring(n - 1) : s.substring(n - 1, n + n2 - 1); }, 'string', 'string', 'number', 'number?');
funlib[kws.RIGHT$] = funcsign(function RIGHT$(s, n) { return s.length < n ? s : s.substring(s.length - n); }, 'string', 'string', 'number');
"POS": funcsign(function POS(n) { unused(n); return env.tty.getCursorPosition().x; }, 'number', 'number'),
"SCRN": funcsign(function SCRN(x, y) {
funlib[kws.POS] = funcsign(function POS(n) { unused(n); return env.tty.getCursorPosition().x; }, 'number', 'number');
funlib[kws.SCRN] = funcsign(function SCRN(x, y) {
if (!env.lores) { runtime_error("Graphics not supported"); }
x = x >> 0;
y = y >> 0;
@ -1034,8 +1137,8 @@ var basic = (function() {
}
return env.lores.getPixel(x, y);
}, 'number', 'number', 'number'),
"HSCRN": funcsign(function HSCRN(x, y) {
}, 'number', 'number', 'number');
funlib[kws.HSCRN] = funcsign(function HSCRN(x, y) {
var hires = env.display.hires_plotting_page === 2 ? env.hires2 : env.hires;
if (!hires) { runtime_error("Graphics not supported"); }
@ -1047,30 +1150,29 @@ var basic = (function() {
}
return hires.getPixel(x, y);
}, 'number', 'number', 'number'),
}, 'number', 'number', 'number');
"PDL": funcsign(function PDL(n) {
funlib[kws.PDL] = funcsign(function PDL(n) {
if (env.paddle) {
return (env.paddle(n) * 255) & 0xff;
} else {
runtime_error('Paddles not attached');
return runtime_error('Paddles not attached');
}
}, 'number', 'number'),
"FRE": funcsign(function FRE(n) {
}, 'number', 'number');
funlib[kws.FRE] = funcsign(function FRE(n) {
unused(n);
return JSON ? JSON.stringify([state.variables, state.arrays, state.functions]).length : 0;
}, 'number', 'number'),
"PEEK": funcsign(function PEEK(address) {
}, 'number', 'number');
funlib[kws.PEEK] = funcsign(function PEEK(address) {
address = address & 0xffff;
if (!{}.hasOwnProperty.call(peek_table, address)) {
runtime_error("Unsupported PEEK location: " + address);
}
return peek_table[address]();
}, 'number', 'number'),
}, 'number', 'number');
// Not supported
"USR": funcsign(function USR(n) { unused(n); runtime_error("USR Function not supported"); }, 'number', 'number')
};
funlib[kws.USR] = funcsign(function USR(n) { unused(n); runtime_error("USR Function not supported"); }, 'number', 'number');
//----------------------------------------------------------------------
@ -1116,34 +1218,38 @@ var basic = (function() {
}
var RESERVED_WORDS = [
kws.ABS, kws.AND, kws.ASC, kws.ATN, kws.AT, kws.CALL, kws.CHR$, kws.CLEAR, kws.COLOR, kws.CONT, kws.COS,
/*kws.DATA,*/
kws.DEF, kws.DEL, kws.DIM, kws.DRAW, kws.END, kws.EXP, kws.FLASH, kws.FN, kws.FOR, kws.FRE, kws.GET,
kws.GOSUB, kws.GOTO, kws.GR, kws.HCOLOR, kws.HGR2, kws.HGR, kws.HIMEM, kws.HLIN, kws.HOME, kws.HPLOT,
kws.HTAB, kws.IF, kws.IN, kws.INPUT, kws.INT, kws.INVERSE, kws.LEFT$, kws.LEN, kws.LET, kws.LIST,
kws.LOAD, kws.LOG, kws.LOMEM, kws.MID$, kws.NEW, kws.NEXT, kws.NORMAL, kws.NOTRACE, kws.NOT, kws.ONERR,
kws.ON, kws.OR, kws.PDL, kws.PEEK, kws.PLOT, kws.POKE, kws.POP, kws.POS, kws.PRINT, kws.PR, kws.READ,
kws.RECALL, /*kws.REM,*/ kws.RESTORE, kws.RESUME, kws.RETURN, kws.RIGHT$, kws.RND, kws.ROT, kws.RUN,
kws.SAVE, kws.SCALE, kws.SCRN, kws.SGN, kws.SHLOAD, kws.SIN, kws.SPC, kws.SPEED, kws.SQR, kws.STEP,
kws.STOP, kws.STORE, kws.STR$, kws.TAB, kws.TAN, kws.TEXT, kws.THEN, kws.TO, kws.TRACE, kws.USR, kws.VAL,
kws.VLIN, kws.VTAB, kws.WAIT, kws.XDRAW, kws.AMPERSAND, kws.QUESTION,
kws.HSCRN
];
// NOTE: keywords that are stems of other words need to go after (e.g. "NOTRACE", "NOT)
"ABS", "AND", "ASC", "ATN", "AT", "CALL", "CHR$", "CLEAR", "COLOR=", "CONT", "COS",
/*"DATA",*/"DEF", "DEL", "DIM", "DRAW", "END", "EXP", "FLASH", "FN", "FOR", "FRE", "GET",
"GOSUB", "GOTO", "GR", "HCOLOR=", "HGR2", "HGR", "HIMEM:", "HLIN", "HOME", "HPLOT",
"HTAB", "IF", "IN#", "INPUT", "INT", "INVERSE", "LEFT$", "LEN", "LET", "LIST",
"LOAD", "LOG", "LOMEM:", "MID$", "NEW", "NEXT", "NORMAL", "NOTRACE", "NOT", "ONERR",
"ON", "OR", "PDL", "PEEK", "PLOT", "POKE", "POP", "POS", "PRINT", "PR#", "READ",
"RECALL", /*"REM",*/"RESTORE", "RESUME", "RETURN", "RIGHT$", "RND", "ROT=", "RUN",
"SAVE", "SCALE=", "SCRN", "SGN", "SHLOAD", "SIN", "SPC", "SPEED=", "SQR", "STEP",
"STOP", "STORE", "STR$", "TAB", "TAN", "TEXT", "THEN", "TO", "TRACE", "USR", "VAL",
"VLIN", "VTAB", "WAIT", "XDRAW", "&", "?",
"HSCRN"
],
regexReservedWords = new RegExp("^(" + RESERVED_WORDS.map(munge).join("|") + ")", "i"),
regexIdentifier = new RegExp('^([A-Za-z][A-Za-z0-9]?)[A-Za-z0-9]*(\\$|%)?'),
regexStringLiteral = new RegExp('^"([^"]*?)(?:"|(?=\\n|\\r|$))'),
regexNumberLiteral = new RegExp('^([0-9]*\\.?[0-9]+(?:[eE]\\s*[\\-+]?\\s*[0-9]+)?)'),
regexOperator = new RegExp('^(;|<[ \t]*=|=[ \t]*<|>[ \t]*=|=[ \t]*>|=[ \t]*=|<[ \t]*>|>[ \t]*<|=|<|>|\\+|-|\\*|/|\\^|\\(|\\)|,)'),
RESERVED_WORDS.sort();
RESERVED_WORDS.reverse();
regexLineNumber = new RegExp('^([0-9]+)'),
regexSeparator = new RegExp('^(:)'),
var regexReservedWords = new RegExp("^(" + RESERVED_WORDS.map(munge).join("|") + ")", "i"),
regexIdentifier = new RegExp('^([A-Za-z][A-Za-z0-9]?)[A-Za-z0-9]*(\\$|%)?'),
regexStringLiteral = new RegExp('^"([^"]*?)(?:"|(?=\\n|\\r|$))'),
regexNumberLiteral = new RegExp('^([0-9]*\\.?[0-9]+(?:[eE]\\s*[\\-+]?\\s*[0-9]+)?)'),
regexOperator = new RegExp('^(;|<[ \t]*=|=[ \t]*<|>[ \t]*=|=[ \t]*>|=[ \t]*=|<[ \t]*>|>[ \t]*<|=|<|>|\\+|-|\\*|/|\\^|\\(|\\)|,)'),
regexRemark = new RegExp('^(' + munge('REM') + '([^\r\n]*))', 'i'),
regexData = new RegExp('^(' + munge('DATA') + ')', 'i'),
regexLineNumber = new RegExp('^([0-9]+)'),
regexSeparator = new RegExp('^(:)'),
regexLinearWhitespace = new RegExp('^[ \t]+'),
regexNewline = new RegExp('^\r?\n');
regexRemark = new RegExp('^(' + munge(kws.REM) + '([^\r\n]*))', 'i'),
regexData = new RegExp('^(' + munge(kws.DATA) + ')', 'i'),
regexLinearWhitespace = new RegExp('^[ \t]+'),
regexNewline = new RegExp('^\r?\n');
// Token types:
// lineNumber - start of a new line
// separator - separates statements on same line
@ -1196,7 +1302,7 @@ var basic = (function() {
parseDataInput(stream, token.data);
} else if (stream.match(regexReservedWords)) {
token.reserved = stream.lastMatch[1].toUpperCase().replace(/\s+/g, '');
if (token.reserved === "?") { token.reserved = "PRINT"; } // HACK
if (token.reserved === kws.QUESTION) { token.reserved = kws.PRINT; } // HACK
} else if (stream.match(regexIdentifier)) {
token.identifier = stream.lastMatch[1].toUpperCase() + (stream.lastMatch[2] || ''); // Canonicalize identifier name
} else if (stream.match(regexStringLiteral)) {
@ -1438,7 +1544,7 @@ var basic = (function() {
return { source: String(match('number')), type: 'number' };
} else if (test('string')) {
return { source: quote(match('string')), type: 'string' };
} else if (test('reserved', 'FN', true)) {
} else if (test('reserved', kws.FN, true)) {
return parseUserfunction();
} else if (test('reserved')) {
return parsefunction(match('reserved'));
@ -1466,7 +1572,7 @@ var basic = (function() {
if (test('operator', '+') || test('operator', '-')) {
op = match('operator');
} else if (test('reserved', 'NOT')) {
} else if (test('reserved', kws.NOT)) {
op = match('reserved');
}
@ -1478,7 +1584,7 @@ var basic = (function() {
switch (op) {
case "+": return rhs;
case "-": return { source: '(-' + rhs.source + ')', type: 'number' };
case "NOT": return { source: '((!' + rhs.source + ')?1:0)', type: 'number' };
case kws.NOT: return { source: '((!' + rhs.source + ')?1:0)', type: 'number' };
}
}
return parseFinalExpression();
@ -1566,7 +1672,7 @@ var basic = (function() {
function parseAndExpression() {
var lhs = parseRelationalExpression(), rhs;
while (test('reserved', 'AND', true)) {
while (test('reserved', kws.AND, true)) {
rhs = parseRelationalExpression();
enforce_type(lhs.type, 'number');
@ -1582,7 +1688,7 @@ var basic = (function() {
function parseOrExpression() {
var lhs = parseAndExpression(), rhs;
while (test('reserved', 'OR', true)) {
while (test('reserved', kws.OR, true)) {
rhs = parseAndExpression();
enforce_type(lhs.type, 'number');
@ -1612,7 +1718,7 @@ var basic = (function() {
return 'lib[' + quote(name) + '](' + args.join(',') + ');';
}
var keyword = test('identifier') ? 'LET' : match('reserved'),
var keyword = test('identifier') ? kws.LET : match('reserved'),
name, type, subscripts, is_to, expr, param, args, prompt, trailing, js;
switch (keyword) {
@ -1622,10 +1728,10 @@ var basic = (function() {
//
//////////////////////////////////////////////////////////////////////
case "CLEAR": // Clear all variables
case kws.CLEAR: // Clear all variables
return slib('clear');
case "LET": // Assign a variable, LET x = expr
case kws.LET: // Assign a variable, LET x = expr
name = match('identifier');
subscripts = parseSubscripts();
match('operator', '=');
@ -1647,7 +1753,7 @@ var basic = (function() {
return 'state.arrays[' + quote(name) + '].set([' + subscripts + '], ' + expr + ')';
}
case "DIM":
case kws.DIM:
js = '';
do {
name = match('identifier');
@ -1657,8 +1763,8 @@ var basic = (function() {
} while (test('operator', ',', true));
return js;
case "DEF": // DEF FN A(X) = expr
match("reserved", "FN");
case kws.DEF: // DEF FN A(X) = expr
match("reserved", kws.FN);
name = match('identifier');
match("operator", "(");
param = match('identifier');
@ -1692,15 +1798,15 @@ var basic = (function() {
//
//////////////////////////////////////////////////////////////////////
case "GOTO": // GOTO linenum
case kws.GOTO: // GOTO linenum
return slib('goto', match("number"));
case "ON": // ON expr (GOTO|GOSUB) linenum[,linenum ... ]
case kws.ON: // ON expr (GOTO|GOSUB) linenum[,linenum ... ]
expr = parseNumericExpression();
keyword = match('reserved');
if (keyword !== "GOTO" && keyword !== "GOSUB") {
parse_error("Syntax error: Expected GOTO or GOSUB");
if (keyword !== kws.GOTO && keyword !== kws.GOSUB) {
parse_error("Syntax error: Expected " + kws.GOTO + " or " + kws.GOSUB);
}
args = [];
@ -1708,18 +1814,18 @@ var basic = (function() {
args.push(match("number"));
} while (test("operator", ",", true));
return slib(keyword === 'GOSUB' ? 'on_gosub' : 'on_goto', expr, args.join(','));
return slib(keyword === kws.GOSUB ? 'on_gosub' : 'on_goto', expr, args.join(','));
case "GOSUB": // GOSUB linenum
case kws.GOSUB: // GOSUB linenum
return slib('gosub', match("number"));
case "RETURN": // Return from the last GOSUB
case kws.RETURN: // Return from the last GOSUB
return slib('return');
case "POP": // Turn last GOSUB into a GOTO
case kws.POP: // Turn last GOSUB into a GOTO
return slib('pop');
case "FOR": // FOR i = m TO n STEP s
case kws.FOR: // FOR i = m TO n STEP s
name = match('identifier');
if (vartype(name) !== 'float') {
parse_error("Syntax error: Expected floating point variable");
@ -1729,10 +1835,10 @@ var basic = (function() {
return slib('for',
quote(name),
match("operator", "=") && parseNumericExpression(),
match("reserved", "TO") && parseNumericExpression(),
test('reserved', 'STEP', true) ? parseNumericExpression() : '1');
match("reserved", kws.TO) && parseNumericExpression(),
test('reserved', kws.STEP, true) ? parseNumericExpression() : '1');
case "NEXT": // NEXT [i [,j ... ] ]
case kws.NEXT: // NEXT [i [,j ... ] ]
args = [];
if (test('identifier')) {
args.push(quote(match('identifier')));
@ -1743,17 +1849,17 @@ var basic = (function() {
return slib('next', args.join(','));
case "IF": // IF expr (GOTO linenum|THEN linenum|THEN statement [:statement ... ]
case kws.IF: // IF expr (GOTO linenum|THEN linenum|THEN statement [:statement ... ]
expr = parseAnyExpression();
js = slib('if', expr);
if (test('reserved', 'GOTO', true)) {
if (test('reserved', kws.GOTO, true)) {
// IF expr GOTO linenum
return js + slib('goto', match('number'));
}
match('reserved', 'THEN');
match('reserved', kws.THEN);
if (test('number')) {
// IF expr THEN linenum
return js + slib('goto', match('number'));
@ -1762,10 +1868,10 @@ var basic = (function() {
return js + parseCommand(); // recurse
}
case "END": // End program
case kws.END: // End program
return slib('end');
case "STOP": // Break, like an error
case kws.STOP: // Break, like an error
return slib('stop');
//////////////////////////////////////////////////////////////////////
@ -1774,11 +1880,11 @@ var basic = (function() {
//
//////////////////////////////////////////////////////////////////////
case "ONERR": // ONERR GOTO linenum
case kws.ONERR: // ONERR GOTO linenum
return slib('onerr_goto',
match("reserved", "GOTO") && match("number"));
match("reserved", kws.GOTO) && match("number"));
case "RESUME":
case kws.RESUME:
return slib('resume');
//////////////////////////////////////////////////////////////////////
@ -1787,10 +1893,10 @@ var basic = (function() {
//
//////////////////////////////////////////////////////////////////////
case "RESTORE":
case kws.RESTORE:
return slib('restore');
case "READ":
case kws.READ:
args = [];
do {
args.push(parsePValue());
@ -1804,7 +1910,7 @@ var basic = (function() {
//
//////////////////////////////////////////////////////////////////////
case "PRINT": // Output to the screen
case kws.PRINT: // Output to the screen
args = [];
trailing = true;
while (!endOfStatement()) {
@ -1813,14 +1919,14 @@ var basic = (function() {
} else if (test('operator', ',', true)) {
trailing = false;
args.push('lib.comma()');
} else if (test('reserved', 'SPC') || test('reserved', 'TAB')) {
} else if (test('reserved', kws.SPC) || test('reserved', kws.TAB)) {
trailing = true;
keyword = match('reserved');
match("operator", "(");
expr = parseNumericExpression();
match("operator", ")");
args.push('lib.' + (keyword === 'SPC' ? 'spc' : 'tab') + '(' + expr + ')');
args.push('lib.' + (keyword === kws.SPC ? 'spc' : 'tab') + '(' + expr + ')');
} else {
trailing = true;
args.push(parseAnyExpression());
@ -1832,7 +1938,7 @@ var basic = (function() {
return slib('print', args.join(','));
case "INPUT": // Read input from keyboard
case kws.INPUT: // Read input from keyboard
prompt = '?';
if (test('string')) {
prompt = match('string');
@ -1847,28 +1953,28 @@ var basic = (function() {
return slib('input', quote(prompt), args.join(','));
case "GET": // Read character from keyboard
case kws.GET: // Read character from keyboard
return slib('get', parsePValue());
case "HOME": // Clear text screen
case kws.HOME: // Clear text screen
return slib('home');
case "HTAB": // Set horizontal cursor position
case kws.HTAB: // Set horizontal cursor position
return slib('htab', parseNumericExpression());
case "VTAB": // Set vertical cursor position
case kws.VTAB: // Set vertical cursor position
return slib('vtab', parseNumericExpression());
case "INVERSE": // Inverse text
case kws.INVERSE: // Inverse text
return slib('inverse');
case "FLASH": // Flashing text
case kws.FLASH: // Flashing text
return slib('flash');
case "NORMAL": // Normal text
case kws.NORMAL: // Normal text
return slib('normal');
case "TEXT": // Set display mode to text
case kws.TEXT: // Set display mode to text
return slib('text');
//////////////////////////////////////////////////////////////////////
@ -1877,10 +1983,10 @@ var basic = (function() {
//
//////////////////////////////////////////////////////////////////////
case "NOTRACE": // Turn off line tracing
case kws.NOTRACE: // Turn off line tracing
return slib('notrace');
case "TRACE": // Turn on line tracing
case kws.TRACE: // Turn on line tracing
return slib('trace');
//////////////////////////////////////////////////////////////////////
@ -1889,28 +1995,28 @@ var basic = (function() {
//
//////////////////////////////////////////////////////////////////////
case "GR": // Set display mode to lores graphics, clear screen
case kws.GR: // Set display mode to lores graphics, clear screen
return slib('gr');
case "COLOR=": // Set lores color
case kws.COLOR: // Set lores color
return slib('color', parseNumericExpression());
case "PLOT": // Plot lores point
case kws.PLOT: // Plot lores point
return slib('plot',
parseNumericExpression(),
match("operator", ",") && parseNumericExpression());
case "HLIN": // Draw lores horizontal line
case kws.HLIN: // Draw lores horizontal line
return slib('hlin',
parseNumericExpression(),
match("operator", ",") && parseNumericExpression(),
match("reserved", "AT") && parseNumericExpression());
match("reserved", kws.AT) && parseNumericExpression());
case "VLIN": // Draw lores vertical line
case kws.VLIN: // Draw lores vertical line
return slib('vlin',
parseNumericExpression(),
match("operator", ",") && parseNumericExpression(),
match("reserved", "AT") && parseNumericExpression());
match("reserved", kws.AT) && parseNumericExpression());
//////////////////////////////////////////////////////////////////////
//
@ -1919,24 +2025,24 @@ var basic = (function() {
//////////////////////////////////////////////////////////////////////
// Hires Display Routines
case "HGR": // Set display mode to hires graphics, clear screen
case kws.HGR: // Set display mode to hires graphics, clear screen
return slib('hgr');
case "HGR2": // Set display mode to hires graphics, page 2, clear screen
case kws.HGR2: // Set display mode to hires graphics, page 2, clear screen
return slib('hgr2');
case "HCOLOR=": // Set hires color
case kws.HCOLOR: // Set hires color
return slib('hcolor', parseNumericExpression());
case "HPLOT": // Draw hires line
is_to = test('reserved', 'TO', true);
case kws.HPLOT: // Draw hires line
is_to = test('reserved', kws.TO, true);
args = [];
do {
args.push(parseNumericExpression());
match("operator", ",");
args.push(parseNumericExpression());
} while (test('reserved', 'TO', true));
} while (test('reserved', kws.TO, true));
return slib(is_to ? 'hplot_to' : 'hplot', args.join(','));
@ -1946,18 +2052,18 @@ var basic = (function() {
//
//////////////////////////////////////////////////////////////////////
case "PR#": // Direct output to slot
case kws.PR: // Direct output to slot
return slib('pr#', parseNumericExpression());
case "CALL": // Call native routine
case kws.CALL: // Call native routine
return slib('call', parseNumericExpression());
case "POKE": // Set memory value
case kws.POKE: // Set memory value
return slib('poke',
parseNumericExpression(),
match("operator", ",") && parseNumericExpression());
case "SPEED=": // Output speed
case kws.SPEED: // Output speed
return slib('speed', parseNumericExpression());
//////////////////////////////////////////////////////////////////////
@ -1966,7 +2072,7 @@ var basic = (function() {
//
//////////////////////////////////////////////////////////////////////
case "LIST": // List program statements
case kws.LIST: // List program statements
parse_error("Introspection statement not supported: " + keyword);
return;
@ -1977,36 +2083,36 @@ var basic = (function() {
//////////////////////////////////////////////////////////////////////
// Shape tables
case "ROT=": // Set rotation angle for hires shape
case "SCALE=": // Set rotation angle for hires shape
case "DRAW": // Draw hires shape
case "XDRAW": // XOR draw hires shape
case kws.ROT: // Set rotation angle for hires shape
case kws.SCALE: // Set rotation angle for hires shape
case kws.DRAW: // Draw hires shape
case kws.XDRAW: // XOR draw hires shape
parse_error("Display statement not supported: " + keyword);
return;
// Interpreter Routines
case "CONT": // Continue stopped program (immediate mode)
case "DEL": // Deletes program statements
case "NEW": // Wipe program
case "RUN": // Execute program
case kws.CONT: // Continue stopped program (immediate mode)
case kws.DEL: // Deletes program statements
case kws.NEW: // Wipe program
case kws.RUN: // Execute program
parse_error("Interpreter statement not supported: " + keyword);
return;
// Native Routines
case "HIMEM:": // Set upper bound of variable memory
case "IN#": // Direct input from slot
case "LOMEM:": // Set low bound of variable memory
case "WAIT": // Wait for memory value to match a condition
case "&": // Command hook
case kws.HIMEM: // Set upper bound of variable memory
case kws.IN: // Direct input from slot
case kws.LOMEM: // Set low bound of variable memory
case kws.WAIT: // Wait for memory value to match a condition
case kws.AMPERSAND: // Command hook
parse_error("Native interop statement not supported: " + keyword);
return;
// Tape Routines
case "LOAD": // Load program from cassette port
case "RECALL": // Load array from cassette port
case "SAVE": // Save program to cassette port
case "STORE": // Store array to cassette port
case "SHLOAD": // Load shape table from cassette port
case kws.LOAD: // Load program from cassette port
case kws.RECALL: // Load array from cassette port
case kws.SAVE: // Save program to cassette port
case kws.STORE: // Store array to cassette port
case kws.SHLOAD: // Load shape table from cassette port
parse_error("Tape statement not supported: " + keyword);
return;
@ -2285,7 +2391,7 @@ var basic = (function() {
// TODO: DOS implementation
if (!window) {
if (typeof window === 'undefined') {
(function() {
/*jslint windows: true, rhino: true */