tbasic_o7.htm

戻る

<!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">
<CENTER>
<BR>
<B><FONT COLOR="#CC0000">Tiny BASIC<BR>(Opera 7)</FONT></B>
<FORM onSubmit="return false" STYLE="margin-top:0; margin-bottom:0">
<DIV STYLE="width:0; height:0; overflow:hidden">
<INPUT TYPE=TEXT 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()">
&nbsp;&nbsp;
<INPUT TYPE=BUTTON ID="save" VALUE="Save" onClick="save_source()">
<BR>
</FORM>

</CENTER>

<SCRIPT LANGUAGE="JavaScript1.3" TYPE="text/javascript">
<!--
// Tiny BASIC interpreter for Opera 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_dummy.focus();
}

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();
  source_focus = true;
  elem_source.focus();
  setTimeout("source_focus = false", 0);
}

function window_focus(e) {
  if(source_focus)
    return;

  win_focussed = true;
  setTimeout("window_focus2()", 0);
}

function window_focus2() {
  if(win_focussed)
    elem_dummy.focus();
}

function dummy_focus(e) {
  if(!scr_focused) {
    scr_focused = true;
    refresh(cur_y);
  }
}

function dummy_blur(e) {
  if(scr_focused) {
    scr_focused = false;
    refresh(cur_y);
  }
}

function control_focus(e) {
  win_focussed = false;
}

function keypress(e) {
  if(!scr_focused)
    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;
}

// added for newer Opera
function keydown(e) {
  if(scr_focused && !e.ctrlKey
       && (i_current < 0 && !list || input)) {  // direct mode except for LIST, or INPUT
    if(e.which == 8) {  // Back Space
      if(buffer.length) {
        putstr("\b");
        buffer = buffer.substr(0, buffer.length - 1);
      }
    }
  }

  return true;
}

// 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
    = document.getElementById("load").onfocus
    = document.getElementById("save").onfocus = control_focus;
  elem_dummy = document.getElementById("dummy");
  elem_dummy.onfocus = dummy_focus;
  elem_dummy.onblur = dummy_blur;
  onfocus = window_focus;
  // added for newer Opera
  if(document.documentElement.tabIndex != undefined) {
    document.documentElement.tabIndex = -1;
    document.documentElement.onfocus = window_focus;
  }
  onkeypress = keypress;
  // added for newer Opera
  onkeydown = keydown;
  var win_focussed;
  source_focus = false;

  elem_dummy.focus();

  restart(false);
//-->
</SCRIPT>

</BODY>

</HTML>