Parsing System (v0.4)
Goldie Home (v0.4) -> 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, but the recommended rule of thumb is:

Use static-style unless what you're trying to do requires dynamic-style (such as Parse or Parse Anything).

Comparison Chart

 

Static-style

Dynamic-style

Primary Benefits

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

Ideal Uses

  • You're processing a very specific language.
  • You need high reliability.
  • You're creating a general language processing tool, like Parse or Parse Anything.
  • 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. Can use any user-supplied language, not just ones known by the developer at development-time. This enables programs like Parse and Parse Anything 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 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 = Goldie.loadCGT("calc.cgt"); // Use 'language_calc'

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;

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.subX[2]; } }

More Examples

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