> Squish3 BASIC V program cruncher ) Mike Harrison 1989 History:(8 CDean 06-Oct-89 added command-line reading2: with syntax Squish -From -To ,<( and removed fancy colours.FPG CDean 10-Oct-89 fixed handling of "FILL BY", fixed numberZG handling (now reads 4000000000, 1.7E5 etc. ok and doesn'tdJ convert to hex). Fixed unwanted concatenations after numericnN constants, FN & PROC names, real variable names, quoted strings.xH Fixed failure to display *-commands or "P" of "TOP". FixedH failure to report computed GOTO/GOSUB, fixed DIM handling.- Tidied the berserk indentation.H CDean 11-Oct-89 fixed "a((" -> "a(", stopped space removal, in "a% (", "a$ (" and "RND (".I CDean 12-Oct-89 re-organised error handling: valid programH constructs that cannot be crunched give errors, those thatH may be mis-crunched give warnings. Scanning of input fileG proceeds to end, displaying errors and warnings. ProgramI then aborts if errors found. Warnings behaviour controlledI by "-Warn " on command line, where = 0 => don't giveL warnings at all, 1 => warn but don't treat as error (default),L and 2 => warn and treat as error (i.e. abort at end of input).J CDean 14-Oct-89 no longer allocates new variable names using"J "E": avoids problems with cases like IF 1=1fred=2. No longer,J uses "Y" either (easiest way to avoid "BY"!). Fixed removal6) of space in "IFX% !Y%=...".@JH CDean 15-Oct-89 added control of listing via "-nolist" andTE "-list ", reinstated colour option via "-colour",^ and added "-help".hrE CDean 09-Dec-89 fixed "IF ... THEN *..." etc, not being|3 recognised as containing a *-command.I PColmer/CDean 12-Jan-90 added translation of SYS "swi name"B into SYS (warns if unable to translate).E SYS conversion can be suppressed using "-nosys" option.K Apologies for the messy nature of this code - it was done in a hurry!O This program has been put into the public domain in the hope that someoneK with the time and inclination will be inspired to do a decent machine code version of it!N This utility crunches a BASIC program by shortening variables, stripping? out REMs and assembler comments, and concatenating lines. This has 3 benefits :L 1) It takes less memory - the saving can be over 50% with big programs3 using long var names (e.g. wimp applications)& 2) It improves performance05 3) It makes the program very difficult to read!:DI Variable crunching is highly optimised. The strategy is as follows:NL For each of real,int,string,real array,string array,int array,FN,PROC:XbP 1) leave all single letter vars alone - this is due to the assumption thatlO the program uses single letters for a reason - speed, or CALL parameters.vJ 2) allocate unused single letter variables to the most commonly used variables.N 3) allocate 2 letter names to any remaining variables. Whilst generatingK 2 letter names, the first letter changes fastest, for optimum runtime. speed when BASIC is looking up the name.M If there aren't enough 2 letter pairs, TOUGH! (This will only happen ifC the program has more than about 3,500 variables of one type!)R Decimal,Hex and Binary constants used to be converted to decimal, unless hexP would be shorter (e.g. &FFFFF is shorter than 1048575) but aren't any moreO baecause it can often isn't worth it (e.g can't concatenate with variable- names beginning with a-f, A-F (CDean)).M You may notice that the program can build a list of all numerics used - I this was intended to be used to allocate variables as constants forN frequently used numbers. This has not (yet) been implemented, as savings  would be small.*4K Line concatenation errs on the side of caution, and sometimes doesn't>F join joinable lines. This has little effect on the cunched size.HR Limitations:\ -----------fpG The cruncher will not cope with references to line numbers (GOTO,zJ GOSUB,RESTORE etc.). These can usually be avoided, as ON ERRORs can be done as follows: PROCinitialstuff> ON ERROR PROCmoan :REM will drop through to main loop REPEAT
 UNTIL " and RESTORE can be done by :M RESTORE +0 :REM restores to data on next line (cruncher won't join) DATA 1,2,3,4L Any occurrence of GOTO or GOSUB causes the program to report an error.Q The same happens with RESTORE . Relative and computedN RESTOREs give warnings: be sure to avoid computed ones (RESTORE (line%))$2 and DATA interspersed with other stuff, e.g..8 RESTORE +4B DATA 1L DATA 2V0 REM this line disappears after crunching` DATA 4j DATA 5tC READ data% : REM should read 4, but reads 5 after crunching~F TOP is misinterpreted if concatenated with a variable name, i.e.9 TOPSIDE is always taken as TO PSIDE, not TOP SIDEI since the tokens for TOP are &B8 (TO token) + "P", and the cruncherE cannot distinguish whether an operator (TO) or an operand (TOP)I is expected. In practice, it is fairly unlikely that a program will contain; IF T%=TOPSIDE=3 instead of IF T%=TOP SIDE=3C (BASIC's CRUNCH will not concatenate after TOP). In cases of . ambiguity, the cruncher issues warnings.G Clever tricks using EVAL on variable names and READing data which(G contains variable names obviously won't work! Any use of EVAL or2 DATA produces a warning.<F Notes:P -----ZdF You may see some spurious trailing colons, which won't be in thenG crunched program image, and assembler mnemonics containing tokensxH (MOVEq ORr AND EOR) won't be displayed correctly onscreen, but the saved code will be OK.H Garbage in program can cause problems - e.g. comments between procG definitions. Ensure REMs are present. This can cause wierd errors> like Bad Hex, Bad binary if there are & or % in garbage.F As most of this program is hacked up in BASIC, it is SLOW, oftenI taking a few minutes. This shouldn't be a problem, as programs only2 need to be crunched when fully debugged etc.K Some speed can be gained by using a crunched version of the cruncher,9 and using RAM Basic (or *RMFaster Basic in RiscOs).@ It is also a good idea to "CRUNCH 15" in BASIC the program3 to be squeezed, before passing it to *Squish.M Crunched programs may be impossible to edit, as lines may detokenise toK longer than 256 bytes, and lack of spaces will confuse the tokeniser."J If a program uses libraries, these must be merged with the main code,I (e.g. using APPEND), crunched, and then split back into the library6B files. Otherwise, the variable/PROC names won't correspond!@JL There are almost certainly some obscure things that I didn't think of,T* which won't crunch properly - sorry!^hH The technicolour listing of the crunched program is for diagnosticr8 purposes to identify any problems of miscrunching.|I Tokens are in red, replaced variables in magenta, numerics in blue,K *-commands in green and everything else in black on white background.J In case of problems, the list of name translations for each type canM be printed using PROCshowlist(xxxptr%) after the cruncher has finished,G where xxx is one of : real int string inta reala stringa proc fn.:& :" (internal error ";;")":ver$="v1.08 12-Jan-90" CODE% 1000bufsize%=--&8000 buf% bufsize%Gdefaultalloc%=3 : bytes to reserve for new name when building list:A=0 2 2:P%=CODE%:[OPTA .gt \R0=token, [nt] nextbyte& \19*40 STMFD R13!,{R14}: ADD R2,R14,#19*4D ADR R14,backN ADR R12,ntX MOV PC,R2b .backl STR R1,tstv LDMFD R13!,{PC} .tst EQUD0 .nt EQUD0 .search .recordloop STR R0,adr ADR R2,searchbuf ADD R1,R0,#5 \->firstchar .searchloop LDRB R3,[R2],#1 LDRB R4,[R1],#1 TEQ R3,R4 : BNE notfound! TEQ R3,#13 : BNE searchloop \foundM LDRB R3,[R0,#4] : ADD R3,R3,#1:TEQ R3,#256:STRNEB R3,[R0,#4] \inc cycle  STR R0,fadr MOV R3,#1:STR R3,adr  MOV PC,R14* .notfound4 LDR R0,[R0]> TEQ R0,#0 : BNE recordloopH MOV PC,R14R .searchbuf\ EQUS 206,"*")f ALIGNp EQUD0z .adr EQUD0 .fadr EQUD-1]::+warn%=1:log%=-1:pos%=0:colour%=:sys%=warnings%=0:errors%=0args(F$,out$) F$="" "Input file: " F$$ out$="" "Output file: " out$&ș"OS_File",&FF,F$,buf% ,,,,len%=list%=(buf%+len%+&100) 3 : linked list for var tables)heapptr%=list%:heapend%=buf%+bufsize%# toupper(out$)=toupper(F$) 8 7:"Output file same name as input - sure? "ans$ % "YES",toupper(ans$))<>1  : $= log%>0 log%=(log$): log%=0: 1,"Can't open log file" .. :" (internal error ";;")":endlog: 8 B( log%<0 colour%<>0: 12:0,7:7,0 LFdiag$="Squish ("+ver$+") crunching program '"+F$+"' to '"+out$+"'" V5eoln:print(diag$):eoln:eoln: log%>=0 'diag$ `=0 j3print("Scanning for variable names...."):eoln tO (ptr%)+8=single var allocation map - 0=available, order as INSTR(nvar$,x) ~ list format : m 0..3 : pointer to next record in list : 0 IF last in list, aligned absolute address of next item if not P 4 : counter byte, holds number of occurrences of variable name.255 if>=255 ! 5.. old var name ! new var name ALIGN 1realptr%=dummy : initialise base pointers intptr%=dummy stringptr%=dummy realaptr%=dummy intaptr%=dummy stringaptr%=dummy fnptr%=dummy procptr%=dummy numptr%=dummy linel%=0:lineh%=0  pt%=buf% (Lvar$="@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_`0123456789" 2Knvar$="@abcdefghijklmnopqrstuvwxyzABCDFGHIJKLMNOPQRSTUVWXZ_`0123456789" =0 'diag$ - errors%<>0 (warn%=2 warnings%<>0) = diag$="** Conversion aborted: no output file produced." 7 eoln:eoln:print(diag$):eoln: log%>=0 'diag$ endlog   warn%=-1 : . :" (internal error ";;")":endlog: Feoln:eoln:print("Allocating single letter variables...."):eoln alocsingle(realptr%) alocsingle(intptr%) alocsingle(stringptr%) alocsingle(realaptr%) alocsingle(intaptr%) alocsingle(stringaptr%) "alocsingle(fnptr%) ,alocsingle(procptr%) 6Feoln:eoln:print("Allocating double letter variables...."):eoln @alocdouble(realptr%) Jalocdouble(intptr%) Talocdouble(stringptr%) ^alocdouble(realaptr%) halocdouble(intaptr%) ralocdouble(stringaptr%) |alocdouble(fnptr%) alocdouble(procptr%) : ?colour(7):eoln:print("Creating crunched code...."):eoln ^ :" (internal error ";;")":"Line number in original = ";linel%+256*lineh%:endlog: mash : colour(7) Pdiag$="Program '"+F$+"' crunched to '"+out$+"' on "+$,5,11)+" at "+$,17) 5eoln:eoln:print(diag$):eoln:log%>=0 'diag$' Ndiag$="Errors : "+(errors%):eoln:print(diag$):log%>=0 diag$ Pdiag$="Warnings : "+(warnings%):eoln:print(diag$):log%>=0 diag$ Mdiag$="Original length : &"+~(len%):eoln:print(diag$):log%>=0 diag$ ]diag$="New length : &"+~(linebase%+2-newbase%):eoln:print(diag$):log%>=0 diag$ ddiag$="Saving : &"+~(len%-(linebase%+2-newbase%)):eoln:print(diag$):log%>=0 diag$ ]diag$="Size of crunched program is "+(((linebase%+2-newbase%)*100)len%)+"% of original" 6eoln:eoln:print(diag$):eoln:log%>=0 'diag$' &Qt%=:diag$="Time Taken : "+(t%6000)+" mins, "+((t%6000)100)+" secs" 0.eoln:print(diag$):eoln:log%>=0 diag$ : endlog D N: X scan b pt%=buf% lasmflag%=0 vȕ pt%?1<>&FF  lineh%=pt%?1  linel%=pt%?2  linelen%=pt%?3  ll%=linelen%+pt% % PRINT"<"linel%+256*lineh%">" pt%+=4 ; lastwas%=0 : last var was asm .var, so next is stmnt  quoteflag%=0  remflag%=0:byflag%=0 what%=0 J dimflag%=0 : TO catch DIM n 100 etc : REM now always zero ! (CDean)  startstmnt%=  ȕ pt%8 get  Ȏ what% : 1 : store(got$,realptr%): print(got$):tab = 2 : store(got$,intptr%): print(got$+"%"):tab*@ 3 : store(got$,stringptr%): print(got$+"$"):tab4@ 4 : store(got$,realaptr%) : print(got$+"("):tab>@ 12 : store(got$,intaptr%) : print(got$+"%("):tabHA 13 : store(got$,stringaptr%):print(got$+"$("):tabRA 5 : store(got$,procptr%) :print("PROC"+got$):tab\< 6 : store(got$,fnptr%):print("FN"+got$):tabfQ 7 : IF FNstore(FNnum(got,got$),numptr%):PRINT"Num "FNnum(got,got$);p2 10 : got$="""" quoteflag%=quoteflag%z ˄ startstmnt%=0 quoteflag%=0  what%=0 N got$=&DF got$=&EC got$=&C8+&92 got$=&C8+&90:byflag%=1& sys% (got$=&C8+&99)  ȕ ?pt%=32:pt%+=1: ?pt%=34 O sysname$="":ȕpt%?1<>34 pt%?1>31:pt%+=1:sysname$+=?pt%::pt%+=2A ș"XOS_SWINumberFromString",,sysname$ sysnum%;fil%V fil%1 warnmsg(,"SYS - can't translate """+sysname$+""" to a number") 1 dimflag%=2 : got$="," : dimflag%=1$ got$=&DE : dimflag%=1> got$="[" asmflag%+=1 : asmflag%=1 : startstmnt%=$A got$="]" asmflag%-=1 : asmflag%<0 : 1,"Too many ]".: got$=":" startstmnt%= : dimflag%=0 :byflag%=08: (what%=1 what%=2) lastwas% : startstmnt%=1B lastwas%=got$="."L V `jt:~+ num(v,v$): print numeric in decimalW turned off using hex when shorter - problems concatenating with variables (CDean)r IF v>=&80000000 AND v<=&7FFFFFFF :IF v=INTv :IF(LEN(STR$v)>LEN(STR$~INTv)+1) OR v=&80000000 : ="&"+STR$~INTv ptr% ?pt%=" " ) ptr%=pt%+1: ȕ ?ptr%=32: ptr%+=1: * "0123456789.E%&",?ptr%): v$+=" "=v$: get pt%-> prog ll%=addr of line end asmflag%,quoteflag% used  got$=item 0 : TOKEN 1 : real var( 2 : int var2 3 : string var< 4 : real array varF 5 : PROC+nameP 6 : FN+nameZ 7 : numconstd 8 : endoflinen 9 : endofprogx 10 : anythingelse (1 char) 11 : * command,DATA stmnt 12 : int array var 13 : string array var what%=-1 quoteflag%  pd%=?pt% got$=pd%:what%=10 pt%+=1< asmflag% =0 startstmnt%<>0 : ȕ?pt%=32 : pt%+=1 :" pt%=ll% :got$="":what%=8: pd%=?pt% Ȏ pd% ; &8D,&E4,&E5,&F7,&B8,&A0,&DC: warn%>=0 warn(pd%)  Ȏ pd% "8 &C6,&C7,&C8 : what%=0 : got$=pd%+pt%?1:pt%+=2,3 &F2 : what%=5 : pt%+=1:got$=gettext(var$)63 &A4 : what%=6 : pt%+=1:got$=gettext(var$)@I "&": what%=7 :got=gettext("0123456789ABCDEFabcdef"):got$=gotJ7 "%" : what%=7 : got=gettext("01"):got$=gotTY 48,49,50,51,52,53,54,55,56,57 : what%=7 : got$=gettext("0123456789."):got=got$^H "*" : star :what%=11:got$=geteol got$="*":what%=10:pt%+=1h$ &DC : what%=11:got$=geteolr& &F4 : what%=8:got$="":pt%=ll%| 3 (startstmnt%<>0) asmflag% pd%<>"."  ȕ ?pt%=32 : pt%+=1 : got$=""U ȕ ?pt%<>32 pt%0 got$,3)<>"opt" got$,3)<>"OPT" got$+=?pt% :pt%+=1  what%=10:* ȕ ?pt%=32 pt%" ":got$+=" "= ((got$,1))&DF)="B" : got$,1)<>" ":got$+=" "' startstmnt%=1 got$=" "+got$ v asmflag% (pd%&DF)="R": "0123456789",pt%?1) : pt%+=1:what%=7 : got$=gettext("0123456789"):got=got$ asmflag% &* sh$=case(?pt%+pt%?1+pt%?2)0 Ȏ sh$ :M "LSR","ASR","LSL","ROR","RRX","ASL" : got$=sh$:pt%+=3:what%=10D N XH byflag% pd%="B": pt%?1="Y" : what%=10:got$="BY ":pt%+=2bZ what%=-1 : var$,pd%) getvar : asmflag%: got$="PC" got$="pc": what%=10lX startstmnt%=0 : pd%="." : what%=7 : got$=gettext("0123456789."):got=got$v , asmflag% (pd%="\" pd%=";") , ȕ ?pt%<>":" pt%&7E  what%=0w IF pd%=&B8 AND pt%?1=ASC"P" AND INSTR(var$,CHR$pt%?2)=0 :got$=CHR$&B8+"P":pt%+=2 ELSE got$ =CHR$?pt%:pt%+=1 got$ =?pt%:pt%+=1  * what%=-1 :what%=10:got$=?pt%:pt%+=1: ݤstar I%  startstmnt% =* I%=0: : I%-=1: pt%?I%<>324Ȏ pt%?I% ># &F5,&8C,&8B,&CC,&7F,&85,&EA:H I%=R \ I%=fp=I%z: getvargot$=gettext(var$)Ȏ ?pt%  "%" : what%=2:pt%+=1 "$" : what%=3:pt%+=1  : what%=1 ?pt% ="("  what%=1:what%=4 what%=2:what%=12 what%=3:what%=13 pt%+=1: case(A$)$Ua%:a%=1 A$ :A$,a%,1)>="a" : A$,a%,1)<="z" : A$,a%,1)=(A$,a%,1)-&20).8=A$B:L gettext(var$)V T$`j T$+=?pt%t pt%+=1~var$,?pt%)=0& var$="0123456789." ?pt%="E"  pt%+=1: var$="0123456789" Ȏ ?pt%  "+","-": pt%+=1L var$,?pt%): T$+="E"+?(pt%-1)::T$+=?pt%:pt%+=1:var$,?pt%)=01 "0","1","2","3","4","5","6","7","8","9":2 T$+="E"::T$+=?pt%:pt%+=1:var$,?pt%)=0 =T$: geteol  T$ T$+=?pt%( pt%+=12 pt%=ll%<=T$F:Ptoken(A$)Z A%= A$d?nt=A$,1)ngtxN%=!tst:tok$=""ȕ ?N%<&7F ?N%>31 tok$+=?N%:N%+=1 A$=&B8+"P": tok$+="P"print(tok$):tok(A%)gt N%=!tst:extok(A%,?nt)gtN%=!tst:tok$="""ȕ ?N%<&7F ?N%>31, tok$+=?N%:N%+=16@print(tok$)JT:^ mashhnewline%=5rlinebase%=0|newbase%=heapptr%+16newptr%=newbase%startline7 put name of output file as 1st line REM statementputstr(&F4+">"+out$)print("REM>"+out$)startline pt%=buf%asmflag%=0.ȕ pt%?1<>&FF : while not end of program lineh%=pt%?1 linel%=pt%?2 linelen%=pt%?3 ll%=linelen%+pt%& pt%+=40; lastwas%=0 : last var was asm .var, so next is stmnt: quoteflag%=0D remflag%=0N what%=0XJ dimflag%=0 : TO catch DIM n 100 etc : REM now always zero ! (CDean)b byflag%=0l startstmnt%=v @%=5:PRINTnewline%; crit%=0 newsrcline%=1 gotsys%=8 ȕ pt%8 : while not at end of line get Ȏ what% [ WHEN 0 : PROCcolour(1):PROCtoken(got$):PROCcolour(7):IF got$=CHR$&F4:pt%=ll% 0 : token gotsys%= Ȏ got$ [ &7F,&C9,&CB,&CC,&CD,&DC,&DD : newsrcline% : newptr%-linebase%<>4:startline  colour(1) l got$=&7F:print("OTHERWISE"):putstr(&7F) got$<>&F4:token(got$):colour(7):putstr(got$) got$=&F4:pt%=ll% $ got$=&C8+&99 gotsys%=*I got$=&B3: ":;,'=/><(""",?pt%)=0: print(" "):putstr(" ")4 Ȏ got$ >J &E7,&EF,&DC,&CA,&CB,&CD,&D7,&E1,&F7,&F9,&FA,&EE,&E0 : crit%=1H R colour(7)\ 1 : real varf gotsys%=p> colour(5):print(newvar(got$,realptr%)):colour(7)z) putstr(newvar(got$,realptr%))= dimflag%=1 : print(" "):dimflag%=2 :putstr(" ") 2 : int var gotsys%== colour(5):print(newvar(got$,intptr%)):colour(7)( putstr(newvar(got$,intptr%))J dimflag%=1 pt%?1<>"!": print(" ") :dimflag%=2:putstr(" ") 3 : string var gotsys%=@ colour(5):print(newvar(got$,stringptr%)):colour(7)+ putstr(newvar(got$,stringptr%)) 4 : real array var gotsys%=? colour(5):print(newvar(got$,realaptr%)):colour(7)* putstr(newvar(got$,realaptr%))! dimflag%=1:dimflag%=2 12 : int array var$ gotsys%=.> colour(5):print(newvar(got$,intaptr%)):colour(7)8) putstr(newvar(got$,intaptr%))B! dimflag%=1:dimflag%=2L" 13 : string array varV gotsys%=`A colour(5):print(newvar(got$,stringaptr%)):colour(7)j, putstr(newvar(got$,stringaptr%))t! dimflag%=1:dimflag%=2~ 5 : procedure gotsys%=X colour(1):print("PROC"):colour(5):print(newvar(got$,procptr%)):colour(7). putstr(&F2+newvar(got$,procptr%)) 6 : function gotsys%=T colour(1):print("FN"):colour(5):print(newvar(got$,fnptr%)):colour(7), putstr(&A4+newvar(got$,fnptr%))! 7 : numeric constant gotsys%=6 colour(4):print(num(got,got$)):colour(7)! putstr(num(got,got$)) 10 : odds 'n ends= asmflag%: got$=":":ȕ newptr%?-1=32:newptr%-=1:  sys% gotsys% 3 quoteflag% got$<>"""" sysname$+=got$ (& print(got$): putstr(got$)2 < got$="""" F" quoteflag%=quoteflag%P quoteflag% Z sys% gotsys% dB ș"XOS_SWINumberFromString",,sysname$ sysnum%;fil%n (fil% 1) xE print(""""+sysname$+""""):putstr(""""+sysname$+"""") 7 colour(4):print((sysnum%)):colour(7)" putstr((sysnum%))  gotsys%= . print(togstr): putstr(togstr) " gotsys% sysname$=""  - 11 : *-command or data statement gotsys%=; newsrcline% : newptr%-linebase%<>4:startlineZ got$,1)>&7E :colour(1):token(got$,1)):colour(7):print(got$,(got$)-1)); got$,1)="*" colour(2):print(got$):colour(7)" putstr(got$):crit%=1, 6 startstmnt%=0@ quoteflag%=0 JY what%=0 : got$=&DF got$=&EC got$=&C8+&92 got$=&C8+&90:byflag%=1T1 dimflag%=2 : got$="," : dimflag%=1^$ got$=&DE : dimflag%=1h> got$="[" asmflag%+=1 : asmflag%=1 : startstmnt%=rA got$="]" asmflag%-=1 : asmflag%<0 : 1,"Too many ]"|M got$=":" startstmnt%= : dimflag%=0:byflag%=0:ȕ ?pt%=32:pt%+=1:: (what%=1 what%=2) lastwas% : startstmnt%=1 lastwas%=got$="."  newsrcline%=0 . concatenate with next line if sensible newptr%<>linebase%+4 A ȕ (newptr%?-1=32) (newptr%<>linebase%+4): newptr%-=1:5 crit%=0 (newptr%-linebase%+pt%?3) < 250 7 (newptr%?-1)<>":":print(":"):putstr(":") startline  &0' newptr%-linebase%<>4 : startline:Dlinebase%?1=&FF : ș"OS_File",10,out$,&FFB,,newbase%,linebase%+2DN:X newvar($searchbuf,A%)bvar$,ptr%l searchv!var$=$(!fadr+6+($(!fadr+5)))Q can't always discard trailing space, because of things like "IF A B=3" etc.Ȏ what%  2: var$+="%" 3: var$+="$" 4: var$+="(" 12: var$+="%(" 13: var$+="$(" ?pt%=" "  Ȏ what% B 1,5,6: extreme care after real or proc or function name+ ptr%=pt%+1: ȕ ?ptr%=32: ptr%+=1: E ?ptr%>&1F ?ptr%<&7F ":;,'=/><""",?ptr%)=0 : var$+=" "8 2: integers - beware brackets and indirection + ptr%=pt%+1: ȕ ?ptr%=32: ptr%+=1: & "(!?|$",?ptr%) : var$+=" " ' 3: strings - beware brackets*+ ptr%=pt%+1: ȕ ?ptr%=32: ptr%+=1: 4 ?ptr%="(" : var$+=" ">' OTHERWISE array so no problemsH R\ =var$f:p allocate memory from heapz allocate(s%) s%3 s%=(s% 3) +4heapptr%+=s%) heapptr%>heapend% 1,"Heap burst!"=heapptr%-s%:ݤstore($searchbuf,A%) searcholdnode%=!adroldnode% =1 :=0Anewnode%=allocate(($searchbuf)+defaultalloc%+7) : +1+1+1+4!oldnode%=newnode%!newnode%=0newnode%?4=1$(newnode%+5)=$searchbuf ?(newnode%+6+$searchbuf)=13=1$:. dummy8newnode%=allocate(16+128)B+A%=newnode%+8 newnode%+128+8:?A%=0:L#newnode%?8=&FF : don't allow @V!newnode%=0`newnode%?4=0j$(newnode%+5)="**"+13+13t=newnode%~:showlist(base%)@%=8\ "NEXT = "~!base%" Cyc = "base%?4" Old = "$(base%+5)" New = "$(($(base%+5))+6+base%) b%=!base% !base% : base%=!base% b%=0:4alocsingle(ptr%) : allocate single char names'base%,eod%,most%,mostptr%,nxtaloc%base%=ptr%  eod%=(!ptr%=0) v$=$(ptr%+5) v$=1 ( $(ptr%+5+v$+1)=v$27 ?(base%+8+nvar$,v$))=&FF : mark as allocated<' ptr%?4=0 : freq=0 if allocatedF print(v$+" ")P Z ptr%=!ptr%d eod%nptr%=base%x1nxtaloc%=2 : next var in string to allocate ?(base%+8+nxtaloc%)=0  ptr%=base% most%=0  eod%=(!ptr%=0)6 ptr%?4>most% : most%=ptr%?4:mostadr%=ptr%: ptr%=!ptr% eod%q most%<>0 :$(mostadr%+6+($(mostadr%+5)))=nvar$,nxtaloc%,1):mostadr%?4=0:print(nvar$,nxtaloc%,1)+" ") most%=1  nxtaloc%+=1 nxtaloc%=nvar$-9 most%=0:"alocdouble(base%), ptr%,first%,second%6ptr%=base%@9first%=2:second%=2 : positions in nvar$ to aloc fromJT eod%=(!ptr%=0)^. ptr%?4 $(ptr%+6+($(ptr%+5)))=aloc2h ptr%=!ptr%r eod%|: aloc2)a$=nvar$,first%,1)+nvar$,second%,1)8first%+=1: nvar$,first%,1)="0" first%=2:second%+=1' second%>nvar$ 1,"out of vars!"print(a$+" ")=a$:putstr(A$)$newptr%=A$newptr%+=A$9 newptr%>=heapend% 1,"Out of memory for new prog!" : startline (eoln:print(" "+(newline%),5)) &p linebase%: ȕ newptr%?-1=":": newptr%-=1::ȕ newptr%?-1=32:newptr%-=1::ȕ newptr%?-1=":": newptr%-=1: 0c linebase% : linebase%?3=newptr%-linebase% : newptr%-linebase%>252 1,"dest line too long!" :linebase%=newptr% D?newptr%=&0D Nnewptr%?1=newline%>>8 Xnewptr%?2=newline% b6newptr%?3=0 : avoid probs with space backtracking lnewptr%+=4 vnewline%+=5  : args( from$, to$)  buf%,p%,w%,env$,word$()  word$(20): buf% 256 #w%=0: from$="": to$="": log$="" ș "OS_GetEnv" env$ ȕ env$,1)=" "  env$=env$,(env$)-1)  ȕ env$ <> "" w%<=20  ȕ env$,1)=" "  env$=env$,2)! !  env$ <> "" !, p%=env$," "): p%=0 p%=1+(env$)! / word$(w%)=env$,p%-1): env$=env$,p%+1)!* w%+=1!4 !>!Hp%=0!Rȕ p% < w%!\ Ȏ toupper(word$(p%)) !f "-HELP":!p "Squish ("+ver$+")"'!zC "Syntax: *Squish -from -to [-warn ]"!B " [-list | -nolist] [-colour] [-nosys]"! ! "-FROM":! p% < w%-1 !" from$=word$(p%+1): p%+=1! ! "-TO":! p% < w%-1 ! to$=word$(p%+1): p%+=1! ! "-WARN":! p% < w%-1 !% warn%=(word$(p%+1)): p%+=1"/ warn%<0 warn%=0 warn%>2 warn%=2" " "-NOLIST":"$ log%=0". "-LIST":"8 p% < w%-1 "B) log$=word$(p%+1): p%+=1: log%=1"L "V "-COLOUR":"` colour%="j "-NOSYS""t sys%="~ " p%+=1""":"ݤtoupper(s$)" c%,p%" s$<>"" " p% = 1 (s$)" c%=(s$,p%,1))": c% >= ("a") c% <= ("z") s$,p%,1)=(c%-32)" p%"#=s$# :#colour(C%)# log%<0 colour%<>0: C%#(#2:#< ݤtogstr#F s$,ptr%#P! avoid concatenating strings#Z ?pt%=" " #d) ptr%=pt%+1: ȕ ?ptr%=32: ptr%+=1: #n ?ptr%="""": s$=" "#x#=s$#:#warn(tok%)# ptr%#ptr%=pt%+1#ȕ ?ptr%=32:ptr%+=1:#Ȏ tok% # &8D: line number#1 pt%+=3: warnmsg(,"Line number reference")# &E4,&E5: GOSUB, GOTO#D ?ptr%=&8D pt%=ptr%-1 warnmsg(,"Line number reference")# &F7: RESTORE# Ȏ ?ptr% $ &8D: pt%=ptr%-1$ &85,&0D,":": ok$:  warnmsg(,"RESTORE - check DATA layout is safe")$" $, &B8: TO(P)$6" pt%?1=("P") pt%?2<>32 $@ ptr%+=1$Ju warnmsg(,"TOP"+(?ptr%)+"... - assuming ""TO P"+(?ptr%)+"..."" - insert space if ""TOP "+(?ptr%)+"...""")$T $^ &A0: EVAL$h> warnmsg(,"EVAL - check not evaluating variable names")$r &DC: DATA$|F warnmsg(,"DATA - check READ will not evaluate variable names")$$$:$warnmsg(errflag%,msg$)$ errflag% $L errors%+=1: diag$="** ERROR at line "+(linel%+256*lineh%)+": "+msg$$2 eoln:print(diag$):eoln: log%>=0 diag$$$W warn%: warnings%+=1: diag$="** WARNING at line "+(linel%+256*lineh%)+": "+msg$$; warn%: eoln:print(diag$):eoln: log%>=0 diag$$$$:%print(str$)% Ȏ % log%<0:%& (str$);%0 log%>0:%: #log%,str$;%D%N!pos%+=(str$): pos%>70 eoln%X%b:%l eoln%v Ȏ % log%<0:% log%>0:#log%,10;%% pos%=0%%:%tab: !adr=1: %1 pos%>70 eoln print((256-pos%)16," "))%%:% endlog% log%>0 #log%:log%=0%