basic: OPTION DIALECT must come 1st, hot restart for FOR loops, chainAssignments, more games, CONVERT, DEC+

This commit is contained in:
Steven Hugg 2020-08-15 06:53:13 -05:00
parent 7f372b0b52
commit 295f1ef9de
10 changed files with 2278 additions and 62 deletions

230
presets/basic/craps.bas Normal file
View File

@ -0,0 +1,230 @@
0 REM http://www.bitsavers.org/bits/HP/paperTapes/JeffM/CRAPS.BAS
1 OPTION DIALECT HP2000
10 REM THIS IS A SPECIAL VERSION OF CRAPS
20 REM 1) MAXIMUM BANK IS $1000
30 REM 2) THE PLAYER GETS 10 CHANCES TO PLACE A BET
40 REM 3) COMMENTS HAVE BEEN ADJUSTED FOR FAMILIES
50 PRINT TAB(18)"G T E S Y L V A N I A"
60 PRINT
70 PRINT TAB(27)"1 9 7 4"
80 PRINT
90 PRINT TAB(20)"F A M I L Y D A Y"
100 PRINT
110 PRINT
120 PRINT TAB(16)"COMPUTERIZED CRAP GAME RULES"
130 PRINT
140 PRINT
150 PRINT
160 PRINT "2, 3, OR 12 ON THE FIRST THROW IS A LOSER."
170 PRINT "7 OR 11 ON THE FIRST THROW WINS."
180 PRINT
190 PRINT "IF FIRST THROW IS NOT A WINNER OR A LOSER IT BECOMES";
200 PRINT " YOUR POINT."
210 PRINT "CONTINUE TO ROLL UNTIL EITHER 7 OR YOUR POINT OCCURS."
220 PRINT "ROLL 7 AND YOU LOSE. MAKE YOUR POINT AND YOU WIN."
230 PRINT
240 PRINT "YOU MAY END THE GAME BY ENTERING A ZERO BET."
250 PRINT
260 PRINT "I RESERVE THE RIGHT TO END THE GAME AT ANY TIME."
270 PRINT
280 LET F6=0
290 PRINT "NO PROFANITY!"
300 PRINT
310 PRINT "WHAT TIME IS IT";
320 INPUT T
330 IF T<2401 THEN 390
340 IF F6<1 THEN 360
350 PRINT "DUMMY! ";
360 PRINT "WHAT TIME";
370 LET F6=F6+1
380 GOTO 320
390 LET T=T*19
400 IF T<1900 THEN 430
410 LET T=T-1900
420 GOTO 400
430 PRINT "THANK YOU - ON WITH THE GAME."
440 FOR D=1 TO T
450 LET R1=RND(0)
460 NEXT D
470 LET F2=0
480 LET F3=0
490 LET F4=0
500 LET F5=0
510 LET X=0
520 LET Y=0
530 PRINT " ENTER YOUR BANKROLL ";
540 INPUT F7
550 IF F7<1001 THEN 620
560 LET F5=F5+1
570 IF F5>1 THEN 600
580 PRINT "YOU'RE TOO RICH FOR MY ELECTRONS!"
590 GOTO 530
600 PRINT "BE REASONABLE! THIS IS NOT LOS VEGAS."
610 GOTO 530
620 LET F8=F7
630 PRINT "YOU NOW HAVE TEN TRYS TO BEAT THE BANK."
640 LET Q=10
650 GOTO 750
660 PRINT
670 IF F8>F7 THEN 710
680 IF F7>F8 THEN 730
690 PRINT "EVEN WITH THE BOARD - HMPH!"
700 GOTO 2050
710 PRINT "YOU HAVE WON "F8-F7" DOLLARS. TRY AND GET IT."
720 GOTO 2050
730 PRINT "YOU HAVE LOST "F7-F8" DOLLARS. SORRY ABOUT THAT."
740 GOTO 2050
750 IF Q=0 THEN 660
760 LET Q=Q-1
770 PRINT " PLACE YOUR BET ";
780 INPUT B
790 IF B=0 THEN 660
800 IF B<1 THEN 1830
810 IF B>F8 THEN 1890
820 LET B=INT(B)
830 GOSUB 2240
840 PRINT " FIRST THROW";V;W;
850 IF A=2 THEN 930
860 IF A=3 THEN 950
870 IF A=7 THEN 970
880 IF A=11 THEN 990
890 IF A=12 THEN 910
900 GOTO 1010
910 PRINT " BOX CARS ";
920 GOTO 1430
930 PRINT " SNAKE EYES ";
940 GOTO 1430
950 PRINT " CRAPS ";
960 GOTO 1430
970 PRINT " BIG SEVEN - A WINNER";
980 GOTO 1090
990 PRINT " BIG ELEVEN - A WINNER";
1000 GOTO 1090
1010 LET C=A
1020 PRINT " YOUR POINT ";A
1030 GOSUB 2240
1040 PRINT " NEXT THROW ";V;W
1050 IF A=7 THEN 1410
1060 IF A#C THEN 1030
1070 PRINT " MADE YOUR POINT";
1080 REM WIN COUNTER
1090 LET X=X+1
1100 IF X>1 THEN 1150
1110 IF X=3 THEN 1170
1120 IF X=4 THEN 1190
1130 IF X>4 THEN 1210
1140 GOTO 1220
1150 PRINT " AGAIN";
1160 GOTO 1110
1170 PRINT ". DARNIT!";
1180 GOTO 1220
1190 PRINT ". STOP THAT!";
1200 GOTO 1220
1210 PRINT ". -DISGUSTING-";
1220 IF Y=2 THEN 1280
1230 IF Y=3 THEN 1300
1240 IF Y=4 THEN 1320
1250 IF Y=5 THEN 1340
1260 IF Y>5 THEN 1360
1270 GOTO 1370
1280 PRINT " FOR A CHANGE - DON'T GET COCKY.";
1290 GOTO 1370
1300 PRINT " IN SPITE OF ME.";
1310 GOTO 1370
1320 PRINT " SPOIL SPORT";
1330 GOTO 1370
1340 PRINT " FEEL BETTER?";
1350 GOTO 1370
1360 PRINT " - - RELAX";
1370 LET Y=0
1380 PRINT
1390 LET F8=F8+B
1400 GOTO 1800
1410 PRINT " SEVEN - - -";
1420 REM LOSS COUNTER
1430 LET Y=Y+1
1440 PRINT " YOU LOSE";
1450 IF Y>1 THEN 1550
1460 IF Y=3 THEN 1570
1470 IF Y=4 THEN 1590
1480 IF Y=5 THEN 1610
1490 IF Y>5 THEN 1630
1500 IF X=2 THEN 1650
1510 IF X=3 THEN 1680
1520 IF X=4 THEN 1710
1530 IF X>4 THEN 1740
1540 GOTO 1760
1550 PRINT " AGAIN. ";
1560 GOTO 1460
1570 PRINT " KEEP UP THE GOOD WORK!";
1580 GOTO 1500
1590 PRINT " EAT YOUR HEART OUT!";
1600 GOTO 1500
1610 PRINT " IT'S NOT YOUR DAY, IS IT, SUCKER?";
1620 GOTO 1500
1630 PRINT " YOU'RE JUST TOO GOOD TO BE TRUE.";
1640 GOTO 1500
1650 PRINT ".";
1660 PRINT " BUT KEEP TRYING.";
1670 GOTO 1760
1680 PRINT ".";
1690 PRINT " THAT ENDS YOUR LITTLE STREAK.";
1700 GOTO 1760
1710 PRINT ".";
1720 PRINT " GOTCHA!";
1730 GOTO 1760
1740 PRINT ".";
1750 PRINT " AT LAST I FIXED YOUR WAGON.";
1760 PRINT ""
1770 LET X=0
1780 LET F8=F8-B
1790 IF F8<1 THEN 2220
1800 PRINT
1810 PRINT "YOU NOW HAVE "F8"DOLLARS IN THE BANK."
1820 GOTO 750
1830 PRINT "NO BETS LESS THAN A BUCK.";
1840 LET F2=F2+1
1850 IF F2=2 THEN 1950
1860 IF F2>2 THEN 1970
1870 PRINT
1880 GOTO 750
1890 PRINT "NO CREDIT AT THIS CASINO.";
1900 LET F3=F3+1
1910 IF F3=2 THEN 1950
1920 IF F3>2 THEN 1970
1930 PRINT
1940 GOTO 750
1950 PRINT " (SECOND WARNING)"
1960 GOTO 750
1970 PRINT " G O T O # @ & % "
1980 LET F4=F4+1
1990 IF F4=2 THEN 2020
2000 IF F4>2 THEN 2040
2010 GOTO 750
2020 PRINT " DO THAT AGAIN AND I'LL QUIT"
2030 GOTO 750
2040 PRINT " I WARNED YOU - SMART GUY - I QUIT"
2050 PRINT
2060 PRINT
2070 PRINT
2080 PRINT
2090 PRINT "NEXT PLAYER. . . PLEASE ENTER YOUR LUCKY NUMBER."
2100 PRINT
2110 PRINT
2120 PRINT
2130 PRINT
2140 PRINT
2150 PRINT
2160 INPUT F6
2170 PRINT
2180 PRINT
2190 PRINT
2200 PRINT
2210 GOTO 50
2220 PRINT " B U S T"
2230 GOTO 2050
2240 LET V=INT(6*RND(0)+1)
2250 LET W=INT(6*RND(0)+1)
2260 LET A=V+W
2270 RETURN
2280 END

1445
presets/basic/haunted.bas Normal file

File diff suppressed because it is too large Load Diff

196
presets/basic/lander.bas Normal file
View File

@ -0,0 +1,196 @@
0 OPTION DIALECT HP
1 REM **** HP BASIC PROGRAM LIBRARY **************************
2 REM
3 REM LANDER: ROCKET LANDING VEHICLE
4 REM
5 REM 36684 REV A -- 10/73
6 REM REV A-1 -- 3/75 BYARD
7 REM **** CONTRIBUTED PROGRAM *******************************
100 REM PROGRAM LANDER
110 REM
130 REM
200 PRINT "WELCOME TO THE PUC SCHOOL FOR BUDDING ASTRONAUTS!"
210 PRINT
220 PRINT " DO YOU WANT INSTRUCTIONS? 1=YES ";
230 INPUT C
240 PRINT
250 IF C#1 THEN 1000
300 PRINT "YOU ARE AT THE CONTROLS OF A ROCKET LANDING VEHICLE."
310 PRINT
320 PRINT "INITIALLY YOU ARE A GIVEN DISTANCE ABOVE THE SURFACE ";
330 PRINT "MOVING"
335 PRINT "DOWNWARD (VELOCITY IS NEGATIVE)."
350 PRINT "YOU CHOOSE THE AMOUNT OF FUEL TO BE BURNED DURING";
360 PRINT " THE NEXT ONE SECOND."
370 PRINT "OF TIME."
400 PRINT
410 PRINT " IF YOU BURN ZERO, THEN YOU WILL FALL FASTER BECAUSE OF ";
420 PRINT "GRAVITY."
430 PRINT " IF YOU BURN EXACTLY THAT REQUIRED TO OVERCOME GRAVITY, ";
440 PRINT "THEN"
445 PRINT "YOUR VELOCITY WILL BE CONSTANT."
450 PRINT " IF YOU BURN MORE, THEN YOU WILL SLOW DOWN OR EVEN";
460 PRINT " START TO MOVE"
470 PRINT "UPWARD (VELOCITY IS POSITIVE)!"
475 PRINT
480 PRINT "THE IDEA IS TO GET THE LANDER DOWN TO THE SURFACE,";
490 PRINT " LANDING WITH AS"
495 PRINT "LITTLE VELOCITY AS POSSIBLE."
500 PRINT "THERE IS MORE THAN ENOUGH FUEL, BUT BE CAREFUL NOT";
510 PRINT " TO WASTE IT!"
600 PRINT
700 PRINT "LANDING ON THE MOON IS EASIER, TRY THAT FIRST."
720 PRINT
730 PRINT
1000 PRINT "GOOD LUCK AND HAPPY LANDINGS!"
1005 REM
1006 LET X0=V0=0
1007 PRINT
1010 PRINT
1030 PRINT "LOCATION: MOON(1) OR EARTH(2) ";
1040 INPUT C
1042 PRINT
1043 LET K=0
1045 IF C=1 THEN 1060
1047 LET K=1
1048 IF C=2 THEN 1060
1050 PRINT "THAT LOCATION IS NOT RECOGNIZED."
1055 GOTO 1030
1060 LET G=5+27*K
1070 LET M=30+60*K
1075 IF X0>0 THEN 1100
1080 LET X=X0=500+1500*K
1090 LET V=V0=-50-100*K
1100 PRINT "INITIAL CONDITIONS: STANDARD(1), OLD(2), OR NEW(3) ";
1110 INPUT D
1120 IF D=1 THEN 1150
1130 LET X=500+1500*K
1140 LET V=-50-100*K
1145 GOTO 1200
1150 IF D=2 THEN 1170
1155 LET X=X0
1160 LET V=V0
1165 GOTO 1200
1170 LET X=INT(RND(0)*(100+K*100))*10+100
1180 LET V=-INT(RND(0)*(10+K*10))*5
1190 IF V*V>2*(M-G)*X THEN 1180
1200 LET X0=X
1210 LET V0=V
1290 LET F=INT((M*(V^2+2*G*X)/(M-G))^.5*.13+.5)*10
1300 PRINT
1302 PRINT "INITIAL HEIGHT: ";X;"FEET"
1303 PRINT "INITIAL VELOCITY: ";V;"FEET/SEC"
1305 PRINT "TOTAL FUEL SUPPLY: ";F;"UNITS"
1307 PRINT "MAXIMUM BURN: ";M;"UNITS/SEC"
1308 PRINT "AMOUNT OF BURN TO CANCEL GRAVITY: ";G;"UNITS/SEC"
1320 PRINT
1330 PRINT
1340 PRINT "TIME","HEIGHT","VELOCITY","FUEL","BURN"
1350 PRINT
2000 LET T=-1
2010 LET T=T+1
2020 PRINT T,X,V,F," ";
2030 INPUT B
2040 LET B1=ABS(B)
2050 IF B1 <= M THEN 2080
2060 PRINT " ","MAX BURN IS ";M;"BURN ";
2070 GOTO 2030
2080 LET T9=T8=2
2090 IF B1=0 THEN 2110
2100 LET T9=F/B1
2110 LET A=B-G
2120 LET R=V*V-2*A*X
2140 IF R<0 THEN 2200
2150 IF A=0 THEN 2180
2160 LET T8=-(V+R^.5)/A
2170 GOTO 2200
2180 IF V >= 0 THEN 2200
2190 LET T8=-X/V
2200 IF (T8>0 AND T8 <= 1) OR T9 <= 1 THEN 2300
2210 LET X=X+V+A/2
2220 LET V=V+A
2230 LET F=F-B1
2235 IF X>1.00000E-04 THEN 2010
2240 LET T=T+1
2245 GOTO 2630
2300 IF T8>0 AND T8 <= T9 THEN 2600
2310 PRINT T+T9,"OUT OF FUEL"
2320 LET F=B1=0
2330 LET X=X+V*T9+A*T9^2/2
2340 LET V=V+A*T9
2350 LET A=-G
2360 LET T8=(V+(V*V-2*A*X)^.5)/G
2370 IF T8<1-T9 THEN 2500
2380 LET X=X+V*(1-T9)+A*(1-T9)^2/2
2390 LET V=V+A*(1-T9)
2400 LET T=T+1
2410 PRINT T,X,V
2420 LET T8=(V+(V^2-2*A*X)^.5)/G
2430 IF T8 <= 1 THEN 2600
2440 LET X=X+V+A/2
2450 LET V=V+A
2460 GOTO 2400
2500 LET T=T+T9
2600 LET F=F-B1*T8
2610 LET T=T+T8
2620 LET V=V+A*T8
2630 PRINT T,0,V,F
2640 PRINT
2650 PRINT
2700 IF V<-1 THEN 2800
2710 LET D=INT(RND(0)*5+1)
2711 IF D=2 THEN 2730
2712 IF D=3 THEN 2745
2713 IF D=4 THEN 2750
2714 IF D=5 THEN 2760
2720 PRINT "YOU ARE NOW A QUALIFIED ASTRONAUT."
2725 GOTO 1007
2730 PRINT "AS GENTLE AS A KITTEN'S PURR!!"
2735 GOTO 1007
2740 PRINT "A BUTTERFLY COULDN'T HAVE DONE BETTER!"
2745 GOTO 1007
2750 PRINT "AS SOFT AS A SNOWFLAKE!"
2755 GOTO 1007
2760 PRINT "MR. SPOCK WOULD BE PROUD OF YOU!!!"
2765 GOTO 1007
2800 IF V<-5 THEN 2900
2810 LET D=INT(RND(0)*4+1)
2811 IF D=2 THEN 2830
2812 IF D=3 THEN 2840
2813 IF D=4 THEN 2850
2820 PRINT "A BIT ROUGH, BUT YOU ARE STILL IN ONE PIECE!"
2825 GOTO 1007
2830 PRINT "IF YOU HAD BEEN DRIVING A 1970 LTD, THAT WOULD HAVE COST";
2831 PRINT " YOU $500!"
2835 GOTO 1007
2840 PRINT "ANY FASTER AND YOU WOULD HAVE BOUNCED!"
2845 GOTO 1007
2850 PRINT "YOU HAD BETTER CHECK YOU LANDING GEAR!!"
2855 GOTO 1007
2900 IF V<-10 THEN 3000
2910 LET D=INT(RND(0)*5+1)
2911 IF D=2 THEN 2930
2912 IF D=3 THEN 2940
2913 IF D=4 THEN 2950
2914 IF D=5 THEN 2960
2920 PRINT "IS YOUR MEDICAL INSURANCE PAID UP??"
2925 GOTO 1007
2930 PRINT "YOU GOT DOWN, BUT YOU WILL NEVER BE AN ASTRONAUT!"
2935 GOTO 1007
2940 PRINT "NEIL ARMSTRONG DID IT THE FIRST TIME!!"
2945 GOTO 1007
2950 PRINT "THE BEST LAID SCHEMES OF MICE AND MEN,"
2951 PRINT " OFT' GO ASTRAY."
2955 GOTO 1007
2960 PRINT "HAVE YOU EVER THOUGHT OF A DIFFERENT LINE OF WORK??"
2965 GOTO 1007
3000 LET D=INT(RND(0)*3+1)
3001 IF D=2 THEN 3020
3002 IF D=3 THEN 3030
3010 PRINT "YOUR NEXT OF KIN WILL BE NOTIFIED."
3015 GOTO 1007
3020 PRINT "YOU JUST CREAMED A 29 MEGABUCK LANDER!"
3025 GOTO 1007
3030 PRINT "AREN'T YOU GLAD THIS IS ONLY A COMPUTER SIMULATION!!"
3035 GOTO 1007
9999 END

View File

@ -0,0 +1,96 @@
1 OPTION DIALECT HP
10 PRINT "DANIEL O'ROURKE FEB. 23, 1977"
20 PRINT "MINI-COMPUTOR 102"
30 PRINT "SUBJECT: MORTGAGE PAYMENT"
40 FOR A=1 TO 3
50 PRINT
60 NEXT A
70 PRINT "HI, I AM A COMPUTOR. YOU HAVE COME TO ME "
80 PRINT "SO I CAN HELP YOU SOLVE YOUR MORTGAGE"
90 PRINT "PROBLEMS, BECAUSE I CAN MAKE CALCULATIONS"
100 PRINT "MUCH FASTER AND MORE ACCURATELY THAN YOU"
110 PRINT "CAN. IF YOU DON'T KNOW HOW TO USE ME,AND"
120 PRINT "YOU WOULD LIKE INSTRUCTIONS, PLEASE TYPE"
125 PRINT "A 1 (ONE). FOLLOW IT WITH A CARRIAGE RETURN."
130 PRINT "IF NOT TYPE A 0 (ZERO) FOLLOWED BY A CARRI-"
140 PRINT "AGE RETURN. DO IT NOW, PLEASE."
142 PRINT
144 PRINT
150 INPUT B
152 IF B=1 THEN 160
155 IF B=0 THEN 285
157 IF B#1 THEN 142
160 PRINT
165 PRINT
170 PRINT "THANK YOU, HERE ARE YOUR INSTRUCTIONS."
180 PRINT
190 PRINT
200 GOTO 250
250 PRINT "TYPE IN THE AMOUNT YOU WISH TO BORROW."
260 PRINT "FOLLOW IT WITH A COMMA (,) THEN TYPE IN"
270 PRINT "THE NUMBER OF YEARS YOU WOULD LIKE TO TAKE"
280 PRINT "TO REPAY THAT LOAN. FOLLOW THAT WITH"
282 PRINT "A CARRIAGE RETURN. DO IT NOW."
285 PRINT
286 PRINT
290 INPUT C,D
295 PRINT
296 PRINT
297 IF B=0 THEN 340
300 PRINT "THANK YOU, NOW TYPE IN THE INTEREST RATE."
310 PRINT "THIS INFORMATION MUST BE IN DECIMAL FORM."
320 PRINT "EXAMPLE: .10=10% INTEREST RATE."
325 PRINT "FOLLOW THIS INFORMATION WITH A CARRIAGE RE-"
330 PRINT "TURN. DO IT NOW."
340 INPUT E
342 LET G=C*(((E/12)*(1+E/12)^(D*12))/((1+E/12)^(D*12)-1))
344 LET F=(G*D*(12))-C
350 PRINT
351 PRINT
352 PRINT "THANK YOU."
354 PRINT "THE TOTAL COST OF YOUR LOAN IS";F
355 PRINT
356 PRINT "YOUR MONTHLY PAYMENTS (INCLUDING INTEREST) ARE";G
358 PRINT
359 PRINT
360 PRINT "NOW YOU WILL RECEIVE A PAYMENT SCHEDULE"
370 PRINT "OF PAYMENTS FOR THE FIRST TWELVE MONTHS OF"
380 PRINT "YOUR REPAYMENT PERIOD. THEN I WILL TELL YOU"
390 PRINT "HOW MUCH YOU HAVE PAID TOWARDS THE PRINCIPLE,"
400 PRINT "THEN I WILL TELL YOU HOW MUCH YOU HAVE PAID"
410 PRINT "ON THE INTEREST, AND THEN I WILL TELL YOU HOW"
420 PRINT "MUCH YOU STILL HAVE LEFT TO REPAY (BALANCE DUE)."
430 PRINT
440 PRINT
510 PRINT "MONTH","PRINCIPLE","INTEREST","BALANCE DUE"
515 LET R=(E/12)*C
520 FOR I=1 TO 12
530 LET K=(E/12)*C
540 LET P=G-K
550 LET C=(C)-(P)
570 PRINT I,P,K,C
600 NEXT I
650 PRINT
660 PRINT
670 PRINT "THE AMOUNT YOU HAVE PAID TOWARDS THE PRINCIPLE IS:"
675 LET Q=G-E
685 PRINT
690 PRINT "$";Q
695 PRINT
700 PRINT
710 PRINT "THE AMOUNT YOU HAVE PAID ON INTEREST IS:"
715 PRINT
720 PRINT "$";R
725 PRINT
730 PRINT
735 PRINT "YOUR BALANCE DUE IS:"
736 PRINT
740 PRINT "$";C
750 PRINT
760 PRINT
780 PRINT "THIS COMPLETES THE MORTGAGE PAYMENT PROGRAM."
790 PRINT "THANK YOU FOR YOUR COOPERATION."
800 FOR T=1 TO 4
810 PRINT
820 NEXT T
999 END

View File

@ -1,3 +1,6 @@
1 REM***STAR TRADER FROM
2 REM***http://www.dunnington.info/public/basicgames/
3 REM***2 chain files merged and ported to 8bitworkshop
5 OPTION DIALECT HP
6 OPTION BASE 0:REM I GUESS HP HAS ZERO BASE???
10010 DIM S[12,15],T[12,12],T$[72],B[3,12]

View File

@ -136,19 +136,15 @@ CodeMirror.defineMode("basic", function(conf, parserConf) {
return null;
}
if (stream.match(doOpening)) {
/*
indent(stream,state);
state.doInCurrentLine = true;
*/
return 'keyword';
}
if (stream.match(opening)) {
/*
if (! state.doInCurrentLine)
indent(stream,state);
else
state.doInCurrentLine = false;
*/
return 'keyword';
}
if (stream.match(middle)) {
@ -222,7 +218,6 @@ CodeMirror.defineMode("basic", function(conf, parserConf) {
}
}
var delimiter_index = '[({'.indexOf(current);
if (delimiter_index !== -1) {
indent(stream, state );
@ -271,12 +266,15 @@ CodeMirror.defineMode("basic", function(conf, parserConf) {
return style;
},
/* TODO: indentation
indent: function(state, textAfter) {
var trueText = textAfter.replace(/^\s+|\s+$/g, '') ;
if (trueText.match(closing) || trueText.match(doubleClosing) || trueText.match(middle)) return conf.indentUnit*(state.currentIndent-1);
if(state.currentIndent < 0) return 0;
return state.currentIndent * conf.indentUnit;
},
*/
lineComment: "'"
};

View File

@ -11,6 +11,7 @@ export interface BASICOptions {
squareBrackets : boolean; // "[" and "]" interchangable with "(" and ")"?
tickComments : boolean; // support 'comments?
hexOctalConsts : boolean; // support &H and &O integer constants?
chainAssignments : boolean; // support A = B = value (HP2000)
validKeywords : string[]; // valid keywords (or null for accept all)
validFunctions : string[]; // valid functions (or null for accept all)
validOperators : string[]; // valid operators (or null for accept all)
@ -59,7 +60,7 @@ export class CompileError extends Error {
// Lexer regular expression -- each (capture group) handles a different token type
// FLOAT INT HEXOCTAL REMARK IDENT STRING RELOP EXP OPERATORS OTHER WS
const re_toks = /([0-9.]+[E][+-]?\d+|\d+[.][E0-9]*|[.][E0-9]+)|[0]*(\d+)|&([OH][0-9A-F]+)|(['].*)|(\w+[$]?)|(".*?")|([<>]?[=<>#])|(\*\*)|([-+*/^,;:()\[\]?\\])|(\S+)|(\s+)/gi;
const re_toks = /([0-9.]+[E][+-]?\d+|\d+[.][E0-9]*|[.][E0-9]+)|[0]*(\d+)|&([OH][0-9A-F]+)|(['].*)|(\w+[$]?)|(".*?")|([<>]?[=<>#])|(\*\*)|([-+*/^,;:()\[\]\?\\])|(\S+)|(\s+)/gi;
export enum TokenType {
EOL = 0,
@ -112,7 +113,7 @@ export interface PRINT_Statement {
export interface LET_Statement {
command: "LET";
lexpr: IndOp;
lexprs: IndOp[];
right: Expr;
}
@ -210,6 +211,12 @@ export interface GET_Statement { // applesoft only?
lexpr: IndOp;
}
export interface CHANGE_Statement {
command: "CHANGE";
src: Expr;
dest: IndOp;
}
export interface NoArgStatement {
command: string;
}
@ -247,10 +254,10 @@ const OPERATORS = {
'||': {f:'lor',p:17}, // not used
'&&': {f:'land',p:18}, // not used
'=': {f:'eq',p:50},
//'==': {f:'eq',p:50},
'==': {f:'eq',p:50},
'<>': {f:'ne',p:50},
'><': {f:'ne',p:50},
//'!=': {f:'ne',p:50},
'!=': {f:'ne',p:50},
'#': {f:'ne',p:50},
'<': {f:'lt',p:50},
'>': {f:'gt',p:50},
@ -299,6 +306,7 @@ function stripQuotes(s: string) {
export class BASICParser {
opts : BASICOptions = DIALECTS['DEFAULT'];
optionCount : number;
errors: WorkerError[];
listings: CodeListingMap;
labels: { [label: string]: BASICLine };
@ -320,6 +328,7 @@ export class BASICParser {
this.curlabel = null;
this.listings = {};
this.refs = {};
this.optionCount = 0;
}
addError(msg: string, loc?: SourceLocation) {
if (!loc) loc = this.peekToken().$loc;
@ -352,8 +361,8 @@ export class BASICParser {
}
return tok;
}
peekToken(): Token {
var tok = this.tokens[0];
peekToken(lookahead?: number): Token {
var tok = this.tokens[lookahead || 0];
return tok ? tok : this.eol;
}
pushbackToken(tok: Token) {
@ -385,6 +394,7 @@ export class BASICParser {
default:
if (this.opts.optionalLabels) this.compileError(`A line must start with a line number, command, or label.`);
else this.compileError(`A line must start with a line number.`);
case TokenType.Remark:
break;
}
}
@ -487,6 +497,10 @@ export class BASICParser {
validKeyword(keyword: string) : string {
return (this.opts.validKeywords && this.opts.validKeywords.indexOf(keyword) < 0) ? null : keyword;
}
supportsKeyword(keyword: string) {
if (this['stmt__' + keyword] != null) return true;
return false;
}
parseStatement(): Statement | null {
var cmdtok = this.consumeToken();
var cmd = cmdtok.str;
@ -735,10 +749,15 @@ export class BASICParser {
//// STATEMENTS
stmt__LET(): LET_Statement {
var lexpr = this.parseLexpr();
var lexprs = [ this.parseLexpr() ];
this.expectToken("=");
// look for A=B=expr (TODO: doesn't work on arrays)
while (this.opts.chainAssignments && this.peekToken().type == TokenType.Ident && this.peekToken(1).str == '=') {
lexprs.push(this.parseLexpr());
this.expectToken("=");
}
var right = this.parseExpr();
return { command: "LET", lexpr: lexpr, right: right };
return { command: "LET", lexprs: lexprs, right: right };
}
stmt__PRINT(): PRINT_Statement {
var sep, lastsep;
@ -784,8 +803,9 @@ export class BASICParser {
stmt__IF(): IF_Statement {
var cond = this.parseExpr();
var iftrue: Statement[];
// we accept GOTO or THEN if line number provided
var thengoto = this.expectTokens(['THEN','GOTO']);
// we accept GOTO or THEN if line number provided (DEC accepts GO TO)
var thengoto = this.expectTokens(['THEN','GOTO','GO']);
if (thengoto.str == 'GO') this.expectToken('TO');
var lineno = this.peekToken();
// assume GOTO if number given after THEN
if (lineno.type == TokenType.Int) {
@ -893,7 +913,7 @@ export class BASICParser {
stmt__ON() : ONGO_Statement {
var expr = this.parseExpr();
var gotok = this.consumeToken();
var cmd = {GOTO:'ONGOTO', GOSUB:'ONGOSUB'}[gotok.str];
var cmd = {GOTO:'ONGOTO', THEN:'ONGOTO', GOSUB:'ONGOSUB'}[gotok.str]; // THEN only for DEC basic?
if (!cmd) this.compileError(`There should be a GOTO or GOSUB here.`);
var labels = this.parseLabelList();
return { command:cmd, expr:expr, labels:labels };
@ -920,6 +940,12 @@ export class BASICParser {
stmt__RANDOMIZE() : NoArgStatement {
return { command:'RANDOMIZE' };
}
stmt__CHANGE() : CHANGE_Statement {
var src = this.parseExpr();
this.expectToken('TO');
var dest = this.parseLexpr();
return { command:'CHANGE', src:src, dest:dest };
}
// TODO: CHANGE A TO A$ (4th edition, A(0) is len and A(1..) are chars)
stmt__OPTION() : OPTION_Statement {
var tokname = this.consumeToken();
@ -938,18 +964,21 @@ export class BASICParser {
}
parseOptions(stmt: OPTION_Statement) {
var arg = stmt.optargs[0];
this.optionCount++;
switch (stmt.optname) {
case 'BASE':
let base = parseInt(arg);
if (base == 0 || base == 1) this.opts.defaultArrayBase = base;
else this.compileError("OPTION BASE can only be 0 or 1.");
break;
case 'DIALECT':
if (this.optionCount > 1)
this.compileError(`OPTION DIALECT must be the first OPTION statement in the file.`);
let dname = arg || "";
let dialect = DIALECTS[dname.toUpperCase()];
if (dialect) this.opts = dialect;
else this.compileError(`OPTION DIALECT ${dname} is not supported by this compiler.`);
break;
case 'BASE':
let base = parseInt(arg);
if (base == 0 || base == 1) this.opts.defaultArrayBase = base;
else this.compileError("OPTION BASE can only be 0 or 1.");
break;
case 'CPUSPEED':
if (!(this.opts.commandsPerSec = Math.min(1e7, arg=='MAX' ? Infinity : parseFloat(arg))))
this.compileError(`OPTION CPUSPEED takes a positive number or MAX.`);
@ -1051,6 +1080,7 @@ export const ECMA55_MINIMAL : BASICOptions = {
squareBrackets : false,
arraysContainChars : false,
endStmtRequired : true,
chainAssignments : false,
}
// TODO: only integers supported
@ -1095,13 +1125,14 @@ export const TINY_BASIC : BASICOptions = {
squareBrackets : false,
arraysContainChars : false,
endStmtRequired : false,
chainAssignments : false,
}
export const HP_TIMESHARED_BASIC : BASICOptions = {
dialectName: "HP2000",
asciiOnly : true,
uppercaseOnly : false,
uppercaseOnly : false, // the terminal is usually uppercase
optionalLabels : false,
optionalWhitespace : false,
varNaming : "A1",
@ -1145,9 +1176,121 @@ export const HP_TIMESHARED_BASIC : BASICOptions = {
squareBrackets : true,
arraysContainChars : true,
endStmtRequired : true,
chainAssignments : true,
// TODO: max line number, array index 9999
}
export const DEC_BASIC_11 : BASICOptions = {
dialectName: "DEC11",
asciiOnly : true,
uppercaseOnly : true, // translates all lower to upper
optionalLabels : false,
optionalWhitespace : false,
varNaming : "A1",
staticArrays : true,
sharedArrayNamespace : false,
defaultArrayBase : 0,
defaultArraySize : 11,
defaultValues : true,
stringConcat : true, // can also use &
typeConvert : false,
maxDimensions : 2,
maxDefArgs : 255, // ?
maxStringLength : 255,
tickComments : false,
hexOctalConsts : false,
validKeywords : [
'OPTION',
'DATA','DEF','DIM','END','FOR','STEP','GOSUB','GOTO','GO','TO',
'IF','THEN','INPUT','LET','NEXT','ON','PRINT','RANDOMIZE',
'READ','REM','RESET','RESTORE','RETURN','STOP',
],
validFunctions : [
'ABS','ATN','COS','EXP','INT','LOG','LOG10','PI','RND','SGN','SIN','SQR','TAB',
'ASC','BIN','CHR$','CLK$','DAT$','LEN','OCT','POS','SEG$','STR$','TRM$','VAL',
'NFORMAT$', // non-standard, substitute for PRINT USING
],
validOperators : [
'=', '<>', '><', '<', '>', '<=', '>=', '+', '-', '*', '/', '^',
],
printZoneLength : 14,
numericPadding : true,
checkOverflow : true, // non-fatal; subst 0 and continue
testInitialFor : true,
optionalNextVar : false,
multipleNextVars : false,
bitwiseLogic : false,
checkOnGotoIndex : true, // might continue
computedGoto : false,
restoreWithLabel : false,
squareBrackets : false,
arraysContainChars : false,
endStmtRequired : false,
chainAssignments : false,
// TODO: max line number 32767
// TODO: \ separator, % int vars and constants, 'single' quoted
// TODO: can't compare strings and numbers
}
export const DEC_BASIC_PLUS : BASICOptions = {
dialectName: "DECPLUS",
asciiOnly : true,
uppercaseOnly : false,
optionalLabels : false,
optionalWhitespace : false,
varNaming : "A1",
staticArrays : true,
sharedArrayNamespace : false,
defaultArrayBase : 0,
defaultArraySize : 11,
defaultValues : true,
stringConcat : true, // can also use "&"
typeConvert : false,
maxDimensions : 2,
maxDefArgs : 255, // ?
maxStringLength : 255,
tickComments : true, // actually use "!"
hexOctalConsts : false,
validKeywords : [
'OPTION',
'REM','LET','DIM','RANDOM','RANDOMIZE','IF','THEN','ELSE',
'FOR','TO','STEP','WHILE','UNTIL','NEXT','DEF','ON','GOTO','GOSUB',
'RETURN','CHANGE','READ','DATA','RESTORE','PRINT','USING',
'INPUT','LINE','NAME','AS','ERROR','RESUME','CHAIN','STOP','END',
'MAT','UNLESS','SLEEP','WAIT',
],
validFunctions : [
'ABS','ATN','COS','EXP','INT','LOG','LOG10','PI','RND','SGN','SIN','SQR','TAB','TAN',
'POS','TAB','ASCII','CHR$','CVT%$','CVTF$','CVT$%','CVT$F',
'LEFT$','RIGHT$','MID$','LEN','INSTR','SPACE$','NUM$','VAL','XLATE',
'DATE$','TIME$','TIME','ERR','ERL','SWAP%','RAD$',
'NFORMAT$', // non-standard, substitute for PRINT USING
],
validOperators : [
'=', '<>', '<', '>', '<=', '>=', '+', '-', '*', '/', '^',
'**', '==',
'NOT', 'AND', 'OR', 'XOR', 'IMP', 'EQV',
],
printZoneLength : 14,
numericPadding : true,
checkOverflow : true, // non-fatal; subst 0 and continue
testInitialFor : true,
optionalNextVar : false,
multipleNextVars : false,
bitwiseLogic : false,
checkOnGotoIndex : true, // might continue
computedGoto : false,
restoreWithLabel : false,
squareBrackets : false,
arraysContainChars : false,
endStmtRequired : false,
chainAssignments : false, // TODO: can chain with "," not "="
// TODO: max line number 32767
// TODO: \ separator, % int vars and constants, 'single' quoted
// TODO: can't compare strings and numbers
// TODO: WHILE/UNTIL/FOR extra statements, etc
}
export const BASICODE : BASICOptions = {
dialectName: "BASICODE",
asciiOnly : true,
@ -1193,6 +1336,7 @@ export const BASICODE : BASICOptions = {
squareBrackets : false,
arraysContainChars : false,
endStmtRequired : false,
chainAssignments : false,
}
export const ALTAIR_BASIC41 : BASICOptions = {
@ -1248,6 +1392,7 @@ export const ALTAIR_BASIC41 : BASICOptions = {
squareBrackets : false,
arraysContainChars : false,
endStmtRequired : false,
chainAssignments : false,
}
export const APPLESOFT_BASIC : BASICOptions = {
@ -1304,6 +1449,7 @@ export const APPLESOFT_BASIC : BASICOptions = {
squareBrackets : false,
arraysContainChars : false,
endStmtRequired : false,
chainAssignments : false,
}
export const BASIC80 : BASICOptions = {
@ -1361,6 +1507,7 @@ export const BASIC80 : BASICOptions = {
squareBrackets : false,
arraysContainChars : false,
endStmtRequired : false,
chainAssignments : false,
}
export const MODERN_BASIC : BASICOptions = {
@ -1398,16 +1545,21 @@ export const MODERN_BASIC : BASICOptions = {
squareBrackets : true,
arraysContainChars : false,
endStmtRequired : false,
chainAssignments : false,
}
// TODO: integer vars
// TODO: DEFINT/DEFSTR
// TODO: superfluous ":" ignored on MS basics only?
// TODO: excess INPUT ignored, error msg
// TODO: max line len?
export const DIALECTS = {
"DEFAULT": ALTAIR_BASIC41,
"ALTAIR": ALTAIR_BASIC41,
"ALTAIR4": ALTAIR_BASIC41,
"ALTAIR41": ALTAIR_BASIC41,
"TINY": TINY_BASIC,
"ECMA55": ECMA55_MINIMAL,
"MINIMAL": ECMA55_MINIMAL,
"HP": HP_TIMESHARED_BASIC,
@ -1416,9 +1568,12 @@ export const DIALECTS = {
"HP2000": HP_TIMESHARED_BASIC,
"HPBASIC": HP_TIMESHARED_BASIC,
"HPACCESS": HP_TIMESHARED_BASIC,
"DEC11": DEC_BASIC_11,
"DEC": DEC_BASIC_PLUS,
"DECPLUS": DEC_BASIC_PLUS,
"BASICPLUS": DEC_BASIC_PLUS,
"BASICODE": BASICODE,
"APPLESOFT": APPLESOFT_BASIC,
"BASIC80": BASIC80,
"MODERN": MODERN_BASIC,
"TINY": TINY_BASIC,
};

View File

@ -6,10 +6,11 @@ import { lpad, rpad } from "../util";
function dumpDialectInfo() {
var dialects = new Set<BASICOptions>();
var array = {};
var SELECTED_DIALECTS = ['TINY','ECMA55','HP','ALTAIR41','BASIC80','MODERN'];
var SELECTED_DIALECTS = ['TINY','ECMA55','HP','DEC','ALTAIR','BASIC80','MODERN'];
SELECTED_DIALECTS.forEach((dkey) => {
dialects.add(DIALECTS[dkey]);
});
var ALL_KEYWORDS = new Set<string>();
dialects.forEach((dialect) => {
Object.entries(dialect).forEach(([key, value]) => {
if (value === null) value = "all";
@ -19,6 +20,16 @@ function dumpDialectInfo() {
value = value.length;
if (!array[key]) array[key] = [];
array[key].push(value);
if (dialect.validKeywords) dialect.validKeywords.map(ALL_KEYWORDS.add.bind(ALL_KEYWORDS));
});
});
dialects.forEach((dialect) => {
ALL_KEYWORDS.forEach((keyword) => {
if (parser.supportsKeyword(keyword)) {
var has = dialect.validKeywords == null || dialect.validKeywords.indexOf(keyword) >= 0;
if (!array[keyword]) array[keyword] = [];
array[keyword].push(has ? "Y" : "-");
}
});
});
Object.entries(array).forEach(([key, arr]) => {

View File

@ -72,7 +72,7 @@ export class BASICRuntime {
allstmts : basic.Statement[];
line2pc : number[];
pc2line : Map<number,number>;
label2lineidx : {[label : string] : number};
pc2label : Map<number,string>;
label2pc : {[label : string] : number};
label2dataptr : {[label : string] : number};
datums : basic.Literal[];
@ -96,25 +96,39 @@ export class BASICRuntime {
exited : boolean = true;
trace : boolean = false;
load(program: basic.BASICProgram) {
let prevlabel = this.label2pc && this.getLabelForPC(this.curpc);
load(program: basic.BASICProgram) : boolean {
// get previous label and offset for hot reload
let prevlabel = null;
let prevpcofs = 0;
if (this.pc2label != null) {
var pc = this.curpc;
while (pc > 0 && (prevlabel = this.pc2label.get(pc)) == null) {
pc--;
}
prevpcofs = this.curpc - pc;
console.log('oldpc=', this.curpc, 'restart @ label', prevlabel, '+', prevpcofs);
}
// initialize program
this.program = program;
this.opts = program.opts;
this.label2lineidx = {};
this.label2pc = {};
this.label2dataptr = {};
this.allstmts = [];
this.line2pc = [];
this.pc2line = new Map();
this.pc2label = new Map();
this.datums = [];
this.builtins = this.getBuiltinFunctions();
// TODO: detect undeclared vars
program.lines.forEach((line, idx) => {
// make lookup tables
if (line.label != null) this.label2lineidx[line.label] = idx;
if (line.label != null) this.label2pc[line.label] = this.allstmts.length;
this.line2pc.push(this.allstmts.length);
this.pc2line.set(this.allstmts.length, idx);
var pc = this.allstmts.length;
if (line.label != null) {
this.label2pc[line.label] = pc;
this.pc2label.set(pc, line.label);
}
this.line2pc.push(pc);
this.pc2line.set(pc, idx);
// combine all statements into single list
line.stmts.forEach((stmt) => this.allstmts.push(stmt));
});
@ -132,8 +146,13 @@ export class BASICRuntime {
});
});
// try to resume where we left off after loading
this.dataptr = Math.min(this.dataptr, this.datums.length);
this.curpc = this.label2pc[prevlabel] || 0;
if (this.label2pc[prevlabel] != null) {
this.curpc = this.label2pc[prevlabel] + prevpcofs;
return true;
} else {
this.curpc = 0;
return false;
}
}
reset() {
@ -438,10 +457,9 @@ export class BASICRuntime {
}
}
assign2js(expr: basic.IndOp, opts?: ExprOptions) {
assign2js(expr: basic.IndOp, opts?: ExprOptions) : string {
if (!opts) opts = {};
var s = '';
var qname = JSON.stringify(expr.name);
// is it a function? not allowed
if (expr.name.startsWith("FN") || this.builtins[expr.name]) this.runtimeError(`I can't call a function here.`);
// is it a subscript?
@ -450,9 +468,7 @@ export class BASICRuntime {
if (this.opts.arraysContainChars && expr.name.endsWith('$')) {
this.runtimeError(`I can't set array slices via this command yet.`);
} else {
s += this.expr2js(expr, {novalid:true}); // check array bounds
s += `;this.getArray(${qname}, ${expr.args.length})`;
s += expr.args.map((arg) => '[this.ROUND('+this.expr2js(arg, opts)+')]').join('');
s += this.array2js(expr, opts);
}
} else { // just a variable
s = `this.vars.${expr.name}`;
@ -460,6 +476,14 @@ export class BASICRuntime {
return s;
}
array2js(expr: basic.IndOp, opts?: ExprOptions) : string {
var qname = JSON.stringify(expr.name);
var args = expr.args || [];
return this.expr2js(expr, {novalid:true}) // check array bounds
+ `;this.getArray(${qname}, ${args.length})`
+ args.map((arg) => '[this.ROUND('+this.expr2js(arg, opts)+')]').join('');
}
checkFuncArgs(expr: basic.IndOp, fn: Function) {
// TODO: check types?
var nargs = expr.args ? expr.args.length : 0;
@ -472,7 +496,9 @@ export class BASICRuntime {
}
startForLoop(forname, init, targ, step) {
var pc = this.curpc;
// save start PC and label in case of hot reload (only works if FOR is first stmt in line)
var looppc = this.curpc - 1;
var looplabel = this.pc2label.get(looppc);
if (!step) step = 1;
this.vars[forname] = init;
if (this.trace) console.log(`FOR ${forname} = ${init} TO ${targ} STEP ${step}`);
@ -499,7 +525,8 @@ export class BASICRuntime {
this.forLoopStack.pop();
delete this.forLoops[forname];
} else {
this.curpc = pc; // go back to FOR location
// go back to FOR loop, adjusting for hot reload (fetch pc by label)
this.curpc = ((looplabel != null && this.label2pc[looplabel]) || looppc) + 1;
}
if (this.trace) console.log(`NEXT ${forname}: ${this.vars[forname]} TO ${targ} STEP ${step} DONE=${done}`);
}
@ -661,6 +688,17 @@ export class BASICRuntime {
return s;
}
preInput() {
this.running=false;
this.curpc--;
}
postInput(valid : boolean) {
if (valid) this.curpc++;
this.running = true;
this.resume();
}
do__INPUT(stmt : basic.INPUT_Statement) {
var prompt = this.expr2js(stmt.prompt);
var setvals = '';
@ -672,29 +710,30 @@ export class BASICRuntime {
${lexpr} = value;
`
});
return `this.running=false; this.curpc--;
return `this.preInput();
this.input(${prompt}, ${stmt.args.length}).then((vals) => {
let valid = 1;
${setvals}
if (valid) this.curpc++;
this.running=true;
this.resume();
this.postInput(valid);
this.column = 0; // assume linefeed
})`;
}
do__LET(stmt : basic.LET_Statement) {
var right = this.expr2js(stmt.right);
// HP BASIC string-slice syntax?
if (this.opts.arraysContainChars && stmt.lexpr.args && stmt.lexpr.name.endsWith('$')) {
var s = `this.vars.${stmt.lexpr.name} = this.modifyStringSlice(this.vars.${stmt.lexpr.name}, ${right}, `
s += stmt.lexpr.args.map((arg) => this.expr2js(arg)).join(', ');
s += ')';
console.log(s);
return s;
} else {
var lexpr = this.assign2js(stmt.lexpr);
return `${lexpr} = this.assign(${JSON.stringify(stmt.lexpr.name)}, ${right});`;
var s = `let _right = ${right};`;
for (var lexpr of stmt.lexprs) {
// HP BASIC string-slice syntax?
if (this.opts.arraysContainChars && lexpr.args && lexpr.name.endsWith('$')) {
s += `this.vars.${lexpr.name} = this.modifyStringSlice(this.vars.${lexpr.name}, _right, `
s += lexpr.args.map((arg) => this.expr2js(arg)).join(', ');
s += ')';
} else {
var ljs = this.assign2js(lexpr);
s += `${ljs} = this.assign(${JSON.stringify(lexpr.name)}, _right);`;
}
}
return s;
}
do__FOR(stmt : basic.FOR_Statement) {
@ -826,11 +865,10 @@ export class BASICRuntime {
do__GET(stmt : basic.GET_Statement) {
var lexpr = this.assign2js(stmt.lexpr);
// TODO: single key input
return `this.running=false; this.curpc--;
return `this.preInput();
this.input().then((vals) => {
${lexpr} = this.convert(${JSON.stringify(stmt.lexpr.name)}, vals[0]);
this.running=true; this.curpc++;
this.resume();
this.postInput(true);
})`;
}
@ -842,6 +880,33 @@ export class BASICRuntime {
return `this.rng.randomize()`;
}
do__CHANGE(stmt : basic.CHANGE_Statement) {
var arr2str = stmt.dest.name.endsWith('$');
if (arr2str) { // array -> string
let arrname = (stmt.src as basic.IndOp).name || this.runtimeError("I can only change to a string from an array.");
let dest = this.assign2js(stmt.dest);
return `
let arrname = ${JSON.stringify(arrname)};
let len = this.arrayGet(arrname, 0);
let s = '';
for (let i=0; i<len; i++) {
s += String.fromCharCode(this.arrayGet(arrname, i+1));
}
${dest} = s;
`;
} else { // string -> array
let src = this.expr2js(stmt.src);
let dest = this.array2js(stmt.dest);
return `
let src = ${src}+"";
${dest}[0] = src.length;
for (let i=0; i<src.length; i++) {
${dest}[i+1] = src.charCodeAt(i);
}
`;
}
}
// TODO: ONERR, ON ERROR GOTO
// TODO: gosubs nested too deeply
// TODO: memory quota
@ -1015,9 +1080,9 @@ export class BASICRuntime {
}
INSTR(a, b, c) : number {
if (c != null) {
return this.checkString(c).indexOf(this.checkString(b), a) + 1;
return this.checkString(b).indexOf(this.checkString(c), this.checkNum(a) - 1) + 1;
} else {
return this.checkString(b).indexOf(this.checkString(a)) + 1;
return this.checkString(a).indexOf(this.checkString(b)) + 1;
}
}
INT(arg : number) : number {
@ -1037,6 +1102,11 @@ export class BASICRuntime {
if (arg < 0) this.runtimeError(`I can't take the logarithm of a negative number (${arg}).`)
return this.checkNum(Math.log(arg));
}
LOG10(arg : number) : number {
if (arg == 0) this.runtimeError(`I can't take the logarithm of zero (${arg}).`)
if (arg < 0) this.runtimeError(`I can't take the logarithm of a negative number (${arg}).`)
return this.checkNum(Math.log10(arg));
}
MID$(arg : string, start : number, count : number) : string {
if (start < 1) this.runtimeError(`I can't compute MID$ if the starting index is less than 1.`)
if (count == 0) count = arg.length;
@ -1045,6 +1115,10 @@ export class BASICRuntime {
OCT$(arg : number) : string {
return this.ROUND(arg).toString(8);
}
PI() : number {
return Math.PI;
}
// TODO: POS(haystack, needle, start)
POS(arg : number) : number { // arg ignored
return this.column + 1;
}
@ -1130,6 +1204,7 @@ export class BASICRuntime {
return arg;
}
NFORMAT$(arg : number, numlen : number) : string {
return this.LPAD$(this.float2str(arg, numlen), numlen);
var s = this.float2str(arg, numlen);
return (numlen > 0) ? this.LPAD$(s, numlen) : this.RPAD$(s, -numlen);
}
}

View File

@ -10,10 +10,14 @@ import { TeleTypeWithKeyboard } from "../common/teletype";
const BASIC_PRESETS = [
{ id: 'hello.bas', name: 'Hello World' },
{ id: 'sieve.bas', name: 'Sieve Benchmark' },
{ id: 'mortgage.bas', name: 'Interest Calculator' },
{ id: '23match.bas', name: '23 Matches' },
{ id: 'wumpus.bas', name: 'Hunt The Wumpus' },
{ id: 'craps.bas', name: 'Craps' },
{ id: 'lander.bas', name: 'Lander' },
{ id: 'hamurabi.bas', name: 'Hammurabi' },
{ id: 'wumpus.bas', name: 'Hunt The Wumpus' },
{ id: 'startrader.bas', name: 'Star Trader' },
{ id: 'haunted.bas', name: 'Haunted House' },
];
class BASICPlatform implements Platform {
@ -123,7 +127,10 @@ class BASICPlatform implements Platform {
this.program = data;
this.runtime.load(data);
this.tty.uppercaseOnly = true; // this.program.opts.uppercaseOnly; //TODO?
// map editor to uppercase-only if need be
views.textMapFunctions.input = this.program.opts.uppercaseOnly ? (s) => s.toUpperCase() : null;
// HP 2000 has cute lil small caps (TODO: messes up grid alignment tho)
//this.tty.page.style.fontVariant = (this.program.opts.dialectName == 'HP2000') ? 'small-caps' : 'normal';
// only reset if we exited, or couldn't restart at label (PC reset to 0)
if (!this.hotReload || didExit || this.runtime.curpc == 0)
this.reset();