├── README └── src └── wasix.winxed /README: -------------------------------------------------------------------------------- 1 | Wasix README 2 | 3 | Wasix is an old style Basic interprter written in Winxed. 4 | 5 | Winxed is a programming language based in parrot. 6 | 7 | http://www.winxed.org/ 8 | http://www.parrot.org/ 9 | -------------------------------------------------------------------------------- /src/wasix.winxed: -------------------------------------------------------------------------------- 1 | #! winxed 2 | 3 | // wasix: a simple old style Basic interpreter 4 | // Revision 23-oct-2011 5 | 6 | /* 7 | 8 | Instructions: 9 | 10 | CHAIN 11 | CLEAR 12 | CLOSE 13 | CONT 14 | DELETE 15 | DIM 16 | END 17 | ERROR 18 | EXIT 19 | FOR 20 | GOSUB 21 | GOTO 22 | IF 23 | LET 24 | LINE INPUT 25 | LIST 26 | LOAD 27 | NEW 28 | NEXT 29 | OPEN 30 | PRINT 31 | REM 32 | REPEAT 33 | RETURN 34 | RUN 35 | SAVE 36 | STOP 37 | TROFF 38 | TRON 39 | UNTIL 40 | WEND 41 | WHILE 42 | 43 | Functions: 44 | 45 | ACOS 46 | ASC 47 | ASIN 48 | ATN 49 | CHR$ 50 | COS 51 | INSTR 52 | LEFT$ 53 | LEN 54 | MID$ 55 | NEW 56 | RIGHT$ 57 | SIN 58 | SQR 59 | STRING$ 60 | TAN 61 | 62 | */ 63 | 64 | // No number, direct command. 65 | const int DIRECT = -1; 66 | 67 | // Token types 68 | const int IDENTIFIER = 0; 69 | const int INTEGER = 1; 70 | const int FLOAT = 2; 71 | const int STRING = 3; 72 | const int OP = 4; 73 | 74 | // Runner states 75 | const int RUNstart = 0; 76 | const int RUNend = 1; 77 | const int RUNinteract = 2; 78 | const int RUNrun = 3; 79 | const int RUNrunning = 4; 80 | const int RUNgoto = 5; 81 | const int RUNerror = 6; 82 | const int RUNexit = 7; 83 | 84 | // Open modes and flags 85 | const int OPEN_input = 1; 86 | const int OPEN_output = 2; 87 | const int OPEN_mode_mask = 3; 88 | const int OPEN_binary = 4; 89 | 90 | // Error codes 91 | const int ERR_no_error = 0; 92 | const int ERR_without_for = 1; 93 | const int ERR_syntax = 2; 94 | const int ERR_without_gosub = 3; 95 | 96 | const int ERR_no_line = 8; 97 | 98 | const int ERR_cant_cont = 17; 99 | 100 | const int ERR_without_next = 26; 101 | 102 | const int ERR_without_wend = 29; 103 | const int ERR_without_while = 30; 104 | const int ERR_not_open = 31; 105 | 106 | const int ERR_return_missing = 97; 107 | const int ERR_load = 98; 108 | const int ERR_stop = 99; 109 | 110 | function errmsg(int code) 111 | { 112 | string str; 113 | switch(code) { 114 | case ERR_without_for: str = 'Unexpected NEXT'; break; 115 | case ERR_syntax: str = 'Syntax error'; break; 116 | case ERR_without_gosub: str = 'Unexpected RETURN'; break; 117 | 118 | case ERR_no_line: str = 'Line does not exist'; break; 119 | 120 | case ERR_cant_cont: str = 'Cannot CONTinue'; break; 121 | 122 | case ERR_without_next: str = 'NEXT missing'; break; 123 | 124 | case ERR_without_wend: str = 'WEND missing'; break; 125 | case ERR_without_while: str = 'Unexpected WEND'; break; 126 | case ERR_not_open: str = 'File not open'; break; 127 | 128 | case ERR_return_missing: str = 'RETURN missing'; break; 129 | case ERR_load: str = 'Cannot load file'; break; 130 | case ERR_stop: str = 'Stop'; break; 131 | default: 132 | str = code; 133 | str = 'Unknown error code(' + str + ')'; 134 | } 135 | return str; 136 | } 137 | 138 | //********************************************************************** 139 | 140 | function isstartidentifier(string c) 141 | { 142 | int i = indexof('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', c); 143 | return i != -1; 144 | } 145 | 146 | function isidentifier(string c) 147 | { 148 | int i = indexof('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', c); 149 | return i != -1; 150 | } 151 | 152 | function isdigit(string c) 153 | { 154 | int i = indexof('0123456789', c); 155 | return i != -1; 156 | } 157 | 158 | function getdigits(string str) 159 | { 160 | int n = length(str); 161 | int i = 0; 162 | while (i < n && indexof('01234567890', substr(str, i, 1)) > -1) 163 | ++i; 164 | return i; 165 | } 166 | 167 | function parseline(string str) 168 | { 169 | int i = getdigits(str); 170 | int numline; 171 | string linecontent; 172 | if (i > 0) { 173 | string sline = substr(str, 0, i); 174 | numline = sline; 175 | int n = length(str); 176 | while (i < n - 1 && substr(str, i, 1) == ' ') 177 | ++i; 178 | if (i < n) 179 | linecontent = substr(str, i); 180 | } 181 | else { 182 | linecontent = str; 183 | numline = DIRECT; 184 | } 185 | return new ProgramLine(numline, linecontent); 186 | } 187 | 188 | //********************************************************************** 189 | 190 | class Token 191 | { 192 | var t; 193 | var str; 194 | function Token(int type, var str) 195 | { 196 | self.t = type; 197 | self.str = str; 198 | return self; 199 | } 200 | function type() 201 | { 202 | return self.t; 203 | } 204 | function isop(string op) 205 | { 206 | return self.t == OP && self.str == op; 207 | } 208 | function isidentifier(string name) 209 | { 210 | return self.t == IDENTIFIER && self.str == name; 211 | } 212 | function checkidentifier() 213 | { 214 | if (self.t == IDENTIFIER) 215 | return string(self.str); 216 | return ''; 217 | } 218 | function get() 219 | { 220 | return self.str; 221 | } 222 | function getint() 223 | { 224 | return int(self.str); 225 | } 226 | function getfloat() 227 | { 228 | return float(self.str); 229 | } 230 | } 231 | 232 | //********************************************************************** 233 | 234 | function parsechunks(string str) 235 | { 236 | var tokens = []; 237 | int n = length(str); 238 | int i = 0; 239 | while (i < n) { 240 | var tok; 241 | string t = ''; 242 | string c = substr(str, i, 1); 243 | if (c == ' ' || c == "\t") { 244 | t = c; 245 | ++i; 246 | while (i < n && ((c= substr(str, i, 1)) == ' ' || c == "\t")) { 247 | t += c; 248 | ++i; 249 | } 250 | continue; 251 | } 252 | else if (isstartidentifier(c)) { 253 | do { 254 | int uc = indexof('abcdefghijklmnopqrstuvwxyz', c); 255 | if (uc > -1) 256 | c = substr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", uc, 1); 257 | t += c; 258 | ++i; 259 | } while (i < n && (isidentifier( c= substr(str, i, 1)))); 260 | if (i < n && c == '$') { 261 | t+= c; 262 | ++i; 263 | } 264 | tok = new Token(IDENTIFIER, t); 265 | } 266 | else if (isdigit(c) || c == '.') { 267 | int isfloat = 0; 268 | if (isdigit(c)) { 269 | do { 270 | t += c; 271 | ++i; 272 | } while (i < n && (isdigit( c= substr(str, i, 1)))); 273 | } 274 | if (i < n && c == '.') { 275 | isfloat = 1; 276 | do { 277 | t += c; 278 | ++i; 279 | } while (i < n && (isdigit( c= substr(str, i, 1)))); 280 | } 281 | if (i < n && (c == 'e' || c == 'E')) { 282 | isfloat = 1; 283 | do { 284 | t += c; 285 | ++i; 286 | } while (i < n && (isdigit( c= substr(str, i, 1)))); 287 | } 288 | if (isfloat) { 289 | if (t == '.') 290 | tok = new Token(OP, t); 291 | else { 292 | float fval = t; 293 | tok = new Token(FLOAT, fval); 294 | } 295 | } 296 | else { 297 | int ival = t; 298 | tok = new Token(INTEGER, ival); 299 | } 300 | } 301 | else if (c == '"') { 302 | for (++i; i < n && (c = substr(str, i, 1)) != '"'; ++i) 303 | t += c; 304 | ++i; 305 | tok = new Token(STRING, t); 306 | } 307 | else { 308 | t = c; 309 | if (++i < n) { 310 | string c1 = substr(str, i, 1); 311 | if (c == '<' && (c1 == '>' || c1 == '=')) { 312 | t += c1; 313 | ++i; 314 | } 315 | else if (c == '>' && c1 == '=') { 316 | t += c1; 317 | ++i; 318 | } 319 | } 320 | tok = new Token(OP, t); 321 | } 322 | tokens.push(tok); 323 | } 324 | int ntok = tokens; 325 | var chunks = []; 326 | var items = []; 327 | for (i = 0; i < ntok; ++i) { 328 | var token = tokens[i]; 329 | if (token.isop(':') || token.isidentifier("THEN")) { 330 | if (int(items) > 0) 331 | chunks.push(createChunk(items)); 332 | items = []; 333 | } 334 | else if (token.isidentifier("ELSE")) { 335 | if (int(items) > 0) 336 | chunks.push(createChunk(items)); 337 | items = []; 338 | items.push(token); 339 | chunks.push(createChunk(items)); 340 | items = []; 341 | } 342 | else 343 | items.push(token); 344 | } 345 | if (int(items) > 0) 346 | chunks.push(createChunk(items)); 347 | return chunks; 348 | } 349 | 350 | //********************************************************************** 351 | 352 | class Matrix 353 | { 354 | var numdims; 355 | var dims; 356 | var values; 357 | function Matrix(runner, initdims) 358 | { 359 | int n = initdims; 360 | int dims[n]; 361 | int size = 1; 362 | for (int i = 0; i < n; ++i) { 363 | var vdim = initdims[i].eval(runner); 364 | int dim = vdim; 365 | ++dim; 366 | size = size * dim; 367 | dims[i] = dim; 368 | } 369 | self.numdims = n; 370 | self.dims = dims; 371 | var values[size]; 372 | for (int v = 0; v < size; ++v) { 373 | var value = 0; 374 | values[v] = value; 375 | } 376 | self.values = values; 377 | return self; 378 | } 379 | } 380 | 381 | function parsedims(var items) 382 | { 383 | var dims = []; 384 | //int n; 385 | var aux; 386 | do { 387 | var expr = parseExpr(items); 388 | if (expr == null) 389 | return null; 390 | dims.push(expr); 391 | if (int(items) < 1) 392 | return null; 393 | } while ((aux = items.shift()).isop(',')); 394 | if (! aux.isop(')')) 395 | return null; 396 | return dims; 397 | } 398 | 399 | function dimoffset(runner, m, indexes) 400 | { 401 | var dims = m.dims; 402 | int n = indexes; 403 | if (n != m.numdims) 404 | return 0; 405 | int offset = 0; 406 | int mult = 1; 407 | for (int i= n - 1; i >= 0; --i) { 408 | var index = indexes[i].eval(runner); 409 | int vind = index; 410 | int dim = dims[i]; 411 | if (vind > dim) 412 | throw 'return 0'; 413 | offset = offset + mult * vind; 414 | mult = mult * dim; 415 | } 416 | return offset; 417 | } 418 | 419 | //********************************************************************** 420 | 421 | class ExprSyntaxError 422 | { 423 | function eval(runner) 424 | { 425 | return null; 426 | } 427 | } 428 | 429 | class ExprInteger 430 | { 431 | var n; 432 | function ExprInteger(item) 433 | { 434 | int n = item.getint(); 435 | self.n = n; 436 | return self; 437 | } 438 | function eval(runner) 439 | { 440 | return self.n; 441 | } 442 | } 443 | 444 | class ExprFloat 445 | { 446 | var n; 447 | function set(item) 448 | { 449 | float n = item.getfloat(); 450 | self.n = n; 451 | return self; 452 | } 453 | function eval(runner) 454 | { 455 | return self.n; 456 | } 457 | } 458 | 459 | class ExprString 460 | { 461 | var s; 462 | function ExprString(item) 463 | { 464 | string s = item.get(); 465 | self.s = s; 466 | return self; 467 | } 468 | function eval(runner) 469 | { 470 | return self.s; 471 | } 472 | } 473 | 474 | class ExprIdentifier 475 | { 476 | var name; 477 | function ExprIdentifier(string name) 478 | { 479 | self.name = name; 480 | return self; 481 | } 482 | function eval(runner) 483 | { 484 | return runner.getvar(self.name); 485 | } 486 | } 487 | 488 | class ExprIndexed 489 | { 490 | var name; 491 | var indexes; 492 | function ExprIndexed(string name, items) 493 | { 494 | self.name = name; 495 | if (int(items) < 1) 496 | return new ExprSyntaxError; 497 | if (!(items.shift()).isop('(')) 498 | return new ExprSyntaxError; 499 | var indexes = parsedims(items); 500 | if (indexes == null) 501 | return new ExprSyntaxError; 502 | self.indexes = indexes; 503 | return self; 504 | } 505 | function eval(runner) 506 | { 507 | var m = runner.getvar(self.name); 508 | int offset = dimoffset(runner, m, self.indexes); 509 | return m.values[offset]; 510 | } 511 | } 512 | 513 | class ExprPredefFunction 514 | { 515 | function init(items) 516 | { 517 | if (int(items) < 1) 518 | return new ExprSyntaxError; 519 | var item = items.shift(); 520 | if (!item.isop('(')) 521 | return new ExprSyntaxError; 522 | self.initfunction(items); 523 | if (int(items) < 1) 524 | return new ExprSyntaxError; 525 | item = items.shift(); 526 | if (!item.isop(')')) 527 | return new ExprSyntaxError; 528 | return self; 529 | } 530 | } 531 | 532 | class ExprPredefArg1 : ExprPredefFunction 533 | { 534 | var arg; 535 | function initfunction(items) 536 | { 537 | self.arg = parseExpr(items); 538 | return self; 539 | } 540 | } 541 | 542 | class ExprPredefArg2 : ExprPredefFunction 543 | { 544 | var arg1; 545 | var arg2; 546 | function initfunction(items) 547 | { 548 | self.arg1 = parseExpr(items); 549 | if (int(items) < 1 || !(items.shift().isop(','))) 550 | return new ExprSyntaxError; 551 | self.arg2 = parseExpr(items); 552 | return self; 553 | } 554 | } 555 | 556 | class ExprPredefArg2_3 : ExprPredefFunction 557 | { 558 | var arg1; 559 | var arg2; 560 | var arg3; 561 | function initfunction(items) 562 | { 563 | self.arg1 = parseExpr(items); 564 | if (int(items) < 1 || !(items.shift().isop(','))) 565 | return new ExprSyntaxError; 566 | self.arg2 = parseExpr(items); 567 | if (int(items) < 1) 568 | return new ExprSyntaxError; 569 | var item = items[0]; 570 | if (item.isop(',')) { 571 | items.shift(); 572 | self.arg3 = parseExpr(items); 573 | } 574 | return self; 575 | } 576 | } 577 | 578 | class ExprNEW : ExprPredefFunction 579 | { 580 | var cl; 581 | function initfunction(items) 582 | { 583 | self.cl = parseExpr(items); 584 | return self; 585 | } 586 | function eval(runner) 587 | { 588 | string clname = self.cl.eval(runner); 589 | var obj = new clname; 590 | return obj; 591 | } 592 | } 593 | 594 | class ExprLEN : ExprPredefArg1 595 | { 596 | function eval(runner) 597 | { 598 | var val = self.arg.eval(runner); 599 | int l = length(val); 600 | return l; 601 | } 602 | } 603 | 604 | class ExprCHR : ExprPredefArg1 605 | { 606 | function eval(runner) 607 | { 608 | var val = self.arg.eval(runner); 609 | string s = chr(val); 610 | return s; 611 | } 612 | } 613 | 614 | class ExprASC : ExprPredefArg1 615 | { 616 | function eval(runner) 617 | { 618 | var val = self.arg.eval(runner); 619 | int code = ord(val); 620 | return code; 621 | } 622 | } 623 | 624 | class ExprLEFT : ExprPredefArg2 625 | { 626 | function eval(runner) 627 | { 628 | var val1 = self.arg1.eval(runner); 629 | var val2 = self.arg2.eval(runner); 630 | string str = val1; 631 | int l1 = length(str); 632 | int l2 = val2; 633 | string r = substr(str, 0, l2); 634 | return r; 635 | } 636 | } 637 | 638 | class ExprRIGHT : ExprPredefArg2 639 | { 640 | function eval(runner) 641 | { 642 | var val1 = self.arg1.eval(runner); 643 | var val2 = self.arg2.eval(runner); 644 | string str = val1; 645 | int l1 = length(str); 646 | int l2 = val2; 647 | if (l2 > l1) 648 | l2 = l1; 649 | return string(substr(str, l1 - l2)); 650 | } 651 | } 652 | 653 | class ExprMID : ExprPredefArg2_3 654 | { 655 | function eval(runner) 656 | { 657 | var val1 = self.arg1.eval(runner); 658 | var val2 = self.arg2.eval(runner); 659 | var arg3 = self.arg3; 660 | string str = val1; 661 | int l2 = val2; 662 | --l2; 663 | string r; 664 | if (arg3 != null) { 665 | var val3 = arg3.eval(runner); 666 | int l3 = val3; 667 | r = substr(str, l2, l3); 668 | } 669 | else 670 | r = substr(str, l2); 671 | return r; 672 | } 673 | } 674 | 675 | class ExprSTRING : ExprPredefArg2 676 | { 677 | function eval(runner) 678 | { 679 | var val1 = self.arg1.eval(runner); 680 | var val2 = self.arg2.eval(runner); 681 | string str = val2; 682 | int l = val1; 683 | string r = str * l; 684 | return r; 685 | } 686 | } 687 | 688 | class ExprINSTR : ExprPredefArg2_3 689 | { 690 | function eval(runner) 691 | { 692 | var val1 = self.arg1.eval(runner); 693 | var val2 = self.arg2.eval(runner); 694 | var arg3 = self.arg3; 695 | string str1 = val1; 696 | string str2 = val2; 697 | int r; 698 | if (arg3 != null) { 699 | var val3 = arg3.eval(runner); 700 | int l3 = val3; 701 | --l3; 702 | r = indexof(str1, str2, l3); 703 | } 704 | else 705 | r = indexof(str1, str2); 706 | ++r; 707 | return r; 708 | } 709 | } 710 | 711 | class ExprSQR : ExprPredefArg1 712 | { 713 | function eval(runner) 714 | { 715 | var val = self.arg.eval(runner); 716 | float l = sqrt(val); 717 | return l; 718 | } 719 | } 720 | 721 | class ExprSIN : ExprPredefArg1 722 | { 723 | function eval(runner) 724 | { 725 | var val = self.arg.eval(runner); 726 | float l = sin(val); 727 | return l; 728 | } 729 | } 730 | 731 | class ExprCOS : ExprPredefArg1 732 | { 733 | function eval(runner) 734 | { 735 | var val = self.arg.eval(runner); 736 | float l = cos(val); 737 | return l; 738 | } 739 | } 740 | 741 | class ExprTAN : ExprPredefArg1 742 | { 743 | function eval(runner) 744 | { 745 | var val = self.arg.eval(runner); 746 | float l = tan(val); 747 | return l; 748 | } 749 | } 750 | 751 | class ExprATN : ExprPredefArg1 752 | { 753 | function eval(runner) 754 | { 755 | var val = self.arg.eval(runner); 756 | float l = atan(val); 757 | return l; 758 | } 759 | } 760 | 761 | class ExprUnaryMinus 762 | { 763 | var subexpr; 764 | function init(subexpr) 765 | { 766 | self.subexpr = subexpr; 767 | return self; 768 | } 769 | function eval(runner) 770 | { 771 | var value = self.subexpr.eval(runner); 772 | return -value; 773 | } 774 | } 775 | 776 | class ExprBinaryOp 777 | { 778 | var lexpr; 779 | var rexpr; 780 | function init(lexpr, rexpr) 781 | { 782 | self.lexpr = lexpr; 783 | self.rexpr = rexpr; 784 | return self; 785 | } 786 | } 787 | 788 | class ExprMul : ExprBinaryOp 789 | { 790 | function eval(runner) 791 | { 792 | var lval = self.lexpr.eval(runner); 793 | var rval = self.rexpr.eval(runner); 794 | if (lval == null || rval == null) 795 | return null; 796 | return lval * rval; 797 | } 798 | } 799 | 800 | class ExprDiv : ExprBinaryOp 801 | { 802 | function eval(runner) 803 | { 804 | var lval = self.lexpr.eval(runner); 805 | var rval = self.rexpr.eval(runner); 806 | if (lval == null || rval == null) 807 | return null; 808 | return lval / rval; 809 | } 810 | } 811 | 812 | class ExprAdd : ExprBinaryOp 813 | { 814 | function eval(runner) 815 | { 816 | var lval = self.lexpr.eval(runner); 817 | var rval = self.rexpr.eval(runner); 818 | if (lval == null || rval == null) 819 | return null; 820 | if ((lval instanceof 'String') && (rval instanceof 'String')) { 821 | string lv = lval; 822 | string rv = rval; 823 | string v = lv + rv; 824 | return v; 825 | } 826 | return lval + rval; 827 | } 828 | } 829 | 830 | class ExprSub : ExprBinaryOp 831 | { 832 | function eval(runner) 833 | { 834 | var lval = self.lexpr.eval(runner); 835 | var rval = self.rexpr.eval(runner); 836 | if (lval == null || rval == null) 837 | return null; 838 | var r= lval + - rval; 839 | return r; 840 | } 841 | } 842 | 843 | class ExprEqual : ExprBinaryOp 844 | { 845 | function eval(runner) 846 | { 847 | var lval = self.lexpr.eval(runner); 848 | var rval = self.rexpr.eval(runner); 849 | if (lval == null || rval == null) 850 | return null; 851 | var r; 852 | if (lval == rval) r = 1; 853 | else r = 0; 854 | return r; 855 | } 856 | } 857 | 858 | class ExprNotEqual : ExprBinaryOp 859 | { 860 | function eval(runner) 861 | { 862 | var lval = self.lexpr.eval(runner); 863 | var rval = self.rexpr.eval(runner); 864 | if (lval == null || rval == null) 865 | return null; 866 | var r; 867 | if (lval == rval) r = 0; 868 | else r = 1; 869 | return r; 870 | } 871 | } 872 | 873 | class ExprLess : ExprBinaryOp 874 | { 875 | function eval(runner) 876 | { 877 | var lval = self.lexpr.eval(runner); 878 | var rval = self.rexpr.eval(runner); 879 | if (lval == null || rval == null) 880 | return null; 881 | var r; 882 | if (lval < rval) r = 1; 883 | else r = 0; 884 | return r; 885 | } 886 | } 887 | 888 | class ExprLessEqual : ExprBinaryOp 889 | { 890 | function eval(runner) 891 | { 892 | var lval = self.lexpr.eval(runner); 893 | var rval = self.rexpr.eval(runner); 894 | if (lval == null || rval == null) 895 | return null; 896 | var r; 897 | if (lval <= rval) r = 1; 898 | else r = 0; 899 | return r; 900 | } 901 | } 902 | 903 | class ExprGreater : ExprBinaryOp 904 | { 905 | function eval(runner) 906 | { 907 | var lval = self.lexpr.eval(runner); 908 | var rval = self.rexpr.eval(runner); 909 | if (lval == null || rval == null) 910 | return null; 911 | var r; 912 | if (lval > rval) r = 1; 913 | else r = 0; 914 | return r; 915 | } 916 | } 917 | 918 | class ExprGreaterEqual : ExprBinaryOp 919 | { 920 | function eval(runner) 921 | { 922 | var lval = self.lexpr.eval(runner); 923 | var rval = self.rexpr.eval(runner); 924 | if (lval == null || rval == null) 925 | return null; 926 | var r; 927 | if (lval >= rval) r = 1; 928 | else r = 0; 929 | return r; 930 | } 931 | } 932 | 933 | //********************************************************************** 934 | 935 | class MemberExpr 936 | { 937 | var subexpr; 938 | var membername; 939 | 940 | function init(subexpr, member) 941 | { 942 | self.subexpr = subexpr; 943 | self.membername = member.get(); 944 | return self; 945 | } 946 | function eval(runner) 947 | { 948 | var left = self.subexpr.eval(runner); 949 | string membername = self.membername; 950 | var attr = getattribute(left, membername); 951 | return attr; 952 | } 953 | } 954 | 955 | //********************************************************************** 956 | 957 | class MethodExpr 958 | { 959 | var subexpr; 960 | var methodname; 961 | var args; 962 | function MethodExpr(subexpr, method, items) 963 | { 964 | self.subexpr = subexpr; 965 | self.methodname = method.get(); 966 | self.args = []; 967 | items.shift(); 968 | if (int(items) == 0) 969 | return new ExprSyntaxError; 970 | if (items[0].isop(')')) 971 | items.shift(); 972 | else { 973 | var t; 974 | var arg; 975 | do { 976 | if (int(items) == 0) 977 | return new ExprSyntaxError; 978 | arg = parseExpr(items); 979 | self.args.push(arg); 980 | if (int(items) == 0) 981 | return new ExprSyntaxError; 982 | } while ((t = items.shift()).isop(',')); 983 | if (! t.isop(')')) 984 | return new ExprSyntaxError; 985 | } 986 | //say('MethodExpr.init end'); 987 | } 988 | function eval(runner) 989 | { 990 | //say('MethodExpr.eval'); 991 | var obj = self.subexpr.eval(runner); 992 | string methodname = self.methodname; 993 | var meth = find_method(obj, methodname); 994 | var methargs = []; 995 | for (var arg in self.args) 996 | methargs.push(arg.eval(runner)); 997 | var res = callmethodwithargs(obj, meth, methargs); 998 | //say('MethodExpr.eval end'); 999 | if (res == null) 1000 | return 0; 1001 | else 1002 | return res; 1003 | } 1004 | } 1005 | 1006 | //********************************************************************** 1007 | 1008 | function checkFunction(string name, items) 1009 | { 1010 | var expr; 1011 | switch(name) { 1012 | case 'ASC': 1013 | expr = new ExprASC; break; 1014 | case 'CHR$': 1015 | expr = new ExprCHR; break; 1016 | case 'INSTR': 1017 | expr = new ExprINSTR; break; 1018 | case 'LEFT$': 1019 | expr = new ExprLEFT; break; 1020 | case 'LEN': 1021 | expr = new ExprLEN; break; 1022 | case 'MID$': 1023 | expr = new ExprMID; break; 1024 | case 'NEW': 1025 | expr = new ExprNEW; break; 1026 | case 'RIGHT$': 1027 | expr = new ExprRIGHT; break; 1028 | case 'STRING$': 1029 | expr = new ExprSTRING; break; 1030 | case 'SQR': 1031 | expr = new ExprSQR; break; 1032 | case 'SIN': 1033 | expr = new ExprSIN; break; 1034 | case 'COS': 1035 | expr = new ExprCOS; break; 1036 | case 'TAN': 1037 | expr = new ExprTAN; break; 1038 | case 'ATN': 1039 | expr = new ExprATN; break; 1040 | default: 1041 | return null; 1042 | } 1043 | expr = expr.init(items); 1044 | return expr; 1045 | } 1046 | 1047 | function parseIdentifier(itname, items) 1048 | { 1049 | string name = itname.get(); 1050 | var func = checkFunction(name, items); 1051 | if (func != null) 1052 | return func; 1053 | else { 1054 | if (int(items) > 0) { 1055 | if (items[0].isop('(')) 1056 | return new ExprIndexed(name, items); 1057 | } 1058 | return new ExprIdentifier(name); 1059 | } 1060 | } 1061 | 1062 | function parseExpr_base(items) 1063 | { 1064 | int n = elements(items); 1065 | if (n == 0) 1066 | return new ExprSyntaxError; 1067 | var item = items.shift(); 1068 | --n; 1069 | var subexpr; 1070 | switch(item.type()) { 1071 | case INTEGER: 1072 | subexpr = new ExprInteger(item); 1073 | break; 1074 | case FLOAT: 1075 | subexpr = new ExprFloat().set(item); 1076 | break; 1077 | case STRING: 1078 | subexpr = new ExprString(item); 1079 | break; 1080 | case IDENTIFIER: 1081 | subexpr = parseIdentifier(item, items); 1082 | break; 1083 | default: 1084 | if (item.isop('(')) { 1085 | subexpr = parseExpr(items); 1086 | n = items; 1087 | if (n == 0) 1088 | return new ExprSyntaxError; 1089 | item = items.shift(); 1090 | if (!item.isop(')')) 1091 | return new ExprSyntaxError; 1092 | } 1093 | else 1094 | return new ExprSyntaxError; 1095 | } 1096 | return subexpr; 1097 | } 1098 | 1099 | function parseExpr_member(items) 1100 | { 1101 | var subexpr = parseExpr_base(items); 1102 | int n = elements(items); 1103 | if (n > 0) { 1104 | var item = items[0]; 1105 | if (item.isop('.')) { 1106 | if (n < 2) 1107 | return new ExprSyntaxError; 1108 | items.shift(); 1109 | item = items.shift(); 1110 | if (item.type() != STRING) 1111 | return new ExprSyntaxError; 1112 | if (n > 2) { 1113 | var t = items[0]; 1114 | if (t.isop('(')) 1115 | return new MethodExpr(subexpr, item, items); 1116 | } 1117 | subexpr = new MemberExpr().init(subexpr, item); 1118 | } 1119 | } 1120 | return subexpr; 1121 | } 1122 | 1123 | function parseExpr_unary(items) 1124 | { 1125 | int n; 1126 | n = items; 1127 | if (n < 1) 1128 | return new ExprSyntaxError; 1129 | var item = items[0]; 1130 | if (item.isop('-')) { 1131 | items.shift(); 1132 | var subexpr = parseExpr_unary(items); 1133 | return new ExprUnaryMinus().init(subexpr); 1134 | } 1135 | else { 1136 | return parseExpr_member(items); 1137 | } 1138 | } 1139 | 1140 | function parseExpr_mul(items) 1141 | { 1142 | var lexpr = parseExpr_unary(items); 1143 | int n; 1144 | var item; 1145 | more: 1146 | n = items; 1147 | if (n > 0) { 1148 | item = items[0]; 1149 | if (item.isop('*') || item.isop('/')) { 1150 | items.shift(); 1151 | var rexpr = parseExpr_unary(items); 1152 | var aux; 1153 | if (item.isop('*')) 1154 | aux = new ExprMul; 1155 | else 1156 | aux = new ExprDiv; 1157 | aux.init(lexpr, rexpr); 1158 | lexpr = aux; 1159 | goto more; 1160 | } 1161 | } 1162 | return lexpr; 1163 | } 1164 | 1165 | function parseExpr_add(items) 1166 | { 1167 | var lexpr = parseExpr_mul(items); 1168 | int n; 1169 | var item; 1170 | more: 1171 | n = items; 1172 | if (n > 0) { 1173 | item = items[0]; 1174 | if (item.isop('+') || item.isop('-')) { 1175 | items.shift(); 1176 | var rexpr = parseExpr_mul(items); 1177 | var aux; 1178 | if (item.isop('+')) 1179 | aux = new ExprAdd; 1180 | else 1181 | aux = new ExprSub; 1182 | aux = aux.init(lexpr, rexpr); 1183 | lexpr = aux; 1184 | goto more; 1185 | } 1186 | } 1187 | return lexpr; 1188 | } 1189 | 1190 | function parseExpr_equal(items) 1191 | { 1192 | var lexpr = parseExpr_add(items); 1193 | int n; 1194 | var item; 1195 | more: 1196 | n = items; 1197 | if (n > 0) { 1198 | item = items[0]; 1199 | if (item.isop('=') || item.isop('<>') || 1200 | item.isop('<') || item.isop('>') || 1201 | item.isop('<=') || item.isop('>=')) { 1202 | items.shift(); 1203 | var rexpr = parseExpr_add(items); 1204 | var aux; 1205 | if (item.isop('=')) 1206 | aux = new ExprEqual; 1207 | else if (item.isop('<>')) 1208 | aux = new ExprNotEqual; 1209 | else if (item.isop('<')) 1210 | aux = new ExprLess; 1211 | else if (item.isop('>')) 1212 | aux = new ExprGreater; 1213 | else if (item.isop('<=')) 1214 | aux = new ExprLessEqual; 1215 | else if (item.isop('>=')) 1216 | aux = new ExprGreaterEqual; 1217 | aux.init(lexpr, rexpr); 1218 | lexpr = aux; 1219 | goto more; 1220 | } 1221 | } 1222 | return lexpr; 1223 | } 1224 | 1225 | function parseExpr(items) 1226 | { 1227 | return parseExpr_equal(items); 1228 | } 1229 | 1230 | //********************************************************************** 1231 | 1232 | class ChunkBad 1233 | { 1234 | function execute(runner, line, int chunk) 1235 | { 1236 | runner.seterror(line, chunk, ERR_syntax); 1237 | return 1; 1238 | } 1239 | } 1240 | 1241 | function nomoreitems(chunk, items) 1242 | { 1243 | int n = items; 1244 | if (n > 0) 1245 | return new ChunkBad; 1246 | else 1247 | return chunk; 1248 | } 1249 | 1250 | class ChunkComment 1251 | { 1252 | function ChunkComment(var items) 1253 | { 1254 | int n; 1255 | while ((n = items) > 0) 1256 | items.shift(); 1257 | } 1258 | function execute(runner, line, int chunk) 1259 | { 1260 | // Do nothing 1261 | return 1; 1262 | } 1263 | } 1264 | 1265 | class ChunkNoArgs 1266 | { 1267 | function init(var items) 1268 | { 1269 | int n = items; 1270 | if (n != 0) 1271 | return new ChunkBad; 1272 | else 1273 | return self; 1274 | } 1275 | } 1276 | 1277 | class ChunkWithLine 1278 | { 1279 | var linenum; 1280 | function init(var items) 1281 | { 1282 | int n = items; 1283 | if (n != 1) 1284 | return new ChunkBad; 1285 | else { 1286 | var item = items[0]; 1287 | if (item.type() != INTEGER) 1288 | return new ChunkBad; 1289 | int linenum = item.getint(); 1290 | self.linenum = linenum; 1291 | return self; 1292 | } 1293 | } 1294 | } 1295 | 1296 | class ChunkWithRange 1297 | { 1298 | var l_from; 1299 | var l_to; 1300 | function ChunkWithRange(items) 1301 | { 1302 | int n = items; 1303 | if (n == 0) 1304 | return new ChunkBad; 1305 | var item = items.shift(); 1306 | int linefrom = -1; 1307 | int lineto = -1; 1308 | if (! item.isop('-')) { 1309 | if (item.type() != INTEGER) 1310 | return new ChunkBad; 1311 | linefrom = item.getint(); 1312 | n = items; 1313 | if (n == 0) 1314 | lineto = linefrom; 1315 | else { 1316 | item = items.shift(); 1317 | if (! item.isop('-')) 1318 | return new ChunkBad; 1319 | } 1320 | } 1321 | n = items; 1322 | if (n != 0) { 1323 | item = items.shift(); 1324 | if (item.type() != INTEGER) 1325 | return new ChunkBad; 1326 | lineto = item.getint(); 1327 | } 1328 | self.l_from = linefrom; 1329 | self.l_to = lineto; 1330 | } 1331 | } 1332 | 1333 | class ChunkWithCondition 1334 | { 1335 | var condition; 1336 | function init(var items) 1337 | { 1338 | int n = items; 1339 | if (n == 0) 1340 | return new ChunkBad; 1341 | self.condition = parseExpr(items); 1342 | return self; 1343 | } 1344 | } 1345 | 1346 | class ChunkNEW : ChunkNoArgs 1347 | { 1348 | function execute(runner, line, int chunk) 1349 | { 1350 | runner.clearall(); 1351 | runner.new(); 1352 | runner.runstate = RUNstart; 1353 | return 1; 1354 | } 1355 | } 1356 | 1357 | class ChunkCLEAR : ChunkNoArgs 1358 | { 1359 | function execute(runner, line, int chunk) 1360 | { 1361 | runner.clearall(); 1362 | return 0; 1363 | } 1364 | } 1365 | 1366 | class ChunkRUN : ChunkNoArgs 1367 | { 1368 | function ChunkRUN(var items) { } 1369 | function execute(runner, line, int chunk) 1370 | { 1371 | runner.clearall(); 1372 | runner.runstart(); 1373 | return 1; 1374 | } 1375 | } 1376 | 1377 | class ChunkEND : ChunkNoArgs 1378 | { 1379 | function execute(runner, line, int chunk) 1380 | { 1381 | runner.runstate = RUNend; 1382 | return 1; 1383 | } 1384 | } 1385 | 1386 | class ChunkSTOP : ChunkNoArgs 1387 | { 1388 | function execute(runner, line, int chunk) 1389 | { 1390 | runner.dostop(line, chunk); 1391 | return 1; 1392 | } 1393 | } 1394 | 1395 | class ChunkCONT : ChunkNoArgs 1396 | { 1397 | function execute(runner, line, int chunk) 1398 | { 1399 | runner.docont(line, chunk); 1400 | return 1; 1401 | } 1402 | } 1403 | 1404 | class ChunkGo : ChunkWithLine 1405 | { 1406 | function go(runner) 1407 | { 1408 | runner.runstate = RUNgoto; 1409 | int linedest = runner.program.findline(self.linenum); 1410 | if (linedest != DIRECT) { 1411 | runner.gotopos(linedest, 0); 1412 | return 0; 1413 | } 1414 | else 1415 | return 1; 1416 | } 1417 | } 1418 | 1419 | class ChunkGOTO : ChunkGo 1420 | { 1421 | function execute(runner, line, int chunk) 1422 | { 1423 | if (self.go(runner)) 1424 | runner.seterror(line, chunk, ERR_no_line); 1425 | return 1; 1426 | } 1427 | } 1428 | 1429 | class ChunkGOSUB : ChunkGo 1430 | { 1431 | function execute(runner, line, int chunk) 1432 | { 1433 | if (self.go(runner)) 1434 | runner.seterror(line, chunk, ERR_no_line); 1435 | else 1436 | runner.pushret(line, chunk); 1437 | return 1; 1438 | } 1439 | } 1440 | 1441 | class ChunkRETURN : ChunkNoArgs 1442 | { 1443 | function execute(runner, line, int chunk) 1444 | { 1445 | if (runner.doreturn()) 1446 | runner.seterror(line, chunk, ERR_without_gosub); 1447 | return 1; 1448 | } 1449 | } 1450 | 1451 | class ChunkLIST 1452 | { 1453 | function execute(runner, line, int chunk) 1454 | { 1455 | runner.list(runner.getstdout()); 1456 | } 1457 | } 1458 | 1459 | class ChunkLISTargs : ChunkWithRange 1460 | { 1461 | function ChunkLISTargs(var items) 1462 | { 1463 | self.ChunkWithRange(items); 1464 | } 1465 | function execute(runner, line, int chunk) 1466 | { 1467 | runner.listlines(runner.getstdout(), self.l_from, self.l_to); 1468 | return 1; 1469 | } 1470 | } 1471 | 1472 | function parseLIST(items) 1473 | { 1474 | int n = items; 1475 | if (n == 0) 1476 | return new ChunkLIST; 1477 | else 1478 | return new ChunkLISTargs(items); 1479 | } 1480 | 1481 | class ChunkLOAD 1482 | { 1483 | var progname; 1484 | function init(var items) 1485 | { 1486 | int n = items; 1487 | if (n < 1) 1488 | return new ChunkBad; 1489 | else { 1490 | self.progname = parseExpr(items); 1491 | return nomoreitems(self, items); 1492 | } 1493 | } 1494 | function execute(runner, line, int chunk) 1495 | { 1496 | var name = self.progname.eval(runner); 1497 | if (name == null) { 1498 | runner.seterror(line, chunk, ERR_syntax); 1499 | return 1; 1500 | } 1501 | if (runner.load(name, line, chunk)) 1502 | return 1; 1503 | runner.runstate = RUNend; 1504 | return 1; 1505 | } 1506 | } 1507 | 1508 | class ChunkCHAIN 1509 | { 1510 | var progname; 1511 | function init(var items) 1512 | { 1513 | int n = items; 1514 | if (n < 1) 1515 | return new ChunkBad; 1516 | else { 1517 | self.progname = parseExpr(items); 1518 | return nomoreitems(self, items); 1519 | } 1520 | } 1521 | function execute(runner, line, int chunk) 1522 | { 1523 | var name = self.progname.eval(runner); 1524 | if (name == null) { 1525 | runner.seterror(line, chunk, ERR_syntax); 1526 | return 1; 1527 | } 1528 | if (runner.load(name, line, chunk)) 1529 | return 1; 1530 | runner.runstart(); 1531 | return 1; 1532 | } 1533 | } 1534 | 1535 | function parseCHAIN(items) 1536 | { 1537 | return new ChunkCHAIN().init(items); 1538 | } 1539 | 1540 | class ChunkSAVE 1541 | { 1542 | var progname; 1543 | function init(var items) 1544 | { 1545 | int n = items; 1546 | if (n < 1) 1547 | return new ChunkBad; 1548 | else { 1549 | self.progname = parseExpr(items); 1550 | return self; 1551 | } 1552 | } 1553 | function execute(runner, line, int chunk) 1554 | { 1555 | var name = self.progname.eval(runner); 1556 | if (name == null) { 1557 | runner.seterror(line, chunk, ERR_syntax); 1558 | return 1; 1559 | } 1560 | runner.save(name); 1561 | return 1; 1562 | } 1563 | } 1564 | 1565 | class ChunkEXIT : ChunkNoArgs 1566 | { 1567 | function ChunkEXIT(var items) { } 1568 | function execute(runner, line, int chunk) 1569 | { 1570 | runner.runstate = RUNexit; 1571 | return 1; 1572 | } 1573 | } 1574 | 1575 | class ChunkLET 1576 | { 1577 | var varname; 1578 | var value; 1579 | function ChunkLET(string strname, var items) 1580 | { 1581 | self.varname = strname; 1582 | self.value = parseExpr(items); 1583 | } 1584 | function execute(runner, line, int chunk) 1585 | { 1586 | string varname = self.varname; 1587 | var value = self.value.eval(runner); 1588 | if (value == null) { 1589 | runner.seterror(line, chunk, ERR_syntax); 1590 | return 1; 1591 | } 1592 | return runner.setvar(varname, value); 1593 | } 1594 | } 1595 | 1596 | class ChunkLETindexed 1597 | { 1598 | var varname; 1599 | var indexes; 1600 | var value; 1601 | function ChunkLETindexed(string strname, var items) 1602 | { 1603 | self.varname = strname; 1604 | int n; 1605 | var aux; 1606 | var indexes = parsedims(items); 1607 | if (indexes == null) 1608 | return new ChunkBad; 1609 | self.indexes = indexes; 1610 | n = items; 1611 | if (n < 1) 1612 | return new ChunkBad; 1613 | aux = items.shift(); 1614 | if (!aux.isop('=')) 1615 | return new ChunkBad; 1616 | self.value = parseExpr(items); 1617 | } 1618 | function execute(runner, line, int chunk) 1619 | { 1620 | string varname = self.varname; 1621 | var value = self.value.eval(runner); 1622 | if (value == null) { 1623 | runner.seterror(line, chunk, ERR_syntax); 1624 | return 1; 1625 | } 1626 | var m = runner.getvar(varname); 1627 | int offset = dimoffset(runner, m, self.indexes); 1628 | var values = m.values; 1629 | values[offset] = value; 1630 | } 1631 | } 1632 | 1633 | function parseLETname(var name, var items) 1634 | { 1635 | string strname = name.str; 1636 | int n = items; 1637 | if (n == 0) 1638 | return new ChunkBad; 1639 | var t = items.shift(); 1640 | if (t.isop('=')) 1641 | return new ChunkLET(strname, items); 1642 | if (t.isop('(')) 1643 | return new ChunkLETindexed(strname, items); 1644 | return new ChunkBad; 1645 | } 1646 | 1647 | function parseLET(var items) 1648 | { 1649 | int n = items; 1650 | if (n < 2) 1651 | return new ChunkBad; 1652 | var name = items.shift(); 1653 | if (name.type() != IDENTIFIER) 1654 | return new ChunkBad; 1655 | else 1656 | return parseLETname(name, items); 1657 | } 1658 | 1659 | class ChunkDIM 1660 | { 1661 | var varname; 1662 | var dims; 1663 | function ChunkDIM(var items) 1664 | { 1665 | int n = items; 1666 | if (n < 2) 1667 | return new ChunkBad; 1668 | var name = items.shift(); 1669 | if (name.type() != IDENTIFIER) 1670 | return new ChunkBad; 1671 | string strname = name.str; 1672 | if (--n == 0) 1673 | return new ChunkBad; 1674 | self.varname = strname; 1675 | var aux = items.shift(); 1676 | if (!aux.isop('(')) 1677 | return new ChunkBad; 1678 | var dims = parsedims(items); 1679 | if (dims == null) 1680 | return new ChunkBad; 1681 | self.dims = dims; 1682 | } 1683 | function execute(runner, line, int chunk) 1684 | { 1685 | var dims = self.dims; 1686 | var m = new Matrix(runner, dims); 1687 | return runner.setvar(self.varname, m); 1688 | } 1689 | } 1690 | 1691 | class ChunkIF : ChunkWithCondition 1692 | { 1693 | function execute(runner, line, int chunk) 1694 | { 1695 | var cond = self.condition.eval(runner); 1696 | if (cond) return 0; 1697 | else { 1698 | var chunks = line.chunks; 1699 | int n = chunks; 1700 | ++chunk; 1701 | int count = 1; 1702 | for (; chunk < n; ++chunk) { 1703 | var ch = chunks[chunk]; 1704 | if (ch instanceof ChunkIF) 1705 | ++count; 1706 | else if (ch instanceof ChunkELSE) { 1707 | --count; 1708 | if (count == 0) 1709 | break; 1710 | } 1711 | } 1712 | if (count == 0) { 1713 | ++chunk; 1714 | runner.gotolinepos(line, chunk); 1715 | } 1716 | return 1; 1717 | } 1718 | } 1719 | } 1720 | 1721 | class ChunkELSE : ChunkNoArgs 1722 | { 1723 | function execute(runner, line, int chunk) 1724 | { 1725 | return 1; 1726 | } 1727 | } 1728 | 1729 | class ChunkFOR 1730 | { 1731 | var varname; 1732 | var initexpr; 1733 | var finalexpr; 1734 | var stepexpr; 1735 | function ChunkFOR(var items) 1736 | { 1737 | int n = items; 1738 | if (n < 2) 1739 | return new ChunkBad; 1740 | var varname = items.shift(); 1741 | self.varname = varname.str; 1742 | var op = items.shift(); 1743 | if (! op.isop('=')) 1744 | return new ChunkBad; 1745 | self.initexpr = parseExpr(items); 1746 | n = items; 1747 | if (n < 2) 1748 | return new ChunkBad; 1749 | op = items.shift(); 1750 | if (! op.isidentifier('TO')) 1751 | return new ChunkBad; 1752 | self.finalexpr = parseExpr(items); 1753 | n = items; 1754 | if (n > 0) { 1755 | op = items.shift(); 1756 | if (!op.isidentifier('STEP')) 1757 | return new ChunkBad; 1758 | self.stepexpr = parseExpr(items); 1759 | } 1760 | } 1761 | function execute(runner, line, int chunk) 1762 | { 1763 | string varname = self.varname; 1764 | var value = self.initexpr.eval(runner); 1765 | if (value == null) { 1766 | runner.seterror(line, chunk, ERR_syntax); 1767 | return 1; 1768 | } 1769 | var finalval = self.finalexpr.eval(runner); 1770 | var stepval; 1771 | if (self.stepexpr != null) 1772 | stepval = self.stepexpr.eval(runner); 1773 | else 1774 | stepval = 1; 1775 | return runner.dofor(line, chunk, varname, value, finalval, stepval); 1776 | } 1777 | } 1778 | 1779 | class ChunkNEXT 1780 | { 1781 | function execute(runner, line, int chunk) 1782 | { 1783 | return runner.donext(line, chunk); 1784 | } 1785 | } 1786 | 1787 | class ChunkNEXTvar : ChunkNEXT 1788 | { 1789 | var varname; 1790 | function execute(runner, line, int chunk) 1791 | { 1792 | return runner.donext(line, chunk, self.varname); 1793 | } 1794 | } 1795 | 1796 | function parseNEXT(var items) 1797 | { 1798 | int n = items; 1799 | if (n == 0) 1800 | return new ChunkNEXT; 1801 | else { 1802 | var name = items.shift(); 1803 | if (name.type() != IDENTIFIER) 1804 | return new ChunkBad; 1805 | n = items; 1806 | if (n != 0) 1807 | return new ChunkNEXT; 1808 | var chunk = new ChunkNEXTvar; 1809 | chunk.varname = name.checkidentifier(); 1810 | return chunk; 1811 | } 1812 | } 1813 | 1814 | class ChunkWHILE : ChunkWithCondition 1815 | { 1816 | function execute(runner, line, int chunk) 1817 | { 1818 | var value = self.condition.eval(runner); 1819 | int v = value; 1820 | if (v) { 1821 | runner.dowhile(line, chunk); 1822 | return 0; 1823 | } 1824 | else { 1825 | var program = runner.program; 1826 | var proglines = program.lines; 1827 | int nlines = proglines; 1828 | int count = 1; 1829 | var curline = line; 1830 | int curch = chunk + 1; 1831 | nextcheck: 1832 | var chunks = curline.getchunks(); 1833 | int nchunks = chunks; 1834 | for (; curch < nchunks; ++curch) { 1835 | var ch = chunks[curch]; 1836 | if (ch instanceof ChunkWHILE) 1837 | ++count; 1838 | else if (ch instanceof ChunkWEND) { 1839 | --count; 1840 | if (count == 0) 1841 | break; 1842 | } 1843 | } 1844 | if (count > 0) { 1845 | curline = program.nextline(curline); 1846 | if (curline != null) { 1847 | curch = 0; 1848 | goto nextcheck; 1849 | } 1850 | runner.seterror(line, chunk, ERR_without_wend); 1851 | } 1852 | else 1853 | runner.gotolinepos(curline, curch + 1); 1854 | return 1; 1855 | 1856 | } 1857 | } 1858 | } 1859 | 1860 | class ChunkWEND : ChunkNoArgs 1861 | { 1862 | function execute(runner, line, int chunk) 1863 | { 1864 | return runner.dowend(line, chunk); 1865 | } 1866 | } 1867 | 1868 | class ChunkREPEAT : ChunkNoArgs 1869 | { 1870 | function execute(runner, line, int chunk) 1871 | { 1872 | return runner.dorepeat(line, chunk); 1873 | } 1874 | } 1875 | 1876 | class ChunkUNTIL : ChunkWithCondition 1877 | { 1878 | function ChunkUNTIL(var items) 1879 | { 1880 | self.init(items); 1881 | } 1882 | function execute(runner, line, int chunk) 1883 | { 1884 | var value = self.condition.eval(runner); 1885 | int v = value; 1886 | return runner.dountil(line, chunk, value); 1887 | } 1888 | } 1889 | 1890 | class ChunkPRINT 1891 | { 1892 | var channel; 1893 | var printitems; 1894 | function ChunkPRINT(var items) 1895 | { 1896 | self.printitems = []; 1897 | var item; 1898 | int n = items; 1899 | if (n > 0) { 1900 | item = items[0]; 1901 | if (item.isop('#')) { 1902 | items.shift(); 1903 | self.channel = parseExpr(items); 1904 | n = items; 1905 | if (n > 0) { 1906 | item = items.shift(); 1907 | if (!item.isop(',')) 1908 | return new ChunkBad; 1909 | } 1910 | } 1911 | } 1912 | var printitem; 1913 | while ((n = items) > 0) { 1914 | item = items[0]; 1915 | if (item.isop(';') || item.isop(',')) { 1916 | printitem = item; 1917 | items.shift(); 1918 | } 1919 | else 1920 | printitem = parseExpr(items); 1921 | self.printitems.push(printitem); 1922 | } 1923 | } 1924 | function execute(runner, line, int chunk) 1925 | { 1926 | int lastisval = 1; 1927 | var channel = self.channel; 1928 | var handle; 1929 | if (channel == null) 1930 | handle = runner.getstdout(); 1931 | else { 1932 | var ch = channel.eval(runner); 1933 | int chnum = ch; 1934 | handle = runner.getchannel(chnum); 1935 | if (handle == null) { 1936 | runner.seterror(line, chunk, ERR_not_open); 1937 | return 1; 1938 | } 1939 | } 1940 | for (var item in self.printitems) { 1941 | if (item instanceof Token) { 1942 | if (item.isop(',')) 1943 | handle.print("\t"); 1944 | lastisval = 0; 1945 | } 1946 | else { 1947 | var value = item.eval(runner); 1948 | if (value == null) { 1949 | runner.seterror(line, chunk, ERR_syntax); 1950 | return 1; 1951 | } 1952 | handle.print(value); 1953 | lastisval = 1; 1954 | } 1955 | } 1956 | if (lastisval) 1957 | handle.print("\n"); 1958 | return 0; 1959 | } 1960 | } 1961 | 1962 | class ChunkLINE_INPUT 1963 | { 1964 | var channel; 1965 | var varname; 1966 | function ChunkLINE_INPUT(var items) 1967 | { 1968 | int n = items; 1969 | if (n < 1) 1970 | return new ChunkBad; 1971 | var item = items[0]; 1972 | if (item.isop('#')) { 1973 | items.shift(); 1974 | self.channel = parseExpr(items); 1975 | item = items.shift(); 1976 | if (!item.isop(',')) 1977 | return new ChunkBad; 1978 | } 1979 | item = items.shift(); 1980 | if (item.type() != IDENTIFIER) 1981 | return new ChunkBad; 1982 | string varname = item.checkidentifier(); 1983 | if (varname == '') 1984 | return new ChunkBad; 1985 | self.varname = varname; 1986 | } 1987 | function execute(runner, line, int chunk) 1988 | { 1989 | var channel = self.channel; 1990 | var input; 1991 | var l; 1992 | if (channel == null) { 1993 | input = runner.getstdin(); 1994 | l = input.readline(); 1995 | } 1996 | else { 1997 | var ch = channel.eval(runner); 1998 | int chnum = ch; 1999 | input = runner.getchannel(ch); 2000 | if (input == null) { 2001 | runner.seterror(line, chunk, ERR_not_open); 2002 | return 1; 2003 | } 2004 | l = input.readline(); 2005 | } 2006 | l = chomp(l); 2007 | runner.setvar(self.varname, l); 2008 | return 0; 2009 | } 2010 | } 2011 | 2012 | function createChunkLINE(var items) 2013 | { 2014 | int n = items; 2015 | if (n < 1) 2016 | return new ChunkBad; 2017 | var item = items.shift(); 2018 | if (!item.isidentifier('INPUT')) 2019 | return new ChunkBad; 2020 | return new ChunkLINE_INPUT(items); 2021 | } 2022 | 2023 | class ChunkOPEN 2024 | { 2025 | var filenameexpr; 2026 | var channelexpr; 2027 | var mode; 2028 | function ChunkOPEN(var items) 2029 | { 2030 | self.filenameexpr = parseExpr(items); 2031 | int n = items; 2032 | if (n < 2) 2033 | return new ChunkBad; 2034 | var item = items.shift(); 2035 | int mode = 0; 2036 | int flags = 0; 2037 | if (item.isidentifier('FOR')) { 2038 | item = items.shift(); 2039 | if (item.isidentifier('BINARY')) { 2040 | flags = OPEN_binary; 2041 | item = items.shift(); 2042 | } 2043 | switch(item.checkidentifier()) { 2044 | case 'INPUT': 2045 | mode = OPEN_input; 2046 | break; 2047 | case 'OUTPUT': 2048 | mode = OPEN_output; 2049 | break; 2050 | default: 2051 | return new ChunkBad; 2052 | } 2053 | item = items.shift(); 2054 | } 2055 | if (!item.isidentifier('AS')) 2056 | return new ChunkBad; 2057 | self.channelexpr = parseExpr(items); 2058 | if (mode == 0) 2059 | mode = OPEN_input; 2060 | mode = mode | flags; 2061 | self.mode = mode; 2062 | } 2063 | function execute(runner, line, int chunk) 2064 | { 2065 | var filename = self.filenameexpr.eval(runner); 2066 | string strfile = filename; 2067 | var channel = self.channelexpr.eval(runner); 2068 | int chnum = channel; 2069 | int mode = self.mode; 2070 | int binmode = mode & OPEN_binary; 2071 | string modestr; 2072 | switch (mode & OPEN_mode_mask) { 2073 | case OPEN_input: 2074 | modestr = 'r'; 2075 | break; 2076 | case OPEN_output: 2077 | modestr = 'w'; 2078 | break; 2079 | default: 2080 | break; 2081 | } 2082 | if (binmode) 2083 | modestr += 't'; 2084 | //say('OPEN ', strfile, chnum); 2085 | var handle = new 'FileHandle'.open(strfile, modestr); 2086 | if (! binmode) 2087 | handle.encoding('utf8'); 2088 | runner.open(chnum, handle); 2089 | } 2090 | } 2091 | 2092 | class ChunkCLOSE 2093 | { 2094 | var channelexpr; 2095 | function ChunkCLOSE(var items) 2096 | { 2097 | int n = items; 2098 | if (n > 0) { 2099 | if (items[0].isop('#')) 2100 | items.shift(); 2101 | self.channelexpr = parseExpr(items); 2102 | } 2103 | } 2104 | function execute(runner, line, int chunk) 2105 | { 2106 | var channelexpr = self.channelexpr; 2107 | if (channelexpr != null) { 2108 | var channel = channelexpr.eval(runner); 2109 | int chnum = channel; 2110 | runner.close(chnum); 2111 | } 2112 | else 2113 | runner.closeall(); 2114 | return 0; 2115 | } 2116 | } 2117 | 2118 | class ChunkTRON : ChunkNoArgs 2119 | { 2120 | function execute(runner, line, int chunk) 2121 | { 2122 | runner.tron = 1; 2123 | return 0; 2124 | } 2125 | } 2126 | 2127 | class ChunkTROFF : ChunkNoArgs 2128 | { 2129 | function execute(runner, line, int chunk) 2130 | { 2131 | runner.tron = 0; 2132 | return 0; 2133 | } 2134 | } 2135 | 2136 | class ChunkDELETE : ChunkWithRange 2137 | { 2138 | function ChunkDELETE(var items) 2139 | { 2140 | self.ChunkWithRange(items); 2141 | } 2142 | function execute(runner, line, int chunk) 2143 | { 2144 | runner.deletelines(self.l_from, self.l_to); 2145 | runner.runstate = RUNend; 2146 | return 1; 2147 | } 2148 | } 2149 | 2150 | class ChunkERROR 2151 | { 2152 | var expr; 2153 | function parse(var items) 2154 | { 2155 | int n = items; 2156 | if (n == 0) 2157 | return new ChunkBad; 2158 | self.expr = parseExpr(items); 2159 | return self; 2160 | } 2161 | function execute(runner, line, int chunk) 2162 | { 2163 | var value = self.expr.eval(runner); 2164 | int errcode = value; 2165 | runner.seterror(line, chunk, errcode); 2166 | return 1; 2167 | } 2168 | } 2169 | 2170 | //********************************************************************** 2171 | 2172 | function createChunk(var items) 2173 | { 2174 | int n = items; 2175 | var item = items.shift(); 2176 | string str = item.checkidentifier(); 2177 | switch(str) { 2178 | case 'REM': 2179 | return new ChunkComment(items); 2180 | case 'NEW': 2181 | return new ChunkNEW().init(items); 2182 | case 'CLEAR': 2183 | return new ChunkCLEAR().init(items); 2184 | case 'RUN': 2185 | return new ChunkRUN(items); 2186 | case 'END': 2187 | return new ChunkEND; 2188 | case 'STOP': 2189 | return new ChunkSTOP().init(items); 2190 | case 'CONT': 2191 | return new ChunkCONT().init(items); 2192 | case 'GOTO': 2193 | return (new ChunkGOTO).init(items); 2194 | case 'GOSUB': 2195 | return (new ChunkGOSUB).init(items); 2196 | case 'RETURN': 2197 | return new ChunkRETURN; 2198 | case 'LIST': 2199 | return parseLIST(items); 2200 | case 'LOAD': 2201 | return new ChunkLOAD().init(items); 2202 | case 'CHAIN': 2203 | return parseCHAIN(items); 2204 | case 'SAVE': 2205 | return new ChunkSAVE().init(items); 2206 | case 'EXIT': 2207 | return new ChunkEXIT(items); 2208 | case 'LET': 2209 | return parseLET(items); 2210 | case 'DIM': 2211 | return new ChunkDIM(items); 2212 | case 'IF': 2213 | return (new ChunkIF).init(items); 2214 | case 'ELSE': 2215 | return new ChunkELSE; 2216 | case 'FOR': 2217 | return new ChunkFOR(items); 2218 | case 'NEXT': 2219 | return parseNEXT(items); 2220 | case 'WHILE': 2221 | return (new ChunkWHILE).init(items); 2222 | case 'WEND': 2223 | return (new ChunkWEND).init(items); 2224 | case 'REPEAT': 2225 | return new ChunkREPEAT; 2226 | case 'UNTIL': 2227 | return new ChunkUNTIL(items); 2228 | case 'PRINT': 2229 | return new ChunkPRINT(items); 2230 | case 'LINE': 2231 | return createChunkLINE(items); 2232 | case 'OPEN': 2233 | return new ChunkOPEN(items); 2234 | case 'CLOSE': 2235 | return new ChunkCLOSE(items); 2236 | case 'TRON': 2237 | return new ChunkTRON; 2238 | case 'TROFF': 2239 | return new ChunkTROFF; 2240 | case 'DELETE': 2241 | return new ChunkDELETE(items); 2242 | case 'ERROR': 2243 | return new ChunkERROR().parse(items); 2244 | default: 2245 | if (item.type() == IDENTIFIER) 2246 | return parseLETname(item, items); 2247 | if (item.isop('?')) 2248 | return new ChunkPRINT(items); 2249 | if (item.isop("'")) 2250 | return new ChunkComment(items); 2251 | } 2252 | return new ChunkBad; 2253 | } 2254 | 2255 | //********************************************************************** 2256 | 2257 | class ProgramLine 2258 | { 2259 | var numline; 2260 | var str; 2261 | var chunks; 2262 | function ProgramLine(int numline, string content) 2263 | { 2264 | self.numline = numline; 2265 | self.str = content; 2266 | return self; 2267 | } 2268 | function getnum() { return self.numline; } 2269 | function isempty() 2270 | { 2271 | var str = self.str; 2272 | if (str == null || str == '') return 1; 2273 | else return 0; 2274 | } 2275 | function parse() 2276 | { 2277 | var chunks = parsechunks(self.str); 2278 | self.chunks = chunks; 2279 | } 2280 | function getchunks() 2281 | { 2282 | var chunks = self.chunks; 2283 | if (chunks == null) { 2284 | chunks = parsechunks(self.str); 2285 | self.chunks = chunks; 2286 | } 2287 | return chunks; 2288 | } 2289 | } 2290 | 2291 | //********************************************************************** 2292 | 2293 | class Program 2294 | { 2295 | var lines; 2296 | 2297 | function Program() 2298 | { 2299 | self.lines = []; 2300 | } 2301 | function findlinepos(int numline) 2302 | { 2303 | var lines = self.lines; 2304 | int n = lines; 2305 | if (n == 0 || numline <= lines[0].getnum()) 2306 | return 0; 2307 | if (n == 1) 2308 | return 1; 2309 | if (numline > lines[n-1].getnum()) 2310 | return n; 2311 | int i1 = 0; 2312 | int i2 = n; 2313 | var line; 2314 | while (i2 - i1 > 1) { 2315 | int i3 = (i1 + i2) / 2; 2316 | line = lines[i3]; 2317 | int nl = line.getnum(); 2318 | if (nl == numline) 2319 | return i3; 2320 | else if (numline > nl) 2321 | i1 = i3; 2322 | else 2323 | i2 = i3; 2324 | } 2325 | return i2; 2326 | } 2327 | function findline(int numline) 2328 | { 2329 | var lines = self.lines; 2330 | int n = lines; 2331 | int i = self.findlinepos(numline); 2332 | if (i >= n) 2333 | i = DIRECT; 2334 | return i; 2335 | } 2336 | function nextline(var line) 2337 | { 2338 | int numline = line.getnum(); 2339 | if (numline == DIRECT) 2340 | return null; 2341 | int l = self.findline(numline); 2342 | if (l == DIRECT) 2343 | return null; 2344 | ++l; 2345 | var lines = self.lines; 2346 | int n = lines; 2347 | if (l < n) 2348 | return lines[l]; 2349 | else 2350 | return null; 2351 | } 2352 | function insert(var newline) 2353 | { 2354 | int numline = newline.getnum(); 2355 | var lines = self.lines; 2356 | int n = lines; 2357 | int i = self.findlinepos(numline); 2358 | if (i < n) { 2359 | var line = lines[i]; 2360 | if (line.getnum() != numline) { 2361 | var empty; 2362 | lines.push(empty); 2363 | for (int j = n; j > i; --j) 2364 | lines[j] = lines[j - 1]; 2365 | } 2366 | } 2367 | lines[i] = newline; 2368 | } 2369 | function delete(int numline) 2370 | { 2371 | var lines = self.lines; 2372 | int n = lines; 2373 | int i = self.findline(numline); 2374 | if (i < n) { 2375 | var curline = lines[i].getnum(); 2376 | if (curline == numline) { 2377 | for (int j = i; j < n; ++j) 2378 | lines[j] = lines[j + 1]; 2379 | lines.pop(); 2380 | } 2381 | } 2382 | } 2383 | function list(handle) 2384 | { 2385 | var lines = self.lines; 2386 | for (var line in lines) { 2387 | int numline = line.getnum(); 2388 | string str = line.str; 2389 | handle.print(numline); 2390 | handle.print(' '); 2391 | handle.print(str); 2392 | handle.print("\n"); 2393 | } 2394 | } 2395 | function listlines(handle, int l_from, int l_to) 2396 | { 2397 | var lines = self.lines; 2398 | int nlines = lines; 2399 | int i_from = self.findlinepos(l_from); 2400 | int i_to; 2401 | if (l_to == -1) 2402 | i_to = nlines; 2403 | else { 2404 | i_to = self.findlinepos(l_to); 2405 | if (i_to < nlines) 2406 | if (lines[i_to].getnum() <= l_to) 2407 | ++i_to; 2408 | } 2409 | for (int i= i_from; i < i_to; ++i) { 2410 | var line = lines[i]; 2411 | int numline = line.getnum(); 2412 | string str = line.str; 2413 | handle.print(numline); 2414 | handle.print(' '); 2415 | handle.print(str); 2416 | handle.print("\n"); 2417 | } 2418 | } 2419 | function deletelines(int l_from, int l_to) 2420 | { 2421 | var lines = self.lines; 2422 | int nlines = lines; 2423 | int i_from = self.findlinepos(l_from); 2424 | int i_to; 2425 | if (l_to == -1) 2426 | i_to = nlines; 2427 | else { 2428 | i_to = self.findlinepos(l_to); 2429 | if (i_to < nlines) 2430 | if (lines[i_to].getnum() <= l_to) 2431 | ++i_to; 2432 | } 2433 | int range = i_to - i_from; 2434 | if (range <= 0) 2435 | return; 2436 | int n = lines; 2437 | int move = n - i_to; 2438 | for (int i = 0; i < move; ++i) 2439 | lines[i_from + i] = lines[i_to + i]; 2440 | lines =: n - range; 2441 | } 2442 | function load(string progname) 2443 | { 2444 | var handle; 2445 | try { 2446 | handle = new 'FileHandle'.open(progname); 2447 | handle.encoding('utf8'); 2448 | var fileline; 2449 | while ((fileline = handle.readline())) { 2450 | fileline = chomp(fileline); 2451 | var line = parseline(fileline); 2452 | int n = line.getnum(); 2453 | if (n == DIRECT) 2454 | throw Error('Direct command found'); 2455 | self.insert(line); 2456 | } 2457 | } 2458 | catch() { 2459 | if (handle != null) 2460 | handle.close(); 2461 | return 1; 2462 | } 2463 | handle.close(); 2464 | return 0; 2465 | } 2466 | function save(string progname) 2467 | { 2468 | var handle = new 'FileHandle'.open(progname, 'w'); 2469 | handle.encoding('utf8'); 2470 | self.list(handle); 2471 | handle.close(); 2472 | } 2473 | } 2474 | 2475 | //********************************************************************** 2476 | 2477 | class Control 2478 | { 2479 | var nline; 2480 | var chunk; 2481 | function init(int numline, int chunk) 2482 | { 2483 | self.nline = numline; 2484 | self.chunk = chunk; 2485 | return self; 2486 | } 2487 | } 2488 | 2489 | class GosubControl : Control 2490 | { 2491 | } 2492 | 2493 | class ForControl : Control 2494 | { 2495 | var varname; 2496 | var finalval; 2497 | var stepval; 2498 | } 2499 | 2500 | class WhileControl : Control 2501 | { 2502 | var line; 2503 | var chunk; 2504 | } 2505 | 2506 | //********************************************************************** 2507 | 2508 | class Runner 2509 | { 2510 | var program; 2511 | var runstate; 2512 | var curline; 2513 | var curchunk; 2514 | var directline; 2515 | var tron; 2516 | var retstack; 2517 | var forstack; 2518 | var whilestack; 2519 | var repeatstack; 2520 | var errorline; 2521 | var errorcode; 2522 | var stopped; 2523 | var variables; 2524 | var stdin; 2525 | var stdout; 2526 | var channels; 2527 | 2528 | function Runner() 2529 | { 2530 | self.clearall(); 2531 | self.runstate = RUNstart; 2532 | self.curline = 0; 2533 | self.curchunk = 0; 2534 | self.tron = 0; 2535 | var handle = getstdin(); 2536 | handle.encoding('utf8'); 2537 | self.stdin = handle; 2538 | handle = getstdout(); 2539 | handle.encoding('utf8'); 2540 | self.stdout = handle; 2541 | } 2542 | function clearall() 2543 | { 2544 | self.closeall(); 2545 | self.retstack = []; 2546 | self.forstack = []; 2547 | self.whilestack = []; 2548 | self.repeatstack = []; 2549 | self.variables = {}; 2550 | } 2551 | function getstdin() 2552 | { 2553 | return self.stdin; 2554 | } 2555 | function getstdout() 2556 | { 2557 | return self.stdout; 2558 | } 2559 | function open(int chnum, var handle) 2560 | { 2561 | var channels = self.channels; 2562 | var old = channels[chnum]; 2563 | if (old != null) 2564 | old.close(); 2565 | channels[chnum] = handle; 2566 | } 2567 | function close(int chnum) 2568 | { 2569 | var channels = self.channels; 2570 | var old = channels[chnum]; 2571 | if (old != null) { 2572 | old.close(); 2573 | channels[chnum] = null; 2574 | } 2575 | } 2576 | function closeall() 2577 | { 2578 | var channels = self.channels; 2579 | if (channels != null) { 2580 | for (var chnum in channels) { 2581 | var old = channels[chnum]; 2582 | old.close(); 2583 | channels[chnum] = null; 2584 | } 2585 | } 2586 | self.channels = {}; 2587 | } 2588 | function getchannel(int chnum) 2589 | { 2590 | var channels = self.channels; 2591 | var old = channels[chnum]; 2592 | return old; 2593 | } 2594 | function new() 2595 | { 2596 | self.program = new Program(); 2597 | } 2598 | function load(string progname, line, int chunk) 2599 | { 2600 | var program = new Program(); 2601 | if (program.load(progname)) { 2602 | self.seterror(line, chunk, ERR_load); 2603 | return 1; 2604 | } 2605 | else { 2606 | self.program = program; 2607 | return 0; 2608 | } 2609 | } 2610 | function save(string progname) 2611 | { 2612 | self.program.save(progname); 2613 | } 2614 | function list(handle) 2615 | { 2616 | self.program.list(handle); 2617 | } 2618 | function listlines(handle, int l_from, int l_to) 2619 | { 2620 | self.program.listlines(handle, l_from, l_to); 2621 | } 2622 | function deletelines(int l_from, int l_to) 2623 | { 2624 | self.program.deletelines(l_from, l_to); 2625 | } 2626 | function setvar(string varname, value) 2627 | { 2628 | var variables = self.variables; 2629 | variables[varname] = value; 2630 | return 0; 2631 | } 2632 | function getvar(string varname) 2633 | { 2634 | var variables = self.variables; 2635 | return variables[varname]; 2636 | } 2637 | function seterror(line, int chunk, int code) 2638 | { 2639 | self.errorline = line; 2640 | self.errorcode = code; 2641 | self.runstate = RUNerror; 2642 | } 2643 | function runcode() 2644 | { 2645 | var lines = self.program.lines; 2646 | var line; 2647 | int n = lines; 2648 | int l; 2649 | for (;;) { 2650 | l = self.curline; 2651 | if (l >= n) { 2652 | self.runstate = RUNend; 2653 | break; 2654 | } 2655 | if (l == DIRECT) 2656 | line = self.directline; 2657 | else 2658 | line = lines[l]; 2659 | if (self.tron != 0) { 2660 | if (l != DIRECT) { 2661 | int numline = line.getnum(); 2662 | print('[', numline, ']'); 2663 | } 2664 | } 2665 | 2666 | var chunks = line.getchunks(); 2667 | var chunk; 2668 | int nc = chunks; 2669 | for (int i = self.curchunk; i < nc; ++i) { 2670 | chunk = chunks[i]; 2671 | if (chunk.execute(self, line, i)) { 2672 | if (self.runstate == RUNgoto) { 2673 | if (l == self.curline) { 2674 | i = self.curchunk; 2675 | if (i < nc) { 2676 | --i; 2677 | self.runstate = RUNrunning; 2678 | continue; 2679 | } 2680 | } 2681 | } 2682 | break; 2683 | } 2684 | } 2685 | 2686 | if (self.runstate != RUNrunning) 2687 | break; 2688 | if (l == DIRECT) { 2689 | self.runstate = RUNinteract; 2690 | break; 2691 | } 2692 | ++l; 2693 | self.curline = l; 2694 | self.curchunk = 0; 2695 | } 2696 | } 2697 | function interact() 2698 | { 2699 | var in = self.getstdin(); 2700 | var str; 2701 | str = in.readline_interactive(); 2702 | var line = parseline(str); 2703 | int n = line.getnum(); 2704 | int empty = line.isempty(); 2705 | if (n != DIRECT) { 2706 | if (empty) 2707 | self.program.delete(n); 2708 | else 2709 | self.program.insert(line); 2710 | } 2711 | else { 2712 | self.curline = DIRECT; 2713 | self.curchunk = 0; 2714 | self.directline = line; 2715 | self.runstate = RUNrunning; 2716 | } 2717 | } 2718 | function mainloop() 2719 | { 2720 | int n; 2721 | int i; 2722 | var lines; 2723 | while (self.runstate != RUNexit) { 2724 | //say('mainloop'); 2725 | switch (self.runstate) { 2726 | case RUNstart: 2727 | say('Ready'); 2728 | self.runstate = RUNinteract; 2729 | break; 2730 | case RUNrun: 2731 | i = self.curline; 2732 | lines = self.program.lines; 2733 | n = lines; 2734 | if (i < n) 2735 | self.runstate = RUNrunning; 2736 | else 2737 | self.runstate = RUNend; 2738 | break; 2739 | case RUNend: 2740 | self.runstate = RUNstart; 2741 | break; 2742 | case RUNinteract: 2743 | self.interact(); 2744 | break; 2745 | case RUNrunning: 2746 | self.runcode(); 2747 | break; 2748 | case RUNgoto: 2749 | i = self.curline; 2750 | lines = self.program.lines; 2751 | n = lines; 2752 | if (i < n) 2753 | self.runstate = RUNrunning; 2754 | else 2755 | self.runstate = RUNend; 2756 | break; 2757 | case RUNerror: 2758 | int errlinenum = self.errorline.getnum(); 2759 | print('Error'); 2760 | if (errlinenum != DIRECT) 2761 | print(' in ', errlinenum); 2762 | print(': ', errmsg(self.errorcode)); 2763 | say(); 2764 | self.runstate = RUNinteract; 2765 | break; 2766 | } 2767 | } 2768 | } 2769 | function runstart() 2770 | { 2771 | self.curline = 0; 2772 | self.curchunk = 0; 2773 | self.runstate = RUNrun; 2774 | } 2775 | function gotolinepos(var line, int chunk) 2776 | { 2777 | int linenum = line.getnum(); 2778 | if (linenum != DIRECT) 2779 | linenum = self.program.findline(linenum); 2780 | self.curline = linenum; 2781 | self.curchunk = chunk; 2782 | self.runstate = RUNgoto; 2783 | } 2784 | function gotopos(int lineindex, int chunk) 2785 | { 2786 | self.curline = lineindex; 2787 | self.curchunk = chunk; 2788 | self.runstate = RUNgoto; 2789 | } 2790 | function gotonumpos(int linenum, int chunk) 2791 | { 2792 | if (linenum != DIRECT) 2793 | linenum = self.program.findline(linenum); 2794 | self.curline = linenum; 2795 | self.curchunk = chunk; 2796 | self.runstate = RUNgoto; 2797 | } 2798 | function dostop(var line, int chunk) 2799 | { 2800 | int numline = line.getnum(); 2801 | if (numline == DIRECT) 2802 | self.stopped = null; 2803 | else 2804 | self.stopped = (new Control).init(numline, chunk); 2805 | self.seterror(line, chunk, ERR_stop); 2806 | } 2807 | function docont(line, chunk) 2808 | { 2809 | var stopped = self.stopped; 2810 | if (stopped == null) 2811 | self.seterror(line, chunk, ERR_cant_cont); 2812 | self.stopped = null; 2813 | self.gotonumpos(stopped.nline, stopped.chunk + 1); 2814 | } 2815 | function pushret(line, int chunk) 2816 | { 2817 | int numline = line.getnum(); 2818 | if (numline != DIRECT) { 2819 | int n = line.chunks; 2820 | if (++chunk >= n) { 2821 | ++numline; 2822 | chunk = 0; 2823 | } 2824 | } 2825 | else 2826 | ++chunk; 2827 | var control = (new GosubControl).init(numline, chunk); 2828 | self.retstack.push(control); 2829 | } 2830 | function doreturn() 2831 | { 2832 | var stack = self.retstack; 2833 | int n = stack; 2834 | if (n > 0) { 2835 | var control = stack.pop(); 2836 | n = control.nline; 2837 | int chunk = control.chunk; 2838 | if (n == DIRECT) { 2839 | var dirline = self.directline; 2840 | int nch = dirline.chunks; 2841 | if (chunk < nch) { 2842 | self.gotopos(DIRECT, chunk); 2843 | } 2844 | else 2845 | self.runstate = RUNend; 2846 | } 2847 | else 2848 | self.gotonumpos(n, chunk); 2849 | return 0; 2850 | } 2851 | else 2852 | return 1; 2853 | } 2854 | function dofor(line, int chunk, string varname, initial, final, step) 2855 | { 2856 | self.setvar(varname, initial); 2857 | int linenum = line.getnum(); 2858 | ++chunk; 2859 | if (linenum != DIRECT) { 2860 | linenum = self.program.findline(linenum); 2861 | int nchunk = line.chunks; 2862 | if (chunk < nchunk) { 2863 | ++linenum; 2864 | chunk = 0; 2865 | } 2866 | } 2867 | int donext; 2868 | if (step > 0) 2869 | donext = initial <= final; 2870 | else 2871 | donext = initial >= final; 2872 | if (donext) { 2873 | var forcontrol = (new ForControl).init(linenum, chunk); 2874 | forcontrol.varname = varname; 2875 | forcontrol.finalval = final; 2876 | forcontrol.stepval = step; 2877 | self.forstack.push(forcontrol); 2878 | return 0; 2879 | } 2880 | else { 2881 | var program = self.program; 2882 | var proglines = program.lines; 2883 | int nlines = proglines; 2884 | int count = 1; 2885 | var curline = line; 2886 | int curch = chunk + 1; 2887 | nextcheck: 2888 | var chunks = curline.getchunks(); 2889 | int nchunks = chunks; 2890 | for (; curch < nchunks; ++curch) { 2891 | var ch = chunks[curch]; 2892 | if (ch instanceof ChunkFOR) 2893 | ++count; 2894 | else if (ch instanceof ChunkNEXT) { 2895 | --count; 2896 | if (count == 0) 2897 | break; 2898 | } 2899 | } 2900 | if (count > 0) { 2901 | curline = program.nextline(curline); 2902 | if (curline != null) { 2903 | curch = 0; 2904 | goto nextcheck; 2905 | } 2906 | self.seterror(line, chunk, ERR_without_next); 2907 | } 2908 | else 2909 | self.gotolinepos(curline, curch + 1); 2910 | return 1; 2911 | } 2912 | } 2913 | function donext(line, int chunk, string nextname[optional]) 2914 | { 2915 | var stack = self.forstack; 2916 | int n = stack; 2917 | if (n < 1) { 2918 | self.seterror(line, chunk, ERR_without_for); 2919 | return 1; 2920 | } 2921 | var forcontrol = stack[n-1]; 2922 | string varname = forcontrol.varname; 2923 | if (nextname != null) 2924 | if (varname != nextname) { 2925 | self.seterror(line, chunk, ERR_without_for); 2926 | return 1; 2927 | } 2928 | var value = self.getvar(varname); 2929 | var step = forcontrol.stepval; 2930 | value = value + step; 2931 | self.setvar(varname, value); 2932 | var final = forcontrol.finalval; 2933 | int doit; 2934 | if (step > 0) 2935 | doit = value <= final; 2936 | else 2937 | doit = value >= final; 2938 | if (doit) { 2939 | int nline = forcontrol.nline; 2940 | int chunk = forcontrol.chunk; 2941 | self.gotopos(nline, chunk); 2942 | return 1; 2943 | } 2944 | stack.pop(); 2945 | return 0; 2946 | } 2947 | function dowhile(line, int chunk) 2948 | { 2949 | int linenum = line.getnum(); 2950 | if (linenum != DIRECT) 2951 | linenum = self.program.findline(linenum); 2952 | var control = (new WhileControl).init(linenum, chunk); 2953 | control.line = line; 2954 | control.chunk = chunk; 2955 | self.whilestack.push(control); 2956 | } 2957 | function dowend(line, int chunk) 2958 | { 2959 | var stack = self.whilestack; 2960 | int n = stack; 2961 | if (n < 1) { 2962 | self.seterror(line, chunk, ERR_without_while); 2963 | return 1; 2964 | } 2965 | var control = stack[n-1]; 2966 | var linewhile = control.line; 2967 | var chunknumwhile = control.chunk; 2968 | var chunks = linewhile.chunks; 2969 | var chunkwhile = chunks[chunknumwhile]; 2970 | var condition = chunkwhile.condition; 2971 | var value = condition.eval(self); 2972 | int v = value; 2973 | if (value) { 2974 | int nline = control.nline; 2975 | int chunk = control.chunk; 2976 | self.gotopos(nline, chunk); 2977 | return 1; 2978 | } 2979 | else { 2980 | stack.pop(); 2981 | return 0; 2982 | } 2983 | } 2984 | function dorepeat(line, int chunk) 2985 | { 2986 | int linenum = line.getnum(); 2987 | if (linenum != DIRECT) 2988 | linenum = self.program.findline(linenum); 2989 | var control = (new Control).init(linenum, chunk + 1); 2990 | self.repeatstack.push(control); 2991 | } 2992 | function dountil(line, int chunk, int value) 2993 | { 2994 | var stack = self.repeatstack; 2995 | int n = stack; 2996 | if (n == 0) { 2997 | self.seterror(line, chunk, ERR_without_while); 2998 | return 1; 2999 | } 3000 | if (value) { 3001 | stack.pop(); 3002 | return 0; 3003 | } 3004 | else { 3005 | var control = stack[n-1]; 3006 | self.gotopos(control.nline, control.chunk); 3007 | return 1; 3008 | } 3009 | } 3010 | } 3011 | 3012 | //********************************************************************** 3013 | 3014 | function main(argv) 3015 | { 3016 | using extern Getopt.Obj; 3017 | var getopts = new [ "Getopt", "Obj" ]; 3018 | getopts.notOptStop(1); 3019 | getopts.push_string('t'); 3020 | getopts.push_string('l'); 3021 | argv.shift(); 3022 | var opts = getopts.get_options(argv); 3023 | var opt_t = opts['t']; 3024 | var opt_l = opts['l']; 3025 | 3026 | var runner = new Runner(); 3027 | if (opt_t != null) 3028 | runner.tron = 1; 3029 | int argc = argv; 3030 | if (argc > 0) { 3031 | if (runner.load(argv[0], null, 0)) { 3032 | throw "Cannot load program"; 3033 | } 3034 | if (opt_l == null) 3035 | runner.runstart(); 3036 | runner.mainloop(); 3037 | } 3038 | else { 3039 | runner.new(); 3040 | runner.mainloop(); 3041 | } 3042 | } 3043 | 3044 | // End of wasix 3045 | --------------------------------------------------------------------------------