Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: petitparser/lib/src/core/expression.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « petitparser/lib/src/core/definition.dart ('k') | petitparser/lib/src/core/parser.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « petitparser/lib/src/core/definition.dart ('k') | petitparser/lib/src/core/parser.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698