Line data Source code
1 : // Copyright 2026 The Authors. See the AUTHORS file for details.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // https://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : import 'grammar.dart';
16 :
17 : class GrammarPrinter extends ElementVisitor<Map<String, String>> {
18 : final _rules = <Rule>{};
19 : final Grammar grammar;
20 : final String Function(Map<String, String>) printerFunction;
21 :
22 1 : GrammarPrinter(this.grammar, {this.printerFunction = formatOutputText});
23 :
24 1 : @override
25 4 : String toString() => printerFunction(visitElement(grammar));
26 :
27 : /// Prints the results from GrammarPrinter in a readable format.
28 : ///
29 : /// The space between the rule names and their definition is set to
30 : /// a standard width based on the longest rule name.
31 1 : static String formatOutputText(Map<String, String> rules) {
32 1 : final maxWidth = rules.keys
33 1 : .reduce((a, b) => a.length > b.length ? a : b)
34 1 : .length;
35 :
36 1 : return rules.entries
37 6 : .map((entry) => '${entry.key.padRight(maxWidth)} := ${entry.value}')
38 1 : .toList()
39 1 : .join('\n');
40 : }
41 :
42 0 : Map<String, String> formatOutputMap() => visitElement(grammar);
43 :
44 1 : Map<String, String> _extractRules(ElementSequence elements) {
45 1 : final r = <String, String>{}; // Start empty
46 :
47 2 : for (final element in elements) {
48 1 : if (element is Rule) {
49 0 : r.addAll(visitRule(element));
50 : } else {
51 : // Visit the nested element AND merge its results into our map
52 2 : r.addAll(visitElement(element));
53 : }
54 : }
55 : return r;
56 : }
57 :
58 0 : @override
59 : Map<String, String> visitAlternativeLiterals(AlternativeLiterals element) =>
60 0 : {};
61 :
62 1 : @override
63 : Map<String, String> visitAlternatives(Alternatives alternatives) =>
64 2 : _extractRules(alternatives.elements);
65 :
66 0 : @override
67 : Map<String, String> visitConcatenation(Concatenation concatenation) =>
68 0 : _extractRules(concatenation.sequence);
69 :
70 1 : @override
71 : Map<String, String> visitGrammar(Grammar grammar) =>
72 2 : visitElement(grammar.entryRule);
73 :
74 1 : @override
75 2 : Map<String, String> visitGroup(Group group) => _extractRules(group.elements);
76 :
77 1 : @override
78 1 : Map<String, String> visitLiteralElement(LiteralElement element) => {};
79 :
80 1 : @override
81 : Map<String, String> visitOptionalSequence(OptionalSequence sequence) =>
82 2 : _extractRules(sequence.elements);
83 :
84 1 : @override
85 : Map<String, String> visitRepetition(Repetition repetition) =>
86 3 : _extractRules([repetition.element]);
87 :
88 1 : @override
89 : Map<String, String> visitRule(Rule rule) {
90 2 : if (_rules.contains(rule)) {
91 0 : return {};
92 : }
93 2 : _rules.add(rule);
94 7 : return {rule.name: rule.element.toString(), ...visitElement(rule.element)};
95 : }
96 :
97 0 : @override
98 : Map<String, String> visitSequence(Sequence sequence) =>
99 0 : _extractRules(sequence.elements);
100 :
101 1 : @override
102 1 : Map<String, String> visitValueRange(ValueRange range) => {};
103 :
104 1 : @override
105 1 : Map<String, String> visitCharacterElement(CharacterElement element) => {};
106 :
107 0 : @override
108 0 : Map<String, String> visitEmptyElement(EmptyElement element) => {};
109 :
110 0 : @override
111 0 : Map<String, String> visitNegativeLookahead(NegativeLookahead element) => {};
112 : }
|