OLD | NEW |
1 part of petitparser; | 1 part of petitparser; |
2 | 2 |
3 /** | 3 /// Helper to conveniently define and build complex, recursive grammars using |
4 * Helper to conveniently define and build complex, recursive grammars using | 4 /// plain Dart code. |
5 * plain Dart code. | 5 /// |
6 * | 6 /// To create a new grammar definition subclass [GrammarDefinition]. For every |
7 * To create a new grammar definition subclass [GrammarDefinition]. For every | 7 /// production create a new method returning the primitive parser defining it. |
8 * production create a new method returning the primitive parser defining it. | 8 /// The method called [start] is supposed to return the start production of the |
9 * The method called [start] is supposed to return the start production of the | 9 /// grammar. To refer to a production defined in the same definition use [ref] |
10 * grammar. To refer to a production defined in the same definition use [ref] | 10 /// with the function reference as the first argument. |
11 * with the function reference as the first argument. | 11 /// |
12 * | 12 /// Consider the following example to parse a list of numbers: |
13 * Consider the following example to parse a list of numbers: | 13 /// |
14 * | 14 /// class ListGrammarDefinition extends GrammarDefinition { |
15 * class ListGrammarDefinition extends GrammarDefinition { | 15 /// start() => ref(list).end(); |
16 * start() => ref(list).end(); | 16 /// list() => ref(element) & char(',') & ref(list) |
17 * list() => ref(element) & char(',') & ref(list) | 17 /// | ref(element); |
18 * | ref(element); | 18 /// element() => digit().plus().flatten(); |
19 * element() => digit().plus().flatten(); | 19 /// } |
20 * } | 20 /// |
21 * | 21 /// Since this is plain Dart code, common refactorings such as renaming a produc
tion |
22 * Since this is plain Dart code, common refactorings such as renaming a product
ion | 22 /// updates all references correctly. Also code navigation and code completion |
23 * updates all references correctly. Also code navigation and code completion | 23 /// works as expected. |
24 * works as expected. | 24 /// |
25 * | 25 /// To attach custom production actions you might want to further subclass your |
26 * To attach custom production actions you might want to further subclass your | 26 /// grammar definition and override overriding the necessary productions defined |
27 * grammar definition and override overriding the necessary productions defined | 27 /// in the superclass: |
28 * in the superclass: | 28 /// |
29 * | 29 /// class ListParserDefinition extends ListGrammarDefinition { |
30 * class ListParserDefinition extends ListGrammarDefinition { | 30 /// element() => super.element().map((value) => int.parse(value)); |
31 * element() => super.element().map((value) => int.parse(value)); | 31 /// } |
32 * } | 32 /// |
33 * | 33 /// Note that productions can be parametrized. Define such productions with posi
tional |
34 * Note that productions can be parametrized. Define such productions with posit
ional | 34 /// arguments and reference to multiple instances by passing the arguments to [r
ef]. |
35 * arguments and reference to multiple instances by passing the arguments to [re
f]. | 35 /// |
36 * | 36 /// Consider extending the above grammar with a parametrized token production: |
37 * Consider extending the above grammar with a parametrized token production: | 37 /// |
38 * | 38 /// class TokenizedListGrammarDefinition extends GrammarDefinition { |
39 * class TokenizedListGrammarDefinition extends GrammarDefinition { | 39 /// start() => ref(list).end(); |
40 * start() => ref(list).end(); | 40 /// list() => ref(element) & ref(token, char(',')) & ref(list) |
41 * list() => ref(element) & ref(token, char(',')) & ref(list) | 41 /// | ref(element); |
42 * | ref(element); | 42 /// element() => ref(token, digit().plus()); |
43 * element() => ref(token, digit().plus()); | 43 /// token(p) => p.token().trim(); |
44 * token(p) => p.token().trim(); | 44 /// } |
45 * } | |
46 */ | |
47 abstract class GrammarDefinition { | 45 abstract class GrammarDefinition { |
48 const GrammarDefinition(); | 46 const GrammarDefinition(); |
49 | 47 |
50 /** | 48 /// The starting production of this definition. |
51 * The starting production of this definition. | |
52 */ | |
53 Parser start(); | 49 Parser start(); |
54 | 50 |
55 /** | 51 /// Returns a parser reference to a production defined by a [function]. |
56 * Returns a parser reference to a production defined by a [function]. | 52 /// |
57 * | 53 /// The optional arguments parametrize the called production. |
58 * The optional arguments parametrize the called production. | |
59 */ | |
60 Parser ref(Function function, [arg1, arg2, arg3, arg4, arg5, arg6]) { | 54 Parser ref(Function function, [arg1, arg2, arg3, arg4, arg5, arg6]) { |
61 var arguments = [arg1, arg2, arg3, arg4, arg5, arg6] | 55 var arguments = [arg1, arg2, arg3, arg4, arg5, arg6] |
62 .takeWhile((each) => each != null) | 56 .takeWhile((each) => each != null) |
63 .toList(growable: false); | 57 .toList(growable: false); |
64 return new _Reference(function, arguments); | 58 return new _Reference(function, arguments); |
65 } | 59 } |
66 | 60 |
67 /** | 61 /// Builds a composite parser from this definition. |
68 * Builds a composite parser from this definition. | 62 /// |
69 * | 63 /// The optional [start] reference specifies a different starting production i
nto |
70 * The optional [start] reference specifies a different starting production in
to | 64 /// the grammar. The optional [arguments] list parametrizes the called product
ion. |
71 * the grammar. The optional [arguments] list parametrizes the called producti
on. | |
72 */ | |
73 Parser build({Function start: null, List arguments: const []}) { | 65 Parser build({Function start: null, List arguments: const []}) { |
74 return _resolve( | 66 return _resolve( |
75 new _Reference(start != null ? start : this.start, arguments)); | 67 new _Reference(start != null ? start : this.start, arguments)); |
76 } | 68 } |
77 | 69 |
78 /** | 70 /// Internal helper to resolve a complete parser graph. |
79 * Internal helper to resolve a complete parser graph. | |
80 */ | |
81 Parser _resolve(_Reference reference) { | 71 Parser _resolve(_Reference reference) { |
82 var mapping = new Map(); | 72 var mapping = new Map(); |
83 | 73 |
84 Parser _dereference(_Reference reference) { | 74 Parser _dereference(_Reference reference) { |
85 var parser = mapping[reference]; | 75 var parser = mapping[reference]; |
86 if (parser == null) { | 76 if (parser == null) { |
87 var references = [reference]; | 77 var references = [reference]; |
88 parser = reference.resolve(); | 78 parser = reference.resolve(); |
89 while (parser is _Reference) { | 79 while (parser is _Reference) { |
90 if (references.contains(parser)) { | 80 if (references.contains(parser)) { |
(...skipping 24 matching lines...) Expand all Loading... |
115 seen.add(child); | 105 seen.add(child); |
116 todo.add(child); | 106 todo.add(child); |
117 } | 107 } |
118 } | 108 } |
119 } | 109 } |
120 | 110 |
121 return mapping[reference]; | 111 return mapping[reference]; |
122 } | 112 } |
123 } | 113 } |
124 | 114 |
125 /** | 115 /// A helper to build a parser from a {@link GrammarDefinition}. |
126 * A helper to build a parser from a {@link GrammarDefinition}. | |
127 */ | |
128 class GrammarParser extends DelegateParser { | 116 class GrammarParser extends DelegateParser { |
129 GrammarParser(GrammarDefinition definition) : super(definition.build()); | 117 GrammarParser(GrammarDefinition definition) : super(definition.build()); |
130 } | 118 } |
131 | 119 |
132 class _Reference extends Parser { | 120 class _Reference extends Parser { |
133 final Function function; | 121 final Function function; |
134 final List arguments; | 122 final List arguments; |
135 | 123 |
136 _Reference(this.function, this.arguments); | 124 _Reference(this.function, this.arguments); |
137 | 125 |
(...skipping 26 matching lines...) Expand all Loading... |
164 | 152 |
165 @override | 153 @override |
166 int get hashCode => function.hashCode; | 154 int get hashCode => function.hashCode; |
167 | 155 |
168 @override | 156 @override |
169 Parser copy() => throw new UnsupportedError('References cannot be copied.'); | 157 Parser copy() => throw new UnsupportedError('References cannot be copied.'); |
170 | 158 |
171 @override | 159 @override |
172 Result parseOn(Context context) => throw new UnsupportedError('References cann
ot be parsed.'); | 160 Result parseOn(Context context) => throw new UnsupportedError('References cann
ot be parsed.'); |
173 } | 161 } |
OLD | NEW |