 |

8.1 About functions
You have already used one function in all your programs.
It's the main function, that is the start point of our programs.
So why use more functions?
Imagine you have to do some calculation that depends on some input data. And you
have to do it at different points in your program and with differnet values for
the input data. Of course you can write the code each time you need it, but what
if it's a complex calculation or if you have to use it pretty often? And what
if later, you want to adjust it a little bit? You will have to go through all
your program and that can be really difficult.
So, to avoid these ugly situations, you group some parts of your program into functions.
To declare a function, you must write the key word func
followed by it's name, the function parameters
names inside brackets ( ), separated by commas, and then
the instructions block enclosed in braces { }.
func name( parameter1, parameter2, ... )
{
instructions
}
If a function has no parameters, like the main function, then nothing will be written inside the brackets.
You can't declare a function inside other function and you can't declare two functions with the same name.
Choosing names for functions is similar to choosing names for variables.
In a function, you can declare a variable with the same name as another variable from another function.
They will not be mistaken, because such variables are considered local variables,
each in it's function.
To use a function, you must call it, from another function. Meaning you expect
the processor to jump to the called function, execute all it's instructions
starting with the first one, and then return and continue the execution from where
it was, after the call instruction.
To call a function, just write it's name and give values for the expected parameters inside brackets.

Let's have an example (copy all the following code into the "program.txt" file).
The function AddTwoNumbers expects to receive two parametsrs, named a and b. Those
parameters are like local variables and can be usable only inside the function. Their values
come from the caller function and they are 1 and 2 for the first call, and 10
and 20 for the second.
The parameters (also called arguments) types must match their purpose in the function where they are send to.
If a function expects an integer parameter, sending a table instead will produce a bad argument type error.
It's like when you must add some numbers that someone dictates you on the phone,
and suddenly he starts telling you some people's names instead the numbers you're expecting.
You will not be able to add them to your sum, would you?
8.2 Return
As said before, after finishing to execute all instructions from a function, the
processor returns to the caller function, from where it left.
You can force the processor to return before it reaches the last instruction
from the current function, using the return instruction.
Using return in the main function will cause the program to
exit right at that point, since there's no other higher function to return to.
Another smart thing functions can do, is to return a result to the caller function.To
do so, use the return instruction, followed by the
constant value or the variable who's value you want to return. To use such a result, in the caller
function you must store it in a variable, use it in an expresion, or give it to another function as a parameter.
Since the returned value have it's own type, we say that the function returns
that specific type and it can be received only in a variable of the same type.

Here is an example of a function with two parameters that returns their sum.
As you see above, before a function call, all parameters are evaluated,
and only then they are sent to the function.

Take great care when calling functions from themselfs.
It's called recursion and it's a pretty useful but quite advanced technique,
that if not used correctly, may lead to a program crash named stack overflow.
Make sure you always have a returning condition, before the self call. Here is a bad example:
The program above will list numbers starting with 1 and, unless corrected, it
it will quickly produce the stack overflow crash.
8.3 More about parameters

Consider you have a function that receives a table as parameter and it must adjust the values inside it.
As seen from the above code, even if we call the function that should increase
the table's elements, instead of [2,3,4] we have the same [1,2,3] printed. But why?
Parameters are not send directly to the called function, but they are send as duplicates.
This means that our table is duplicated in a temporary memory location and, inside our function,
we change that duplicated table, not the original one.
The changes are made but, when returning back to the main function,
the duplicated table gets lost, and so our changes. So what's to be done?

We can return the changed table and receive it back into the same variable.

Or, instead of sending the whole table as a parameter, we can only send a reference to it
Not only this method solves our problem, but it is also much faster.
Instead of waisting time and memory to duplicate the table, the processor will only send a small reference to it.
And if our table has lots of elements or if we need to write a really fast program, this would make the big difference.
The same thing can be said about sending strings.

For integers, floats or pointers, that are small variables and don't consume memory, this may be used not for speed,
but for receiving multiple values from a function.

Never, ever return from a function a reference to a variable declared inside it, or to one of the received parameters.
This will lead to a program crash, because these kind of local variables are lost when the function returns and
pointing to lost variables is a really bad thing!
Another tricky thing you can do with parameter in the GS9 language is to have functions with variable number of parameters.
That means you can call such function with less parameters than it is declared to expect.
Of course the function must handle such cases, and it will do that by testing if the parameter is declared with the ? operator,
because the missing parameters will be NUL.
Even more, you can handle situations when parameters can come with different types, using the typeof operator.

Here is an example of handling variable number of parameters:
8.4 Global variables
So far, all our variables were local variables, usable only inside the function
where they were declared. But what if we need to access the same variable inside more functions.
We could send it as a parameter to all of them, or we can declare it global
and it will be usable in every function of your program.
You can declare such a global variable, outside any function, with or without
type. To declare it with a specified type, you must write the type's name ( int,
float, str, tab, ref, ptr ) before the variable's name. Or, for no specified
type, you must use the key word var and the variable
it will be associated a type the first time you set a value in it. In the global
space you can declare global variables but you can't initialize them with values.
You must do that somewhere, inside a function.
When declared, a global variable is initialized with a default value, 0 for integers,
0.0 for floats, VOID (invalid) for strings, empty table for tables and NULL for pointers.
A global variable must be declared before the functions that use it, otherwise
those functions will not be able to find it and they will declare a local variable
instead. So the name of a global variable can't be used for local variables and,
as a recomanded convention, their names can begin with "g_" from global.

Here is an example of global variables.
8.5 Advanced access
The processor associates each function and global variable to an internal integer id.
Usually you access functions and global variables in a program by their given names, but internaly,
the processor converts those names into ids to use them.
Of course if you try to access an undeclared function, the processor will not be able to find
a valid id for it's name and will report an error.
This is GS9 language specific behaviour.
GS9 allows you to access those internal ids for advanced access to functions and global variables.
To obtain the internal id of a global variable use gs_gid( variable_name )
To access a global variable using its internal id use var( gid )
To obtain the internal id of a function use gs_fid( variable_name )
To call a function using its internal id use call( p1,p2,...,fid )

Here is an example of such advanced access.
You probably wonder why bother with those internal ids when you could just
use the names of the variables or functions to access them.
Indeed it doesn't makes much sense now, unless used in a specific context.
Imagine you want to write a program that, given an integer value as input,
it will call a function named "callback_1", "callback_2", ... or "callback_100",
depending on that input.
Since the user can give any integer value, it's not recommended to use thousands of "if"s instructions.
Instead, you can form a string with the function name, and seek for a function id associated with that name.
If the function was declared the id will be different than -1 and you can call it.

Such functions are sometimes refered as "callbacks", or "handlers".
This kind of situations is specific to game engines that use script programs,
where the user can write his own functions to respond to different actions in the game.
The engine will look for such functions and call them when the action requires it.
|
 |