home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 6
/
AACD06.ISO
/
AACD
/
Graphics
/
picFX
/
e-source
/
parser.e
next >
Wrap
Text File
|
1999-12-27
|
31KB
|
857 lines
OPT MODULE,LARGE,PREPROCESS
MODULE 'amigalib/lists',
'dos/dos',
'exec/lists','exec/memory','exec/nodes','exec/ports','exec/semaphores','exec/tasks',
'graphics/rastport',->'cybergraphics','picasso96api',
'*varlist'
MODULE 'tools/debug' -> kputfmt('',[])
#define pixel(raw,w,x,y) raw[x+(y*w)]
-> Can't we export a #define?
/* Some stuff for picFX... I was forced to put it here, as I couldn't reference the main executable when needing them */
EXPORT OBJECT project_Node
ln:ln -> base node structure
window -> window object
item -> string that appears in opened_lst
pid -> Project ID
ENDOBJECT
EXPORT OBJECT subtaskmsg
stm_Message:mn
stm_Command:INT
stm_Parameter:LONG
stm_Result:LONG
ENDOBJECT
EXPORT OBJECT subtask
st_Task:PTR TO tc /* sub task pointer */
st_Port:PTR TO mp /* allocated by sub task */
st_Reply:PTR TO mp /* allocated by main task */
st_Data:LONG /* more initial data to pass to the sub task */
st_Message:subtaskmsg /* Message buffer */
ENDOBJECT
EXPORT OBJECT planeFunc_data
projectid -> Project ID, used when referencing a project from "outside"
/***attributes storage***/
bfunc:PTR TO expression
bstr:PTR TO CHAR
failure /*$RGB*/
gfunc:PTR TO expression
gstr:PTR TO CHAR
height
imagefile:PTR TO CHAR
left,newleft ->scroll when newleft is non-zero
loading
loadm
lock -> -1 when there's a writelock, 0 when it is free, number of readlocks otherwise
name:PTR TO CHAR
paused -> this is used by the 'state' attribute, with the 'lock' var
projectnode:PTR TO project_Node
ratio -> scale ratio to which display the picture, multiplied by one million.
rfunc:PTR TO expression
rstr:PTR TO CHAR
outputr,outputg,outputb
quiet
top,newtop -> scroll when newtop is non-zero
type
width
xmin,xmax,ymin,ymax -> area to draw to
/***some pointers...***/
hscroll,vscroll -> scrollers of the window
app -> true if we can send an update message.
self -> pointer to ourselves
/***some useful data***/
savepixel -> true if the previous pixel must be saved
rupd -> copy of the render-update preference variable.
sema:ss -> data item protection
subtask:PTR TO subtask -> our sub task
drawhandle -> draw handle
imagedata:PTR TO LONG,vec-> pixel information for the picture. vec is TRUE if we did allocate it with AllocVec()
picture -> picture
drawn -> the last line that has been drawn to the window
calculated -> the last line that has been calculated by the subtask
ds:datestamp -> planeFunc stores the datestamp when a calculation is started
ENDOBJECT
EXPORT ENUM OUT_Integer=0, -> 32bits integer
OUT_Float32, -> 32Bits float
OUT_Float64, -> 64Bits float
OUT_OldR,OUT_OldG,OUT_OldB, -> Components of the pixel (previous calculation!)
OUT_CopyR,OUT_CopyG -> Copies the new expression (e.g. black'n'white)
/* Function class. This class handles the functions like sin, +, r(), and so on, the old function class has been renamed to expression.
You can easily add functions, just put the evaluation code somewhere (better is to keep the order !) and then put it in initfuncs, in
the same way as the other.*/
OBJECT function
ln:ln
argcount:LONG
readpid -> if TRUE: cte::projectid ->-> data
evalI,evalF:LONG -> address of the evaluating procedures
ENDOBJECT
/* For compatibility with previous versions, a function having a constant will
be read as f(cte,arg1,arg2), and not f(arg1,arg2,cte), as you might think*/
EXPORT DEF fconstant,fvariable, -> The addresses of these two function nodes (!FUNCTION OBJECTS!)
firstb,lastb:PTR TO ln -> first & last x_y . (!LN OBJECTS!)
DEF funclist:PTR TO lh,
gtd,-> gtd is the address of the getdata(dat,num) PROC
dat,-> dat is the address of the ProjectlistObject's data
vlo -> vlo is the current VariableListObject
DEF c,p,s -> c,p = number of used A5, respectively by calc and precalc; s is used A1.
DEF count,mc,bs, -> precompilation stuff mc stands for max-count, bs for buffer-size
code:PTR TO INT,crs:PTR TO INT, -> code is the beginning, crs is the point we are at.
precalc:PTR TO INT,prs:PTR TO INT,
buffer -> *temporary* storage of the current buffer (*copy* of self.buffer, the address)
PROC create(name,argcount,evalI,evalF) OF function
self.ln.name:=String(StrLen(name))
StrCopy(self.ln.name,name)
self.argcount:=argcount
self.evalI:=evalI
self.evalF:=evalF
ENDPROC
PROC end() OF function
DisposeLink(self.ln.name)
ENDPROC
EXPORT PROC initfuncs(gtda,data,varlst) -> getData(), projectList_data, VarListObject
DEF thatfunc:PTR TO function,k:PTR TO function
gtd:=gtda
dat:=data
vlo:=varlst
NEW funclist
newList(funclist)
NEW thatfunc.create('Constant',0,{e_constant},{e_constant})
AddTail(funclist,thatfunc.ln)
fconstant:=thatfunc
NEW thatfunc.create('Variable',0,{e_variable},{e_variable})
AddTail(funclist,thatfunc.ln)
fvariable:=thatfunc
/*Following functions are sorted by order of priority*/
NEW thatfunc.create('%',2,{e_mod},{f_mod})
AddTail(funclist,thatfunc.ln)
firstb:=thatfunc.ln -> This is the first x_y style operator
NEW thatfunc.create('+',2,{e_add},{f_add})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('-',2,{e_sub},{f_sub})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('*',2,{e_mul},{f_mul})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('/',2,{e_div},{f_div})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('^',2,{e_pow},{f_pow})
AddTail(funclist,thatfunc.ln)
lastb:=thatfunc.ln -> this is the last x_y style operator
NEW thatfunc.create('abs',1,{e_abs},{f_abs})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('neg',1,{e_neg},{f_neg}) -> '-a' is possible but slower than 'neg(a)', because interpreted as '0-a'
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('cos',1,{e_constant},{f_cos}) -> return zero for int32 :-)
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('sin',1,{e_constant},{f_sin})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('tan',1,{e_constant},{f_tan})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('acos',1,{e_constant},{f_acos})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('asin',1,{e_constant},{f_asin})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('atan',1,{e_constant},{f_atan})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('angle',2,{e_constant},{f_angle})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('min',2,{e_min},{f_min})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('max',2,{e_max},{f_max})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('sqrt',1,{e_sqrt},{f_sqrt})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('ln',1,{e_ln},{f_ln})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('log',2,{e_log},{f_log})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('cosh',1,{e_constant},{f_cosh}) -> maybe later, approximated int value?
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('sinh',1,{e_constant},{f_sinh})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('tanh',1,{e_constant},{f_tanh})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('acosh',1,{e_constant},{e_constant})->,{f_acosh})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('asinh',1,{e_constant},{e_constant})->,{f_asinh})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('atanh',1,{e_constant},{e_constant})->,{f_atanh})
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('r',3,{e_red},{f_red})
thatfunc.readpid:=TRUE
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('g',3,{e_green},{f_green})
thatfunc.readpid:=TRUE
AddTail(funclist,thatfunc.ln)
NEW thatfunc.create('b',3,{e_blue},{f_blue})
thatfunc.readpid:=TRUE
AddTail(funclist,thatfunc.ln)
ENDPROC
EXPORT PROC cleanfuncs()
DEF thisnode:PTR TO ln,nextone
thisnode:=funclist.head
WHILE (nextone:=thisnode.succ) AND Not(CtrlC()) -> Destroy all functions...
DisposeLink(thisnode.name)
END thisnode;thisnode:=nextone
ENDWHILE
END funclist
ENDPROC
/* I am required to have the three arguments, so I cannot get rid of the unreferenced messages.*/
PROC e_constant(x,y,c) IS c
ENUM VAR_x,VAR_y
PROC e_variable(x,y,c)
IF c=VAR_x THEN RETURN x
ENDPROC y
/*PROC f_variable(x,y,c) -> Test: Let's use the above one instead and have picFX convert the vars...
IF c=VAR_x THEN RETURN x!
ENDPROC y!
*/
EXPORT PROC e_mod(x,y,c=0) -> This is also used in picFX as a kind of replacement to Mod()
IF y = 0; RETURN 0
ELSEIF x >= 0; RETURN Mod(x,y)
ENDIF
ENDPROC Mod(x,y)+y
PROC f_mod(x,y,c)
IF y = 0. THEN RETURN 0.
ENDPROC !x-(!Ffloor(!x/y)*y)
PROC e_add(x,y,c) IS x+y
PROC f_add(x,y,c) IS !x+y
PROC e_sub(x,y,c) IS x-y
PROC f_sub(x,y,c) IS !x-y
PROC e_mul(x,y,c) IS Mul(x,y)
PROC f_mul(x,y,c) IS !x*y
PROC e_div(x,y,c)
IF y = 0 THEN RETURN 123456 ELSE RETURN Div(x,y)
ENDPROC
PROC f_div(x,y,c)
IF y = 0. THEN RETURN 123456. ELSE RETURN !x/y
ENDPROC
PROC e_pow(x,y,c) -> maybe would be quicker to convert to float, power, and back to int again?
DEF result
IF y = 0 THEN RETURN 1
result:=x
WHILE y > 1
result:=Mul(result,x)
y--
ENDWHILE
ENDPROC result
PROC f_pow(x,y,c) IS Fpow(y,x)
PROC e_abs(x,y,c) IS Abs(x)
PROC f_abs(x,y,c) IS Fabs(x)
PROC e_neg(x,y,c) IS -x
PROC f_neg(x,y,c) IS !-x
PROC f_sin(x,y,c) IS Fsin(x)
PROC f_cos(x,y,c) IS Fcos(x)
PROC f_tan(x,y,c) IS Ftan(x) -> I don't know if we can pass pi/2 to this function. But as it is approximated, it shouldn't cause problems...
PROC f_asin(x,y,c) IS Fasin(x)
PROC f_acos(x,y,c) IS Facos(x)
PROC f_atan(x,y,c) IS Fatan(x)
PROC f_angle(x,y,c)
IF y=0.
IF x<0. THEN RETURN 3.14159265 ELSE RETURN 0.
ELSEIF !y<0
RETURN !Fatan(!x/y)+1.5707963
ENDIF
ENDPROC !Fatan(!x/y)-1.5707963
PROC e_min(x,y,c) IS Min(x,y)
PROC f_min(x,y,c)
IF !x>y THEN RETURN y ELSE RETURN x
ENDPROC
PROC e_max(x,y,c) IS Max(x,y)
PROC f_max(x,y,c)
IF !x<y THEN RETURN y ELSE RETURN x
ENDPROC
PROC e_sqrt(x,y,c) IS !Fsqrt(x!)!
PROC f_sqrt(x,y,c) IS Fsqrt(x)
PROC e_ln(x,y,c)
IF x <= 0 THEN RETURN 123456 ELSE RETURN !Flog(x!)!
ENDPROC
PROC f_ln(x,y,c)
IF x<=0 THEN RETURN 123456. ELSE RETURN Flog(x)
ENDPROC
PROC e_log(x,y,c) -> All these tests are annoying, but I do not know how to avoid them...
IF (x<=0) OR (y<=0) OR (y=1) THEN RETURN 123456 ELSE RETURN !Flog(x!)/Flog(y!)!
ENDPROC
PROC f_log(x,y,c)
IF (x<=0.) OR (y<=0.) OR (y=1. ) THEN RETURN 123456. ELSE RETURN !Flog(x)/Flog(y)
ENDPROC
PROC f_sinh(x,y,c) IS !Fcosh(x)*Ftanh(x) -> Fsinh(x) -> I am doing something wrong somewhere, anyway putting fsinh causes a crash...
PROC f_cosh(x,y,c) IS Fcosh(x)
PROC f_tanh(x,y,c) IS Ftanh(x)
/*PROC f_asinh(x,y,c) IS Fasinh(x) -> These three aren't implemented in E...
PROC f_acosh(x,y,c) IS Facosh(x)
PROC f_atanh(x,y,c) IS Fatanh(x)*/
PROC e_red(x,y,c:PTR TO planeFunc_data) IS Shr(pixel(c.imagedata,c.width,e_mod(x,c.width),e_mod(y,c.height)),16)
PROC f_red(x,y,c:PTR TO planeFunc_data) IS (Shr(pixel(c.imagedata,c.width,e_mod(!x!,c.width),e_mod(!y!,c.height)),16))!
PROC e_green(x,y,c:PTR TO planeFunc_data) IS Shr(pixel(c.imagedata,c.width,e_mod(x,c.width),e_mod(y,c.height)),8) AND $FF
PROC f_green(x,y,c:PTR TO planeFunc_data) IS (Shr(pixel(c.imagedata,c.width,e_mod(!x!,c.width),e_mod(!y!,c.height)),8) AND $FF)!
PROC e_blue(x,y,c:PTR TO planeFunc_data) IS pixel(c.imagedata,c.width,e_mod(x,c.width),e_mod(y,c.height)) AND $FF
PROC f_blue(x,y,c:PTR TO planeFunc_data) IS (pixel(c.imagedata,c.width,e_mod(!x!,c.width),e_mod(!y!,c.height)) AND $FF)!
/* Expression class. This is equivalent to the old function one. Decoding is now put in the constructor (=> a little bit less code ;)
If there's a problem while decoding, the function will get f(x,y)=0*/
EXPORT SET NEEDX,SHORT -> short is x,y,e,pi,numeric-constant, i.e. what does not need computation.
EXPORT ENUM FAILURE=$100,ERR_NoParse,ERR_NoFunc,ERR_NoChar
EXPORT SET WARN_NoSupport,WARN_EPi
EXPORT OBJECT expression
func:PTR TO function
arg1:PTR TO expression
arg2:PTR TO expression
cte:LONG -> third arg. For reason of backward compatibility, it is the arg #1 in the expression string, when present.
precision
evaluate -> evaluate THIS proc.
/*precalculation stuff*/
/*following: allocated with AllocVec()!!!!*/
precalc:PTR TO LONG -> pre-calculates. Values put in buffer (only in main expr)
calculate:PTR TO LONG -> calculates function value (only in main expr)
buffer:PTR TO LONG -> pre-calculated value (only in main expr)
message -> how last decoding went: WARN_#? or ERR_#? or NIL
need:LONG -> what vars are required to eval. it
-> value:LONG -> precalculated value
ENDOBJECT
/*EXPORT OBJECT outinfo
error:LONG
quote:PTR TO CHAR
ENDOBJECT*/
PROC setfunc(address:PTR TO function) OF expression
self.func:=address
IF self.precision = OUT_Integer
self.evaluate:=self.func.evalI
ELSEIF self.precision = OUT_Float32
self.evaluate:=self.func.evalF
ELSE -> Actually this happens when using a non-direct output mode (copy, old,..?)
-> WriteF('Unknown output mode\n')
self.evaluate:=self.func.evalF -> (Just in case of problem, but it shouldn't be useful)
ENDIF
ENDPROC
/* A Little But Serious change done here: start is the first char to decode, but end is the last plus one. (It made code a little simpler)*/
PROC create(precision,input:PTR TO CHAR,start=0,end=-1,first=TRUE) OF expression HANDLE
DEF currfunc:PTR TO ln,name:PTR TO CHAR,ee:PTR TO expression,var,
open,close,nxt:PTR TO LONG,dest:PTR TO LONG,fnc,out=NIL ->:PTR TO outinfo
self.precision:=precision
IF end=-1
end:=StrLen(input)
mc:=0;bs:=0
ENDIF
IF start=end -> Empty expressions => constant zero
self.setfunc(fconstant)
self.cte:=0
JUMP toEnd
ENDIF
IF input[start] = "(" -> If the whole expression is surrounded by brackets, decode inside.
IF findseparator(input,start,')') = (end-1)
self.create(precision,input,start+1,end-1,first)
JUMP toEnd
ENDIF
ENDIF
/* Let's seek x_y operators */
currfunc:=firstb
REPEAT
nxt:=findseparator(input,start-1,currfunc.name)
IF (nxt<>-1) AND (nxt<end)
self.setfunc(currfunc-4) ->!!! There's a four-bytes gap between function and ln (because function is not simply an object: it's a class :-)
NEW self.arg1.create(precision,input,start,nxt,FALSE)
out:=out OR self.arg1.message -> Gather eventual warning..
count++;mc:=Max(mc,count)
NEW self.arg2.create(precision,input,nxt+1,end,FALSE)
out:=out OR self.arg2.message
self.need:=self.arg1.need OR self.arg2.need AND Not(SHORT)
count--
JUMP toEnd
ENDIF
currfunc:=currfunc.succ
UNTIL (currfunc = lastb.succ) OR CtrlC()
/* Now look for f() functions */
open:=InStr(input,'(',start)
IF (open=-1) OR (open >= end) -> No () in expression, let's treat it as a whole
var:=getvariable(input+start,end-start,vlo) -> this is inside varlist.m
IF var <> -1
self.setfunc(fconstant)
IF self.precision=OUT_Integer
self.cte:=!var! -> If we are integer, then convert the flt32 into int32...
ELSE
self.cte:=var
ENDIF
self.need:=SHORT
ELSEIF StrCmp(input+start,'x',1)
self.setfunc(fvariable)
self.cte:=VAR_x
IF end<>(start+1) THEN Throw(ERR_NoParse,input)
self.need:=NEEDX OR SHORT
ELSEIF StrCmp(input+start,'y',1)
self.setfunc(fvariable)
self.cte:=VAR_y
IF end<>(start+1) THEN Throw(ERR_NoParse,input)
self.need:=SHORT
ELSEIF StrCmp(input+start,'e',1)
self.setfunc(fconstant)
IF self.precision=OUT_Integer
self.cte:=3
out:=out OR WARN_EPi
ELSE
self.cte:=2.71828183
ENDIF
IF end<>(start+1) THEN Throw(ERR_NoParse,input)
self.need:=SHORT
ELSEIF StrCmp(input+start,'pi',2)
self.setfunc(fconstant)
IF self.precision = OUT_Integer
self.cte:=3
out:=out OR WARN_EPi
ELSE
self.cte:=3.14159265
ENDIF
IF end<>(start+2) THEN Throw(ERR_NoParse,input)
self.need:=SHORT
ELSE
self.setfunc(fconstant)
IF self.precision=OUT_Integer
open,close:=Val(input+start)
ELSE
open,close:=RealVal(input+start)
ENDIF
self.cte:=open -> putting multiple return values in objects (x.y) is not accepted...
IF close <> (end-start) THEN Throw(ERR_NoParse,input)
self.need:=SHORT
ENDIF
ELSE
IF open=start THEN -> THEN in cases like '(a)(b)', or '(a))'
Throw(ERR_NoParse,input)
name:=String(open-start)
MidStr(name,input,start)
fnc:=FindName(lastb.pred,name) -> .pred because abs must also be found
IF fnc=NIL THEN Throw(ERR_NoFunc,name)
self.setfunc(fnc-4)
IF self.func.argcount>1
close:=findseparator(input,open,',')
IF (close=-1) OR (close>=end) THEN Throw(ERR_NoChar,',')
IF self.func.argcount>2 -> In this case, read cte,arg1,arg2
self.cte:=Val(input+open+1) -> Second return value: # of read chars. For error handling.
IF self.func.readpid -> This is a project id
self.cte:=gtd(dat,self.cte)
ELSEIF self.precision=OUT_Float32 -> Not used for now, but may come once :-)
self.cte:=RealVal(input+open)
ENDIF
open:=close
close:=findseparator(input,open,',')
IF (close=-1) OR (close>=end) THEN Throw(ERR_NoChar,',')
NEW self.arg1.create(precision,input,open+1,close,FALSE)
self.need:=self.arg1.need AND Not(SHORT)
count++;mc:=Max(mc,count)
open:=close
close:=findseparator(input,open,')')
IF (close=-1) OR (close>=end) THEN Throw(ERR_NoChar,')')
NEW self.arg2.create(precision,input,open+1,close,FALSE)
self.need:=self.need OR self.arg2.need AND Not(SHORT)
count-- -> storage not needed anymore.
ELSE -> two args
NEW self.arg1.create(precision,input,open+1,close,FALSE)
self.need:=self.arg1.need AND Not(SHORT)
count++;mc:=Max(mc,count)
open:=close
close:=findseparator(input,open,')')
IF (close=-1) OR (close>=end) THEN Throw(ERR_NoChar,')')
NEW self.arg2.create(precision,input,open+1,close,FALSE)
self.need:=self.need OR self.arg2.need AND Not(SHORT)
count-- -> storage not needed anymore.
ENDIF
out:=out OR self.arg2.message
ELSE -> Single arg
close:=findseparator(input,open,')')
IF (close=-1) OR (close>=end) THEN Throw(ERR_NoChar,')')
NEW self.arg1.create(precision,input,open+1,close,FALSE)
self.need:=self.arg1.need AND Not(SHORT)
ENDIF
out:=out OR self.arg1.message
IF self.evaluate={e_constant} THEN -> I assume that if a function with args is constant, then there's a problem...
out:=out OR WARN_NoSupport
ENDIF
toEnd:
EXCEPT DO
IF exception
IF Not(first) THEN Throw(exception,exceptioninfo)
/*NEW out
out.error:= exception;out.quote:=exceptioninfo*/
out:=/*out OR */exception -> Once there will be some more error information sent...
self.end()
self.create(self.precision,'')
ENDIF
IF self.need AND NEEDX
IF self.arg1 ->THEN
IF (self.arg1.need AND NEEDX) = $0 THEN
bs++
ENDIF
IF self.arg2 ->THEN
IF (self.arg2.need AND NEEDX) = $0 THEN
bs++
ENDIF
ELSEIF first -> x *never* used in whole expression: use one buffer slot.
bs:=1
ENDIF
IF first THEN
self.compile((end-start)*10+32,mc,bs) -> Be sure that there's enough room in string :-)
self.message:=out
ENDPROC
/* find first separator (sep: ')', ',', or '+', .....) following index, in input */
PROC findseparator(input:PTR TO CHAR,index,sep)
DEF open,nxt
open:=InStr(input,'(',index+1)
IF open=-1 THEN open:=StrLen(input)
nxt:=InStr(input,sep,index+1)
IF nxt < open THEN RETURN nxt
/* nxt > open */
nxt:=findseparator(input,open,')') -> Where does that "open" close?
IF nxt=-1 THEN Throw(ERR_NoChar,')') -> If it doesn't => error
ENDPROC findseparator(input,nxt,sep) -> If it does, let's return the following sep
/* Reg usage: A5 is for temporary values, valid during one single expr evaluation;
A1 points to the pre-calculated values*/
PROC compile(len,maxcount,buffsize) OF expression
DEF nxt:PTR TO LONG,dest:PTR TO LONG
c:=0;p:=0;s:=-1
NEW code[len];crs:=code
NEW precalc[len];prs:=precalc
buffer:=AllocVec(buffsize*4+4,MEMF_PUBLIC) -> 4 is SIZEOF LONG
self.buffer:=buffer
crs[]:=$4E55;crs++; crs[]:=(-maxcount-2)*4 AND $FFFF;crs++ -> LINK A5,#$<..maxcount..>
prs[]:=$4E55;prs++; prs[]:=(-maxcount-2)*4 AND $FFFF;prs++ -> LINK A5,#$<maxcount>
self.subcompile()
IF (self.need AND NEEDX)=$0 -> If *nothing* in the expression depends on x, let's save+recall here.
s++
crs[]:=$227C;crs++; crs[]:=Shr(buffer,16);crs++; crs[]:=buffer;crs++ ->MOVE.L buffer,A1
prs[]:=$227C;prs++; prs[]:=Shr(buffer,16);prs++; prs[]:=buffer;prs++ ->MOVE.L buffer,A1
prs[]:=$2340;prs++;prs[]:=s*4;prs++ -> MOVE.L D0,<s*4>(A1)
crs[]:=$2029;crs++;crs[]:=s*4;crs++ -> MOVE.L <s*4>(A1),D0
ENDIF
crs[]:=$4E5D;crs++ -> UNLK A5
crs[]:=$4E75;crs++ -> RTS
prs[]:=$4E5D;prs++ -> UNLK A5
prs[]:=$4E75;prs++ -> RTS
self.calculate:=AllocVec(crs-code+16,NIL)
nxt:=code;dest:=self.calculate
WHILE nxt <= (crs)
dest[]:=nxt[]
nxt++;dest++
ENDWHILE
END code[len]
self.precalc:=AllocVec(prs-precalc+16,NIL)
nxt:=precalc;dest:=self.precalc
WHILE nxt <= (prs)
dest[]:=nxt[]
nxt++;dest++
ENDWHILE
END precalc[len]
ENDPROC
PROC long(k:PTR TO expression)
IF k
RETURN (k.need AND SHORT) = $0
ELSE
RETURN FALSE
ENDIF
ENDPROC
PROC subcompile() OF expression
DEF saved=FALSE, -> TRUE if arg1 was saved in A5.
buff=-1 -> buffer-slot (in case "f(y)_f(x)") where f(y) is stored
IF long(self) = FALSE -> That's only if the global (whole) expression is short.
IF self.func=fvariable -> For now, use pre-calculation as usual, I mean, 'y' expression will be pre-calculated even if it is quite useless...
IF self.cte=VAR_x
crs[]:=$202D;crs++ -> MOVE.L $C(A5),D0
crs[]:=$C;crs++
ELSEIF self.cte=VAR_y
prs[]:=$202D;prs++ -> MOVE.L $8(A5),D0
prs[]:=$8;prs++
ENDIF
ELSEIF self.func=fconstant
prs[]:=$203C ;prs++ -> MOVE.L #self.cte,D0
prs[]:=Shr(self.cte,16);prs++;prs[]:=self.cte ;prs++ -> constant argument
ENDIF
RETURN
ENDIF
/* First of all calculate subexpressions that need to be calculated */
IF long(self.arg1)
self.arg1.subcompile()
IF long(self.arg2) -> If we have two args that need computation, then we'll have to save one to do the other one...
IF self.arg1.need AND NEEDX -> Neither arg1, nor us can pre-calculate.
c++
crs[]:=$2B40;crs++-> MOVE.L D0,$-<c*4>(A5)
crs[]:=-c*4 ;crs++-> store that arg
saved:=TRUE
ELSEIF (self.need AND NEEDX) = $0
p++
prs[]:=$2B40;prs++-> MOVE.L D0,$-<p*4>(A5)
prs[]:=-p*4 ;prs++-> store that arg
saved:=TRUE
ENDIF
ENDIF
IF self.need AND Not(self.arg1.need) AND NEEDX -> save pre-calculated arg1 to A1
s++
buff:=s
prs[]:=$227C;prs++;prs[]:=Shr(buffer,16);prs++; prs[]:=buffer;prs++ ->MOVE.L buffer,A1
prs[]:=$2340;prs++;prs[]:=s*4;prs++ -> MOVE.L D0,<s*4>(A1)
ENDIF
ENDIF
IF long(self.arg2)
self.arg2.subcompile()
IF self.need AND Not(self.arg2.need) AND NEEDX -> arg2 doesn't needx but we do => save pre-calculated arg2 to A1
s++
prs[]:=$227C;prs++;prs[]:=Shr(buffer,16);prs++; prs[]:=buffer;prs++ ->MOVE.L buffer,A1
prs[]:=$2340;prs++;prs[]:=s*4;prs++ -> MOVE.L D0,<s*4>(A1)
ENDIF
ENDIF
/* Now put arguments in stack and execute function. */
IF long(self.arg1)=FALSE -> I assume that self.arg1 exists
IF self.arg1.func=fvariable
IF self.arg1.cte=VAR_x
crs[]:=$2F2D;crs++ -> MOVE.L $C(A5),-(A7)
crs[]:=$C;crs++
ELSEIF self.arg1.cte=VAR_y
write($2F2D,self.need) -> MOVE.L $8(A5),-(A7)
write($8,self.need) -> "write", as we wouldn't need pre-calculation if there's only a single y.
ENDIF
ELSEIF self.arg1.func=fconstant
write($2F3C,self.need) -> MOVE.L #self.cte,-(A7)
write(Shr(self.arg1.cte,16),self.need);write(self.arg1.cte,self.need)
ENDIF
ELSEIF saved
IF self.need AND NEEDX
crs[]:=$2F2D ;crs++ -> MOVE.L $-<c*4>(A5),-(A7)
crs[]:=-c*4 ;crs++-> push copied argument (a)
c--
ELSE
prs[]:=$2F2D ;prs++ -> MOVE.L $-<p*4>(A5),-(A7)
prs[]:=-p*4 ;prs++-> push copied argument (a)
p--
ENDIF
ELSEIF buff>-1 -> Copy pre-calculated value.
crs[]:=$227C;crs++;crs[]:=Shr(buffer,16);crs++; crs[]:=buffer;crs++ ->MOVE.L buffer,A1
crs[]:=$2F29;crs++ -> MOVE.L <buff*4>(A1),-(A7)
crs[]:=buff*4;crs++
ELSE -> Long arg1(x), Short arg2
IF self.need AND NEEDX
crs[]:=$2F00 ;crs++ -> MOVE.L D0,-(A7)
ELSE
prs[]:=$2F00 ;prs++ -> MOVE.L D0,-(A7)
ENDIF
ENDIF
IF self.arg2
IF long(self.arg2)
IF self.need AND NEEDX
IF self.arg2.need AND NEEDX
crs[]:=$2F00 ;crs++ -> MOVE.L D0,-(A7)
ELSE
crs[]:=$227C;crs++;crs[]:=Shr(buffer,16);crs++; crs[]:=buffer;crs++ ->MOVE.L buffer,A1
crs[]:=$2F29;crs++ -> MOVE.L <s*4>(A1),-(A7)
crs[]:=s*4;crs++
ENDIF
ELSE
prs[]:=$2F00 ;prs++ -> MOVE.L D0,-(A7)
ENDIF
ELSE
IF self.arg2.func=fvariable
IF self.arg2.cte=VAR_x
crs[]:=$2F2D;crs++ -> MOVE.L $C(A5),-(A7)
crs[]:=$C;crs++
ELSEIF self.arg2.cte=VAR_y
write($2F2D,self.need) -> MOVE.L $8(A5),-(A7)
write($8,self.need) -> "write", as we wouldn't need pre-calculation if there's only a single y.
ENDIF
ELSEIF self.arg2.func=fconstant
write($2F3C,self.need) -> MOVE.L #self.arg2.cte,-(A7)
write(Shr(self.arg2.cte,16),self.need);write(self.arg2.cte,self.need) -> constant argument
ENDIF
ENDIF
ELSE -> No arg2: push zero
write($2F3C,self.need) -> MOVE.L #0,-(A7)
write(0,self.need);write(0,self.need)
ENDIF
IF self.need AND NEEDX
crs[]:=$2F3C ;crs++ -> MOVE.L #self.cte,-(A7)
crs[]:=Shr(self.cte,16);crs++;crs[]:=self.cte ;crs++
crs[]:=$4EB9 ;crs++ -> JSR self.evaluate
crs[]:=Shr(self.evaluate,16);crs++;crs[]:=self.evaluate ;crs++
ELSE
prs[]:=$2F3C ;prs++ -> MOVE.L #self.cte,-(A7)
prs[]:=Shr(self.cte,16);prs++;prs[]:=self.cte ;prs++
prs[]:=$4EB9 ;prs++ -> JSR self.evaluate
prs[]:=Shr(self.evaluate,16);prs++;prs[]:=self.evaluate ;prs++
ENDIF
ENDPROC
PROC write(value,need) -> Little loss of time here at pre-compilation-time, but it is nicer so.
IF need AND NEEDX
crs[]:=value;crs++
ELSE
prs[]:=value;prs++
ENDIF
ENDPROC
PROC reference() OF expression
IF self.func.readpid = TRUE THEN -> I guess that all function that need a data require having its project frozen...
RETURN self.cte,self.arg1,self.arg2
ENDPROC self.func.argcount+1,self.arg1,self.arg2
PROC evaluate(x,y) OF expression
DEF a,b,p
IF self.func=fvariable
p:=self.evaluate
RETURN p(x,y,self.cte)
ENDIF
IF self.func.argcount>0
a:=self.arg1.evaluate(x,y)
IF self.func.argcount>1 ->THEN
b:=self.arg2.evaluate(x,y)
ENDIF
ENDIF
p:=self.evaluate
ENDPROC p(a,b,self.cte)
PROC end() OF expression
IF self.func -> Maybe the decoding was aborted before we could know the function...
IF self.func.argcount>0
END self.arg1
IF self.func.argcount>1 THEN END self.arg2
ENDIF
ENDIF
IF self.calculate THEN
FreeVec(self.calculate)
IF self.precalc THEN
FreeVec(self.precalc)
IF self.buffer THEN
FreeVec(self.buffer)
ENDPROC
/*last(): Exactely like InStr(), but from right to left..*/
PROC last(str,findme,before=-1)
DEF found=0,next
IF before=-1 THEN before:=StrLen(findme)
WHILE ((next:=InStr(str,findme,found)) < before) AND (next <> -1)
found:=next
IF CtrlC()
WriteF('Infinite loop?\nnext=\d;found=\d... [returning -1]\n')
RETURN -1
ENDIF
ENDWHILE
IF found=0 THEN found:=-1
ENDPROC found