1 module gamma.parsgen.lalr1.ParserGrammarProperties;
2 
3 import gamma.grammar.Grammar;
4 import gamma.grammar.GrammarProperties;
5 import gamma.grammar.Node;
6 import gamma.grammar.Nonterminal;
7 import gamma.grammar.Symbol;
8 import gamma.grammar.SymbolNode;
9 import gamma.parsgen.lalr1.SCCSetComputation;
10 import gamma.util.Indexed;
11 import log;
12 
13 version (unittest) import gamma.grammar.GrammarBuilder;
14 
15 /**
16  * Extended grammar properties for LR parser generation.
17  */
18 public class ParserGrammarProperties : GrammarProperties
19 {
20     private bool[Indexed][] rightDeriv;
21 
22 	/**
23 	 * Constructor for ParserGramarProperties.
24 	 * @param grammar The grammar of which to present the properties.
25 	 */
26 	public this(Grammar grammar)
27 	{
28 		super(grammar);
29         computeRightDerivationLeaders;
30 	}
31 
32     /**
33      * Computes which nonterminals may appear as the leading symbol in a
34      * right-derivation from any given nonterminal.
35      */
36     private void computeRightDerivationLeaders()
37     {
38         import std.algorithm : map;
39         import std.array : array;
40 
41         Indexed[] nodes = this.grammar.nonterminals
42             .map!(nonterminal => cast(Indexed) nonterminal)
43             .array;
44 
45         rightDeriv = new bool[Indexed][this.grammar.nonterminals.length];
46         SCCSetComputation.computeSets(
47             nodes,
48             new class SCCSetComputation.TransitionEnumerator
49             {
50                 public void visitNeighborsOf( Indexed x, SCCSetComputation.TransitionWalker edge)
51                 {
52                 	assert(cast(Nonterminal) x);
53 
54                     foreach (alt; this.outer.grammar.ruleOf(cast(Nonterminal) x).alternatives)
55                     {
56                         Node[] rhs = alt.rhs;
57 
58                         if (rhs.length > 0)
59                         {
60                             Symbol y = (cast(SymbolNode) rhs[0]).symbol;
61 
62                             if (cast(Nonterminal) y)
63                                 edge.walk(x, cast(Nonterminal) y);
64                         }
65                     }
66                 }
67             },
68             new class SCCSetComputation.SetOperator
69             {
70                 public void initSet(Indexed x)
71                 {
72                     rightDeriv[x.index] = null;
73                     rightDeriv[x.index][x] = true;
74                 }
75 
76                 public void includeSet(Indexed x, Indexed y)
77                 {
78                     foreach (indexed; rightDeriv[y.index].byKey)
79                         rightDeriv[x.index][indexed] = true;
80                 }
81             });
82 
83         // Debug output
84         foreach (nont1; this.grammar.nonterminals)
85         {
86             Nonterminal[] nonterminals;
87 
88             foreach (indexedNont; rightDeriv[nont1.index].byKey)
89             {
90             	assert(cast(Nonterminal) indexedNont);
91 
92                 nonterminals ~= cast(Nonterminal) indexedNont;
93             }
94             trace!"%s right derives %-(%s, %)"(nont1, nonterminals);
95         }
96     }
97 
98     /**
99      * Yields an iterator that iterates the nonterminals which may appear
100      * as the leading symbol in a right-derivation from the given nonterminal
101      * <code>symbol</code>.
102      */
103     auto rightDerivationLeadersOf(Nonterminal nont)
104     {
105         return rightDeriv[nont.index].byKey;
106     }
107 }
108 
109 @("compute right-derivation leaders of grammar from Tremblay and Sorenson")
110 unittest
111 {
112     with (TestGrammarBuilder())
113     {
114         rule("G: E = E | f");
115         rule("E: T | E + T");
116         rule("T: f | T * f");
117 
118         with (new ParserGrammarProperties(grammar))
119         {
120             auto rightDerivationLeaders(string representation)
121             {
122                 import std.algorithm : map, sort;
123                 import std.array : array;
124 
125                 return rightDerivationLeadersOf(buildNonterminal(representation))
126                     .map!(indexed => cast(Symbol) indexed)
127                     .map!"a.toString"
128                     .array
129                     .sort
130                     .array;
131             }
132 
133             assert(rightDerivationLeaders("G") == ["E", "G", "T"]);
134             assert(rightDerivationLeaders("E") == ["E", "T"]);
135             assert(rightDerivationLeaders("T") == ["T"]);
136         }
137     }
138 }