| import 'package:meta/meta.dart'; |
| |
| import '../../core/parser.dart'; |
| import '../../shared/types.dart'; |
| |
| /// A continuous path through the parser graph. |
| class ParserPath { |
| /// Constructs a path from a list of parsers and indexes. |
| ParserPath(this.parsers, this.indexes) |
| : assert(parsers.isNotEmpty, 'parsers cannot be empty'), |
| assert(indexes.length == parsers.length - 1, 'indexes wrong size'), |
| assert((() { |
| for (var i = 0; i < indexes.length; i++) { |
| if (parsers[i].children[indexes[i]] != parsers[i + 1]) { |
| return false; |
| } |
| } |
| return true; |
| })(), 'indexes invalid'); |
| |
| /// The non-empty list of parsers in this path. |
| final List<Parser> parsers; |
| |
| /// The parser where this path starts. |
| Parser get source => parsers.first; |
| |
| /// The parser where this path ends. |
| Parser get target => parsers.last; |
| |
| /// The number of parsers in this path. |
| int get length => parsers.length; |
| |
| /// The child-indexes that navigate from one parser to the next one. This |
| /// collection contains one element less than the number of parsers in the |
| /// path. |
| final List<int> indexes; |
| |
| void _push(Parser parser, int index) { |
| parsers.add(parser); |
| indexes.add(index); |
| } |
| |
| void _pop() { |
| parsers.removeLast(); |
| indexes.removeLast(); |
| } |
| } |
| |
| @internal |
| Iterable<ParserPath> depthFirstSearch( |
| ParserPath path, Predicate<ParserPath> predicate) sync* { |
| if (predicate(path)) { |
| yield ParserPath(List.from(path.parsers), List.from(path.indexes)); |
| } else { |
| final children = path.target.children; |
| for (var i = 0; i < children.length; i++) { |
| final child = children[i]; |
| if (!path.parsers.contains(child)) { |
| path._push(child, i); |
| yield* depthFirstSearch(path, predicate); |
| path._pop(); |
| } |
| } |
| } |
| } |