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 |