![]() |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML LANG="en"> <HEAD> <META HTTP-EQUIV="Content-Type" CONTENT="text/html;charset=ISO-8859-1"> <TITLE>Tiny BASIC</TITLE> </HEAD> <BODY BGCOLOR="#CCFFFF" STYLE="margin:0"> <CENTER ID="back" STYLE="height:100%"> <BR> <B><FONT COLOR="#CC0000">Tiny BASIC<BR>(Netscape 6)</FONT></B> <FORM STYLE="margin-top:0; margin-bottom:0"> <DIV STYLE="width:0; height:0; overflow:hidden"> <INPUT TYPE=BUTTON ID="dummy"> </DIV> </FORM> <TABLE BORDER=0> <TR><TD BGCOLOR=BLACK><TT> <SPAN ID="scr0" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr1" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr2" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr3" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr4" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr5" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr6" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr7" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr8" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr9" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr10" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr11" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr12" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr13" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr14" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr15" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr16" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr17" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr18" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr19" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr20" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr21" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr22" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr23" STYLE="color:white; text-align:left"></SPAN><BR> <SPAN ID="scr24" STYLE="color:white; text-align:left"></SPAN><BR> </TT></TD></TR> <TR><TD STYLE="visibility:hidden"><TT> <SPAN>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</SPAN> </TT></TD></TR> </TABLE> <FORM> Load/Save<BR> <TEXTAREA ID="source" ROWS=6 COLS=80></TEXTAREA><BR> <INPUT TYPE=BUTTON ID="load" VALUE="Load" onClick="load_source()"> <INPUT TYPE=BUTTON ID="save" VALUE="Save" onClick="save_source()"> <BR> </FORM> </CENTER> <SCRIPT LANGUAGE="JavaScript1.3" TYPE="text/javascript"> <!-- // Tiny BASIC interpreter for Netscape 6/7 // This program is based on "Palo Alto Tiny BASIC" written by Li-Chen Wang. // This JavaScript version was written by Mabuchi Yoshihiko. function load_source() { line.length = 0; // clear var source = elem_source.value.split("\n"); var last_num = 0; for(var i = 0; i < source.length; i++) { var w = source[i].match(/^ *(\d*) *(.*)/); if(!w[1].length) { if(w[2].length) { alert("The line number is missing."); break; } continue; } var num = parseInt(w[1], 10) if(num > 32767) { alert("The line number is out of range."); break; } if(num <= last_num) { alert("The line number is unordered."); break; } if(w[2].length) { line.push({num:num, text:w[2]}); last_num = num; } } restart(true); elem_load.blur(); } function save_source() { var source = ""; for(var i = 0; i < line.length; i++) { var str = line[i].num.toString(); if(str.length < 4) str = (" " + str).substr(-4); source += str + " " + line[i].text + "\n"; } elem_source.value = source; elem_source.select(); elem_source.focus(); } function window_focus(e) { if(e.target != elem_source && e.target != elem_load && e.target != elem_save) { if(!scr_focused) { scr_focused = true; refresh(cur_y); } } } function window_blur(e) { if(e.target != elem_source && e.target != elem_load && e.target != elem_save) control_focus(0); } function control_focus(e) { if(scr_focused) { scr_focused = false; refresh(cur_y); } } function keypress(e) { if(!scr_focused) return true; if(e.keyCode == 9) // Tab return true; if(e.ctrlKey) { // Ctrl if((e.which & 0xdf) == 0x43) { // Ctrl-C if(i_current < 0 && !list || input) { // direct mode except for LIST, or INPUT if(to_id != undefined) { clearTimeout(to_id); to_id = undefined; } restart(true); } else { brk = true; } } return false; } if(i_current < 0 && !list || input) { // direct mode except for LIST, or INPUT switch(e.which) { case 8: // Back Space if(buffer.length) { putstr("\b"); buffer = buffer.substr(0, buffer.length - 1); } return false; case 13: // Enter putstr("\r"); if(input) // INPUT com_input3(); else edit_direct(); return false; default: if(e.which < 32 || e.which >= 127) return false; } if(buffer.length < 132) { var c = String.fromCharCode(e.which); putstr(c); buffer += c; } } return false; } // put a string to the screen function putstr(str) { for(var i = 0; i < str.length; i++) putc(str.charAt(i)); // screen refresh for(var y = 0; y < 25; y++) { if(scrmod[y]) refresh(y); } } function putc(c) { switch(c) { case "\b": // Back Space if(cur_x) { scrbuf[cur_y] = scrbuf[cur_y].substr(0, scrbuf[cur_y].length - 1); cur_x--; scrmod[cur_y] = true; } else { if(cur_y) { cur_y--; if(scrbuf[cur_y].length == 80) { scrbuf[cur_y] = scrbuf[cur_y].substr(0, 79); } else { while(scrbuf[cur_y].length < 79) scrbuf[cur_y] += " "; } cur_x = 79; scrmod[cur_y] = scrmod[cur_y + 1] = true; } } break; case "\t": // Tab for(; ; ) { putc(" "); if(!(cur_x % 8)) break; } break; case "\f": // Form Feed for(var y = 0; y < 25; y++) { scrbuf[y] = ""; scrmod[y] = true; } cur_x = cur_y = 0; break; default: if(c == "\r") cur_x = 79; else scrbuf[cur_y] += c; if(cur_x == 79) { cur_x = 0; if(cur_y == 24) { // scroll scrbuf.shift(); scrbuf.push(""); for(var y = 0; y < 25; y++) scrmod[y] = true; } else { cur_y++; scrmod[cur_y - 1] = scrmod[cur_y] = true; } } else { cur_x++; scrmod[cur_y] = true; } } } // screen refresh function refresh(y) { var scr = scrbuf[y].replace(/ /g, nbsp); if(scr_focused) { if(y == cur_y) // cursor scr += "<FONT STYLE=\"background-color:white\">" + nbsp + "</FONT>"; } document.getElementById("scr" + y.toString()).innerHTML = scr; scrmod[y] = false; } // restart function restart(init) { buffer = ""; if(init) putstr("\r"); putstr("OK\r>"); i_current = -1; list = input = false; for_info = {i_var:-1}; for_stack.length = gosub_stack.length = 0; brk = false; } // edit program/direct command function edit_direct() { text = buffer; var num; if(isNaN(num = get_num())) { restart(true); return; } if(num > 0) { // edit program ignore_blanks(); var f = false; for(var i = 0; i < line.length; i++) { if(line[i].num >= num) { if(line[i].num == num) f = true; break; } } if(f) { if(text.length) // replace line[i].text = text; else // delete line.splice(i, 1); } else { if(text.length) // insert line.splice(i, 0, {num:num, text:text}); } buffer = ""; putstr(">"); } else { exec(false); } } // execute commands function exec(cont) { to_id = undefined; var sts; if(cont) { sts = 1; } else { var i; if((i = get_token((i_current < 0) ? token1 : token2)) < 0) { // unmatch if(text.length) // LET sts = com_let(); else sts = 2; } else { sts = ((i_current < 0) ? func1 : func2)[i](); } } switch(sts) { case -1: // error or NEW restart(true); return; case 0: // return to command prompt restart(false); return; case 1: // next command if(test_char(";")) // same line break; if(text.length) { // not EOL error_what(); restart(true); return; } // fall thru case 2: // next line if(i_current < 0) { // direct mode restart(false); return; } if(++i_current == line.length) { // end of program restart(false); return; } // fall thru case 3: // new line text = line[i_current].text; break; case 4: // continue break; case 5: // in progress (LIST, INPUT) return; } if(brk) restart(true); else to_id = setTimeout("exec(false)", 0); } //-------- commands -------------------- // LIST function com_list() { var num, lines; if(isNaN(num = get_num())) return -1; if(test_char(",")) { if(isNaN(lines = get_num())) return -1; if(lines < 0) // not a number lines = 0; } else { lines = Infinity; } if(check_eol()) return -1; var i = 0; if(num > 0) { for(; i < line.length; i++) { if(line[i].num >= num) break; } } list = true; com_list2(i, lines); return 5; } function com_list2(i, lines) { if(!lines || i == line.length || brk) { restart(false); return; } var str = line[i].num.toString(); if(str.length < 4) str = (" " + str).substr(-4); putstr(str + " " + line[i].text + "\r"); setTimeout("com_list2(" + (i + 1).toString() + "," + (lines - 1).toString() + ")", 0); } // NEW function com_new() { if(check_eol()) return -1; line.length = 0; // clear return -1; } // RUN function com_run() { if(check_eol()) return -1; if(line.length) { i_current = 0; return 3; } return 0; } // NEXT function com_next() { var i_var; if((i_var = test_var()) < 0) return -1; if(i_var == 0x10000) { // not a variable error_what(); return -1; } for(; ; ) { if(for_info.i_var < 0) { error_what(); return -1; } if(for_info.i_var == i_var) break; for_info = for_stack.pop(); } var val = tb_vars[for_info.i_var] + for_info.inc; if(val >= -32768 && val <= 32767) { tb_vars[for_info.i_var] = val; if((for_info.inc < 0) ? (val >= for_info.lmt) : (val <= for_info.lmt)) { // within limit i_current = for_info.i_line; text = for_info.text; return 1; } } for_info = for_stack.pop(); return 1; } // LET function com_let() { for(; ; ) { if(assign_val() < 0) return -1; if(!test_char(",")) return 1; } } // IF function com_if() { var val; if(isNaN(val = expr())) return -1; if(val) return 4; return 2; } // GOTO function com_goto() { // line number var num; if(isNaN(num = expr())) return -1; if(check_eol()) return -1; var i; if((i = find_line(num)) < 0) return -1; i_current = i; return 3; } // GOSUB function com_gosub() { // line number var num; if(isNaN(num = expr())) return -1; var i; if((i = find_line(num)) < 0) return -1; gosub_stack.push({for_info:for_info, i_line:i_current, text:text}); // save current state for_info = {i_var:-1}; i_current = i; return 3; } // RETURN function com_return() { if(check_eol()) return -1; if(!gosub_stack.length) { error_what(); return -1; } var info = gosub_stack.pop(); for_info = info.for_info; i_current = info.i_line; text = info.text; return 1; } // REM function com_rem() { return 2; } // FOR function com_for() { for_stack.push(for_info); // save old FOR info. for_info = {}; // initial value if((for_info.i_var = assign_val()) < 0) return -1; // limit if(get_token(["TO"]) < 0) { // unmatch error_what(); return -1; } if(isNaN(for_info.lmt = expr())) return -1; // increment if(get_token(["STEP"]) < 0) { // unmatch for_info.inc = 1; } else { if(isNaN(for_info.inc = expr())) return -1; } // current position for_info.i_line = i_current; for_info.text = text; // the same variable was used ? for(var i = for_stack.length - 1; i >= 0; i--) { if(for_stack[i].i_var == for_info.i_var) { // used // purge old info. for_stack.splice(i, 1); break; } } return 1; } // INPUT function com_input() { if(com_input2()) return -1; return 5; } function com_input2() { input_text = text; if(quoted_str()) { if(!text.length) { // EOL if(i_current < 0) restart(false); else exec(true); return 0; } if((input_i_var = test_var()) < 0) return -1; } else { if((input_i_var = test_var()) < 0) return -1; if(input_i_var == 0x10000) { // not a variable error_what(); return -1; } putstr(input_text.substr(0, input_text.length - text.length)); } if(input_i_var != 0x10000) { // variable buffer = ""; putstr(":"); input = true; return 0; } to_id = setTimeout("com_input3()", 0); return 0; } function com_input3() { to_id = undefined; if(input_i_var != 0x10000) { var save_text = text; text = buffer; var f; var val; if(!(f = isNaN(val = expr()))) { if(check_eol()) f = true; } input = false; if(f) { // error // redo input text = input_text; com_input2(); return; } text = save_text; tb_vars[input_i_var] = val; } if(test_char(",")) { // more items if(com_input2() < 0) restart(true); return; } exec(true); } // PRINT function com_print() { ignore_blanks(); if(!text.length) { // EOL putstr("\r"); return 1; } if(text.charAt(0) == ";") { putstr("\r"); return 1; } var width = 8; for(; ; ) { if(test_char("#")) { // format if(isNaN(width = expr())) return -1; if(width >= 64) { error_how(); return -1; } } else { if(!quoted_str()) { var val; if(isNaN(val = expr())) return -1; var str = (val + 0).toString(); // "+ 0": avoid "-0" while(str.length < width) str = " " + str; putstr(str); } } if(test_char(",")) { while(test_char(",")) putstr(" "); if(!text.length) // EOL return 1; if(text.charAt(0) == ";") return 1; } else { putstr("\r"); return 1; } } } // STOP function com_stop() { if(check_eol()) return -1; return 0; } //-------- functions ------------------- // RND function fnc_rnd() { var n; if(isNaN(n = paren())) return NaN; if(n < 0) { error_how(); return NaN; } return Math.floor(Math.random() * n) + 1; } // ABS function fnc_abs() { var n; if(isNaN(n = paren())) return NaN; if(n == -32768) { error_how(); return NaN; } return Math.abs(n); } // SIZE function fnc_size() { return 32766; } //-------------------------------------- // get a token // return value - index of token table(>=0), -1: unmatch function get_token(token_tbl) { ignore_blanks(); for(var i_token = 0; i_token < token_tbl.length; i_token++) { var token = token_tbl[i_token]; var f = true; for(var i = 0; i < token.length; i++) { if(text.charAt(i) == ".") { i++; break; } if(text.charAt(i) != token.charAt(i)) { f = false; break; } } if(f) { // match text = text.substr(i); return i_token; } } // unmatch return -1; } // assign a value to the variable // return value - index of tb_vars(>=0), -1: error function assign_val() { var i_var; if((i_var = test_var()) < 0) return -1; if(i_var == 0x10000) { // not a variable error_what(); return -1; } if(!test_char("=")) { error_what(); return -1; } var val; if(isNaN(val = expr())) return -1; tb_vars[i_var] = val; return i_var; } // test if variable // return value - index of tb_vars(>=0), 0x10000: not a variable, -1: error function test_var() { ignore_blanks(); if(!text.length) // EOL return 0x10000; var c = text.charCodeAt(); if(c >= 64 && c <= 90) { // @, A-Z text = text.substr(1); if(c == 64) { // array var i; if(isNaN(i = paren())) return -1; if(i < 0 || i > 16383) { error_how(); return -1; } return i + 26; } return c - 65; } return 0x10000; } // print a quoted string/control code // return value - true: quoted string/control code was found, false: not found function quoted_str() { var quot; switch(test_char("\"", "'", "^")) { case 1: quot = "\""; break; case 2: quot = "'"; break; case 3: // ^ if(text.length) { putstr(String.fromCharCode(text.charCodeAt() ^ 0x40)); text = text.substr(1); } // original PATB prints even ^CR // it seems to be useless return true; default: return false; } var str = text.split(quot, 1)[0]; putstr(str); text = text.substr(str.length + 1); return true; } // expression // return value - result of expression(0 or 1), NaN: error function expr() { var val1, val2; if(isNaN(val1 = expr1())) return NaN; var op; if((op = get_token([">=", "#", ">", "=", "<=", "<"])) < 0) // relational operators // not a relational operator return val1; if(isNaN(val2 = expr1())) return NaN; switch(op) { case 0: // >= return (val1 >= val2) ? 1 : 0; case 1: // # return (val1 != val2) ? 1 : 0; case 2: // > return (val1 > val2) ? 1 : 0; case 3: // = return (val1 == val2) ? 1 : 0; case 4: // <= return (val1 <= val2) ? 1 : 0; case 5: // < return (val1 < val2) ? 1 : 0; } } // return value - result of expression, NaN: error function expr1() { var val1, val2; switch(test_char("-", "+")) { case 1: // minus if(isNaN(val1 = expr2())) return NaN; val1 = - val1; break; case 2: // plus // fall thru default: if(isNaN(val1 = expr2())) return NaN; } for(; ; ) { switch(test_char("+", "-")) { case 1: // plus if(isNaN(val2 = expr2())) return NaN; val1 += val2; break; case 2: // minus if(isNaN(val2 = expr2())) return NaN; val1 -= val2; break; default: return val1; } if(val1 > 32767 || val1 < -32768) { error_how(); return NaN; } } } // return value - result of expression, NaN: error function expr2() { var val1, val2; if(isNaN(val1 = expr3())) return NaN; if(val1 == -32768) { error_how(); return NaN; } for(; ; ) { var op; if(!(op = test_char("*", "/"))) return val1; if(isNaN(val2 = expr3())) return NaN; if(val2 == -32768) { error_how(); return NaN; } if(op == 1) { // multiply val1 *= val2; if(val1 > 32767 || val1 < -32767) { error_how(); return NaN; } } else { // divide if(!val2) { // divide by zero error_how(); return NaN; } val1 /= val2; if(val1 < 0) val1 = Math.ceil(val1); else val1 = Math.floor(val1); } } } // return value - result of expression, NaN: error function expr3() { var i; if((i = get_token(token3)) >= 0) // functions return func3[i](); // not a function var i_var; if((i_var = test_var()) < 0) return NaN; if(i_var != 0x10000) { // variable if(tb_vars[i_var] == undefined) return 0; return tb_vars[i_var]; } var val; if(isNaN(val = get_num())) return NaN; if(val >= 0) // number return val; if(isNaN(val = paren())) return NaN; return val; // (expression) } // func. parameter/array index/(expression) // return value - result of expression, NaN: error function paren() { if(test_char("(")) { var val; if(isNaN(val = expr())) return NaN; if(test_char(")")) return val; } error_what(); return NaN; } // get a number // return value - number(>=0), -1: not a number, NaN: error function get_num() { ignore_blanks(); var w = text.match(/^(\d*)(.*)/); if(w[1].length) { // number text = w[2]; var val = parseInt(w[1], 10); if(val > 32767) { error_how(); return NaN; } return val; } return -1; } // test characters // arguments - any number of char.s, say, test_char("A", "B", "C") // return value - 0: didn't match, 1: matched to 1st char., 2: 2nd, ... function test_char() { ignore_blanks(); for(var i = 0; i < arguments.length; i++) { if(text.charAt(0) == arguments[i]) { text = text.substr(1); return i + 1; } } return 0; } // check if end of line // return value - 0: OK(EOL), -1: not EOL function check_eol() { ignore_blanks(); if(text.length) { // not EOL error_what(); return -1; } return 0; } // ignore leading blanks function ignore_blanks() { text = text.replace(/^ */, ""); } // find the target line // return value - line index(>=0), -1: not found function find_line(num) { for(var i = 0; i < line.length; i++) { if(line[i].num >= num) { if(line[i].num == num) return i; break; } } error_how(); return -1; } // error WHAT? function error_what() { put_error("WHAT?"); } // error HOW? function error_how() { put_error("HOW?"); } // error message function put_error(msg) { putstr("\r" + msg + "\r"); if(i_current >= 0 && !input) { // except for direct mode and INPUT var str = line[i_current].num.toString(); if(str.length < 4) str = (" " + str).substr(-4); putstr(str + " " + line[i_current].text.substr(0, line[i_current].text.length - text.length) + "?" + text + "\r"); } } //---------------------------------------------------------- // direct commands token1 = ["LIST", "NEW", "RUN"]; func1 = [com_list, com_new, com_run]; // direct/statement commands token2 = ["NEXT", "LET", "IF", "GOTO", "GOSUB", "RETURN", "REM", "FOR", "INPUT", "PRINT", "STOP"]; func2 = [com_next, com_let, com_if, com_goto, com_gosub, com_return, com_rem, com_for, com_input, com_print, com_stop]; // functions token3 = ["RND", "ABS", "SIZE"]; func3 = [fnc_rnd, fnc_abs, fnc_size]; token1 = token1.concat(token2); func1 = func1.concat(func2); nbsp = String.fromCharCode(160); scrbuf = new Array(); // screen buffer scrbuf.length = 25; scrmod = new Array(); // screen buffer modify flag scrmod.length = 25; for(i = 0; i < 25; i++) { scrbuf[i] = ""; scrmod[i] = false; } cur_x = cur_y = 0; // screen current position scr_focused = false; line = []; // program save area var i_current; // current line index (-1: direct mode) var list, input; // LIST, INPUT in-progress flag tb_vars = new Array(); // variables [0]-[25]: A-Z, [26]-: @ array tb_vars.length = 26; var text; // command text var for_info; // FOR loop info. for_stack = []; // FOR loop info. stack gosub_stack = []; // GOSUB info. stack var input_text; // INPUT text save area var input_i_var; // INPUT variable index var buffer; // input buffer var brk; // break flag var to_id; // timeout ID (elem_source = document.getElementById("source")).onfocus = (elem_load = document.getElementById("load")).onfocus = (elem_save = document.getElementById("save")).onfocus = control_focus; onfocus = window_focus; // added for Firefox elem_back = document.getElementById("back"); elem_dummy = document.getElementById("dummy"); if(elem_back.tabIndex != undefined) { elem_back.tabIndex = -1; elem_back.onfocus = window_focus; elem_dummy.tabIndex = -1; } onblur = window_blur; onkeypress = keypress; elem_dummy.focus(); elem_dummy.blur(); restart(false); //--> </SCRIPT> </BODY> </HTML> |