blob: 727057e0b5538ce86997a8a8323df58dd77117e8 [file] [log] [blame]
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<T> resolve<T>(Parser<T> parser) {
final mapping = <ResolvableParser, Parser>{};
parser = _dereference(parser, mapping);
final todo = <Parser>[parser];
final seen = <Parser>{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<T> _dereference<T>(Parser<T> parser, Map<Parser, Parser> mapping) {
final references = <ResolvableParser<T>>{};
while (parser is ResolvableParser<T>) {
if (mapping.containsKey(parser)) {
return mapping[parser]! as Parser<T>;
} else if (!references.add(parser)) {
throw StateError('Recursive references detected: $references');
}
parser = parser.resolve();
}
if (parser is ResolvableParser) {
throw StateError('Type error in reference parser: $parser');
}
for (final reference in references) {
mapping[reference] = parser;
}
return parser;
}