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 }