| OLD | NEW |
| (Empty) |
| 1 part of petitparser; | |
| 2 | |
| 3 /** | |
| 4 * Helper to compose complex grammars from various primitive parsers. | |
| 5 * | |
| 6 * Note, this class is deprecated in favor of [GrammarDefinition] that provides | |
| 7 * a more flexible way to define composite parsers. | |
| 8 * | |
| 9 * To create a new composite grammar subclass [CompositeParser]. Override | |
| 10 * the method [initialize] and for every production call [def] giving the | |
| 11 * production a name. The start production must be named 'start'. To refer | |
| 12 * to other productions (forward and backward) use [ref]. | |
| 13 * | |
| 14 * Consider the following example to parse a list of numbers: | |
| 15 * | |
| 16 * class NumberListGrammar extends CompositeParser { | |
| 17 * void initialize() { | |
| 18 * def('start', ref('list').end()); | |
| 19 * def('list', ref('element').separatedBy(char(','), | |
| 20 * includeSeparators: false)); | |
| 21 * def('element', digit().plus().flatten()); | |
| 22 * } | |
| 23 * } | |
| 24 * | |
| 25 * You might want to create future subclasses of your composite grammar | |
| 26 * to redefine the grammar or attach custom actions. In such a subclass | |
| 27 * override the method [initialize] again and call super. Then use | |
| 28 * [redef] to redefine an existing production, and [action] to attach an | |
| 29 * action to an existing production. | |
| 30 * | |
| 31 * Consider the following example that attaches a production action and | |
| 32 * converts the digits to actual numbers: | |
| 33 * | |
| 34 * class NumberListParser extends NumberListGrammar { | |
| 35 * void initialize() { | |
| 36 * action('element', (value) => int.parse(value)); | |
| 37 * } | |
| 38 * } | |
| 39 */ | |
| 40 @deprecated | |
| 41 abstract class CompositeParser extends DelegateParser { | |
| 42 bool _completed = false; | |
| 43 final Map<String, Parser> _defined = new Map(); | |
| 44 final Map<String, SettableParser> _undefined = new Map(); | |
| 45 | |
| 46 CompositeParser() : super(failure('Not initalized production: start')) { | |
| 47 initialize(); | |
| 48 _complete(); | |
| 49 } | |
| 50 | |
| 51 /** | |
| 52 * Initializes the composite grammar. | |
| 53 */ | |
| 54 void initialize(); | |
| 55 | |
| 56 /** | |
| 57 * Internal method to complete the grammar. | |
| 58 */ | |
| 59 void _complete() { | |
| 60 _delegate = ref('start'); | |
| 61 _undefined.forEach((name, parser) { | |
| 62 if (!_defined.containsKey(name)) { | |
| 63 throw new UndefinedProductionError(name); | |
| 64 } | |
| 65 parser.set(_defined[name]); | |
| 66 }); | |
| 67 _undefined.clear(); | |
| 68 _completed = true; | |
| 69 _delegate = ref('start'); | |
| 70 } | |
| 71 | |
| 72 /** | |
| 73 * Returns a reference to a production with a [name]. | |
| 74 * | |
| 75 * This method works during initialization and after completion of the | |
| 76 * initialization. During the initialization it returns delegate parsers | |
| 77 * that are eventually replaced by the real parsers. Afterwards it | |
| 78 * returns the defined parser (mostly useful for testing). | |
| 79 */ | |
| 80 Parser ref(String name) { | |
| 81 if (_completed) { | |
| 82 if (_defined.containsKey(name)) { | |
| 83 return _defined[name]; | |
| 84 } else { | |
| 85 throw new UndefinedProductionError(name); | |
| 86 } | |
| 87 } else { | |
| 88 return _undefined.putIfAbsent(name, () { | |
| 89 return failure('Not initalized production: $name').settable(); | |
| 90 }); | |
| 91 } | |
| 92 } | |
| 93 | |
| 94 /** | |
| 95 * Convenience operator returning a reference to a production with | |
| 96 * a [name]. See [CompositeParser.ref] for details. | |
| 97 */ | |
| 98 Parser operator [](String name) => ref(name); | |
| 99 | |
| 100 /** | |
| 101 * Defines a production with a [name] and a [parser]. Only call this method | |
| 102 * from [initialize]. | |
| 103 * | |
| 104 * The following example defines a list production that consumes | |
| 105 * several elements separated by a comma. | |
| 106 * | |
| 107 * def('list', ref('element').separatedBy(char(','))); | |
| 108 */ | |
| 109 void def(String name, Parser parser) { | |
| 110 if (_completed) { | |
| 111 throw new CompletedParserError(); | |
| 112 } else if (_defined.containsKey(name)) { | |
| 113 throw new RedefinedProductionError(name); | |
| 114 } else { | |
| 115 _defined[name] = parser; | |
| 116 } | |
| 117 } | |
| 118 | |
| 119 /** | |
| 120 * Redefines an existing production with a [name] and a [replacement] | |
| 121 * parser or function producing a new parser. The code raises an | |
| 122 * [UndefinedProductionError] if [name] is an undefined production. Only call | |
| 123 * this method from [initialize]. | |
| 124 * | |
| 125 * The following example redefines the previously defined list production | |
| 126 * by making it optional: | |
| 127 * | |
| 128 * redef('list', (parser) => parser.optional()); | |
| 129 */ | |
| 130 void redef(String name, replacement) { | |
| 131 if (_completed) { | |
| 132 throw new CompletedParserError(); | |
| 133 } else if (!_defined.containsKey(name)) { | |
| 134 throw new UndefinedProductionError(name); | |
| 135 } else { | |
| 136 _defined[name] = | |
| 137 replacement is Parser ? replacement : replacement(_defined[name]); | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 /** | |
| 142 * Attaches an action [function] to an existing production [name]. The code | |
| 143 * raises an [UndefinedProductionError] if [name] is an undefined production. | |
| 144 * Only call this method from [initialize]. | |
| 145 * | |
| 146 * The following example attaches an action returning the size of list of | |
| 147 * the previously defined list production: | |
| 148 * | |
| 149 * action('list', (list) => list.length); | |
| 150 */ | |
| 151 void action(String name, Function function) { | |
| 152 redef(name, (parser) => parser.map(function)); | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 /** | |
| 157 * Error raised when somebody tries to modify a [CompositeParser] outside | |
| 158 * the [CompositeParser.initialize] method. | |
| 159 */ | |
| 160 class CompletedParserError extends Error { | |
| 161 CompletedParserError(); | |
| 162 | |
| 163 @override | |
| 164 String toString() => 'Completed parser'; | |
| 165 } | |
| 166 | |
| 167 /** | |
| 168 * Error raised when an undefined production is accessed. | |
| 169 */ | |
| 170 class UndefinedProductionError extends Error { | |
| 171 final String name; | |
| 172 | |
| 173 UndefinedProductionError(this.name); | |
| 174 | |
| 175 @override | |
| 176 String toString() => 'Undefined production: $name'; | |
| 177 } | |
| 178 | |
| 179 /** | |
| 180 * Error raised when a production is accidentally redefined. | |
| 181 */ | |
| 182 class RedefinedProductionError extends Error { | |
| 183 final String name; | |
| 184 | |
| 185 RedefinedProductionError(this.name); | |
| 186 | |
| 187 @override | |
| 188 String toString() => 'Redefined production: $name'; | |
| 189 } | |
| OLD | NEW |