OLD | NEW |
| (Empty) |
1 part of petitparser; | |
2 | |
3 /** | |
4 * A builder that allows the simple definition of expression grammars with | |
5 * prefix, postfix, and left- and right-associative infix operators. | |
6 * | |
7 * The following code creates the empty expression builder: | |
8 * | |
9 * var builder = new ExpressionBuilder(); | |
10 * | |
11 * Then we define the operator-groups in descending precedence. The highest | |
12 * precedence have the literal numbers themselves: | |
13 * | |
14 * builder.group() | |
15 * ..primitive(digit().plus() | |
16 * .seq(char('.').seq(digit().plus()).optional()) | |
17 * .flatten().trim().map((a) => double.parse(a))); | |
18 * | |
19 * Then come the normal arithmetic operators. Note, that the action blocks recei
ve | |
20 * both, the terms and the parsed operator in the order they appear in the parse
d | |
21 * input. | |
22 * | |
23 * // negation is a prefix operator | |
24 * builder.group() | |
25 * ..prefix(char('-').trim(), (op, a) => -a); | |
26 * | |
27 * // power is right-associative | |
28 * builder.group() | |
29 * ..right(char('^').trim(), (a, op, b) => math.pow(a, b)); | |
30 * | |
31 * // multiplication and addition is left-associative | |
32 * builder.group() | |
33 * ..left(char('*').trim(), (a, op, b) => a * b) | |
34 * ..left(char('/').trim(), (a, op, b) => a / b); | |
35 * builder.group() | |
36 * ..left(char('+').trim(), (a, op, b) => a + b) | |
37 * ..left(char('-').trim(), (a, op, b) => a - b); | |
38 * | |
39 * Finally we can build the parser: | |
40 * | |
41 * var parser = builder.build(); | |
42 * | |
43 * After executing the above code we get an efficient parser that correctly | |
44 * evaluates expressions like: | |
45 * | |
46 * parser.parse('-8'); // -8 | |
47 * parser.parse('1+2*3'); // 7 | |
48 * parser.parse('1*2+3'); // 5 | |
49 * parser.parse('8/4/2'); // 2 | |
50 * parser.parse('2^2^3'); // 256 | |
51 */ | |
52 class ExpressionBuilder { | |
53 final List<ExpressionGroup> _groups = new List(); | |
54 | |
55 /** | |
56 * Creates a new group of operators that share the same priority. | |
57 */ | |
58 ExpressionGroup group() { | |
59 var group = new ExpressionGroup(); | |
60 _groups.add(group); | |
61 return group; | |
62 } | |
63 | |
64 /** | |
65 * Builds the expression parser. | |
66 */ | |
67 Parser build() => _groups.fold( | |
68 failure('Highest priority group should define a primitive parser.'), | |
69 (a, b) => b._build(a)); | |
70 } | |
71 | |
72 /** | |
73 * Models a group of operators of the same precedence. | |
74 */ | |
75 class ExpressionGroup { | |
76 | |
77 /** | |
78 * Defines a new primitive or literal [parser]. | |
79 */ | |
80 void primitive(Parser parser) { | |
81 _primitives.add(parser); | |
82 } | |
83 | |
84 Parser _build_primitive(Parser inner) { | |
85 return _build_choice(_primitives, inner); | |
86 } | |
87 | |
88 final List<Parser> _primitives = new List(); | |
89 | |
90 /** | |
91 * Adds a prefix operator [parser]. Evaluates the optional [action] with the | |
92 * parsed `operator` and `value`. | |
93 */ | |
94 void prefix(Parser parser, [action(operator, value)]) { | |
95 if (action == null) { | |
96 action = (operator, value) => [operator, value]; | |
97 } | |
98 _prefix.add(parser.map((operator) => new _ExpressionResult(operator, action)
)); | |
99 } | |
100 | |
101 Parser _build_prefix(Parser inner) { | |
102 if (_prefix.isEmpty) { | |
103 return inner; | |
104 } else { | |
105 return new SequenceParser([_build_choice(_prefix).star(), inner]).map( | |
106 (tuple) { | |
107 return tuple.first.reversed.fold(tuple.last, (value, result) { | |
108 return result.action(result.operator, value); | |
109 }); | |
110 }); | |
111 } | |
112 } | |
113 | |
114 final List<Parser> _prefix = new List(); | |
115 | |
116 /** | |
117 * Adds a postfix operator [parser]. Evaluates the optional [action] with the | |
118 * parsed `value` and `operator`. | |
119 */ | |
120 void postfix(Parser parser, [action(value, operator)]) { | |
121 if (action == null) { | |
122 action = (value, operator) => [value, operator]; | |
123 } | |
124 _postfix.add(parser.map((operator) => new _ExpressionResult(operator, action
))); | |
125 } | |
126 | |
127 Parser _build_postfix(Parser inner) { | |
128 if (_postfix.isEmpty) { | |
129 return inner; | |
130 } else { | |
131 return new SequenceParser([inner, _build_choice(_postfix).star()]).map( | |
132 (tuple) { | |
133 return tuple.last.fold(tuple.first, (value, result) { | |
134 return result.action(value, result.operator); | |
135 }); | |
136 }); | |
137 } | |
138 } | |
139 | |
140 final List<Parser> _postfix = new List(); | |
141 | |
142 /** | |
143 * Adds a right-associative operator [parser]. Evaluates the optional [action]
with | |
144 * the parsed `left` term, `operator`, and `right` term. | |
145 */ | |
146 void right(Parser parser, [action(left, operator, right)]) { | |
147 if (action == null) { | |
148 action = (left, operator, right) => [left, operator, right]; | |
149 } | |
150 _right.add(parser.map((operator) => new _ExpressionResult(operator, action))
); | |
151 } | |
152 | |
153 Parser _build_right(Parser inner) { | |
154 if (_right.isEmpty) { | |
155 return inner; | |
156 } else { | |
157 return inner.separatedBy(_build_choice(_right)).map((sequence) { | |
158 var result = sequence.last; | |
159 for (var i = sequence.length - 2; i > 0; i -= 2) { | |
160 result = | |
161 sequence[i].action(sequence[i - 1], sequence[i].operator, result); | |
162 } | |
163 return result; | |
164 }); | |
165 } | |
166 } | |
167 | |
168 final List<Parser> _right = new List(); | |
169 | |
170 /** | |
171 * Adds a left-associative operator [parser]. Evaluates the optional [action]
with | |
172 * the parsed `left` term, `operator`, and `right` term. | |
173 */ | |
174 void left(Parser parser, [action(left, operator, right)]) { | |
175 if (action == null) { | |
176 action = (left, operator, right) => [left, operator, right]; | |
177 } | |
178 _left.add(parser.map((operator) => new _ExpressionResult(operator, action)))
; | |
179 } | |
180 | |
181 Parser _build_left(Parser inner) { | |
182 if (_left.isEmpty) { | |
183 return inner; | |
184 } else { | |
185 return inner.separatedBy(_build_choice(_left)).map((sequence) { | |
186 var result = sequence.first; | |
187 for (var i = 1; i < sequence.length; i += 2) { | |
188 result = | |
189 sequence[i].action(result, sequence[i].operator, sequence[i + 1]); | |
190 } | |
191 return result; | |
192 }); | |
193 } | |
194 } | |
195 | |
196 final List<Parser> _left = new List(); | |
197 | |
198 // helper to build an optimal choice parser | |
199 Parser _build_choice(List<Parser> parsers, [Parser otherwise]) { | |
200 if (parsers.isEmpty) { | |
201 return otherwise; | |
202 } else if (parsers.length == 1) { | |
203 return parsers.first; | |
204 } else { | |
205 return new ChoiceParser(parsers); | |
206 } | |
207 } | |
208 | |
209 // helper to build the group of parsers | |
210 Parser _build(Parser inner) { | |
211 return _build_left(_build_right(_build_postfix(_build_prefix(_build_primitiv
e(inner))))); | |
212 } | |
213 } | |
214 | |
215 // helper class to associate operators and actions | |
216 class _ExpressionResult { | |
217 final operator; | |
218 final Function action; | |
219 _ExpressionResult(this.operator, this.action); | |
220 } | |
OLD | NEW |