logo
The Falcon Programming Language
A fast, easy and powerful programming language
Location: Home page >> Falcon wiki
User ID:
Password:
 

Falcon Wiki - Survival:Advanced topics


Advanced topics

The vast majority of falcon language is now covered. There are still a few of issues that are not covered by the tutorial-like part of this manual, and that are covered here just for reference.

Coroutines

Coroutines are routines that run concurrently in the same Virtual Machine. The VM executes some instructions from one coroutine, then it swaps it out and goes on executing some instructions of another coroutine and so on until the first coroutine is called again. From a user standpoint, it seems that that all the routines are running at the same time.

Coroutines are not OS level threads. Calling a function that can cause the physical machine to block will suspend the execution of all the coroutines. Also, even if the target platform is provided with more than one CPU, all the coroutines will use the one on which the Virtual Machine is running.

The launch statement creates a new coroutine:

launch  function_name ( [function parameters] )

The coroutine is executed by calling the specified function with the required parameters; the execution continues in the same context where launch is called at the next lines, while the coroutine is beginning its processing.

Coroutines can launch other coroutines. Each one is independent from its parent; from a Virtual Machine point of view, the launcher and the new coroutine are identical in every respect. The VM will terminate the execution only when the last coroutine that has been launched completes its operation, or when a exit() function is explicitly called by any of the running coroutines. Of course, coroutines can call other functions, methods, code blocks, even recursively.

A coroutine terminates its execution when the function that was originally launched returns; also, it may terminate in any moment if it calls the yieldOut() function.

The functions named in this chapter are all provided by the core module. Although the core module is physically stored in the virtual machine library, it must be created and linked in the VM as any other module. Embedding applications may override it partially or completely.

Synchronization

Synchronization is a very important aspect under any parallel environment; however, coroutine parallelism is just a "fake" parallelism, and this allows some simplification with respect to a full multi-threading model: while running, each coroutine is completely owning the Virtual Machine. Reads and writes to shared variables can be considered atomic. A coroutine needing more data can swap out and require another coroutine to be executed by calling the yield() function; if it thinks that it won't have anything useful to do for a certain time, it can call the sleep( seconds ) function and it will be called only after the timeout has expired.

The sleep( seconds ) function can also be called when the VM has not started any coroutine; this will make it to be idle for the required time. The parameter may be a floating point number if a pause shorter than a second is required.

If a coroutine is preparing a complex set of data on which other coroutines may depend upon, it can be prevented from being swapped out when the job is not complete by calling the }} {{beginCritical () function; this will force the VM to continue executing the coroutine without any interruption until it signals it is done by calling either yield(), sleep( seconds ) or endCritical () functions. In the latter case, the coroutine may continue its processing if its timeslice was not completely consumed.

Finally, a coroutine may put itself in wait for other parts of the process to be completed. This is done by synchronizing on a semaphore object, created from the Semaphore class.

synch = Semaphore( <initial value> ) 

The semaphore as an integer initial value (zero if not provided) which regulates its behavior. When the value is greater than zero, waiting on the semaphore by using the wait() method will allow the coroutine to continue its processing and will contextually reduce the semaphore value by 1. When the value is zero, the coroutine will be swapped out and won't be swapped in again until the semaphore value is incremented with the post(). In the following example, we use a semaphore to start a coroutine at a time:

function coro( id, syn )
   syn.wait()
   printl( "Coroutine ", id, " started" )
end 
 
semaphore = Semaphore()   // will create a semaphore initially set to 0
 
launch coro( 1, semaphore )
launch coro( 2, semaphore )
launch coro( 3, semaphore )
 
for i in [0:3]
   sleep( 1 )
   semaphore.post()
end 
 
printl( "Main program end" ) 

This script creates a semaphore object that is initially set to 0. When the first coroutine tries to wait on the semaphore, it gets blocked and swapped out. So happens to the other coroutines. Then, the main coroutine sleeps a bit and post on the semaphore. The expected output from this script is as follows:

Coroutine 1 started 
Coroutine 2 started 
Main program end 
Coroutine 3 started 

When some coroutines are waiting and a semaphore is posted, the coroutine that had been waiting for first is the first one to be re-enabled as soon as the current coroutine swaps out. To have a semaphore waiter to immediately punch in after a post(), yield() must be called after that. The sleep() function has actually the same effect, and it also tells the VM that the coroutine calling it shouldn't be reactivated again before a certain time; so after the last semaphore post, as the loop ends, the main coroutine is allowed to proceed before the third coroutine can punch in.

Program internationalization

Some of the strings contained in a Falcon program may be automatically translated into a target language of choice. Using the i character in front of a string, that gets marked as an international string and gets readied to be exported to an XML dictionary. Translators (generally humans) may take this extracted XML file and fill translations for a certain language. An utility called fallc.fal (Falcon language compiler) will then convert one or more translated XML files into a single ftr (Falcon translation).

At link time, the modules will be able to load a translated table instead of their original string table, and will then show the translated strings with 0 overhead.

The falcon command line tool can generate a translation file for a source or a binary module (either an already compiled .fam or a native . dll /. so module) through the -y option. It is then possible to set the language for an application through the command line option -l (applicable to falcon or falrun ). If the language is present in the .ftr files existing besides the loaded modules, it will be used, otherwise the operation will fail silently.

The languages must be indicated through the 5 characters ISO language names, as en_US, en_GB, it_IT, ja_JP, and so on.

A source file willing to be internationalized should declare the language in which is written through the directive language. If not declared, the starting language will be set to C (none).

For example, let's internationalize a small sample file:

directive lang="en_US"
 
> i"Hello world!" 
 
// Let's add some variable... 
var = int( random() * 100 ) 
 
// using string expansion operator in international strings 
> @i"You are $(var)% lucky." 
 
> "Test complete"    // this string will stay untouched.

Saving the file as inat.fal and running the command

$ falcon -y inat.fal 

The file inat.temp.ftt is generated. It is possible to change the name of the output template translation file adding the -o option; for example :

$ falcon -o my_template_file -y inat.fal 

will save the empty translation table into my_template_file.

The contents of the template translation table looks like the following:

<?xml version="1.0" encoding="utf-8"?> 
<translation module="inat" from="en_US" into="Your language code here"> 
<string id="2"> 
<original>Hello world!</original> 
<translated></translated> 
</string> 
<string id="7"> 
<original>You are $(var)% lucky.</original> 
<translated></translated> 
</string> 
</translation> 

Supposing to translate the program into French and Italian, we'll copy this template into two files; their name is not relevant, but it may be convenient to save them as <module_name>.<lang_code>.ftt.

So, we'll write inat.fr_FR.ftt as follows:

<?xml version="1.0" encoding="utf-8"?> 
<translation module="inat" from="en_US" into="fr_FR"> 
<string id="2"> 
<original>Hello world!</original> 
<translated>Bonjour a tout le monde!</translated> 
</string> 
<string id="7"> 
<original>You are $(var)% lucky.</original> 
<translated>Vous avez $(var)% de chances.</translated> 
</string> 
</translation> 

and inat.it_IT.ftt as follows:

<?xml version="1.0" encoding="utf-8"?> 
<translation module="inat" from="en_US" into="it_IT"> 
<string id="2"> 
<original>Hello world!</original> 
<translated>Buongiorno, mondo!</translated> 
</string> 
<string id="7"> 
<original>You are $(var)% lucky.</original> 
<translated>Sei fortunato al $(var)%.</translated> 
</string> 
</translation> 

It is not necessary to translate all the strings; if the translated block is empty, the original string is used instead.

The final step is that to compile the translation tables into a single translation file for a module. This is done through fallc.fal, which works as follows:

$ fallc.fal inat.it_IT.ftt inat.fr_FR.ftt 

The command will inform you about an inat.ftr file being created for the inat module.

Fallc.fal will also check for escaped variables contained in the original strings to be present in the translations; this will prevent mistakes as forgetting a variable or mixing up its name. To turn off this feature, use the -c command line switch.

Now, using the falcon -l option, we can change the language of our program:

$ falcon -l fr_FR inat.fal 
Bonjour a tout le monde! 
Vous avez 84% de chances. 
Test complete 
 
$ falcon -l it_IT inat.fal 
Buongiorno, mondo! 
Sei fortunato al 84%. 
Test complete 

Merging newer versions of the string table

It is possible that the source code which has been internationalized is changed after being translated in various languages. In that case, it is necessary to merge the existing translations with the new template generated by falcon -y. The -m <merge_table> option of fallc.fal loads a template and fixes one or more translations, applying the new string ids, filling the missing new strings and removing unused old ones.

Suppose to change our program adding a new internationalized string at the beginning:

directive lang="en_US"
 
> i"Program begin..." 
//... rest unchanged 

The following command sequence will fix already translated tables and generate a new working ftr.

$ falcon -y inat.fal 
$ fallc.fal -m inat.temp.ftt inat.it_IT.ftt inat.fr_FR.ftt 
$ fallc.fal inat.it_IT.ftt inat.fr_FR.ftt 

Examining the translation tables, you will notice the updated indexes and the new string to be translated.

It is possible to provide a specific different output for the result of the merge using the -o option, but in that case fallc will write only one translation on the designed output stream.

The indirect operator

It is possible to access local, global and imported symbols by name through the unary indirect operator (#). The string can contain a variable followed by an arbitrary sequence of valid accessors; when the indirect operator is applied (either to the literal string or to a variable containing it), the value of the named variable is returned; if the variable is not defined in the VM, or if the accessors are invalid, the VM will raise an access error.

Actually, the string expansion operator is implemented through the indirect operator; so many of the sequences that can be used in string expansion can also be used with the indirect operator.

For example, consider the following code:

var = 2 
 
object test
   prop = [ "zero", "first", "second", "third" ]
end 
 
value = # "test.prop[ var ]" 
printl( "Value is now: ", value ) 

As for the string expansion operator, the indirect operator will translate accessors even recursively (i.e. decoding var as 2 and then accessing the element 2 in the property of the test object), but it will return the desired value.

The indirect operator only supports reading from variables, or accessing them. Function or method calling is not supported.

As the indirect operator is an unary operator, it is possible to apply it more than once in a row to do indirect indirections:

value = 1000 
value_id = "value" 
p_id = "value_id" 
> ## p_id        // prints 1000

Template Files

Falcon provides a “template file” mode that is designed to output an expanded input, not unlike dynamic web oriented languages as ASP and PHP. The escape sequence “<?” or “<?fal” escapes to the Falcon parser. With respect to those languages, Falcon presents three relevant differences. First, the compiler creates a Falcon binary module out of the template file, which can then be processed through normal VM output. In other words, embedded applications can take advantage of template files by intercepting the VM streams, and it is possible to pre-compile active pages and store them as FAM modules directly at the server site to cut compilation time.

Second, it is possible to enter and exit from escaped code at any level; for example, if wide sections of text or whollyeor partially un-escaped code is to be generated inside a loop in a function, it is possible to simply exit the escaped code and enter the plain template text.

Third, it is possible to seamlessly merge pure Falcon modules, template documents and binary modules into a completely integrated template-based application.

In this example, procedural and escape based templates are seamlessly merged:

<HTML>
<HEAD><TITLE>Hello world</TITLE></HEAD>
<BODY>
   <H1>Parameters:</H1>
   <TABLE><TR><TH>Id</TH><TH>Name</TH></TR>
   <? // We'll echo the parameters of this script in a table 
      say_parameters() 
   ?>
   </TABLE>
</BODY>
</HTML>
<? // we'll see a sample of template procedural generation first ...
function say_parameters()
   count = 1
   for elem in args
     print( "<TR>" )
     say_line( count++, elem )
     printl( "</TR>" )
   end
end
//... and then a sample of reverse template
function say_line( id, name )
   // here we exit the code and re­enter the templating.
   // we'll re­escape again to print variables 
?>  <TD><?= id ?></TD><TD><? print( name ) ?></TD><?
   // and here we can close the function.
end ?>

The effect of this test can be seen saving this script as test.ftd and running it with the falcon command line tool.

Let's review: A "Template document" is a document that is parsed as-is and eventually escaped to Falcon. For example, the following test:

A document...
<? print( "Hello world" ) ?>
A second part of the document
<?
   testfunc( args )
?>End of the document
<?
function testfunc( argsp )
   for word in argsp
      > "Argument: ", word
   end
end
?>

Outputs:

pnema@LX-D620:/dev/falcon$ falcon test1 s2
A document...Hello world
A second part of the documentArgument: s2
End of the document 

Together with the Apache module which passes fal, fam and ftd files to the compiler, and provides the script with a webserver integration modules, this constitutes the base for web-based applications written in Falcon.

Meta compilation

Falcon provides a virtual machine that can be directly used by the script compiler. In other words, it is possible to program the compiler and its output directly from a complete inner script. The \[ ... \] escape can contain a meta script, which can also include references to external modules, and everything that is delivered to the standard output stream in that section is fed into the compiler at that position in the host script.

For example :

formula = \[ 
   if SIDE == "left"
      >> "sin"
   else
      >> "cos"
   end
\] ( angle )
 
// or more compact 
formula =   \[ >> (SIDE == "left" ? "sin":"cos") \] ( angle )

This will compile into either one or the other version depending on the SIDE variable (or constant) setting.

The meta compiler also provides a macro command which makes the task of writing meta-scripts a bit simpler:

macro square(x) ( ($x * $x) )
> "9 square is: ", \\square(9) 

Macros can contain arbitrary code; for example they can create classes:

macro makeClassWithProp( name, property ) (
   class $name
      $property = nil
   end
) 
 
\\makeClassWithProp( one, speed ) 
\\makeClassWithProp( two, weight ) 
 
inst_one = one() 
inst_two = two() 
 
> "Instance of class one: " 
inspect( inst_one ) 
 
> "Instance of class two: " 
inspect( inst_two ) 

Macros are actually syntactic sugar for meta-compilation; having defined the macro like in the last example, it is possible to produce a set of classes with this meta-code:

\[ 
   for clname in [ "one", "two", "three", "four" ]
       makeClassWithProp( clname, "property" )
   end
\] 

Attributes

Attributes are arbitrary compile-time symbols to which a static value is associated. Modules, functions, classes and objects can be provided with attributes, that can tell something about their functionality or be used for various purposes.

// a program
stand_alone: true
author: "Giancarlo Niccolai"

function test()
   group: "coolies"
   author: "Rookie"
   
   // do something...
end

// show attributes of this module
inspect( attributes() )

// show attributes of the test function
inspect( test.attributes() )

Module attributes can be inspected by other modules through the Compiler feather module, or by the C++ API in embedding applications.


Navigation
Go To Page...

Loading

Elapsed time: 0.125 secs. (VM time 0.117 secs.)