Parsing System (v0.8)
|
Static And Dynamic StylesGoldie supports two different styles of interfaces: a static-style and a dynamic-style. Both styles have pros and cons, and are suitable for different purposes. The differences are explained in the comparison chart and examples below. Note that if you use StaticLang to embed a language directly into your program, you can switch back and forth between static and dynamic styles however you wish. Which Style Should I Use?You can use whichever you want. In fact, you can mix and match both in the same code. When in doubt, the recommended rule of thumb is: Use static-style unless what you're trying to do requires dynamic-style (such as Parse or Sample Generic Parse). Note: Static-style doesn't work on DMD 2.057 due to DMD Issue #7375. Comparison Chart
Types and InheritanceConsider this grammar: ! This is grammar "numberList.grm"
"Start Symbol" = <List>
Number = {Digit}+
<List> ::= <List> '+' Number
| Number
|
In both dynamic-style and static-style, there are the types Language, Lexer and Parser. These classes work for any grammar. Static-style additionally subclasses those with the following types, which can *only* be used with the "numberList" grammar: Language_numberList, Lexer_numberList and Parser_numberList. These contain additional/modified functionality that provides better type-safety. The Token type works similarly, but with some extra subclasses: In dynamic-style, all of the language's symbols (ie, Number, <List> and the implicitly-defined Whitespace, Error and EOF) are represented by type Token. That is still true in static-style, but static-style also defines the following types: Subclass of Token:
Subclasses of Token_numberList:
Subclasses of Token_numberList!"Number":
Subclasses of Token_numberList!"<List>":
(Note: A future version of Goldie might be made to accept the actual rule, such as Token_numberList!"<List> ::= <List> '+' Number", instead of a list of separate strings.) See also Ambiguous Symbols. ExamplesThe following examples use the calc grammar. Example: Loading a LanguageStatic-style// Run "goldie-staticlang calc.cgt --pack:myapp.mylangs" at the command-line
import goldie.all;
import myapp.mylangs.calc.all;
// Use 'language_calc'
Dynamic-styleDo the same as static-style, or: import goldie.all;
Language language_calc = Language.loadCGT("calc.cgt");
// Use 'language_calc'
Or (slower) compile a grammar at runtime: import goldie.all;
Language language_calcA = Language.compileGrammar(`...actual grammar here...`);
// -or-
Language language_calcB = Language.compileGrammarFile("calc.grm");
// Use 'language_calcA' or 'language_calcB'
Example: Parsing a sourceStatic-stylestring src = ...;
Token_calc!"<Add Exp>" rootToken = language_calc.parseCode(src).parseTree;
See Token_ for details. ! Dynamic-stylestring src = ...;
Token rootToken = language_calc.parseCodeX(src).parseTreeX;
See also:Example: Checking a token's symbolStatic-style// Check static/compile-time type (ie, not polymorphic)
bool isAddExp(Token_calc!"<Add Exp>" tok)
{
return true;
}
bool isAddExp(Token tok)
{
return false;
}
// Check run-time type (ie, polymorphic)
bool isAddExp(Token_calc tok)
{
return cast(Token_calc!"<Add Exp>")tok is null;
// Or to disambiguate (See the "Ambiguous Symbols" page):
return cast(Token_calc!(SymbolType.NonTerminal, "<Add Exp>"))tok is null;
}
Dynamic-stylebool isAddExp(Token tok)
{
return tok.name == "<Add Exp>";
// Or to disambiguate (See the "Ambiguous Symbols" page):
return tok.name == "<Add Exp>" && tok.type == SymbolType.NonTerminal;
}
Example: Checking a token's rule and retrieving a sub-tokenStatic-styleToken_calc!"<Mult Exp>" getMultExp(Token_calc!"<Add Exp>" tok)
{
// This represents the rule: <Add Exp> ::= <Add Exp> '+' <Mult Exp>
if( auto t = cast(Token_calc!("<Add Exp>", "<Add Exp>", "+", "<Mult Exp>"))tok )
{
return t.sub!2;
}
}
Dynamic-styleToken getMultExp(Token tok)
{
// This represents the rule: <Add Exp> ::= <Add Exp> '+' <Mult Exp>
if( tok.matches("<Add Exp>", "<Add Exp>", "+", "<Mult Exp>") )
{
return tok[2];
}
}
Example: Finding a descendant tokenStatic-stylealias Token_calc Tok; // For convenience
// Returns:
// this
// .sub!(/+ the first <Mult Exp> +/)
// .sub!(/+ the first <Negate Exp> +/)
// .sub!(/+ the first <Value> that was created from <Value> ::= Number +/);
//
// If not found, returns null
Tok!("<Value>","Number") foo(Token tok)
{
return tok.get!( Tok!"<Mult Exp>", Tok!"<Negate Exp>", Tok!("<Value>","Number") )();
}
Dynamic-style// Returns:
// this
// [/+ the first <Mult Exp> +/]
// [/+ the first <Negate Exp> +/]
// [/+ the first <Value> that was created from <Value> ::= Number +/];
//
// If not found, returns null
Token foo(Token tok)
{
auto t = tok.get( ["<Mult Exp>", "<Negate Exp>", "<Value>"] );
if(t && t.matches("<Value>", "Number"))
return t;
return null;
}
See also:Example: Traverse each node in a parse tree, with optional filtering
See documentation of traverse for examples.
More ExamplesFor a full-program example of static-style versus dynamic-style, see Calculator Static and Calculator Dynamic. |