Module Compiler

The Reflexive Compiler Module

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
   end

   > "The main part of the module"
'

try
   // 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
end

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
   fromLoader()
end
The loader will look like this:
load compiler
c = Compiler()

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

// 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." )
   end
end
Then the loader may do:
load compiler
c = Compiler()

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

// 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
obj.callProp()

As expected, the result is:

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

Classes

CompilerMain interface to the reflexive compiler.
ICompilerInteraface to incremental evaluator.
ModuleHandle to loaded modules.
_BaseCompiler Abstract base class for the Compiler and ICompiler classes.

Made with faldoc 2.2.1