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