import '../core/parser.dart'; import '../parser/combinator/settable.dart'; import '../parser/utils/resolvable.dart'; import 'reference.dart'; /// Resolves all parser references reachable through [parser]. Returns an /// optimized parser graph that inlines all references directly. /// /// This code in-lines parsers that purely reference another one (subclasses /// of [ResolvableParser]). This includes, but is not limited to, parsers /// created with [ref0], [ref1], [ref2], ..., [undefined], or /// [SettableParserExtension], Parser resolve(Parser parser) { final mapping = , Parser>{}; parser = _dereference(parser, mapping); final todo = [parser]; final seen = {parser}; while (todo.isNotEmpty) { final parent = todo.removeLast(); for (var child in parent.children) { if (child is ResolvableParser) { final referenced = _dereference(child, mapping); parent.replace(child, referenced); child = referenced; } if (seen.add(child)) { todo.add(child); } } } return parser; } /// Internal helper to dereference and resolve a chain of [ResolvableParser] /// instances to their resolved counterpart. Throws a [StateError] if the there /// is a directly cyclic dependency on itself. Parser _dereference(Parser parser, Map mapping) { final references = >{}; while (parser is ResolvableParser) { if (mapping.containsKey(parser)) { return mapping[parser] as Parser; } else if (!references.add(parser)) { throw StateError('Recursive references detected: $references'); } parser = parser.resolve(); } for (final reference in references) { mapping[reference] = parser; } return parser; }