2.2Reflexive compiler

Falcon provides a reflexive compiler and module loader that allows scripts to load other falcon sources, pre-compiled modules or binary modules. The reflexive compiler is actually an interface to the internal compilation and link engine, so anything that can be done by the falcon command line utility can be done also through this facility.

Falcon code structure is fixed; modules are read-only from a Virtual Machine stand point. This means that Falcon cannot alter the structure of scripts being currently executed. In example, it is not possible to compile a single code line and have it executed in the current script context.

However, the reflexive compiler is so flexible that this limitation is hardly felt. It is possible to load a module, or to compile it on the fly, and then get or set any of the global symbols in the target module. By extracting items from the loaded module, or injecting known items into that, it is possible to configure, alter, and execute arbitrary parts of the loaded module as if it were coded internally to the loader script.

Example usages

The following script shows how a source module may be compiled and executed on the fly.

   load compiler

   // create the compiler
   c = Compiler()

   // try to compile a source string
   str = '
      function func( a )
         > "Hello from compiled source: ", a

      > "The main part of the module"

      // First param is the module logical name.
      sourceMod = c.compile( "aString", str )

      // in case of compilation error, we had an error and we bailed out

      // load the symbol func from our module...
      func = sourceMod.get( "func" )
      // and execute it...
      func( "test param" )

      // execute directly the main module, notice the () at eol
      sourceMod.get( "__main__" )()

   catch CodeError in e
      > "Had an error: ", e

We may also put symbols in target scripts, forcing them to execute some part of the loader code. In example, suppose this is a falcon source called "module1.fal":

   // this global variable will be mangled by the loader
   fromLoader = nil

   function test()
      > "Test from module 1"

      // trustfully call the readied global
   The loader will look like this:
   load compiler
   c = Compiler()

   function saySomething()
      > "Something said from the main module."

   // Load a module by its path.
   mod = c.loadModule( "./module1.fal" )

   // change target symbol
   mod.set( "fromLoader", saySomething )

   // call loaded function
   mod.get( "test" )()

Of course, scripts can exchange objects, classes, methods, functions, lambdas and in general any value.

It is not possible to set directly a global object method in the target module in this way. In example, if the target module defines an object Obj, with a property called prop, it is not possible to call

         mod.set( "Obj.prop", someValue )

However, the object can be imported in the local script, and any change to its structure will be reflected in both the original owner and the loader. In example, if alphadef.fal defines the object:

   object alpha
      prop = nil

      function callProp()
         self.prop( "Called internally from self." )
   Then the loader may do:
   load compiler
   c = Compiler()

   function saySomething( param )
      > "Parameter is: ", param

   // Load a module by its name.
   // It means, search a suitable .fal, .fam or binary module.
   mod = c.loadByName( "alphadef" )

   // get the object
   obj = mod.get( "alpha" )

   // and change the property to something in our script
   obj.prop = saySomething

   // then call it

As expected, the result is:

   $ ./falcon alphaload.fal
   Parameter is: Called internally from self.

Contents of this module

Made with http://www.falconpl.org