Skip to content

Domain Bound Grammars

Dávid Németi edited this page Sep 5, 2013 · 4 revisions

Now that we see what the weak points of conventional grammars, let's try to eliminate the first weak point: indices. To do that we introduce the concept of domain-grammar bindings.

A specific domain-grammar binding is a bidirectional relationship between a specific bnfterm and a specific member (field or property) of a domain type.

A grammar is a domain-bound grammar if it uses domain-grammar bindings.

Here is the improved version of the conventional grammar, which is now a domain-bound grammar using Sarcasm domain-grammar bindings:

using System;
using Irony;
using Irony.Ast;
using Irony.Parsing;
using Sarcasm;
using Sarcasm.GrammarAst;
using Sarcasm.Parsing;

using D = MyExpression.DomainDefinitions;

namespace MyExpression
{
    public class DomainBoundGrammar : Sarcasm.GrammarAst.Grammar
    {
        public DomainBoundGrammar()
            : base(new MyExpression.Domain())    // the grammar needs the domain specific settings while building the AST
        {
            var TerminalFactoryS = new TerminalFactoryS(this);

            // definitions of nonterminals
            
            var expression = new BnfiTermChoiceTL(typeof(D.Expression));
            var binaryExpression = new BnfiTermRecordTL(typeof(D.BinaryExpression));
            var numberLiteral = new BnfiTermRecordTL(typeof(D.NumberLiteral));
            var binaryOperator = new BnfiTermChoiceTL(typeof(D.BinaryOperator));

            // definitions of terminals

            var ADD_OP = TerminalFactoryS.CreateKeyTermTL("+", D.BinaryOperator.Add);
            var SUB_OP = TerminalFactoryS.CreateKeyTermTL("-", D.BinaryOperator.Sub);
            var MUL_OP = TerminalFactoryS.CreateKeyTermTL("*", D.BinaryOperator.Mul);
            var DIV_OP = TerminalFactoryS.CreateKeyTermTL("/", D.BinaryOperator.Div);
            var POW_OP = TerminalFactoryS.CreateKeyTermTL("^", D.BinaryOperator.Pow);

            var LEFT_PAREN = TerminalFactoryS.CreateKeyTerm("(");
            var RIGHT_PAREN = TerminalFactoryS.CreateKeyTerm(")");

            // syntax rules
            
            expression.Rule =
                binaryExpression
                | numberLiteral
                | LEFT_PAREN + expression + RIGHT_PAREN
                ;

            binaryExpression.Rule =
                expression.BindTo(() => new D.BinaryExpression().Term1)
                + binaryOperator.BindTo(() => new D.BinaryExpression().Op)
                + expression.BindTo(() => new D.BinaryExpression().Term2)
                ;

            numberLiteral.Rule = TerminalFactoryS.CreateNumberLiteral().BindTo(() => new D.NumberLiteral().Value);

            binaryOperator.Rule = ADD_OP | SUB_OP | MUL_OP | DIV_OP | POW_OP;

            // operator precedences and associativities
            
            RegisterOperators(10, ADD_OP, SUB_OP);
            RegisterOperators(20, MUL_OP, DIV_OP);
            RegisterOperators(30, Associativity.Right, POW_OP);
        }
    }
}

Now, we don't need to use indices during AST building, in fact we don't need to write a separate AST building code at all, since the domain-grammar bindings take care of it. The domain-grammar bindings have been "weaved" into the grammar, and they determine which bnfterms' AST values go into which domain members.

So we have eliminated the indices, but our grammar is still not typesafe yet. To see how Sarcasm can add typesafety, continue with Typesafe Grammars.

Clone this wiki locally