| 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 |