@@ -18,30 +18,107 @@ use RegexParser\Exception\LexerException;
1818use RegexParser \Exception \ParserException ;
1919use RegexParser \Regex ;
2020
21+ // ANSI color codes
22+ const RESET = "\033[0m " ;
23+ const RED = "\033[31m " ;
24+ const GREEN = "\033[32m " ;
25+ const YELLOW = "\033[33m " ;
26+ const BLUE = "\033[34m " ;
27+ const MAGENTA = "\033[35m " ;
28+ const CYAN = "\033[36m " ;
29+ const WHITE = "\033[37m " ;
30+ const BOLD = "\033[1m " ;
31+
32+ $ options = getopt ('q ' , ['quiet ' , 'silent ' , 'ansi ' , 'no-ansi ' , 'help ' ]);
33+ $ quiet = isset ($ options ['q ' ]) || isset ($ options ['quiet ' ]) || isset ($ options ['silent ' ]);
34+ $ ansi = !isset ($ options ['no-ansi ' ]) && (isset ($ options ['ansi ' ]) || (function_exists ('posix_isatty ' ) && posix_isatty (STDOUT )));
35+
36+ function color (string $ text , string $ color ): string
37+ {
38+ global $ ansi ;
39+ return $ ansi ? $ color . $ text . RESET : $ text ;
40+ }
41+
42+ function success (string $ text ): string
43+ {
44+ return color ($ text , GREEN );
45+ }
46+
47+ function error (string $ text ): string
48+ {
49+ return color ($ text , RED );
50+ }
51+
52+ function warning (string $ text ): string
53+ {
54+ return color ($ text , YELLOW );
55+ }
56+
57+ function info (string $ text ): string
58+ {
59+ return color ($ text , BLUE );
60+ }
61+
62+ function bold (string $ text ): string
63+ {
64+ return color ($ text , BOLD );
65+ }
66+
67+ function output (string $ text ): void
68+ {
69+ global $ quiet ;
70+ if (!$ quiet ) {
71+ echo $ text ;
72+ }
73+ }
74+
2175function showHelp (): void
2276{
23- echo "Regex Parser CLI Tool \n\n" ;
24- echo "Usage: regex <command> [options] <pattern> \n\n" ;
25- echo "Commands: \n" ;
26- echo " parse <pattern> [--validate] Parse and recompile a regex pattern \n" ;
27- echo " analyze <pattern> Analyze a regex (parse, validate, ReDoS, explain) \n" ;
28- echo " highlight <pattern> [--format=auto|cli|html] Highlight regex for display \n" ;
29- echo " validate <pattern> Validate a regex pattern \n" ;
30- echo " help Show this help \n\n" ;
31- echo "Examples: \n" ;
32- echo " regex parse '/a+/' \n" ;
33- echo " regex analyze '/a+/' \n" ;
34- echo " regex highlight '/a+/' --format=cli \n" ;
35- echo " regex validate '/a+/' \n" ;
77+ output (bold ("Regex Parser CLI Tool " ) . "\n\n" );
78+ output ("Usage: regex <command> [options] <pattern> \n\n" );
79+ output (bold ("Commands: \n" ));
80+ output (" " . info ("parse " ) . " <pattern> [--validate] Parse and recompile a regex pattern \n" );
81+ output (" " . info ("analyze " ) . " <pattern> Analyze a regex (parse, validate, ReDoS, explain) \n" );
82+ output (" " . info ("highlight " ) . " <pattern> [--format=auto|cli|html] Highlight regex for display \n" );
83+ output (" " . info ("validate " ) . " <pattern> Validate a regex pattern \n" );
84+ output (" " . info ("help " ) . " Show this help \n\n" );
85+ output (bold ("Global Options: \n" ));
86+ output (" --ansi Force ANSI output \n" );
87+ output (" --no-ansi Disable ANSI output \n" );
88+ output (" -q, --quiet Suppress output \n" );
89+ output (" --silent Same as --quiet \n" );
90+ output (" --help Show this help \n\n" );
91+ output (bold ("Examples: \n" ));
92+ output (" regex parse '/a+/' \n" );
93+ output (" regex analyze '/a+/' \n" );
94+ output (" regex highlight '/a+/' --format=cli \n" );
95+ output (" regex validate '/a+/' \n" );
96+ output (" regex '/a+/' # Shortcut for highlight \n" );
3697}
3798
38- if ($ argc < 2 ) {
99+ // Remove processed options from argv
100+ $ argv = array_values (array_filter ($ argv , fn ($ arg ) => !str_starts_with ($ arg , '- ' )));
101+
102+ if (isset ($ options ['help ' ])) {
103+ showHelp ();
104+ exit (0 );
105+ }
106+
107+ if (count ($ argv ) < 2 ) {
39108 showHelp ();
40109 exit (1 );
41110}
42111
43112$ command = $ argv [1 ];
44113
114+ // Shortcut: if command looks like a regex pattern, assume highlight
115+ if (str_starts_with ($ command , '/ ' )) {
116+ $ pattern = $ command ;
117+ $ command = 'highlight ' ;
118+ } else {
119+ $ pattern = $ argv [2 ] ?? '' ;
120+ }
121+
45122switch ($ command ) {
46123 case 'help ' :
47124 case '--help ' :
@@ -50,43 +127,41 @@ switch ($command) {
50127 exit (0 );
51128
52129 case 'parse ' :
53- if ($ argc < 3 ) {
54- echo "Error: Missing pattern \n" ;
55- echo "Usage: regex parse <pattern> [--validate] \n" ;
130+ if (empty ( $ pattern ) ) {
131+ output ( error ( "Error: Missing pattern \n" )) ;
132+ output ( "Usage: regex parse <pattern> [--validate] \n" ) ;
56133 exit (1 );
57134 }
58- $ pattern = $ argv [2 ];
59135 $ validate = in_array ('--validate ' , $ argv , true );
60136 $ regex = Regex::create ();
61137
62138 try {
63139 $ ast = $ regex ->parse ($ pattern );
64140 $ compiled = $ ast ->accept (new RegexParser \NodeVisitor \CompilerNodeVisitor ());
65141
66- echo " Parsed successfully! \n" ;
67- echo "Original: {$ pattern }\n" ;
68- echo "Recompiled: {$ compiled }\n" ;
142+ output ( success ( " ✓ Parsed successfully! \n")) ;
143+ output ( "Original: {$ pattern }\n" ) ;
144+ output ( "Recompiled: {$ compiled }\n" ) ;
69145
70146 if ($ validate ) {
71147 $ validation = $ regex ->validate ($ pattern );
72- echo "Validation: " . ($ validation ->isValid ? 'OK ' : 'INVALID ' ) . "\n" ;
148+ output ( "Validation: " . ($ validation ->isValid ? success ( 'OK ' ) : error ( 'INVALID ' )) . "\n" ) ;
73149 if (!$ validation ->isValid && $ validation ->error ) {
74- echo "Error: {$ validation ->error }\n" ;
150+ output ( error ( "Error: {$ validation ->error }\n" )) ;
75151 }
76152 }
77153 } catch (LexerException |ParserException $ e ) {
78- echo " Error: {$ e ->getMessage ()}\n" ;
154+ output ( error ( " ✗ Error: {$ e ->getMessage ()}\n")) ;
79155 exit (1 );
80156 }
81157 break ;
82158
83159 case 'analyze ' :
84- if ($ argc < 3 ) {
85- echo "Error: Missing pattern \n" ;
86- echo "Usage: regex analyze <pattern> \n" ;
160+ if (empty ( $ pattern ) ) {
161+ output ( error ( "Error: Missing pattern \n" )) ;
162+ output ( "Usage: regex analyze <pattern> \n" ) ;
87163 exit (1 );
88164 }
89- $ pattern = $ argv [2 ];
90165 $ regex = Regex::create ();
91166
92167 try {
@@ -95,32 +170,31 @@ switch ($command) {
95170 $ analysis = $ regex ->analyzeReDoS ($ pattern );
96171 $ explain = $ regex ->explain ($ pattern );
97172
98- echo "Pattern: {$ pattern }\n" ;
99- echo "Parse: OK \n" ;
100- echo "Validation: " . ($ validation ->isValid ? 'OK ' : 'INVALID ' ) . "\n" ;
173+ output ( bold ( "Pattern: " ) . " {$ pattern }\n") ;
174+ output ( "Parse: " . success ( " OK " ) . " \n") ;
175+ output ( "Validation: " . ($ validation ->isValid ? success ( 'OK ' ) : error ( 'INVALID ' )) . "\n" ) ;
101176 if (!$ validation ->isValid && $ validation ->error ) {
102- echo "Error: {$ validation ->error }\n" ;
177+ output ( error ( "Error: {$ validation ->error }\n" )) ;
103178 }
104- echo "ReDoS severity: { $ analysis ->severity ->value } \n" ;
105- echo "ReDoS score: {$ analysis ->score }\n" ;
179+ output ( "ReDoS severity: " . warning ( $ analysis ->severity ->value ) . " \n") ;
180+ output ( "ReDoS score: {$ analysis ->score }\n" ) ;
106181 if ($ analysis ->error ) {
107- echo "ReDoS error: {$ analysis ->error }\n" ;
182+ output ( error ( "ReDoS error: {$ analysis ->error }\n" )) ;
108183 }
109- echo "\n Explanation: \n" ;
110- echo $ explain . "\n" ;
184+ output ( "\n" . bold ( " Explanation: " ) . " \n") ;
185+ output ( $ explain . "\n" ) ;
111186 } catch (LexerException |ParserException $ e ) {
112- echo " Error: {$ e ->getMessage ()}\n" ;
187+ output ( error ( " ✗ Error: {$ e ->getMessage ()}\n")) ;
113188 exit (1 );
114189 }
115190 break ;
116191
117192 case 'highlight ' :
118- if ($ argc < 3 ) {
119- echo "Error: Missing pattern \n" ;
120- echo "Usage: regex highlight <pattern> [--format=auto|cli|html] \n" ;
193+ if (empty ( $ pattern ) ) {
194+ output ( error ( "Error: Missing pattern \n" )) ;
195+ output ( "Usage: regex highlight <pattern> [--format=auto|cli|html] \n" ) ;
121196 exit (1 );
122197 }
123- $ pattern = $ argv [2 ];
124198 $ format = 'auto ' ;
125199 foreach ($ argv as $ arg ) {
126200 if (str_starts_with ($ arg , '--format= ' )) {
@@ -138,32 +212,32 @@ switch ($command) {
138212 $ highlighted = $ regex ->highlight ($ pattern );
139213 }
140214
141- echo $ highlighted . "\n" ;
215+ output ( $ highlighted . "\n" ) ;
142216 } catch (LexerException |ParserException $ e ) {
143- echo " Error: {$ e ->getMessage ()}\n" ;
217+ output ( error ( " ✗ Error: {$ e ->getMessage ()}\n")) ;
144218 exit (1 );
145219 }
146220 break ;
147221
148222 case 'validate ' :
149- if ($ argc < 3 ) {
150- echo "Error: Missing pattern \n" ;
151- echo "Usage: regex validate <pattern> \n" ;
223+ if (empty ( $ pattern ) ) {
224+ output ( error ( "Error: Missing pattern \n" )) ;
225+ output ( "Usage: regex validate <pattern> \n" ) ;
152226 exit (1 );
153227 }
154- $ pattern = $ argv [2 ];
155228 $ regex = Regex::create ();
156229
157230 $ validation = $ regex ->validate ($ pattern );
158- echo ($ validation ->isValid ? 'OK ' : 'INVALID ' ) . "\n" ;
231+ output ($ validation ->isValid ? success ('✓ OK ' ) : error ('✗ INVALID ' ));
232+ output ("\n" );
159233 if (!$ validation ->isValid && $ validation ->error ) {
160- echo "Error: {$ validation ->error }\n" ;
234+ output ( error ( "Error: {$ validation ->error }\n" )) ;
161235 exit (1 );
162236 }
163237 break ;
164238
165239 default :
166- echo "Unknown command: {$ command }\n\n" ;
240+ output ( error ( "Unknown command: {$ command }\n\n" )) ;
167241 showHelp ();
168242 exit (1 );
169243}
0 commit comments