Create a template for your compiler, and mixin the default signature.
struct myCompiler(ParseTree T) {
mixin Compiler!(T,myCompiler);
// ....
}
The "Compiler" mixin binds the "compileNode" function to a nested scope. We can override the compileNode function
for specific matches in the parse tree. To do this, we use string mixins, that bind to
the local scope. The template "nodeOverride" takes a condition and a function, and creates a local override
that binds the function to "compileNode" if the static condition matches against a tree node. The "compilerOverride" template
binds to a node name.
This goes inside the parser template definition, and in this case we're overriding the behaviour
for a nodes matching a rule named "GRAMMAR.Text". The override template accepts a second string argument
representing the code to be mixed in when the name matches. The code should return a string that will be output
from the compiler, to be mixed in to the users program. In this example, we concantenate the "matches" property.
"T" is an alias to the current node.
// ...
mixin (compilerOverride!("GRAMMAR.Text","T.matches.join(\"\")"));
// ...
Then, we invoke the compiler.
enum _d = q{ ... the input to your parser ... };
mixin Compiler!(myParser,GRAMMAR(_d));
Or we can just pull out the text and not mix it in to our program right away
enum code = myParser!(GRAMMAR(_d)).compileNode();
The compilerOverride template takes two strings:
a string matching the name of the node to override,
and a string representing the code to mixin in order to parse
that particular node type.
A compile-time compiler (meta-compiler).
To use: (see the unittest for a running example)
Create your pegged grammar. enum _g = q{ GRAMMAR: ... }; mixin (grammar(_g));
Create a template for your compiler, and mixin the default signature. struct myCompiler(ParseTree T) { mixin Compiler!(T,myCompiler); // .... }
The "Compiler" mixin binds the "compileNode" function to a nested scope. We can override the compileNode function for specific matches in the parse tree. To do this, we use string mixins, that bind to the local scope. The template "nodeOverride" takes a condition and a function, and creates a local override that binds the function to "compileNode" if the static condition matches against a tree node. The "compilerOverride" template binds to a node name. This goes inside the parser template definition, and in this case we're overriding the behaviour for a nodes matching a rule named "GRAMMAR.Text". The override template accepts a second string argument representing the code to be mixed in when the name matches. The code should return a string that will be output from the compiler, to be mixed in to the users program. In this example, we concantenate the "matches" property. "T" is an alias to the current node. // ... mixin (compilerOverride!("GRAMMAR.Text","T.matches.join(\"\")")); // ...
Then, we invoke the compiler. enum _d = q{ ... the input to your parser ... }; mixin Compiler!(myParser,GRAMMAR(_d));
Or we can just pull out the text and not mix it in to our program right away enum code = myParser!(GRAMMAR(_d)).compileNode();
The compilerOverride template takes two strings: a string matching the name of the node to override, and a string representing the code to mixin in order to parse that particular node type.