home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 2
/
FFMCD02.bin
/
new
/
dev
/
misc
/
p2c
/
examples
/
basic.p
< prev
next >
Wrap
Text File
|
1993-12-21
|
67KB
|
2,247 lines
$ sysprog, ucsd, heap_dispose, partial_eval $
{$ debug$}
{ Added some hacks to allow interrupting BASIC programs by pressing
CTRL-C on the console. This will only work on the AMIGA. It will
slow down a bit the program execution but I think it is worth the price.
- Günther -
}
{EMBED
#ifdef MCH_AMIGA
# include <dos/dos.h>
# include <clib/exec_protos.h>
# ifdef AZTEC_C
# include <pragmas/exec_lib.h>
extern int Enable_Abort;
# endif
#endif
}
program basic(input, output);
const
checking = true;
varnamelen = 20;
maxdims = 4;
type
varnamestring = string[varnamelen];
string255 = string[255];
string255ptr = ^string255;
tokenkinds = (tokvar, toknum, tokstr, toksnerr,
tokplus, tokminus, toktimes, tokdiv, tokup, toklp, tokrp,
tokcomma, toksemi, tokcolon, tokeq, toklt, tokgt,
tokle, tokge, tokne,
tokand, tokor, tokxor, tokmod, toknot, toksqr, toksqrt, toksin,
tokcos, toktan, tokarctan, toklog, tokexp, tokabs, toksgn,
tokstr_, tokval, tokchr_, tokasc, toklen, tokmid_, tokpeek,
tokrem, toklet, tokprint, tokinput, tokgoto, tokif, tokend,
tokstop, tokfor, toknext, tokwhile, tokwend, tokgosub,
tokreturn, tokread, tokdata, tokrestore, tokgotoxy, tokon,
tokdim, tokpoke,
toklist, tokrun, toknew, tokload, tokmerge, toksave, tokbye,
tokdel, tokrenum,
tokthen, tokelse, tokto, tokstep);
realptr = ^real;
basicstring = string255ptr;
stringptr = ^basicstring;
numarray = array[0..maxint] of real;
arrayptr = ^numarray;
strarray = array[0..maxint] of basicstring;
strarrayptr = ^strarray;
tokenptr = ^tokenrec;
lineptr = ^linerec;
varptr = ^varrec;
loopptr = ^looprec;
tokenrec =
record
next : tokenptr;
case kind : tokenkinds of
tokvar : (vp : varptr);
toknum : (num : real);
tokstr, tokrem : (sp : string255ptr);
toksnerr : (snch : char);
end;
linerec =
record
num, num2 : integer;
txt : tokenptr;
next : lineptr;
end;
varrec =
record
name : varnamestring;
next : varptr;
dims : array [1..maxdims] of integer;
numdims : 0..maxdims;
case stringvar : boolean of
false : (arr : arrayptr; val : realptr; rv : real);
true : (sarr : strarrayptr; sval : stringptr; sv : basicstring);
end;
valrec =
record
case stringval : boolean of
false : (val : real);
true : (sval : basicstring);
end;
loopkind = (forloop, whileloop, gosubloop);
looprec =
record
next : loopptr;
homeline : lineptr;
hometok : tokenptr;
case kind : loopkind of
forloop :
( vp : varptr;
max, step : real );
end;
var
inbuf : string255ptr;
linebase : lineptr;
varbase : varptr;
loopbase : loopptr;
curline : integer;
stmtline, dataline : lineptr;
stmttok, datatok, buf : tokenptr;
exitflag : boolean;
excp_line ['EXCP_LINE'] : integer;
$if not checking$
$range off$
$end$
procedure misc_getioerrmsg(var s : string; io : integer);
external;
procedure misc_printerror(er, io : integer);
external;
function asm_iand(a, b : integer) : integer;
external;
function asm_ior(a, b : integer) : integer;
external;
procedure hpm_new(var p : anyptr; size : integer);
external;
procedure hpm_dispose(var p : anyptr; size : integer);
external;
procedure restoredata;
begin
dataline := nil;
datatok := nil;
end;
procedure clearloops;
var
l : loopptr;
begin
while loopbase <> nil do
begin
l := loopbase^.next;
dispose(loopbase);
loopbase := l;
end;
end;
function arraysize(v : varptr) : integer;
var
i, j : integer;
begin
with v^ do
begin
if stringvar then
j := 4
else
j := 8;
for i := 1 to numdims do
j := j * dims[i];
end;
arraysize := j;
end;
procedure clearvar(v : varptr);
begin
with v^ do
begin
if numdims <> 0 then
hpm_dispose(arr, arraysize(v))
else if stringvar and (sv <> nil) then
dispose(sv);
numdims := 0;
if stringvar then
begin
sv := nil;
sval := addr(sv);
end
else
begin
rv := 0;
val := addr(rv);
end;
end;
end;
procedure clearvars;
var
v : varptr;
begin
v := varbase;
while v <> nil do
begin
clearvar(v);
v := v^.next;
end;
end;
function numtostr(n : real) : string255;
var
s : string255;
i : integer;
begin
setstrlen(s, 255);
if (n <> 0) and (abs(n) < 1e-2) or (abs(n) >= 1e12) then
begin
strwrite(s, 1, i, n);
setstrlen(s, i-1);
numtostr := s;
end
else
begin
strwrite(s, 1, i, n:30:10);
repeat
i := i - 1;
until s[i] <> '0';
if s[i] = '.' then
i := i - 1;
setstrlen(s, i);
numtostr := strltrim(s);
end;
end;
procedure parse(inbuf : string255ptr; var buf : tokenptr);
const
toklength = 20;
type
chset = set of char;
const
idchars = chset ['A'..'Z','a'..'z','0'..'9','_','$'];
var
i, j, k : integer;
token : string[toklength];
t, tptr : tokenptr;
v : varptr;
ch : char;
n, d, d1 : real;
begin
tptr := nil;
buf := nil;
i := 1;
repeat
ch := ' ';
while (i <= strlen(inbuf^)) and (ch = ' ') do
begin
ch := inbuf^[i];
i := i + 1;
end;
if ch <> ' ' then
begin
new(t);
if tptr = nil then
buf := t
else
tptr^.next := t;
tptr := t;
t^.next := nil;
case ch of
'A'..'Z', 'a'..'z' :
begin
i := i - 1;
j := 0;
setstrlen(token, strmax(token));
while (i <= strlen(inbuf^)) and (inbuf^[i] in idchars) do
begin
if j < toklength then
begin
j := j + 1;
token[j] := inbuf^[i];
end;
i := i + 1;
end;
setstrlen(token, j);
if (token = 'and') or (token = 'AND') then t^.kind := tokand
else if (token = 'or') or (token = 'OR') then t^.kind := tokor
else if (token = 'xor') or (token = 'XOR') then t^.kind := tokxor
else if (token = 'not') or (token = 'NOT') then t^.kind := toknot
else if (token = 'mod') or (token = 'MOD') then t^.kind := tokmod
else if (token = 'sqr') or (token = 'SQR') then t^.kind := toksqr
else if (token = 'sqrt') or (token = 'SQRT') then t^.kind := toksqrt
else if (token = 'sin') or (token = 'SIN') then t^.kind := toksin
else if (token = 'cos') or (token = 'COS') then t^.kind := tokcos
else if (token = 'tan') or (token = 'TAN') then t^.kind := toktan
else if (token = 'arctan') or (token = 'ARCTAN') then t^.kind := tokarctan
else if (token = 'log') or (token = 'LOG') then t^.kind := toklog
else if (token = 'exp') or (token = 'EXP') then t^.kind := tokexp
else if (token = 'abs') or (token = 'ABS') then t^.kind := tokabs
else if (token = 'sgn') or (token = 'SGN') then t^.kind := toksgn
else if (token = 'str$') or (token = 'STR$') then t^.kind := tokstr_
else if (token = 'val') or (token = 'VAL') then t^.kind := tokval
else if (token = 'chr$') or (token = 'CHR$') then t^.kind := tokchr_
else if (token = 'asc') or (token = 'ASC') then t^.kind := tokasc
else if (token = 'len') or (token = 'LEN') then t^.kind := toklen
else if (token = 'mid$') or (token = 'MID$') then t^.kind := tokmid_
else if (token = 'peek') or (token = 'PEEK') then t^.kind := tokpeek
else if (token = 'let') or (token = 'LET') then t^.kind := toklet
else if (token = 'print') or (token = 'PRINT') then t^.kind := tokprint
else if (token = 'input') or (token = 'INPUT') then t^.kind := tokinput
else if (token = 'goto') or (token = 'GOTO') then t^.kind := tokgoto
else if (token = 'go to') or (token = 'GO TO') then t^.kind := tokgoto
else if (token = 'if') or (token = 'IF') then t^.kind := tokif
else if (token = 'end') or (token = 'END') then t^.kind := tokend
else if (token = 'stop') or (token = 'STOP') then t^.kind := tokstop
else if (token = 'for') or (token = 'FOR') then t^.kind := tokfor
else if (token = 'next') or (token = 'NEXT') then t^.kind := toknext
else if (token = 'while') or (token = 'WHILE') then t^.kind := tokwhile
else if (token = 'wend') or (token = 'WEND') then t^.kind := tokwend
else if (token = 'gosub') or (token = 'GOSUB') then t^.kind := tokgosub
else if (token = 'return') or (token = 'RETURN') then t^.kind := tokreturn
else if (token = 'read') or (token = 'READ') then t^.kind := tokread
else if (token = 'data') or (token = 'DATA') then t^.kind := tokdata
else if (token = 'restore') or (token = 'RESTORE') then t^.kind := tokrestore
else if (token = 'gotoxy') or (token = 'GOTOXY') then t^.kind := tokgotoxy
else if (token = 'on') or (token = 'ON') then t^.kind := tokon
else if (token = 'dim') or (token = 'DIM') then t^.kind := tokdim
else if (token = 'poke') or (token = 'POKE') then t^.kind := tokpoke
else if (token = 'list') or (token = 'LIST') then t^.kind := toklist
else if (token = 'run') or (token = 'RUN') then t^.kind := tokrun
else if (token = 'new') or (token = 'NEW') then t^.kind := toknew
else if (token = 'load') or (token = 'LOAD') then t^.kind := tokload
else if (token = 'merge') or (token = 'MERGE') then t^.kind := tokmerge
else if (token = 'save') or (token = 'SAVE') then t^.kind := toksave
else if (token = 'bye') or (token = 'BYE') then t^.kind := tokbye
else if (token = 'quit') or (token = 'QUIT') then t^.kind := tokbye
else if (token = 'del') or (token = 'DEL') then t^.kind := tokdel
else if (token = 'renum') or (token = 'RENUM') then t^.kind := tokrenum
else if (token = 'then') or (token = 'THEN') then t^.kind := tokthen
else if (token = 'else') or (token = 'ELSE') then t^.kind := tokelse
else if (token = 'to') or (token = 'TO') then t^.kind := tokto
else if (token = 'step') or (token = 'STEP') then t^.kind := tokstep
else if (token = 'rem') or (token = 'REM') then
begin
t^.kind := tokrem;
new(t^.sp);
t^.sp^ := str(inbuf^, i, strlen(inbuf^)-i+1);
i := strlen(inbuf^)+1;
end
else
begin
t^.kind := tokvar;
v := varbase;
while (v <> nil) and (v^.name <> token) do
v := v^.next;
if v = nil then
begin
new(v);
v^.next := varbase;
varbase := v;
v^.name := token;
v^.numdims := 0;
if token[strlen(token)] = '$' then
begin
v^.stringvar := true;
v^.sv := nil;
v^.sval := addr(v^.sv);
end
else
begin
v^.stringvar := false;
v^.rv := 0;
v^.val := addr(v^.rv);
end;
end;
t^.vp := v;
end;
end;
'"', '''' :
begin
t^.kind := tokstr;
new(t^.sp);
setstrlen(t^.sp^, 255);
j := 0;
while (i <= strlen(inbuf^)) and (inbuf^[i] <> ch) do
begin
j := j + 1;
t^.sp^[j] := inbuf^[i];
i := i + 1;
end;
setstrlen(t^.sp^, j);
i := i + 1;
end;
'0'..'9', '.' :
begin
t^.kind := toknum;
n := 0;
d := 1;
d1 := 1;
i := i - 1;
while (i <= strlen(inbuf^)) and ((inbuf^[i] in ['0'..'9'])
or ((inbuf^[i] = '.') and (d1 = 1))) do
begin
if inbuf^[i] = '.' then
d1 := 10
else
begin
n := n * 10 + ord(inbuf^[i]) - 48;
d := d * d1;
end;
i := i + 1;
end;
n := n / d;
if (i <= strlen(inbuf^)) and (inbuf^[i] in ['e','E']) then
begin
i := i + 1;
d1 := 10;
if (i <= strlen(inbuf^)) and (inbuf^[i] in ['+','-']) then
begin
if inbuf^[i] = '-' then
d1 := 0.1;
i := i + 1;
end;
j := 0;
while (i <= strlen(inbuf^)) and (inbuf^[i] in ['0'..'9']) do
begin
j := j * 10 + ord(inbuf^[i]) - 48;
i := i + 1;
end;
for k := 1 to j do
n := n * d1;
end;
t^.num := n;
end;
'+' : t^.kind := tokplus;
'-' : t^.kind := tokminus;
'*' : t^.kind := toktimes;
'/' : t^.kind := tokdiv;
'^' : t^.kind := tokup;
'(', '[' : t^.kind := toklp;
')', ']' : t^.kind := tokrp;
',' : t^.kind := tokcomma;
';' : t^.kind := toksemi;
':' : t^.kind := tokcolon;
'?' : t^.kind := tokprint;
'=' : t^.kind := tokeq;
'<' :
begin
if (i <= strlen(inbuf^)) and (inbuf^[i] = '=') then
begin
t^.kind := tokle;
i := i + 1;
end
else if (i <= strlen(inbuf^)) and (inbuf^[i] = '>') then
begin
t^.kind := tokne;
i := i + 1;
end
else
t^.kind := toklt;
end;
'>' :
begin
if (i <= strlen(inbuf^)) and (inbuf^[i] = '=') then
begin
t^.kind := tokge;
i := i + 1;
end
else
t^.kind := tokgt;
end;
otherwise
begin
t^.kind := toksnerr;
t^.snch := ch;
end;
end;
end;
until i > strlen(inbuf^);
end;
procedure listtokens(var f : text; buf : tokenptr);
var
ltr, ltr0 : boolean;
begin
ltr := false;
while buf <> nil do
begin
if buf^.kind in [tokvar, toknum, toknot..tokrenum] then
begin
if ltr then write(f, ' ');
ltr := (buf^.kind <> toknot);
end
else
ltr := false;
case buf^.kind of
tokvar : write(f, buf^.vp^.name);
toknum : write(f, numtostr(buf^.num));
tokstr : write(f, '"', buf^.sp^, '"');
toksnerr : write(f, '{', buf^.snch, '}');
tokplus : write(f, '+');
tokminus : write(f, '-');
toktimes : write(f, '*');
tokdiv : write(f, '/');
tokup : write(f, '^');
toklp : write(f, '(');
tokrp : write(f, ')');
tokcomma : write(f, ',');
toksemi : write(f, ';');
tokcolon : write(f, ' : ');
tokeq : write(f, ' = ');
toklt : write(f, ' < ');
tokgt : write(f, ' > ');
tokle : write(f, ' <= ');
tokge : write(f, ' >= ');
tokne : write(f, ' <> ');
tokand : write(f, ' AND ');
tokor : write(f, ' OR ');
tokxor : write(f, ' XOR ');
tokmod : write(f, ' MOD ');
toknot : write(f, 'NOT ');
toksqr : write(f, 'SQR');
toksqrt : write(f, 'SQRT');
toksin : write(f, 'SIN');
tokcos : write(f, 'COS');
toktan : write(f, 'TAN');
tokarctan : write(f, 'ARCTAN');
toklog : write(f, 'LOG');
tokexp : write(f, 'EXP');
tokabs : write(f, 'ABS');
toksgn : write(f, 'SGN');
tokstr_ : write(f, 'STR$');
tokval : write(f, 'VAL');
tokchr_ : write(f, 'CHR$');
tokasc : write(f, 'ASC');
toklen : write(f, 'LEN');
tokmid_ : write(f, 'MID$');
tokpeek : write(f, 'PEEK');
toklet : write(f, 'LET');
tokprint : write(f, 'PRINT');
tokinput : write(f, 'INPUT');
tokgoto : write(f, 'GOTO');
tokif : write(f, 'IF');
tokend : write(f, 'END');
tokstop : write(f, 'STOP');
tokfor : write(f, 'FOR');
toknext : write(f, 'NEXT');
tokwhile : write(f, 'WHILE');
tokwend : write(f, 'WEND');
tokgosub : write(f, 'GOSUB');
tokreturn : write(f, 'RETURN');
tokread : write(f, 'READ');
tokdata : write(f, 'DATA');
tokrestore : write(f, 'RESTORE');
tokgotoxy : write(f, 'GOTOXY');
tokon : write(f, 'ON');
tokdim : write(f, 'DIM');
tokpoke : write(f, 'POKE');
toklist : write(f, 'LIST');
tokrun : write(f, 'RUN');
toknew : write(f, 'NEW');
tokload : write(f, 'LOAD');
tokmerge : write(f, 'MERGE');
toksave : write(f, 'SAVE');
tokdel : write(f, 'DEL');
tokbye : write(f, 'BYE');
tokrenum : write(f, 'RENUM');
tokthen : write(f, ' THEN ');
tokelse : write(f, ' ELSE ');
tokto : write(f, ' TO ');
tokstep : write(f, ' STEP ');
tokrem : write(f, 'REM', buf^.sp^);
end;
buf := buf^.next;
end;
end;
procedure disposetokens(var tok : tokenptr);
var
tok1 : tokenptr;
begin
while tok <> nil do
begin
tok1 := tok^.next;
if tok^.kind in [tokstr, tokrem] then
dispose(tok^.sp);
dispose(tok);
tok := tok1;
end;
end;
procedure parseinput(var buf : tokenptr);
var
l, l0, l1 : lineptr;
begin
inbuf^ := strltrim(inbuf^);
curline := 0;
while (strlen(inbuf^) <> 0) and (inbuf^[1] in ['0'..'9']) do
begin
curline := curline * 10 + ord(inbuf^[1]) - 48;
strdelete(inbuf^, 1, 1);
end;
parse(inbuf, buf);
if curline <> 0 then
begin
l := linebase;
l0 := nil;
while (l <> nil) and (l^.num < curline) do
begin
l0 := l;
l := l^.next;
end;
if (l <> nil) and (l^.num = curline) then
begin
l1 := l;
l := l^.next;
if l0 = nil then
linebase := l
else
l0^.next := l;
disposetokens(l1^.txt);
dispose(l1);
end;
if buf <> nil then
begin
new(l1);
l1^.next := l;
if l0 = nil then
linebase := l1
else
l0^.next := l1;
l1^.num := curline;
l1^.txt := buf;
end;
clearloops;
restoredata;
end;
end;
procedure errormsg(s : string255);
begin
write(#7, s);
escape(42);
end;
procedure snerr;
begin
errormsg('Syntax error');
end;
procedure tmerr;
begin
errormsg('Type mismatch error');
end;
procedure badsubscr;
begin
errormsg('Bad subscript');
end;
procedure exec;
var
gotoflag, elseflag : boolean;
t : tokenptr;
ioerrmsg : string255ptr;
function factor : valrec;
forward;
function expr : valrec;
forward;
function realfactor : real;
var
n : valrec;
begin
n := factor;
if n.stringval then tmerr;
realfactor := n.val;
end;
function strfactor : basicstring;
var
n : valrec;
begin
n := factor;
if not n.stringval then tmerr;
strfactor := n.sval;
end;
function stringfactor : string255;
var
n : valrec;
begin
n := factor;
if not n.stringval then tmerr;
stringfactor := n.sval^;
dispose(n.sval);
end;
function intfactor : integer;
begin
intfactor := round(realfactor);
end;
function realexpr : real;
var
n : valrec;
begin
n := expr;
if n.stringval then tmerr;
realexpr := n.val;
end;
function strexpr : basicstring;
var
n : valrec;
begin
n := expr;
if not n.stringval then tmerr;
strexpr := n.sval;
end;
function stringexpr : string255;
var
n : valrec;
begin
n := expr;
if not n.stringval then tmerr;
stringexpr := n.sval^;
dispose(n.sval);
end;
function intexpr : integer;
begin
intexpr := round(realexpr);
end;
procedure require(k : tokenkinds);
begin
if (t = nil) or (t^.kind <> k) then
snerr;
t := t^.next;
end;
procedure skipparen;
label 1;
begin
repeat
if t = nil then snerr;
if (t^.kind = tokrp) or (t^.kind = tokcomma) then
goto 1;
if t^.kind = toklp then
begin
t := t^.next;
skipparen;
end;
t := t^.next;
until false;
1 :
end;
function findvar : varptr;
var
v : varptr;
i, j, k : integer;
tok : tokenptr;
begin
if (t = nil) or (t^.kind <> tokvar) then snerr;
v := t^.vp;
t := t^.next;
if (t <> nil) and (t^.kind = toklp) then
with v^ do
begin
if numdims = 0 then
begin
tok := t;
i := 0;
j := 1;
repeat
if i >= maxdims then badsubscr;
t := t^.next;
skipparen;
j := j * 11;
i := i + 1;
dims[i] := 11;
until t^.kind = tokrp;
numdims := i;
if stringvar then
begin
hpm_new(sarr, j*4);
for k := 0 to j-1 do
sarr^[k] := nil;
end
else
begin
hpm_new(arr, j*8);
for k := 0 to j-1 do
arr^[k] := 0;
end;
t := tok;
end;
k := 0;
t := t^.next;
for i := 1 to numdims do
begin
j := intexpr;
if (j < 0) or (j >= dims[i]) then
badsubscr;
k := k * dims[i] + j;
if i < numdims then
require(tokcomma);
end;
require(tokrp);
if stringvar then
sval := addr(sarr^[k])
else
val := addr(arr^[k]);
end
else
begin
if v^.numdims <> 0 then
badsubscr;
end;
findvar := v;
end;
function inot(i : integer) : integer;
begin
inot := -1 - i;
end;
function ixor(a, b : integer) : integer;
begin
ixor := asm_ior(asm_iand(a, inot(b)), asm_iand(inot(a), b));
end;
function factor : valrec;
var
v : varptr;
facttok : tokenptr;
n : valrec;
i, j : integer;
tok, tok1 : tokenptr;
s : basicstring;
trick :
record
case boolean of
true : (i : integer);
false : (c : ^char);
end;
begin
if t = nil then snerr;
facttok := t;
t := t^.next;
n.stringval := false;
case facttok^.kind of
toknum :
n.val := facttok^.num;
tokstr :
begin
n.stringval := true;
new(n.sval);
n.sval^ := facttok^.sp^;
end;
tokvar :
begin
t := facttok;
v := findvar;
n.stringval := v^.stringvar;
if n.stringval then
begin
new(n.sval);
n.sval^ := v^.sval^^;
end
else
n.val := v^.val^;
end;
toklp :
begin
n := expr;
require(tokrp);
end;
tokminus :
n.val := - realfactor;
tokplus :
n.val := realfactor;
toknot :
n.val := inot(intfactor);
toksqr :
n.val := sqr(realfactor);
toksqrt :
n.val := sqrt(realfactor);
toksin :
n.val := sin(realfactor);
tokcos :
n.val := cos(realfactor);
toktan :
begin
n.val := realfactor;
n.val := sin(n.val) / cos(n.val);
end;
tokarctan :
n.val := arctan(realfactor);
toklog:
n.val := ln(realfactor);
tokexp :
n.val := exp(realfactor);
tokabs :
n.val := abs(realfactor);
toksgn :
begin
n.val := realfactor;
n.val := ord(n.val > 0) - ord(n.val < 0);
end;
tokstr_ :
begin
n.stringval := true;
new(n.sval);
n.sval^ := numtostr(realfactor);
end;
tokval :
begin
s := strfactor;
tok1 := t;
parse(s, t);
tok := t;
if tok = nil then
n.val := 0
else
n := expr;
disposetokens(tok);
t := tok1;
dispose(s);
end;
tokchr_ :
begin
n.stringval := true;
new(n.sval);
n.sval^ := ' ';
n.sval^[1] := chr(intfactor);
end;
tokasc :
begin
s := strfactor;
if strlen(s^) = 0 then
n.val := 0
else
n.val := ord(s^[1]);
dispose(s);
end;
tokmid_ :
begin
n.stringval := true;
require(toklp);
n.sval := strexpr;
require(tokcomma);
i := intexpr;
if i < 1 then i := 1;
j := 255;
if (t <> nil) and (t^.kind = tokcomma) then
begin
t := t^.next;
j := intexpr;
end;
if j > strlen(n.sval^)-i+1 then
j := strlen(n.sval^)-i+1;
if i > strlen(n.sval^) then
n.sval^ := ''
else
n.sval^ := str(n.sval^, i, j);
require(tokrp);
end;
toklen :
begin
s := strfactor;
n.val := strlen(s^);
dispose(s);
end;
tokpeek :
begin
$range off$
trick.i := intfactor;
n.val := ord(trick.c^);
$if checking$ $range on$ $end$
end;
otherwise
snerr;
end;
factor := n;
end;
function upexpr : valrec;
var
n, n2 : valrec;
begin
n := factor;
while (t <> nil) and (t^.kind = tokup) do
begin
if n.stringval then tmerr;
t := t^.next;
n2 := upexpr;
if n2.stringval then tmerr;
if n.val < 0 then
begin
if n2.val <> trunc(n2.val) then n.val := ln(n.val);
n.val := exp(n2.val * ln(-n.val));
if odd(trunc(n2.val)) then
n.val := - n.val;
end
else
n.val := exp(n2.val * ln(n.val));
end;
upexpr := n;
end;
function term : valrec;
var
n, n2 : valrec;
k : tokenkinds;
begin
n := upexpr;
while (t <> nil) and (t^.kind in [toktimes, tokdiv, tokmod]) do
begin
k := t^.kind;
t := t^.next;
n2 := upexpr;
if n.stringval or n2.stringval then tmerr;
if k = tokmod then
n.val := round(n.val) mod round(n2.val)
else if k = toktimes then
n.val := n.val * n2.val
else
n.val := n.val / n2.val;
end;
term := n;
end;
function sexpr : valrec;
var
n, n2 : valrec;
k : tokenkinds;
begin
n := term;
while (t <> nil) and (t^.kind in [tokplus, tokminus]) do
begin
k := t^.kind;
t := t^.next;
n2 := term;
if n.stringval <> n2.stringval then tmerr;
if k = tokplus then
if n.stringval then
begin
n.sval^ := n.sval^ + n2.sval^;
dispose(n2.sval);
end
else
n.val := n.val + n2.val
else
if n.stringval then
tmerr
else
n.val := n.val - n2.val;
end;
sexpr := n;
end;
function relexpr : valrec;
var
n, n2 : valrec;
f : boolean;
k : tokenkinds;
begin
n := sexpr;
while (t <> nil) and (t^.kind in [tokeq..tokne]) do
begin
k := t^.kind;
t := t^.next;
n2 := sexpr;
if n.stringval <> n2.stringval then tmerr;
if n.stringval then
begin
f := ((n.sval^ = n2.sval^) and (k in [tokeq, tokge, tokle]) or
(n.sval^ < n2.sval^) and (k in [toklt, tokle, tokne]) or
(n.sval^ > n2.sval^) and (k in [tokgt, tokge, tokne]));
dispose(n.sval);
dispose(n2.sval);
end
else
f := ((n.val = n2.val) and (k in [tokeq, tokge, tokle]) or
(n.val < n2.val) and (k in [toklt, tokle, tokne]) or
(n.val > n2.val) and (k in [tokgt, tokge, tokne]));
n.stringval := false;
n.val := ord(f);
end;
relexpr := n;
end;
function andexpr : valrec;
var
n, n2 : valrec;
begin
n := relexpr;
while (t <> nil) and (t^.kind = tokand) do
begin
t := t^.next;
n2 := relexpr;
if n.stringval or n2.stringval then tmerr;
n.val := asm_iand(trunc(n.val), trunc(n2.val));
end;
andexpr := n;
end;
function expr : valrec;
var
n, n2 : valrec;
k : tokenkinds;
begin
n := andexpr;
while (t <> nil) and (t^.kind in [tokor, tokxor]) do
begin
k := t^.kind;
t := t^.next;
n2 := andexpr;
if n.stringval or n2.stringval then tmerr;
if k = tokor then
n.val := asm_ior(trunc(n.val), trunc(n2.val))
else
n.val := ixor(trunc(n.val), trunc(n2.val));
end;
expr := n;
end;
procedure checkextra;
begin
if t <> nil then
errormsg('Extra information on line');
end;
function iseos : boolean;
begin
iseos := (t = nil) or (t^.kind in [tokcolon, tokelse]);
end;
procedure skiptoeos;
begin
while not iseos do
t := t^.next;
end;
function findline(n : integer) : lineptr;
var
l : lineptr;
begin
l := linebase;
while (l <> nil) and (l^.num <> n) do
l := l^.next;
findline := l;
end;
function mustfindline(n : integer) : lineptr;
var
l : lineptr;
begin
l := findline(n);
if l = nil then
errormsg('Undefined line');
mustfindline := l;
end;
procedure cmdend;
begin
stmtline := nil;
t := nil;
end;
procedure cmdnew;
var
p : anyptr;
begin
cmdend;
clearloops;
restoredata;
while linebase <> nil do
begin
p := linebase^.next;
disposetokens(linebase^.txt);
dispose(linebase);
linebase := p;
end;
while varbase <> nil do
begin
p := varbase^.next;
if varbase^.stringvar then
if varbase^.sval^ <> nil then
dispose(varbase^.sval^);
dispose(varbase);
varbase := p;
end;
end;
procedure cmdlist;
var
l : lineptr;
n1, n2 : integer;
begin
repeat
n1 := 0;
n2 := maxint;
if (t <> nil) and (t^.kind = toknum) then
begin
n1 := trunc(t^.num);
t := t^.next;
if (t = nil) or (t^.kind <> tokminus) then
n2 := n1;
end;
if (t <> nil) and (t^.kind = tokminus) then
begin
t := t^.next;
if (t <> nil) and (t^.kind = toknum) then
begin
n2 := trunc(t^.num);
t := t^.next;
end
else
n2 := maxint;
end;
l := linebase;
while (l <> nil) and (l^.num <= n2) do
begin
if (l^.num >= n1) then
begin
write(l^.num:1, ' ');
listtokens(output, l^.txt);
writeln;
end;
l := l^.next;
end;
if not iseos then
require(tokcomma);
until iseos;
end;
procedure cmdload(merging : boolean; name : string255);
var
f : text;
buf : tokenptr;
begin
if not merging then
cmdnew;
reset(f, name + '.TEXT', 'shared');
while not eof(f) do
begin
readln(f, inbuf^);
parseinput(buf);
if curline = 0 then
begin
writeln('Bad line in file');
disposetokens(buf);
end;
end;
close(f);
end;
procedure cmdrun;
var
l : lineptr;
i : integer;
s : string255;
begin
l := linebase;
if not iseos then
begin
if t^.kind = toknum then
l := mustfindline(intexpr)
else
begin
s := stringexpr;
i := 0;
if not iseos then
begin
require(tokcomma);
i := intexpr;
end;
checkextra;
cmdload(false, s);
if i = 0 then
l := linebase
else
l := mustfindline(i)
end
end;
stmtline := l;
gotoflag := true;
clearvars;
clearloops;
restoredata;
end;
procedure cmdsave;
var
f : text;
l : lineptr;
begin
rewrite(f, stringexpr + '.TEXT');
l := linebase;
while l <> nil do
begin
write(f, l^.num:1, ' ');
listtokens(f, l^.txt);
writeln(f);
l := l^.next;
end;
close(f, 'save');
end;
procedure cmdbye;
begin
exitflag := true;
end;
procedure cmddel;
var
l, l0, l1 : lineptr;
n1, n2 : integer;
begin
repeat
if iseos then snerr;
n1 := 0;
n2 := maxint;
if (t <> nil) and (t^.kind = toknum) then
begin
n1 := trunc(t^.num);
t := t^.next;
if (t = nil) or (t^.kind <> tokminus) then
n2 := n1;
end;
if (t <> nil) and (t^.kind = tokminus) then
begin
t := t^.next;
if (t <> nil) and (t^.kind = toknum) then
begin
n2 := trunc(t^.num);
t := t^.next;
end
else
n2 := maxint;
end;
l := linebase;
l0 := nil;
while (l <> nil) and (l^.num <= n2) do
begin
l1 := l^.next;
if (l^.num >= n1) then
begin
if l = stmtline then
begin
cmdend;
clearloops;
restoredata;
end;
if l0 = nil then
linebase := l^.next
else
l0^.next := l^.next;
disposetokens(l^.txt);
dispose(l);
end
else
l0 := l;
l := l1;
end;
if not iseos then
require(tokcomma);
until iseos;
end;
procedure cmdrenum;
var
l, l1 : lineptr;
tok : tokenptr;
lnum, step : integer;
begin
lnum := 10;
step := 10;
if not iseos then
begin
lnum := intexpr;
if not iseos then
begin
require(tokcomma);
step := intexpr;
end;
end;
l := linebase;
if l <> nil then
begin
while l <> nil do
begin
l^.num2 := lnum;
lnum := lnum + step;
l := l^.next;
end;
l := linebase;
repeat
tok := l^.txt;
repeat
if tok^.kind in [tokgoto, tokgosub, tokthen, tokelse,
tokrun, toklist, tokrestore, tokdel] then
while (tok^.next <> nil) and (tok^.next^.kind = toknum) do
begin
tok := tok^.next;
lnum := round(tok^.num);
l1 := linebase;
while (l1 <> nil) and (l1^.num <> lnum) do
l1 := l1^.next;
if l1 = nil then
writeln('Undefined line ', lnum:1, ' in line ', l^.num2:1)
else
tok^.num := l1^.num2;
if (tok^.next <> nil) and (tok^.next^.kind = tokcomma) then
tok := tok^.next;
end;
tok := tok^.next;
until tok = nil;
l := l^.next;
until l = nil;
l := linebase;
while l <> nil do
begin
l^.num := l^.num2;
l := l^.next;
end;
end;
end;
procedure cmdprint;
var
semiflag : boolean;
n : valrec;
begin
semiflag := false;
while not iseos do
begin
semiflag := false;
if t^.kind in [toksemi, tokcomma] then
begin
semiflag := true;
t := t^.next;
end
else
begin
n := expr;
if n.stringval then
begin
write(n.sval^);
dispose(n.sval);
end
else
write(numtostr(n.val), ' ');
end;
end;
if not semiflag then
writeln;
end;
procedure cmdinput;
var
v : varptr;
s : string255;
tok, tok0, tok1 : tokenptr;
strflag : boolean;
begin
if (t <> nil) and (t^.kind = tokstr) then
begin
write(t^.sp^);
t := t^.next;
require(toksemi);
end
else
begin
write('? ');
end;
tok := t;
if (t = nil) or (t^.kind <> tokvar) then snerr;
strflag := t^.vp^.stringvar;
repeat
if (t <> nil) and (t^.kind = tokvar) then
if t^.vp^.stringvar <> strflag then snerr;
t := t^.next;
until iseos;
t := tok;
if strflag then
begin
repeat
readln(s);
v := findvar;
if v^.sval^ <> nil then
dispose(v^.sval^);
new(v^.sval^);
v^.sval^^ := s;
if not iseos then
begin
require(tokcomma);
write('?? ');
end;
until iseos;
end
else
begin
readln(s);
parse(addr(s), tok);
tok0 := tok;
repeat
v := findvar;
while tok = nil do
begin
write('?? ');
readln(s);
disposetokens(tok0);
parse(addr(s), tok);
tok0 := tok;
end;
tok1 := t;
t := tok;
v^.val^ := realexpr;
if t <> nil then
if t^.kind = tokcomma then
t := t^.next
else
snerr;
tok := t;
t := tok1;
if not iseos then
require(tokcomma);
until iseos;
disposetokens(tok0);
end;
end;
procedure cmdlet(implied : boolean);
var
v : varptr;
old : basicstring;
begin
if implied then
t := stmttok;
v := findvar;
require(tokeq);
if v^.stringvar then
begin
old := v^.sval^;
v^.sval^ := strexpr;
if old <> nil then
dispose(old);
end
else
v^.val^ := realexpr;
end;
procedure cmdgoto;
begin
stmtline := mustfindline(intexpr);
t := nil;
gotoflag := true;
end;
procedure cmdif;
var
n : real;
i : integer;
begin
n := realexpr;
require(tokthen);
if n = 0 then
begin
i := 0;
repeat
if t <> nil then
begin
if t^.kind = tokif then
i := i + 1;
if t^.kind = tokelse then
i := i - 1;
t := t^.next;
end;
until (t = nil) or (i < 0);
end;
if (t <> nil) and (t^.kind = toknum) then
cmdgoto
else
elseflag := true;
end;
procedure cmdelse;
begin
t := nil;
end;
function skiploop(up, dn : tokenkinds) : boolean;
label 1;
var
i : integer;
saveline : lineptr;
begin
saveline := stmtline;
i := 0;
repeat
while t = nil do
begin
if (stmtline = nil) or (stmtline^.next = nil) then
begin
skiploop := false;
stmtline := saveline;
goto 1;
end;
stmtline := stmtline^.next;
t := stmtline^.txt;
end;
if t^.kind = up then
i := i + 1;
if t^.kind = dn then
i := i - 1;
t := t^.next;
until i < 0;
skiploop := true;
1 :
end;
procedure cmdfor;
var
l : loopptr;
lr : looprec;
saveline : lineptr;
i, j : integer;
begin
lr.vp := findvar;
if lr.vp^.stringvar then snerr;
require(tokeq);
lr.vp^.val^ := realexpr;
require(tokto);
lr.max := realexpr;
if (t <> nil) and (t^.kind = tokstep) then
begin
t := t^.next;
lr.step := realexpr;
end
else
lr.step := 1;
lr.homeline := stmtline;
lr.hometok := t;
lr.kind := forloop;
lr.next := loopbase;
with lr do
if ((step >= 0) and (vp^.val^ > max)) or ((step <= 0) and (vp^.val^ < max)) then
begin
saveline := stmtline;
i := 0;
j := 0;
repeat
while t = nil do
begin
if (stmtline = nil) or (stmtline^.next = nil) then
begin
stmtline := saveline;
errormsg('FOR without NEXT');
end;
stmtline := stmtline^.next;
t := stmtline^.txt;
end;
if t^.kind = tokfor then
if (t^.next <> nil) and (t^.next^.kind = tokvar) and (t^.next^.vp = vp) then
j := j + 1
else
i := i + 1;
if (t^.kind = toknext) then
if (t^.next <> nil) and (t^.next^.kind = tokvar) and (t^.next^.vp = vp) then
j := j - 1
else
i := i - 1;
t := t^.next;
until (i < 0) or (j < 0);
skiptoeos;
end
else
begin
new(l);
l^ := lr;
loopbase := l;
end;
end;
procedure cmdnext;
var
v : varptr;
found : boolean;
l : loopptr;
begin
if not iseos then
v := findvar
else
v := nil;
repeat
if (loopbase = nil) or (loopbase^.kind = gosubloop) then
errormsg('NEXT without FOR');
found := (loopbase^.kind = forloop) and
((v = nil) or (loopbase^.vp = v));
if not found then
begin
l := loopbase^.next;
dispose(loopbase);
loopbase := l;
end;
until found;
with loopbase^ do
begin
vp^.val^ := vp^.val^ + step;
if ((step >= 0) and (vp^.val^ > max)) or ((step <= 0) and (vp^.val^ < max)) then
begin
l := loopbase^.next;
dispose(loopbase);
loopbase := l;
end
else
begin
stmtline := homeline;
t := hometok;
end;
end;
end;
procedure cmdwhile;
var
l : loopptr;
begin
new(l);
l^.next := loopbase;
loopbase := l;
l^.kind := whileloop;
l^.homeline := stmtline;
l^.hometok := t;
if not iseos then
if realexpr = 0 then
begin
if not skiploop(tokwhile, tokwend) then
errormsg('WHILE without WEND');
l := loopbase^.next;
dispose(loopbase);
loopbase := l;
skiptoeos;
end;
end;
procedure cmdwend;
var
tok : tokenptr;
tokline : lineptr;
l : loopptr;
found : boolean;
begin
repeat
if (loopbase = nil) or (loopbase^.kind = gosubloop) then
errormsg('WEND without WHILE');
found := (loopbase^.kind = whileloop);
if not found then
begin
l := loopbase^.next;
dispose(loopbase);
loopbase := l;
end;
until found;
if not iseos then
if realexpr <> 0 then
found := false;
tok := t;
tokline := stmtline;
if found then
begin
stmtline := loopbase^.homeline;
t := loopbase^.hometok;
if not iseos then
if realexpr = 0 then
found := false;
end;
if not found then
begin
t := tok;
stmtline := tokline;
l := loopbase^.next;
dispose(loopbase);
loopbase := l;
end;
end;
procedure cmdgosub;
var
l : loopptr;
begin
new(l);
l^.next := loopbase;
loopbase := l;
l^.kind := gosubloop;
l^.homeline := stmtline;
l^.hometok := t;
cmdgoto;
end;
procedure cmdreturn;
var
l : loopptr;
found : boolean;
begin
repeat
if loopbase = nil then
errormsg('RETURN without GOSUB');
found := (loopbase^.kind = gosubloop);
if not found then
begin
l := loopbase^.next;
dispose(loopbase);
loopbase := l;
end;
until found;
stmtline := loopbase^.homeline;
t := loopbase^.hometok;
l := loopbase^.next;
dispose(loopbase);
loopbase := l;
skiptoeos;
end;
procedure cmdread;
var
v : varptr;
tok : tokenptr;
found : boolean;
begin
repeat
v := findvar;
tok := t;
t := datatok;
if dataline = nil then
begin
dataline := linebase;
t := dataline^.txt;
end;
if (t = nil) or (t^.kind <> tokcomma) then
repeat
while t = nil do
begin
if (dataline = nil) or (dataline^.next = nil) then
errormsg('Out of Data');
dataline := dataline^.next;
t := dataline^.txt;
end;
found := (t^.kind = tokdata);
t := t^.next;
until found and not iseos
else
t := t^.next;
if v^.stringvar then
begin
if v^.sval^ <> nil then
dispose(v^.sval^);
v^.sval^ := strexpr;
end
else
v^.val^ := realexpr;
datatok := t;
t := tok;
if not iseos then
require(tokcomma);
until iseos;
end;
procedure cmddata;
begin
skiptoeos;
end;
procedure cmdrestore;
begin
if iseos then
restoredata
else
begin
dataline := mustfindline(intexpr);
datatok := dataline^.txt;
end;
end;
procedure cmdgotoxy;
var
i : integer;
begin
i := intexpr;
require(tokcomma);
gotoxy(i, intexpr);
end;
procedure cmdon;
var
i : integer;
l : loopptr;
begin
i := intexpr;
if (t <> nil) and (t^.kind = tokgosub) then
begin
new(l);
l^.next := loopbase;
loopbase := l;
l^.kind := gosubloop;
l^.homeline := stmtline;
l^.hometok := t;
t := t^.next;
end
else
require(tokgoto);
if i < 1 then
skiptoeos
else
begin
while (i > 1) and not iseos do
begin
require(toknum);
if not iseos then
require(tokcomma);
i := i - 1;
end;
if not iseos then
cmdgoto;
end;
end;
procedure cmddim;
var
i, j, k : integer;
v : varptr;
done : boolean;
begin
repeat
if (t = nil) or (t^.kind <> tokvar) then snerr;
v := t^.vp;
t := t^.next;
with v^ do
begin
if numdims <> 0 then
errormsg('Array already dimensioned');
j := 1;
i := 0;
require(toklp);
repeat
k := intexpr + 1;
if k < 1 then badsubscr;
if i >= maxdims then badsubscr;
i := i + 1;
dims[i] := k;
j := j * k;
done := (t <> nil) and (t^.kind = tokrp);
if not done then
require(tokcomma);
until done;
t := t^.next;
numdims := i;
if stringvar then
begin
hpm_new(sarr, j*4);
for i := 0 to j-1 do
sarr^[i] := nil;
end
else
begin
hpm_new(arr, j*8);
for i := 0 to j-1 do
arr^[i] := 0;
end;
end;
if not iseos then
require(tokcomma);
until iseos;
end;
procedure cmdpoke;
var
trick :
record
case boolean of
true : (i : integer);
false : (c : ^char);
end;
begin
$range off$
trick.i := intexpr;
require(tokcomma);
trick.c^ := chr(intexpr);
$if checking$ $range on$ $end$
end;
begin {exec}
try
repeat
repeat
gotoflag := false;
elseflag := false;
while (stmttok <> nil) and (stmttok^.kind = tokcolon) do
stmttok := stmttok^.next;
t := stmttok;
if t <> nil then
begin
t := t^.next;
{EMBED
#ifdef MCH_AMIGA
if(SetSignal(0L, 0L) & SIGBREAKF_CTRL_C)
\[
P_escapecode = -20;
goto _Ltry1;
\]
#endif
}
case stmttok^.kind of
tokrem : ;
toklist : cmdlist;
tokrun : cmdrun;
toknew : cmdnew;
tokload : cmdload(false, stringexpr);
tokmerge : cmdload(true, stringexpr);
toksave : cmdsave;
tokbye : cmdbye;
tokdel : cmddel;
tokrenum : cmdrenum;
toklet : cmdlet(false);
tokvar : cmdlet(true);
tokprint : cmdprint;
tokinput : cmdinput;
tokgoto : cmdgoto;
tokif : cmdif;
tokelse : cmdelse;
tokend : cmdend;
tokstop : escape(-20);
tokfor : cmdfor;
toknext : cmdnext;
tokwhile : cmdwhile;
tokwend : cmdwend;
tokgosub : cmdgosub;
tokreturn : cmdreturn;
tokread : cmdread;
tokdata : cmddata;
tokrestore : cmdrestore;
tokgotoxy : cmdgotoxy;
tokon : cmdon;
tokdim : cmddim;
tokpoke : cmdpoke;
otherwise
errormsg('Illegal command');
end;
end;
if not elseflag and not iseos then
checkextra;
stmttok := t;
until t = nil;
if stmtline <> nil then
begin
if not gotoflag then
stmtline := stmtline^.next;
if stmtline <> nil then
stmttok := stmtline^.txt;
end;
until stmtline = nil;
recover
begin
if escapecode = -20 then
begin
write('Break');
end
else if escapecode = 42 then
begin end
else
case escapecode of
-4 : write(#7'Integer overflow');
-5 : write(#7'Divide by zero');
-6 : write(#7'Real math overflow');
-7 : write(#7'Real math underflow');
-8, -19..-15 : write(#7'Value range error');
-10 :
begin
new(ioerrmsg);
misc_getioerrmsg(ioerrmsg^, ioresult);
write(#7, ioerrmsg^);
dispose(ioerrmsg);
end;
otherwise
begin
if excp_line <> -1 then
writeln(excp_line);
escape(escapecode);
end;
end;
if stmtline <> nil then
write(' in ', stmtline^.num:1);
writeln;
end;
end; {exec}
begin {main}
new(inbuf);
linebase := nil;
varbase := nil;
loopbase := nil;
{EMBED
#ifdef MCH_AMIGA
# ifdef AZTEC_C
Enable_Abort = 0;
# endif
#endif
}
writeln('Chipmunk BASIC 1.0');
writeln;
exitflag := false;
repeat
try
repeat
write('>');
readln(inbuf^);
parseinput(buf);
if curline = 0 then
begin
stmtline := nil;
stmttok := buf;
if stmttok <> nil then
exec;
disposetokens(buf);
end;
until exitflag or eof(input);
recover
if escapecode <> -20 then
misc_printerror(escapecode, ioresult)
else
writeln;
until exitflag or eof(input);
end.