PDA

View Full Version : How to create a new thinBasic keyword in Delphi



ErosOlmi
20-12-2006, 22:55
OK,

you got thinBasic SDK for Delphi. You open the demo Delphi project but still you didn't figure out how interface is done. I will give you here some few points in some post.

All starts when thinBasic parser find the USES (http://www.thinbasic.com/public/products/thinBasic/help/html/uses.htm) keyword.
When thinBasic encounter USES (http://www.thinbasic.com/public/products/thinBasic/help/html/uses.htm) it start to find the specified module as described in USES (http://www.thinbasic.com/public/products/thinBasic/help/html/uses.htm) help.
So a sentence like


USES "MyDelphi"

will load thinBasic_MyDelphi.dll. To know from where this dll will be loaded see USES (http://www.thinbasic.com/public/products/thinBasic/help/html/uses.htm) help.

OK, now the dll is loaded but what now?
thinBasic search inside the dll for a function named LoadLocalSymbols or _LoadLocalSymbols or LoadLocalSymbols_Delphi.
If none will be found, operation end but script will continue to be executed (but I can imagine a runtime error will be quite close)
If one of those function will be found, it will be executed.
LoadLocalSymbols (and its possible variants) have the duty to initialize any needed code by the module and link new keyword names to internal dll functions using thinBasic_LoadSymbol or thinBasic_LoadSymbol_Delphi function. Please see ThinCore.pas included in thinBasic SDK for Delphi ZIP file for thinBasic_LoadSymbol_Delphi syntax.


OK, but how can thinBasic knows how is the syntax of my new keyword?
Imagine you want to add a new keyword that calculates the length of the hypotenuse of a right triangle.
Delphi has the Hypot function in Math unit. So you want to add to thinBasic keywords.


1. Decide the syntax you want thinBasic will have to parse
Native Hypot Delphi syntax is: n = Hypot(const X: Extended; const Y: Extended): Extended;
thinBasic syntax can be something like: n = Hypot(Side1, Side2)

So thinBasic has to parse:

an open (
a first numeric expression (Side1)
a comma ,
a second numeric expression (Side2)
a closed )




2. Start parsing using exported thinCore.dll fucntionalities.
Responsability to parse the syntax is moved at module level, so you as programmer has the duty to do it. Here is a possible code example of the above syntax:



//----------------------------------------------------------------------------
// Hypot returns the length of the hypotenuse of a right triangle
//--
// thinBasic Syntax: n = Hypot(FirstSide, SecondSide)
//----------------------------------------------------------------------------
function Exec_Hypot() : extended ; stdcall;
var FirstSide, SecondSide : extended;

begin

//---Init to zero to avoid compiler warning about possible undefined return
Exec_Hypot := 0;

//---Parse the open parenthesis
if thinBasic_CheckOpenParens = true then
begin
//---Parse the X numeric expression
thinBasic_ParseNumber(FirstSide);

//---Parse the comma
if thinBasic_CheckComma = true then
begin
//---Parse the Y numeric expression
thinBasic_ParseNumber(SecondSide);

//---Parse the close parenthesis
if thinBasic_CheckCloseParens = true then
begin
//---Execute the function and assign as return value
Exec_Hypot := Hypot(FirstSide, SecondSide) ;
end;

end;

end;

end;

So, to summarize:
1. parse the open parens with thinBasic_CheckOpenParens function
2. parse the first number using thinBasic_ParseNumber(...) passing an extended variable byref
3. parse a comma with thinBasic_CheckComma function
4. parse the second number using thinBasic_ParseNumber(...) passing an extended variable byref
5. parse the close parens with thinBasic_CheckCloseParens function

All those functions has internal error checking so if something goes wrong a thinBasic script run time error is generated.
If all is ok you will have now 2 numbers to be used to calculate hypotenuse.
Assign result value to function return value and the trick is done.

More info will follow if more interest will be expressed.

Regards
Eros

kryton9
21-12-2006, 00:58
I started with your samples before this post even was posted Eros, been spending hours on it. Thanks for this explanation and any coming in the future!!

May I ask why are you parsing the commands as you are with thinBasic_command to parse instead of passing the parameters directly to the dll? Or was this just to show another way to do it without passing parameters that way?

ErosOlmi
21-12-2006, 01:04
Sorry, I do not understand your question.

Remember you are in the script and parser (thinCore.dll) does not know anything about new keyword and its syntax.
thinBasic is interpreting on the fly so it has no clue of what a command syntax is, how many parameters and which type.
thinBasic just give you the tools (exported functions) to make what you prefer.
All thinBasic modules are done in this way. Petr TBGL module does exactly that but using PowerBasic as development language.

Not sure I've replied correctly to your question. If not please give me more info.

Ciao
Eros

kryton9
21-12-2006, 01:19
I will play with it Eros and see if I can give an example, will post back in a bit after I see what happens :)

kryton9
21-12-2006, 01:30
Ok here is what I was trying, this is just changing the function in Delphi: I tried to pass the parameter to the function and commented out all the parsing commands, but I see that this will not work as thinBasic is giving me an error about Invalid Delimiter.
But this is what I was trying to ask earlier, nothing like an example :)

//----------------------------------------------------------------------------
FUNCTION Exec_CalculatePowerOfTwo(mvalue : extended) : extended ; stdcall;
//var mvalue : extended;

begin

//IF (thinBasic_CheckOpenParens) = true THEN
//begin
//thinBasic_ParseNumber(mvalue);
// IF (thinBasic_CheckCloseParens) = true THEN
//begin
Exec_CalculatePowerOfTwo := mvalue * mvalue ;
//end;
//end;

END;

ErosOlmi
21-12-2006, 01:43
kryton9,

functions defined inside Delphi cannot have parameters passed because thinBasic cannot know the number of parameters to pass.
Functions in modules are compiled and there is no way to know anything about them. For this reason the only way for the parser to pass one or more value is to pass control of parsing over the module.

So your function should be something like

//----------------------------------------------------------------------------
FUNCTION Exec_CalculatePowerOfTwo() : extended ; stdcall;
var mvalue : extended;

begin

IF (thinBasic_CheckOpenParens) = true THEN
begin
thinBasic_ParseNumber(mvalue);
IF (thinBasic_CheckCloseParens) = true THEN
begin
Exec_CalculatePowerOfTwo := mvalue * mvalue ;
end;
end;

END;

DLL in general has no info about the functions they have inside other than the name of the exported functions. When a dll function is executed it expects that all needed parameters are already in the process stack pushed by calling process. But to do this, calling process has to know EXACTLY how many parameters called function wants and EXACTLY of what type. If something is not correct a GPF is generated by the operating system.

Back to the problem. Imagine you have linked a new keyword "CalculatePowerOfTwo" to the function Exec_CalculatePowerOfTwo
When thinBasic encounter "CalculatePowerOfTwo" keyword, it search into its internal dictionary, found the related module and function pointer and than it PASS THE WHOOLE CONTROL to Exec_CalculatePowerOfTwo BECAUSE thinBasic does not know anything about Exec_CalculatePowerOfTwo.

It can seem quite complex but this is the way all functions are executed.

Another example I can give you. When you call an OS API function like, for example, WinBeep in KERNEL32.DLL, thinBasic is able to execute it only because you have declared as:

DECLARE FUNCTION WinBeep LIB "KERNEL32.DLL" ALIAS "Beep" (BYVAL dwFreq AS DWORD, BYVAL dwDuration AS DWORD) AS LONG
In this way thinBasic knows exactly what parameters and of which type WinBeep need. thinBasic start reading values from the script and before calling WinBeep parameter values (or references) are loaded into the process stack. At this point thinBasic can call WinBeep and WinBeep will start immediatelly to pop parameters from the calling stack. If something is not in the correct sequence in the stack, GPF occurs.

Not sure if I gave you enough and clear info on why things are done in a certain way. Of course there can be other possible roads but this is how it is done now.

Eros

kryton9
21-12-2006, 03:02
Eros it all makes sense the way you explained it. All of this is new to me so as you explain it, I can understand more and more. Thanks you explained it really well and when you think about it, it makes a lot of sense. Just have to think about all that is happening and you really appreciate all that happens and what you have coded!!

I am excited to report that I will soon have a very exciting example, the example is very simple, but you will see that it could lead to great things, mainly that any thinbasic user can download the free Turbo Delphi, use the IDE to develop their forms and then call with one call the form. Hope to have an example up real soon.