Parsing System (v0.9)
Goldie Home (v0.9) -> GoldieLib Overview -> Static And Dynamic Styles

Static And Dynamic Styles

Goldie 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

 

Static-style

Dynamic-style

Primary Benefits

Better compile-time type-safety More run-time flexibility and slightly simpler API

Ideal Uses

  • You're processing a single specific language.
  • You need high reliability.
  • You're creating a general language processing tool, like Parse or Sample Generic Parse.
  • You need to support user-created languages.
  • You're just working on a quick-and-dirty project.

Advance Knowledge of Language

Languages must be known ahead-of-time by the developer. (This is usually the case.) Can use any user-supplied language, not just ones known by the developer at development-time. This enables programs like Parse and Sample Generic Parse to be possible.

Note: Using a language not known at development-time naturally limits what you can do with the language, unless you have a way to programmatically infer semantic meaning from a grammar.

Loading a Language

Use StaticLang to embed the language directly into your program at compile-time.
  • Use StaticLang to embed the language directly into your program at compile-time.
  • OR Load a .cgt file at run-time.
  • OR Compile a grammar definition at run-time.

Token Types

All tokens are subclassed from Token with a different type for each symbol or rule (rule-tokens are subclassed from nonterminal symbol-tokens). All tokens are of the type Token.

Use of Non-Existent Symbol/Rule

Can be caught at compile-time. Only detectable at run-time.

Use of a Different Symbol/Rule Than Expected

Can be caught at compile-time. Only detectable at run-time.

Unhandled Symbols

Can be caught at compile-time. Only detectable at run-time.

Unhandled Rules

Effectively caught at run-time.

(It's possible to catch this at compile-time, but at some point you still have to do a run-time check to see what rule a nonterminal was created from. And that check currently can't detect an unhandled rule at compile-time.)
Only detectable at run-time.

Types and Inheritance

Consider 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":

  • None, because Number is a terminal.

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.

Examples

The following examples use the calc grammar.

Example: Loading a Language

Static-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-style

Do the same as static-style, or:

import goldie.all; Language language_calc = Language.load("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 source

Static-style

string src = ...; Token_calc!"<Add Exp>" rootToken = language_calc.parseCode(src).parseTree;

See Token_{languageName}!{symbol} for details.

Dynamic-style

string src = ...; Token rootToken = language_calc.parseCodeX(src).parseTreeX;

See also:

Example: Checking a token's symbol

Static-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-style

bool 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-token

Static-style

Token_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-style

Token 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 token

Static-style

alias 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

More Examples

For a full-program example of static-style versus dynamic-style, see Calculator Static and Calculator Dynamic.