Day 035 - Some very cool dynamic code compilation and a Windows timer

Page 365 of the tutorial covers dynamic compilation. Here's the demo window:

The Draw button calls a local procedure called DisplayLine. Here's the code:

PROCEDURE DisplayLine()
// Procedure used to draw the curve in the IMAGE control
X,Y are reals			// Cartesian coordinates of curve points
Xold, Yold are reals	// Coordinates of previous dot (when browsing the X axis)

X0, Y0 are reals			// Coordinates of center of image control
X0 = 200
Y0 = 200
// Draw the background and the axes
dStartDrawing(IMG_Chart..FullName)
dBackGround(Bckgrd)
dPen(Bckgrd)
dRectangle(0,0,500,500)
dPen(iBlack)
dLine(0,200,400,200)
dLine(200,0,200,400)
dPen(Line)
//check whether no overflow was caused by zoom
WHEN EXCEPTION
	Info("Error "+ExceptionInfo(errCode),"Caution: zoom value too high.")
	RETURN
END
ErrCompile is string
// Code of the function that was compiled dynamically
sFunctionCode is string = [
Function YValue(Funct, X)
Y is int
%1
RESULT IntegerPart(Y)
]
ErrCompile = Compile("YValue",StringBuild(sFunctionCode,Funct))
					
IF ErrCompile= "" THEN
	Xold = -201							// Calculate the previous values
	Yold = YVALUE(Funct,-201)
	FOR X=-200 TO 200					// Browse the horizontal axis and draw
		Y = YVALUE(Funct,X)
		dLine((XScale*Xold)+X0,-(YScale*Yold)+Y0,(XScale*X)+X0,-(YScale*Y)+Y0)
		Xold = X						// Store the previous values
		Yold = Y
	END
ELSE
	Error("The function could not be compiled dynamically... "+ErrCompile)
END

If you've worked with Clarion, then you can think of this as being along the lines of Evaluate(), only easier. 

This is the code that gets compiled, and it's stored as a string:

sFunctionCode is string = [
Function YValue(Funct, X)
Y is int
%1
RESULT IntegerPart(Y)
]

This function receives two parameters, Funct and X. I was trying to figure out the role of Funct, because I couldn't see how it had any role to play inside the YValue function. And in fact I removed Funct from that definition, and from the two calls to YValue, and the example ran perfectly. So here's my modified version of the code which I think will be easier to follow:

// Code of the function that was compiled dynamically
sFunctionCode is string = [
Function YValue( X)
Y is int
%1
RESULT IntegerPart(Y)
]

ErrCompile = Compile("YValue",StringBuild(sFunctionCode,Funct))
					
IF ErrCompile= "" THEN
	Xold = -201							// Calculate the previous values
	Yold = YVALUE(-201)
	FOR X=-200 TO 200					// Browse the horizontal axis and draw
		Y = YVALUE(X)
		dLine((XScale*Xold)+X0,-(YScale*Yold)+Y0,(XScale*X)+X0,-(YScale*Y)+Y0)
		Xold = X						// Store the previous values
		Yold = Y
	END
ELSE
	Error("The function could not be compiled dynamically... "+ErrCompile)
END

The key is the Compile statement, which uses the StringBuild function to combine two strings into one, using a placeholder. That's what the %1 is doing in the YValue code.

But where does Funct come from? It's the currently selected item in the list box:

If you've selected the first entry, then after StringBuild the function code looks like this:

sFunctionCode is string = [
Function YValue( X)
Y is int
Degree1.Y=(2*X)+30
RESULT IntegerPart(Y)
]

That makes a whole lot more sense. The string is compiled and called in a loop that returns the Y value for the passed value of X. The loop code draws a line from the previous point to this point. 

Every time you select a new formula, that formula is plugged in in place of %1, the code is compiled, and the graph is redrawn. 

That's pretty slick. 

Timers

In WinDev timers can be automatically applied to procedures, or you can handle timers in code. The tutorial only covers automatic timers.

The example window showed a procedure configured as an automatic timer, along with the little circular arrow icon in the upper left corner of the procedure editor that told me the procedure had a timer and on which I could double-click to get at the settings. But it wasn't immediately apparent to me (probably didn't read the tutorial closely enough, bit of a theme emerging here) how to add a timer to an existing procedure. I eventually found that I could do this via the project explorer by right-clicking on the procedure. 

And the settings for the example, which displays clock values:

Since this particular procedure isn't marked to run as a background task, I suspect it's simply being called by a Windows timer event. But true background processing, with database access, is clearly possible. 

sFunctionCode is string = [
Function YValue(Funct, X)
Y is int
%1
RESULT IntegerPart(Y)
]
ErrCompile = Compile("YValue",StringBuild(sFunctionCode,Funct))