Pliant compiler machinery
This article is assuming that you already red and understood Pliant meta programming basic concepts. It is intended to explain how the Pliant compiler works.
The parser (stage 1 to stage 2 transition)
This article is explaining how the Pliant parser internally works. If you want to just understand how to use it, or define simple extensions, rather refer to Pliant syntax article.
The outermost function is 'compile_text' at the end of /pliant/language/parser/engine.pli
What 'ParserContext_parse_one_token' does is:
Parsing filters do three things:
Among the token filters, some play a special role.
'ParserContext_execute' first calls 'ParserContext_fold_operators',
'ParserContext_fold_operators' just calls each folding functions according to operators priority.
A sample is provided in /pliant/sample/parser.pli module that defines a new set of parsing rules from scratch, implementing a tiny subset of C syntax.
The compiler (stage 2 to stage 3 transition)
We have seen in the previous section that the parser will end through calling 'Expression_execute'.
'Expression_compile' is defined in /pliant/language/compiler/expression/expression.c
First, 'Expression_compile_step2' will call 'Expression_compile_step3' which is just a wrapper to 'Expression_compile_step4',
function foo -> s
Compiling (foo len) directly fails because 'foo' function is expecting no argument.
'Expression_compile_step4' that is responsible to do effective compiling of the expression, that is attaching an instructions list and a result argument, does very few in facts. Beside setting a error handler and doing cleanups, it just calls 'call_active' function on the expression node value.
'call_active' is implemented in /pliant/language/compiler/active.c
As a summary, everything we have seen starting from 'Expression_compile' is mostly glue code. The effective code that will compile expressions are the active_type_xxx functions defined in /pliant/language/compiler/active.c
As an example, 'active_type_Ident' is responsible to compile a node with an identifier at it's root. What it will do is scan the main dictionary for available definitions, and try each of them. Then it will check that one of the definitions is clearly better because it casts less arguments, or 'weak' and 'strong' attributes have been used to provide guidelines.
'active_type_Function' will check that the number of arguments is correct, and that each argument can be casted to the type selected in the prototype.
'active_type_Meta' will just call the meta function and let do the job.
'active_type_Argument' will try to compile (a b c) as ('. b' a c), then ('' a b c)
In the introduction to meta programming article, I have explained that compiling a stage 2 expression means attaching to it a stage 3 instructions list and a result argument.
'Expression_cast3' which is the center of the Pliant casting machinery is looping on two functions to do the job:
'Expression_test_cast_to' returns a boolean specifying if it succeeded.
and it means that (assuming that we want to cast to something to 'Float' target data type) it's scanning the Pliant general definitions dictionary for 'cast Float' definitions, and assuming that we find a casting function from 'Int' to 'Float', it will recusively call itself with 'Int' as the target data type.
Back to meta programming overview, the stage 3 instruction list and argument we attach to the stage 2 expression is just a bit mode complicated. The instructions list has to part. The head is computing the uncasted result, and the tail is casting to the expected data type. Now there is a result argument associated with the head of the instructions list that specify the uncasted result, and another one associated with the tail that specifies the final result.
The code generator (stage 3 to stage 4 transition)
'Expression_execute' will call 'GeneratorContext_optimize' to perform the stage 3 (instructions list) to stage 4 (processor native code) transition.
'GeneratorContext_optimize' is fairly simple.
↣ Meta programmation illustration
Feel free to ask questions if you have problem to understand how some existing code generator filter works.
This section is intended to list all hooks available in Pliant compiler machinery, also I'm sure I have forgotten some at the moment:
'Expression_precompile_rewrite' calls all functions with name 'pliant precompile rewrite' at the end of the parser process, after nodes folding, before compiling the expression.
'Expression_failedtocompile_rewrite' calls all functions with name 'pliant failedtocompile rewrite' when compiling an expression the standard way failed.
'Expression_postcompile_rewrite' calls all functions with name 'pliant postcompile rewrite' at the end of the compiler process, after attaching the instructions list, before generating code.
'pliant_generator_context_begin_hooks' is a list of functions that are called before executing any code generator filter.