basic: input validation, array bounds check
This commit is contained in:
parent
946037a1c9
commit
d5405c4db1
10
css/ui.css
10
css/ui.css
|
@ -600,17 +600,23 @@ div.asset_toolbar {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.transcript-line {
|
.transcript-line {
|
||||||
line-height: 1.5;
|
line-height: 1.5em;
|
||||||
min-height: 1em;
|
min-height: 1em;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
.transcript-fanfold > .transcript-line {
|
||||||
|
min-height: 1.5em;
|
||||||
|
}
|
||||||
|
.transcript-fanfold > .transcript-line:nth-child(even) {
|
||||||
|
background: RGBA(225,245,225,1.0);
|
||||||
|
}
|
||||||
.transcript-style-1 {
|
.transcript-style-1 {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
.transcript-style-2 {
|
.transcript-style-2 {
|
||||||
white-space: pre-wrap; /* css-3 */
|
white-space: pre-wrap; /* css-3 */
|
||||||
font-family: "Andale Mono", "Menlo", "Lucida Console", monospace;
|
font-family: "Andale Mono", "Menlo", "Lucida Console", monospace;
|
||||||
line-height: 1;
|
line-height: 1em;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
.transcript-style-4 {
|
.transcript-style-4 {
|
||||||
|
|
|
@ -214,7 +214,7 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="dropdown dropdown-submenu">
|
<li class="dropdown dropdown-submenu">
|
||||||
<a tabindex="-1" href="#">Text-Based</a>
|
<a tabindex="-1" href="#">Interpreters</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item" href="?platform=basic">BASIC</a></li>
|
<li><a class="dropdown-item" href="?platform=basic">BASIC</a></li>
|
||||||
<li><a class="dropdown-item" href="?platform=zmachine">Z-Machine</a></li>
|
<li><a class="dropdown-item" href="?platform=zmachine">Z-Machine</a></li>
|
||||||
|
|
|
@ -2,13 +2,14 @@
|
||||||
010 REM***A PRIME NUMBER SIEVE BENCHMARK
|
010 REM***A PRIME NUMBER SIEVE BENCHMARK
|
||||||
020 T = TIMER
|
020 T = TIMER
|
||||||
025 N = 8191
|
025 N = 8191
|
||||||
030 DIM F(N+3)
|
030 DIM F(N+1)
|
||||||
040 FOR I = 0 TO N : F(I) = 1 : NEXT I
|
040 FOR I = 0 TO N : F(I)=1 : NEXT I
|
||||||
050 S = N
|
050 S = N
|
||||||
060 FOR I = 0 TO S
|
060 FOR I = 0 TO S
|
||||||
070 IF F(I) = 0 THEN GOTO 110
|
070 IF F(I)=0 THEN GOTO 110
|
||||||
080 P = I+I+3
|
080 P = I+I+3
|
||||||
090 FOR K = I+P TO S STEP P : F(K) = 0 : NEXT K
|
085 IF I+P>S THEN GOTO 110
|
||||||
|
090 FOR K=I+P TO S STEP P : F(K)=0 : NEXT K
|
||||||
100 C = C+1
|
100 C = C+1
|
||||||
110 NEXT I
|
110 NEXT I
|
||||||
120 T = TIMER-T
|
120 T = TIMER-T
|
||||||
|
|
|
@ -288,7 +288,7 @@ export class BASICParser {
|
||||||
throw new CompileError(`${msg} (line ${loc.line})`); // TODO: label too?
|
throw new CompileError(`${msg} (line ${loc.line})`); // TODO: label too?
|
||||||
}
|
}
|
||||||
dialectError(what: string, loc?: SourceLocation) {
|
dialectError(what: string, loc?: SourceLocation) {
|
||||||
this.compileError(`The selected BASIC dialect doesn't support ${what}.`, loc); // TODO
|
this.compileError(`The selected BASIC dialect (${this.opts.dialectName}) doesn't support ${what}.`, loc); // TODO
|
||||||
}
|
}
|
||||||
consumeToken(): Token {
|
consumeToken(): Token {
|
||||||
var tok = this.lasttoken = (this.tokens.shift() || this.eol);
|
var tok = this.lasttoken = (this.tokens.shift() || this.eol);
|
||||||
|
@ -624,7 +624,9 @@ export class BASICParser {
|
||||||
stmt__IF(): IF_Statement {
|
stmt__IF(): IF_Statement {
|
||||||
var cond = this.parseExpr();
|
var cond = this.parseExpr();
|
||||||
var iftrue: Statement[];
|
var iftrue: Statement[];
|
||||||
this.expectToken('THEN');
|
// we accept GOTO or THEN if line number provided
|
||||||
|
if (this.peekToken().str == 'GOTO') this.consumeToken();
|
||||||
|
else this.expectToken('THEN');
|
||||||
var lineno = this.peekToken();
|
var lineno = this.peekToken();
|
||||||
// assume GOTO if number given after THEN
|
// assume GOTO if number given after THEN
|
||||||
if (lineno.type == TokenType.Int) {
|
if (lineno.type == TokenType.Int) {
|
||||||
|
@ -890,6 +892,7 @@ export const APPLESOFT_BASIC : BASICOptions = {
|
||||||
sparseArrays : false,
|
sparseArrays : false,
|
||||||
tickComments : false,
|
tickComments : false,
|
||||||
validKeywords : [
|
validKeywords : [
|
||||||
|
'OPTION',
|
||||||
'CLEAR','LET','DIM','DEF','FN','GOTO','GOSUB','RETURN','ON','POP',
|
'CLEAR','LET','DIM','DEF','FN','GOTO','GOSUB','RETURN','ON','POP',
|
||||||
'FOR','TO','NEXT','IF','THEN','END','STOP','ONERR','RESUME',
|
'FOR','TO','NEXT','IF','THEN','END','STOP','ONERR','RESUME',
|
||||||
'PRINT','INPUT','GET','HOME','HTAB','VTAB',
|
'PRINT','INPUT','GET','HOME','HTAB','VTAB',
|
||||||
|
@ -912,12 +915,12 @@ export const APPLESOFT_BASIC : BASICOptions = {
|
||||||
bitwiseLogic : false,
|
bitwiseLogic : false,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MAX8_BASIC : BASICOptions = {
|
export const MODERN_BASIC : BASICOptions = {
|
||||||
dialectName: "MAX8",
|
dialectName: "MODERN",
|
||||||
asciiOnly : false,
|
asciiOnly : false,
|
||||||
uppercaseOnly : false,
|
uppercaseOnly : false,
|
||||||
optionalLabels : true,
|
optionalLabels : true,
|
||||||
strictVarNames : false, // TODO: first two alphanum chars
|
strictVarNames : false,
|
||||||
sharedArrayNamespace : false,
|
sharedArrayNamespace : false,
|
||||||
defaultArrayBase : 0,
|
defaultArrayBase : 0,
|
||||||
defaultArraySize : 11,
|
defaultArraySize : 11,
|
||||||
|
@ -925,7 +928,7 @@ export const MAX8_BASIC : BASICOptions = {
|
||||||
stringConcat : true,
|
stringConcat : true,
|
||||||
typeConvert : true,
|
typeConvert : true,
|
||||||
maxDimensions : 255,
|
maxDimensions : 255,
|
||||||
maxDefArgs : 255, // TODO: no string FNs
|
maxDefArgs : 255,
|
||||||
maxStringLength : 1024, // TODO?
|
maxStringLength : 1024, // TODO?
|
||||||
sparseArrays : false,
|
sparseArrays : false,
|
||||||
tickComments : true,
|
tickComments : true,
|
||||||
|
@ -942,6 +945,7 @@ export const MAX8_BASIC : BASICOptions = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: integer vars
|
// TODO: integer vars
|
||||||
|
// TODO: short-circuit FOR loop
|
||||||
|
|
||||||
export const DIALECTS = {
|
export const DIALECTS = {
|
||||||
"DEFAULT": ALTAIR_BASIC40,
|
"DEFAULT": ALTAIR_BASIC40,
|
||||||
|
@ -950,4 +954,5 @@ export const DIALECTS = {
|
||||||
"ECMA55": ECMA55_MINIMAL,
|
"ECMA55": ECMA55_MINIMAL,
|
||||||
"MINIMAL": ECMA55_MINIMAL,
|
"MINIMAL": ECMA55_MINIMAL,
|
||||||
"APPLESOFT": APPLESOFT_BASIC,
|
"APPLESOFT": APPLESOFT_BASIC,
|
||||||
|
"MODERN": MODERN_BASIC,
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,8 +6,15 @@ var readline = require('readline');
|
||||||
var rl = readline.createInterface({
|
var rl = readline.createInterface({
|
||||||
input: process.stdin,
|
input: process.stdin,
|
||||||
output: process.stdout,
|
output: process.stdout,
|
||||||
//terminal: false
|
terminal: false,
|
||||||
|
crlfDelay: Infinity,
|
||||||
});
|
});
|
||||||
|
var inputlines = [];
|
||||||
|
rl.on('line', (line) => {
|
||||||
|
//console.log(`Line from file: ${line}`);
|
||||||
|
inputlines.push(line);
|
||||||
|
});
|
||||||
|
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
|
|
||||||
var parser = new BASICParser();
|
var parser = new BASICParser();
|
||||||
|
@ -46,10 +53,16 @@ runtime.print = (s:string) => {
|
||||||
}
|
}
|
||||||
runtime.input = async (prompt:string) => {
|
runtime.input = async (prompt:string) => {
|
||||||
return new Promise( (resolve, reject) => {
|
return new Promise( (resolve, reject) => {
|
||||||
fs.writeSync(1, "\n");
|
function answered(answer) {
|
||||||
rl.question(prompt, (answer) => {
|
|
||||||
var vals = answer.toUpperCase().split(',');
|
var vals = answer.toUpperCase().split(',');
|
||||||
|
console.log(">>>",vals);
|
||||||
resolve(vals);
|
resolve(vals);
|
||||||
|
}
|
||||||
|
fs.writeSync(1, prompt+"?");
|
||||||
|
if (inputlines.length) {
|
||||||
|
answered(inputlines.shift());
|
||||||
|
} else rl.question(prompt, (answer) => {
|
||||||
|
answered(answer);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -59,7 +72,8 @@ runtime.resume = function() {
|
||||||
if (runtime.step()) {
|
if (runtime.step()) {
|
||||||
if (runtime.running) runtime.resume();
|
if (runtime.running) runtime.resume();
|
||||||
} else if (runtime.exited) {
|
} else if (runtime.exited) {
|
||||||
rl.close();
|
console.log("*** PROGRAM EXITED ***");
|
||||||
|
process.exit(0);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`### ${e.message} (line ${runtime.getCurrentSourceLocation().label})`);
|
console.log(`### ${e.message} (line ${runtime.getCurrentSourceLocation().label})`);
|
||||||
|
|
|
@ -16,15 +16,21 @@ function isUnOp(arg: basic.Expr): arg is basic.UnOp {
|
||||||
return (arg as any).op != null && (arg as any).expr != null;
|
return (arg as any).op != null && (arg as any).expr != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// expr2js() options
|
||||||
class ExprOptions {
|
class ExprOptions {
|
||||||
isconst?: boolean;
|
isconst?: boolean; // only allow constant operations
|
||||||
locals?: string[];
|
novalid?: boolean; // check for valid values when fetching
|
||||||
|
locals?: string[]; // pass local variable names when defining functions
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CompiledStatement {
|
interface CompiledStatement {
|
||||||
$run?: () => void;
|
$run?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isArray(obj) {
|
||||||
|
return obj != null && (Array.isArray(obj) || obj.BYTES_PER_ELEMENT);
|
||||||
|
}
|
||||||
|
|
||||||
export class BASICRuntime {
|
export class BASICRuntime {
|
||||||
|
|
||||||
program : basic.BASICProgram;
|
program : basic.BASICProgram;
|
||||||
|
@ -158,7 +164,6 @@ export class BASICRuntime {
|
||||||
// skip to next statment
|
// skip to next statment
|
||||||
this.curpc++;
|
this.curpc++;
|
||||||
// compile (unless cached) and execute statement
|
// compile (unless cached) and execute statement
|
||||||
this.compileStatement(stmt);
|
|
||||||
this.executeStatement(stmt);
|
this.executeStatement(stmt);
|
||||||
return this.running;
|
return this.running;
|
||||||
}
|
}
|
||||||
|
@ -178,6 +183,8 @@ export class BASICRuntime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
executeStatement(stmt: basic.Statement & CompiledStatement) {
|
executeStatement(stmt: basic.Statement & CompiledStatement) {
|
||||||
|
// compile (unless cached)
|
||||||
|
this.compileStatement(stmt);
|
||||||
// run compiled statement
|
// run compiled statement
|
||||||
stmt.$run();
|
stmt.$run();
|
||||||
}
|
}
|
||||||
|
@ -215,6 +222,8 @@ export class BASICRuntime {
|
||||||
}
|
}
|
||||||
|
|
||||||
gosubLabel(label) {
|
gosubLabel(label) {
|
||||||
|
if (this.returnStack.length > 65535)
|
||||||
|
this.runtimeError(`I did too many GOSUBs without a RETURN.`)
|
||||||
this.returnStack.push(this.curpc);
|
this.returnStack.push(this.curpc);
|
||||||
this.gotoLabel(label);
|
this.gotoLabel(label);
|
||||||
}
|
}
|
||||||
|
@ -291,9 +300,22 @@ export class BASICRuntime {
|
||||||
if (!expr.args && opts.locals && opts.locals.indexOf(expr.name) >= 0) {
|
if (!expr.args && opts.locals && opts.locals.indexOf(expr.name) >= 0) {
|
||||||
return expr.name; // local arg in DEF
|
return expr.name; // local arg in DEF
|
||||||
} else {
|
} else {
|
||||||
if (opts.isconst) this.runtimeError(`I expected a constant value here.`);
|
var s = '';
|
||||||
var s = this.assign2js(expr, opts);
|
var qname = JSON.stringify(expr.name);
|
||||||
return `this.checkValue(${s}, ${JSON.stringify(expr.name)})`;
|
let jsargs = expr.args && expr.args.map((arg) => this.expr2js(arg, opts)).join(', ');
|
||||||
|
if (expr.name.startsWith("FN")) { // is it a user-defined function?
|
||||||
|
// TODO: check argument count?
|
||||||
|
s += `this.getDef(${qname})(${jsargs})`;
|
||||||
|
// TODO: detect recursion?
|
||||||
|
} else if (this.builtins[expr.name]) { // is it a built-in function?
|
||||||
|
this.checkFuncArgs(expr, this.builtins[expr.name]);
|
||||||
|
s += `this.builtins.${expr.name}(${jsargs})`;
|
||||||
|
} else if (expr.args) {
|
||||||
|
s += `this.arrayGet(${qname}, ${jsargs})`;
|
||||||
|
} else { // just a variable
|
||||||
|
s += `this.vars.${expr.name}`;
|
||||||
|
}
|
||||||
|
return opts.novalid ? s : `this.checkValue(${s}, ${qname})`;
|
||||||
}
|
}
|
||||||
} else if (isBinOp(expr)) {
|
} else if (isBinOp(expr)) {
|
||||||
var left = this.expr2js(expr.left, opts);
|
var left = this.expr2js(expr.left, opts);
|
||||||
|
@ -309,18 +331,12 @@ export class BASICRuntime {
|
||||||
if (!opts) opts = {};
|
if (!opts) opts = {};
|
||||||
var s = '';
|
var s = '';
|
||||||
var qname = JSON.stringify(expr.name);
|
var qname = JSON.stringify(expr.name);
|
||||||
if (expr.name.startsWith("FN")) { // is it a user-defined function?
|
// is it a function? not allowed
|
||||||
// TODO: check argument count?
|
if (expr.name.startsWith("FN") || this.builtins[expr.name]) this.runtimeError(`I can't call a function here.`);
|
||||||
let jsargs = expr.args && expr.args.map((arg) => this.expr2js(arg, opts)).join(', ');
|
// is it a subscript?
|
||||||
s += `this.getDef(${qname})(${jsargs})`;
|
if (expr.args) {
|
||||||
// TODO: detect recursion?
|
s += this.expr2js(expr, {novalid:true}); // check array bounds
|
||||||
} else if (this.builtins[expr.name]) { // is it a built-in function?
|
s += `;this.getArray(${qname}, ${expr.args.length})`;
|
||||||
this.checkFuncArgs(expr, this.builtins[expr.name]);
|
|
||||||
let jsargs = expr.args && expr.args.map((arg) => this.expr2js(arg, opts)).join(', ');
|
|
||||||
s += `this.builtins.${expr.name}(${jsargs})`;
|
|
||||||
} else if (expr.args) { // is it a subscript?
|
|
||||||
// TODO: check array bounds?
|
|
||||||
s += `this.getArray(${qname}, ${expr.args.length})`;
|
|
||||||
s += expr.args.map((arg) => '[this.ROUND('+this.expr2js(arg, opts)+')]').join('');
|
s += expr.args.map((arg) => '[this.ROUND('+this.expr2js(arg, opts)+')]').join('');
|
||||||
} else { // just a variable
|
} else { // just a variable
|
||||||
s = `this.vars.${expr.name}`;
|
s = `this.vars.${expr.name}`;
|
||||||
|
@ -388,7 +404,7 @@ export class BASICRuntime {
|
||||||
|
|
||||||
convert(name: string, right: number|string) : number|string {
|
convert(name: string, right: number|string) : number|string {
|
||||||
if (name.endsWith("$")) {
|
if (name.endsWith("$")) {
|
||||||
return right+"";
|
return right == null ? "" : right.toString();
|
||||||
} else if (typeof right === 'number') {
|
} else if (typeof right === 'number') {
|
||||||
return right;
|
return right;
|
||||||
} else {
|
} else {
|
||||||
|
@ -418,8 +434,9 @@ export class BASICRuntime {
|
||||||
this.arrays[name] = new arrcons(dims[0]+1);
|
this.arrays[name] = new arrcons(dims[0]+1);
|
||||||
} else if (dims.length == 2) {
|
} else if (dims.length == 2) {
|
||||||
this.arrays[name] = new Array(dims[0]+1);
|
this.arrays[name] = new Array(dims[0]+1);
|
||||||
for (var i=0; i<dims[0]+1; i++)
|
for (var i=0; i<dims[0]+1; i++) {
|
||||||
this.arrays[name][i] = new arrcons(dims[1]+1);
|
this.arrays[name][i] = new arrcons(dims[1]+1);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.runtimeError(`I only support arrays of one or two dimensions.`)
|
this.runtimeError(`I only support arrays of one or two dimensions.`)
|
||||||
}
|
}
|
||||||
|
@ -437,6 +454,25 @@ export class BASICRuntime {
|
||||||
return this.arrays[name];
|
return this.arrays[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arrayGet(name: string, ...indices: number[]) : basic.Value {
|
||||||
|
var arr = this.getArray(name, indices.length);
|
||||||
|
indices = indices.map(Math.round);
|
||||||
|
var v = arr;
|
||||||
|
for (var i=0; i<indices.length; i++) {
|
||||||
|
var idx = indices[i];
|
||||||
|
if (!isArray(v))
|
||||||
|
this.runtimeError(`I tried to lookup ${name}(${indices}) but used too many dimensions.`);
|
||||||
|
if (idx < this.opts.defaultArrayBase)
|
||||||
|
this.runtimeError(`I tried to lookup ${name}(${indices}) but an index was less than ${this.opts.defaultArrayBase}.`);
|
||||||
|
if (idx >= v.length)
|
||||||
|
this.runtimeError(`I tried to lookup ${name}(${indices}) but it exceeded the dimensions of the array.`);
|
||||||
|
v = v[indices[i]];
|
||||||
|
}
|
||||||
|
if (isArray(v)) // i.e. is an array?
|
||||||
|
this.runtimeError(`I tried to lookup ${name}(${indices}) but used too few dimensions.`);
|
||||||
|
return (v as any) as basic.Value;
|
||||||
|
}
|
||||||
|
|
||||||
onGotoLabel(value: number, ...labels: string[]) {
|
onGotoLabel(value: number, ...labels: string[]) {
|
||||||
value = this.ROUND(value);
|
value = this.ROUND(value);
|
||||||
if (value < 1 || value > labels.length)
|
if (value < 1 || value > labels.length)
|
||||||
|
@ -473,7 +509,11 @@ export class BASICRuntime {
|
||||||
var setvals = '';
|
var setvals = '';
|
||||||
stmt.args.forEach((arg, index) => {
|
stmt.args.forEach((arg, index) => {
|
||||||
var lexpr = this.assign2js(arg);
|
var lexpr = this.assign2js(arg);
|
||||||
setvals += `valid &= this.isValid(${lexpr} = this.convert(${JSON.stringify(arg.name)}, vals[${index}]));`
|
setvals += `
|
||||||
|
var value = this.convert(${JSON.stringify(arg.name)}, vals[${index}]);
|
||||||
|
valid &= this.isValid(value);
|
||||||
|
${lexpr} = value;
|
||||||
|
`
|
||||||
});
|
});
|
||||||
return `this.running=false;
|
return `this.running=false;
|
||||||
this.input(${prompt}, ${stmt.args.length}).then((vals) => {
|
this.input(${prompt}, ${stmt.args.length}).then((vals) => {
|
||||||
|
@ -619,12 +659,12 @@ export class BASICRuntime {
|
||||||
// TODO: memory quota
|
// TODO: memory quota
|
||||||
// TODO: useless loop (! 4th edition)
|
// TODO: useless loop (! 4th edition)
|
||||||
// TODO: other 4th edition errors
|
// TODO: other 4th edition errors
|
||||||
|
// TODO: ecma55 all-or-none input checking?
|
||||||
|
|
||||||
// FUNCTIONS
|
// FUNCTIONS
|
||||||
|
|
||||||
isValid(obj:number|string) : boolean {
|
isValid(obj:number|string) : boolean {
|
||||||
if (typeof obj === 'number' && !isNaN(obj))
|
if (typeof obj === 'number' && !isNaN(obj) && isFinite(obj))
|
||||||
return true;
|
return true;
|
||||||
else if (typeof obj === 'string')
|
else if (typeof obj === 'string')
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -138,11 +138,18 @@ export class TeleType {
|
||||||
if (show) ph.show(); else ph.hide();
|
if (show) ph.show(); else ph.hide();
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
resize(columns: number) {
|
||||||
|
// set font size proportional to window width
|
||||||
|
var charwidth = $(this.page).width() * 1.6 / columns;
|
||||||
|
$(this.page).css('font-size', charwidth + 'px');
|
||||||
|
this.scrollToBottom();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TeleTypeWithKeyboard extends TeleType {
|
export class TeleTypeWithKeyboard extends TeleType {
|
||||||
input : HTMLInputElement;
|
input : HTMLInputElement;
|
||||||
keepinput : boolean = true;
|
keepinput : boolean = true;
|
||||||
|
msecPerLine : number = 100; // IBM 1403
|
||||||
|
|
||||||
focused : boolean = true;
|
focused : boolean = true;
|
||||||
scrolling : number = 0;
|
scrolling : number = 0;
|
||||||
|
@ -236,7 +243,7 @@ export class TeleTypeWithKeyboard extends TeleType {
|
||||||
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();
|
||||||
$(this.scrolldiv).stop().animate({scrollTop: top}, 200, 'swing', () => {
|
$(this.scrolldiv).stop().animate({scrollTop: top}, this.msecPerLine, 'swing', () => {
|
||||||
this.scrolling = 0;
|
this.scrolling = 0;
|
||||||
this.ncharsout = 0;
|
this.ncharsout = 0;
|
||||||
});
|
});
|
||||||
|
|
|
@ -63,10 +63,7 @@ class BASICPlatform implements Platform {
|
||||||
}
|
}
|
||||||
this.timer = new AnimationTimer(60, this.animate.bind(this));
|
this.timer = new AnimationTimer(60, this.animate.bind(this));
|
||||||
this.resize = () => {
|
this.resize = () => {
|
||||||
// set font size proportional to window width
|
this.tty.resize(80);
|
||||||
var charwidth = $(gameport).width() * 1.6 / 80;
|
|
||||||
$(windowport).css('font-size', charwidth + 'px');
|
|
||||||
this.tty.scrollToBottom();
|
|
||||||
}
|
}
|
||||||
this.resize();
|
this.resize();
|
||||||
this.runtime.print = (s:string) => {
|
this.runtime.print = (s:string) => {
|
||||||
|
@ -174,12 +171,12 @@ class BASICPlatform implements Platform {
|
||||||
}
|
}
|
||||||
getDebugTree() {
|
getDebugTree() {
|
||||||
return {
|
return {
|
||||||
|
CurrentLine: this.runtime.getCurrentLabel(),
|
||||||
Variables: this.runtime.vars,
|
Variables: this.runtime.vars,
|
||||||
Arrays: this.runtime.arrays,
|
Arrays: this.runtime.arrays,
|
||||||
Functions: this.runtime.defs,
|
Functions: this.runtime.defs,
|
||||||
ForLoops: this.runtime.forLoops,
|
ForLoops: this.runtime.forLoops,
|
||||||
ReturnStack: this.runtime.returnStack,
|
ReturnStack: this.runtime.returnStack,
|
||||||
CurrentLine: this.runtime.getCurrentLabel(),
|
|
||||||
NextDatum: this.runtime.datums[this.runtime.dataptr],
|
NextDatum: this.runtime.datums[this.runtime.dataptr],
|
||||||
Dialect: this.runtime.opts,
|
Dialect: this.runtime.opts,
|
||||||
Internals: this.runtime,
|
Internals: this.runtime,
|
||||||
|
|
Loading…
Reference in New Issue