1717
1818/**
1919 * Highlights regex syntax for console output using ANSI escape codes.
20- *
21- * @extends AbstractNodeVisitor<string>
2220 */
23- final class ConsoleHighlighterVisitor extends AbstractNodeVisitor
21+ final class ConsoleHighlighterVisitor extends HighlighterVisitor
2422{
25- private const RESET = "\033[0m " ;
23+ private const string RESET = "\033[0m " ;
2624
27- private const COLORS = [
25+ private const array COLORS = [
2826 'meta ' => "\033[1;34m " , // Bold Blue
2927 'quantifier ' => "\033[1;33m " , // Bold Yellow
3028 'type ' => "\033[0;32m " , // Green
3129 'anchor ' => "\033[0;35m " , // Magenta
3230 'literal ' => '' , // Default
3331 ];
3432
35- public function visitRegex (Node \RegexNode $ node ): string
36- {
37- return $ node ->pattern ->accept ($ this );
38- }
39-
40- public function visitAlternation (Node \AlternationNode $ node ): string
41- {
42- $ parts = [];
43- foreach ($ node ->alternatives as $ alt ) {
44- $ parts [] = $ alt ->accept ($ this );
45- }
46- return implode (self ::COLORS ['meta ' ] . '| ' . self ::RESET , $ parts );
47- }
48-
49- public function visitSequence (Node \SequenceNode $ node ): string
50- {
51- $ parts = [];
52- foreach ($ node ->children as $ child ) {
53- $ parts [] = $ child ->accept ($ this );
54- }
55- return implode ('' , $ parts );
56- }
57-
33+ #[\Override]
5834 public function visitGroup (Node \GroupNode $ node ): string
5935 {
6036 $ inner = $ node ->child ->accept ($ this );
@@ -68,174 +44,30 @@ public function visitGroup(Node\GroupNode $node): string
6844 Node \GroupType::T_GROUP_NAMED => "?< {$ node ->name }> " ,
6945 default => '' ,
7046 };
71- $ opening = self ::COLORS ['meta ' ] . '( ' . $ prefix . self ::RESET ;
72- $ closing = self ::COLORS ['meta ' ] . ') ' . self ::RESET ;
73- return $ opening . $ inner . $ closing ;
74- }
75-
76- public function visitQuantifier (Node \QuantifierNode $ node ): string
77- {
78- $ inner = $ node ->node ->accept ($ this );
79- $ quant = $ node ->quantifier ;
80- if ($ node ->type === Node \QuantifierType::T_LAZY ) {
81- $ quant .= '? ' ;
82- } elseif ($ node ->type === Node \QuantifierType::T_POSSESSIVE ) {
83- $ quant .= '+ ' ;
84- }
85- return $ inner . self ::COLORS ['quantifier ' ] . $ quant . self ::RESET ;
86- }
87-
88- public function visitLiteral (Node \LiteralNode $ node ): string
89- {
90- return self ::COLORS ['literal ' ] . $ node ->value . self ::RESET ;
91- }
92-
93- public function visitCharType (Node \CharTypeNode $ node ): string
94- {
95- return self ::COLORS ['type ' ] . '\\' . $ node ->value . self ::RESET ;
96- }
97-
98- public function visitDot (Node \DotNode $ node ): string
99- {
100- return self ::COLORS ['meta ' ] . '. ' . self ::RESET ;
101- }
102-
103- public function visitAnchor (Node \AnchorNode $ node ): string
104- {
105- return self ::COLORS ['anchor ' ] . $ node ->value . self ::RESET ;
106- }
107-
108- public function visitAssertion (Node \AssertionNode $ node ): string
109- {
110- return self ::COLORS ['type ' ] . '\\' . $ node ->value . self ::RESET ;
111- }
112-
113- public function visitCharClass (Node \CharClassNode $ node ): string
114- {
115- $ parts = $ node ->expression instanceof Node \AlternationNode
116- ? $ node ->expression ->alternatives
117- : [$ node ->expression ];
118- $ inner = '' ;
119- foreach ($ parts as $ part ) {
120- $ inner .= $ part ->accept ($ this );
121- }
122- $ neg = $ node ->isNegated ? '^ ' : '' ;
123- return self ::COLORS ['meta ' ] . '[ ' . $ neg . self ::RESET . $ inner . self ::COLORS ['meta ' ] . '] ' . self ::RESET ;
124- }
125-
126- public function visitRange (Node \RangeNode $ node ): string
127- {
128- $ start = $ node ->start ->accept ($ this );
129- $ end = $ node ->end ->accept ($ this );
130- return $ start . self ::COLORS ['meta ' ] . '- ' . self ::RESET . $ end ;
131- }
132-
133- // Implement other visit methods similarly, defaulting to literal or meta
134- public function visitBackref (Node \BackrefNode $ node ): string
135- {
136- return self ::COLORS ['type ' ] . '\\' . $ node ->ref . self ::RESET ;
137- }
138-
139- public function visitUnicode (Node \UnicodeNode $ node ): string
140- {
141- return self ::COLORS ['type ' ] . '\\x ' . $ node ->code . self ::RESET ;
142- }
143-
144- public function visitUnicodeNamed (Node \UnicodeNamedNode $ node ): string
145- {
146- return self ::COLORS ['type ' ] . '\\N{ ' . $ node ->name . '} ' . self ::RESET ;
147- }
148-
149- public function visitUnicodeProp (Node \UnicodePropNode $ node ): string
150- {
151- $ prop = $ node ->prop ;
152- if (strlen ($ prop ) > 1 || str_starts_with ($ prop , '^ ' )) {
153- $ prop = '{ ' . $ prop . '} ' ;
154- }
155- return self ::COLORS ['type ' ] . '\\p ' . $ prop . self ::RESET ;
156- }
157-
158- public function visitOctal (Node \OctalNode $ node ): string
159- {
160- return self ::COLORS ['type ' ] . '\\o{ ' . $ node ->code . '} ' . self ::RESET ;
161- }
47+ $ opening = self ::COLORS ['meta ' ].'( ' .$ prefix .self ::RESET ;
48+ $ closing = self ::COLORS ['meta ' ].') ' .self ::RESET ;
16249
163- public function visitOctalLegacy (Node \OctalLegacyNode $ node ): string
164- {
165- return self ::COLORS ['type ' ] . '\\' . $ node ->code . self ::RESET ;
166- }
167-
168- public function visitPosixClass (Node \PosixClassNode $ node ): string
169- {
170- return self ::COLORS ['type ' ] . '[: ' . $ node ->class . ':] ' . self ::RESET ;
171- }
172-
173- public function visitComment (Node \CommentNode $ node ): string
174- {
175- return self ::COLORS ['meta ' ] . '(?#... ' . ') ' . self ::RESET ;
50+ return $ opening .$ inner .$ closing ;
17651 }
17752
53+ #[\Override]
17854 public function visitConditional (Node \ConditionalNode $ node ): string
17955 {
18056 $ condition = $ node ->condition ->accept ($ this );
18157 $ yes = $ node ->yes ->accept ($ this );
18258 $ no = $ node ->no ->accept ($ this );
183- $ noPart = $ no ? self ::COLORS ['meta ' ] . '| ' . self ::RESET . $ no : '' ;
184- return self ::COLORS ['meta ' ] . '(?( ' . self ::RESET . $ condition . self ::COLORS ['meta ' ] . ') ' . self ::RESET . $ yes . $ noPart . self ::COLORS ['meta ' ] . ') ' . self ::RESET ;
185- }
186-
187- public function visitSubroutine (Node \SubroutineNode $ node ): string
188- {
189- return self ::COLORS ['type ' ] . '(? ' . $ node ->reference . ') ' . self ::RESET ;
190- }
191-
192- public function visitPcreVerb (Node \PcreVerbNode $ node ): string
193- {
194- return self ::COLORS ['meta ' ] . '(* ' . $ node ->verb . ') ' . self ::RESET ;
195- }
196-
197- public function visitDefine (Node \DefineNode $ node ): string
198- {
199- $ inner = $ node ->content ->accept ($ this );
200- return self ::COLORS ['meta ' ] . '(?(DEFINE) ' . self ::RESET . $ inner . self ::COLORS ['meta ' ] . ') ' . self ::RESET ;
201- }
202-
203- public function visitLimitMatch (Node \LimitMatchNode $ node ): string
204- {
205- return self ::COLORS ['meta ' ] . '(*LIMIT_MATCH= ' . $ node ->limit . ') ' . self ::RESET ;
206- }
59+ $ noPart = $ no ? self ::COLORS ['meta ' ].'| ' .self ::RESET .$ no : '' ;
20760
208- public function visitCallout (Node \CalloutNode $ node ): string
209- {
210- $ content = $ node ->isStringIdentifier ? '" ' . (string )$ node ->identifier . '" ' : (string )$ node ->identifier ;
211- return self ::COLORS ['meta ' ] . '(?C ' . $ content . ') ' . self ::RESET ;
212- }
213-
214- public function visitScriptRun (Node \ScriptRunNode $ node ): string
215- {
216- return self ::COLORS ['meta ' ] . '(*script_run: ' . $ node ->script . ') ' . self ::RESET ;
217- }
218-
219- public function visitVersionCondition (Node \VersionConditionNode $ node ): string
220- {
221- return self ::COLORS ['meta ' ] . '(?(VERSION>= ' . $ node ->version . ') ' . self ::RESET ;
222- }
223-
224- public function visitKeep (Node \KeepNode $ node ): string
225- {
226- return self ::COLORS ['type ' ] . '\\K ' . self ::RESET ;
61+ return self ::COLORS ['meta ' ].'(?( ' .self ::RESET .$ condition .self ::COLORS ['meta ' ].') ' .self ::RESET .$ yes .$ noPart .self ::COLORS ['meta ' ].') ' .self ::RESET ;
22762 }
22863
229- public function visitControlChar ( Node \ ControlCharNode $ node ): string
64+ protected function wrap ( string $ content , string $ type ): string
23065 {
231- return self ::COLORS [' type ' ] . '\\ c ' . $ node -> char . self ::RESET ;
66+ return self ::COLORS [$ type]. $ content . self ::RESET ;
23267 }
23368
234- public function visitClassOperation ( Node \ ClassOperationNode $ node ): string
69+ protected function escape ( string $ string ): string
23570 {
236- $ left = $ node ->left ->accept ($ this );
237- $ right = $ node ->right ->accept ($ this );
238- $ op = $ node ->type === Node \ClassOperationType::INTERSECTION ? '&& ' : '-- ' ;
239- return self ::COLORS ['meta ' ] . '[ ' . self ::RESET . $ left . self ::COLORS ['meta ' ] . $ op . self ::RESET . $ right . self ::COLORS ['meta ' ] . '] ' . self ::RESET ;
71+ return $ string ;
24072 }
241- }
73+ }
0 commit comments