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