1 /**
2 This module contains a example of a reusable arithmetic expression grammar and
3 a recursive ParseTree parser that returns the evaluation of the arithmetic expression
4 */
5 module pegged.examples.arithmetic;
6 
7 import std.conv: to;
8 
9 import pegged.grammar;
10 
11 mixin(grammar(`
12 # Arithmetic grammar with variable terminal
13 Arithmetic:
14     Term     < Factor (Add / Sub)*
15     Add      < "+" Factor
16     Sub      < "-" Factor
17     Factor   < Primary (Mul / Div)*
18     Mul      < "*" Primary
19     Div      < "/" Primary
20     Primary  < Parens / Neg / Number / Variable
21     Parens   < :"(" Term :")"
22     Neg      < "-" Primary
23     Number   < ~([0-9]+)
24     Variable <- identifier
25 `));
26 
27 mixin(grammar(`
28 # Arithmetic grammar without variable terminal
29 ArithmeticNoVar:
30     Term     < Factor (Add / Sub)*
31     Add      < "+" Factor
32     Sub      < "-" Factor
33     Factor   < Primary (Mul / Div)*
34     Mul      < "*" Primary
35     Div      < "/" Primary
36     Primary  < Parens / Neg / Number
37     Parens   < :"(" Term :")"
38     Neg      < "-" Primary
39     Number   < ~([0-9]+)
40 `));
41 
42 /**
43  * Parses a ParseTree as a arithmetic expression
44  * @param T arithmetic type. By default is float.
45  * @param grammarName Name of the arithmetic grammar. Must be "ArithmeticNoVar" or "Arithmetic". By default is "ArithmeticNoVar"
46  * @param ParseTree p ParseTree generated by ArithmeticNoVar
47  * @param variable Associative array with variable values
48  * @return The result of the arithmetic expresion. If the ParseTree is invalid
49  *  or contains unexpected nodes, then will return NaN if T is a float or 0
50  *  if T is a integral
51  */
52 T parseArithmetic(T = float, string grammarName = "ArithmeticNoVar")(ParseTree p,
53     const ref T[string] variables)
54 if (__traits(isArithmetic, T) && (grammarName == "ArithmeticNoVar" || grammarName == "Arithmetic"))
55 {
56     switch (p.name)
57     {
58         case grammarName:
59             return parseArithmetic!(T, grammarName)(p.children[0], variables);
60         case grammarName ~ ".Term":
61             T val;
62             static if (__traits(isFloating, T)) {
63                 val = 0.0; // float init is NaN
64             }
65             foreach(child; p.children) {
66                 val += parseArithmetic!(T, grammarName)(child, variables);
67             }
68             return val;
69         case grammarName ~ ".Add":
70             return parseArithmetic!(T, grammarName)(p.children[0], variables);
71         case grammarName ~ ".Sub":
72             return -parseArithmetic!(T, grammarName)(p.children[0], variables);
73         case grammarName ~ ".Factor":
74             import std.string : lastIndexOf;
75             T val = to!T(1.0);
76             foreach(child; p.children) {
77                 const childName = child.name[lastIndexOf(child.name, '.') + 1..$];
78                 if (childName == "Div") {
79                     val /= parseArithmetic!(T, grammarName)(child, variables);
80                 } else { // Process Primary and Mul nodes
81                     val *= parseArithmetic!(T, grammarName)(child, variables);
82                 }
83             }
84             return val;
85         case grammarName ~ ".Mul":
86             return parseArithmetic!(T, grammarName)(p.children[0], variables);
87         case grammarName ~ ".Div":
88             return parseArithmetic!(T, grammarName)(p.children[0], variables);
89         case grammarName ~ ".Primary":
90             return parseArithmetic!(T, grammarName)(p.children[0], variables);
91         case grammarName ~ ".Parens":
92             return parseArithmetic!(T, grammarName)(p.children[0], variables);
93         case grammarName ~ ".Neg":
94             return -parseArithmetic!(T, grammarName)(p.children[0], variables);
95         case grammarName ~ ".Number":
96             return to!T(p.matches[0]);
97         case grammarName ~ ".Variable":
98             return variables[p.matches[0]];
99         default:
100             return T.init;
101     }
102 }
103 
104 /**
105  * Parses a ParseTree as a arithmetic expression
106  * @param T arithmetic type. By default is float.
107  * @param grammarName Name of the arithmetic grammar. Must be "ArithmeticNoVar" or "Arithmetic". By default is "ArithmeticNoVar"
108  * @param ParseTree p ParseTree generated by ArithmeticNoVar
109  * @return The result of the arithmetic expresion. If the ParseTree is invalid
110  *  or contains unexpected nodes, then will return NaN if T is a float or 0
111  *  if T is a integral
112  */
113 T parseArithmetic(T = float, string grammarName = "ArithmeticNoVar")(ParseTree p)
114 {
115     // TODO If someone know how declare a empty T[string] as default paramater value, replace this!
116     T[string] emptyDic;
117     return parseArithmetic!(T, grammarName)(p, emptyDic);
118 }
119 
120 unittest
121 {   // Testing parsing arithmetic expression without variables
122     import std.stdio : writeln;
123 
124     string testExpression = "1 + 2 * (3 + 10 / 4)";
125     auto pNoVar = ArithmeticNoVar(testExpression);
126 
127     //writeln(pNoVar);
128     writeln(pNoVar.matches);
129 
130     assert(pNoVar.successful);
131 
132     // Parsing as a float
133     const f = parseArithmetic(pNoVar);
134     assert(f == 12.0f);
135 
136     // Parsing as integer
137     const i = parseArithmetic!int(pNoVar);
138     assert(i == 11);
139 }
140 
141 unittest
142 {
143     // Testing parsing arithmetic expression witht variables
144     import std.stdio : writeln;
145 
146     string testExpressionWithVar = "1 + 2 * (3 + 10 / 4) + fooBar";
147     auto p = Arithmetic(testExpressionWithVar);
148 
149     //writeln(p);
150     writeln(p.matches);
151 
152     assert(p.successful);
153 
154     // Parsing as a float
155     const float[string] fVars = ["fooBar": 10.25f];
156     const fWithVar = parseArithmetic!(float, "Arithmetic")(p, fVars );
157 
158     assert(fWithVar == 22.25f);
159 
160     // Parsing as integer
161     const iVars = ["fooBar": 10];
162     const iWithVar = parseArithmetic!(int, "Arithmetic")(p, iVars);
163 
164     assert(iWithVar == 21);
165 }
166 
167 
168 unittest
169 {
170     // Some additional test borrowed from simple_arithmetic
171     float interpreter(string expr)
172     {
173         auto p = ArithmeticNoVar(expr);
174         return parseArithmetic(p);
175     }
176 
177     assert(interpreter("1") == 1.0);
178     assert(interpreter("-1") == -1.0);
179     assert(interpreter("1+1") == 2.0);
180     assert(interpreter("1-1") == 0.0);
181 
182     assert(interpreter("1+1+1") == 3.0);
183     assert(interpreter("1-1-1") == -1.0);
184     assert(interpreter("1+1-1") == 1.0);
185     assert(interpreter("1-1+1") == 1.0);
186     assert(interpreter("-1+1+1") == 1.0);
187 
188     assert(interpreter("(-1+1)+1") == 1.0);
189     assert(interpreter("-1+(1+1)") == 1.0);
190     assert(interpreter("(-1+1+1)") == 1.0);
191     assert(interpreter("1-(1-1)") == 1.0);
192 
193     assert(interpreter("1*1") == 1.0);
194     assert(interpreter("1/1") == 1.0);
195     assert(interpreter("-1*1") == -1.0);
196     assert(interpreter("-1/1") == -1.0);
197 
198     assert(interpreter("1+2*3") == 7.0);
199     assert(interpreter("1-2*3") == -5.0);
200     assert(interpreter("-1-2*-3") == 5.0);
201     assert(interpreter("-1+2*-3") == -7.0);
202 
203     assert(interpreter("1/2/(1/2)") == 1.0);
204     assert(interpreter("1/2/1/2") == .25);
205     assert(interpreter("1 - 2*3 - 2*3") == -11.0);
206 
207     assert(interpreter("2*3*3 - 3*3 + 3*4") == 21.0);
208     assert(interpreter("2 * 3 * 3 - 3 * (3 + 3 * 4)") == -27.0);
209 }