Skip to content

Commit 70cc184

Browse files
committed
Refactors CLI tool to use plain PHP
Removes Symfony Console component to simplify the CLI tool. Improves the tool's usability by providing a clearer help message and usage instructions. Adds `analyze` and `highlight` commands. Fixes test namespace.
1 parent 59fc475 commit 70cc184

File tree

4 files changed

+125
-113
lines changed

4 files changed

+125
-113
lines changed

bin/regex

Lines changed: 104 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -17,66 +17,76 @@ require __DIR__.'/../vendor/autoload.php';
1717
use RegexParser\Exception\LexerException;
1818
use RegexParser\Exception\ParserException;
1919
use RegexParser\Regex;
20-
use Symfony\Component\Console\Application;
21-
use Symfony\Component\Console\Command\Command;
22-
use Symfony\Component\Console\Input\InputArgument;
23-
use Symfony\Component\Console\Input\InputInterface;
24-
use Symfony\Component\Console\Input\InputOption;
25-
use Symfony\Component\Console\Output\OutputInterface;
26-
27-
$app = new Application('Regex Parser', '1.0.0');
28-
29-
$app->addCommand((new class extends Command {
30-
protected function configure(): void
31-
{
32-
$this
33-
->setName('parse')
34-
->setDescription('Parse a regex pattern and show the recompiled version')
35-
->addArgument('pattern', InputArgument::REQUIRED, 'The regex pattern to parse')
36-
->addOption('validate', null, InputOption::VALUE_NONE, 'Also validate the pattern');
37-
}
38-
39-
protected function execute(InputInterface $input, OutputInterface $output): int
40-
{
41-
$pattern = $input->getArgument('pattern');
20+
21+
function showHelp(): void
22+
{
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";
36+
}
37+
38+
if ($argc < 2) {
39+
showHelp();
40+
exit(1);
41+
}
42+
43+
$command = $argv[1];
44+
45+
switch ($command) {
46+
case 'help':
47+
case '--help':
48+
case '-h':
49+
showHelp();
50+
exit(0);
51+
52+
case 'parse':
53+
if ($argc < 3) {
54+
echo "Error: Missing pattern\n";
55+
echo "Usage: regex parse <pattern> [--validate]\n";
56+
exit(1);
57+
}
58+
$pattern = $argv[2];
59+
$validate = in_array('--validate', $argv, true);
4260
$regex = Regex::create();
4361

4462
try {
4563
$ast = $regex->parse($pattern);
4664
$compiled = $ast->accept(new RegexParser\NodeVisitor\CompilerNodeVisitor());
4765

48-
$output->writeln("Parsed successfully!");
49-
$output->writeln("Original: {$pattern}");
50-
$output->writeln("Recompiled: {$compiled}");
66+
echo "Parsed successfully!\n";
67+
echo "Original: {$pattern}\n";
68+
echo "Recompiled: {$compiled}\n";
5169

52-
if ($input->getOption('validate')) {
70+
if ($validate) {
5371
$validation = $regex->validate($pattern);
54-
$output->writeln("Validation: " . ($validation->isValid ? 'OK' : 'INVALID'));
72+
echo "Validation: " . ($validation->isValid ? 'OK' : 'INVALID') . "\n";
5573
if (!$validation->isValid && $validation->error) {
56-
$output->writeln("Error: {$validation->error}");
74+
echo "Error: {$validation->error}\n";
5775
}
5876
}
59-
60-
return Command::SUCCESS;
6177
} catch (LexerException|ParserException $e) {
62-
$output->writeln("<error>Error: {$e->getMessage()}</error>");
63-
return Command::FAILURE;
78+
echo "Error: {$e->getMessage()}\n";
79+
exit(1);
6480
}
65-
}
66-
}));
67-
68-
$app->addCommand((new class extends Command {
69-
protected function configure(): void
70-
{
71-
$this
72-
->setName('analyze')
73-
->setDescription('Analyze a regex pattern (parse, validate, ReDoS, explain)')
74-
->addArgument('pattern', InputArgument::REQUIRED, 'The regex pattern to analyze');
75-
}
76-
77-
protected function execute(InputInterface $input, OutputInterface $output): int
78-
{
79-
$pattern = $input->getArgument('pattern');
81+
break;
82+
83+
case 'analyze':
84+
if ($argc < 3) {
85+
echo "Error: Missing pattern\n";
86+
echo "Usage: regex analyze <pattern>\n";
87+
exit(1);
88+
}
89+
$pattern = $argv[2];
8090
$regex = Regex::create();
8191

8292
try {
@@ -85,43 +95,38 @@ $app->addCommand((new class extends Command {
8595
$analysis = $regex->analyzeReDoS($pattern);
8696
$explain = $regex->explain($pattern);
8797

88-
$output->writeln("Pattern: {$pattern}");
89-
$output->writeln("Parse: OK");
90-
$output->writeln("Validation: " . ($validation->isValid ? 'OK' : 'INVALID'));
98+
echo "Pattern: {$pattern}\n";
99+
echo "Parse: OK\n";
100+
echo "Validation: " . ($validation->isValid ? 'OK' : 'INVALID') . "\n";
91101
if (!$validation->isValid && $validation->error) {
92-
$output->writeln("Error: {$validation->error}");
102+
echo "Error: {$validation->error}\n";
93103
}
94-
$output->writeln("ReDoS severity: {$analysis->severity->value}");
95-
$output->writeln("ReDoS score: {$analysis->score}");
104+
echo "ReDoS severity: {$analysis->severity->value}\n";
105+
echo "ReDoS score: {$analysis->score}\n";
96106
if ($analysis->error) {
97-
$output->writeln("ReDoS error: {$analysis->error}");
107+
echo "ReDoS error: {$analysis->error}\n";
98108
}
99-
$output->writeln("");
100-
$output->writeln("Explanation:");
101-
$output->writeln($explain);
102-
103-
return Command::SUCCESS;
109+
echo "\nExplanation:\n";
110+
echo $explain . "\n";
104111
} catch (LexerException|ParserException $e) {
105-
$output->writeln("<error>Error: {$e->getMessage()}</error>");
106-
return Command::FAILURE;
112+
echo "Error: {$e->getMessage()}\n";
113+
exit(1);
114+
}
115+
break;
116+
117+
case 'highlight':
118+
if ($argc < 3) {
119+
echo "Error: Missing pattern\n";
120+
echo "Usage: regex highlight <pattern> [--format=auto|cli|html]\n";
121+
exit(1);
122+
}
123+
$pattern = $argv[2];
124+
$format = 'auto';
125+
foreach ($argv as $arg) {
126+
if (str_starts_with($arg, '--format=')) {
127+
$format = substr($arg, 9);
128+
}
107129
}
108-
}
109-
}));
110-
111-
$app->addCommand((new class extends Command {
112-
protected function configure(): void
113-
{
114-
$this
115-
->setName('highlight')
116-
->setDescription('Highlight a regex pattern for display')
117-
->addArgument('pattern', InputArgument::REQUIRED, 'The regex pattern to highlight')
118-
->addOption('format', null, InputOption::VALUE_OPTIONAL, 'Output format: auto, cli, html', 'auto');
119-
}
120-
121-
protected function execute(InputInterface $input, OutputInterface $output): int
122-
{
123-
$pattern = $input->getArgument('pattern');
124-
$format = $input->getOption('format');
125130
$regex = Regex::create();
126131

127132
try {
@@ -133,39 +138,32 @@ $app->addCommand((new class extends Command {
133138
$highlighted = $regex->highlight($pattern);
134139
}
135140

136-
$output->writeln($highlighted);
137-
138-
return Command::SUCCESS;
141+
echo $highlighted . "\n";
139142
} catch (LexerException|ParserException $e) {
140-
$output->writeln("<error>Error: {$e->getMessage()}</error>");
141-
return Command::FAILURE;
143+
echo "Error: {$e->getMessage()}\n";
144+
exit(1);
142145
}
143-
}
144-
}));
145-
146-
$app->addCommand((new class extends Command {
147-
protected function configure(): void
148-
{
149-
$this
150-
->setName('validate')
151-
->setDescription('Validate a regex pattern')
152-
->addArgument('pattern', InputArgument::REQUIRED, 'The regex pattern to validate');
153-
}
154-
155-
protected function execute(InputInterface $input, OutputInterface $output): int
156-
{
157-
$pattern = $input->getArgument('pattern');
146+
break;
147+
148+
case 'validate':
149+
if ($argc < 3) {
150+
echo "Error: Missing pattern\n";
151+
echo "Usage: regex validate <pattern>\n";
152+
exit(1);
153+
}
154+
$pattern = $argv[2];
158155
$regex = Regex::create();
159156

160157
$validation = $regex->validate($pattern);
161-
$output->writeln($validation->isValid ? 'OK' : 'INVALID');
158+
echo ($validation->isValid ? 'OK' : 'INVALID') . "\n";
162159
if (!$validation->isValid && $validation->error) {
163-
$output->writeln("Error: {$validation->error}");
164-
return Command::FAILURE;
160+
echo "Error: {$validation->error}\n";
161+
exit(1);
165162
}
163+
break;
166164

167-
return Command::SUCCESS;
168-
}
169-
}));
170-
171-
$app->run();
165+
default:
166+
echo "Unknown command: {$command}\n\n";
167+
showHelp();
168+
exit(1);
169+
}

composer.json

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
11
{
22
"name": "yoeunes/regex-parser",
33
"description": "A PCRE regex parser with lexer, AST, and validation",
4-
"type": "symfony-bundle",
4+
"keywords": ["regex", "pcre", "parser", "lexer", "ast", "validation", "symfony"],
5+
"type": "library",
56
"license": "MIT",
7+
"support": {
8+
"issues": "https://github.com/yoeunes/regex-parser/issues",
9+
"source": "https://github.com/yoeunes/regex-parser"
10+
},
611
"authors": [
712
{
813
"name": "Younes ENNAJI",
9-
"email": "[email protected]"
14+
"email": "[email protected]",
15+
"homepage": "https://www.linkedin.com/in/younes--ennaji/",
16+
"role": "Developer"
1017
}
1118
],
19+
"minimum-stability": "dev",
20+
"prefer-stable": true,
1221
"require": {
13-
"php": ">=8.4",
14-
"symfony/console": "^8.0"
22+
"php": ">=8.4"
1523
},
1624
"require-dev": {
1725
"phpunit/phpunit": "^12.5.1",
@@ -25,7 +33,8 @@
2533
"symfony/validator": "^8.0",
2634
"psr/cache": "^3.0",
2735
"psr/simple-cache": "^3.0",
28-
"rector/rector": "^2.2.11"
36+
"rector/rector": "^2.2.11",
37+
"symfony/console": "^8.0"
2938
},
3039
"autoload": {
3140
"psr-4": {
@@ -40,6 +49,11 @@
4049
"bin": [
4150
"bin/regex"
4251
],
52+
"config": {
53+
"optimize-autoloader": true,
54+
"preferred-install": "dist",
55+
"sort-packages": true
56+
},
4357
"extra": {
4458
"phpstan": {
4559
"includes": [

tests/NodeVisitor/LengthRangeNodeVisitorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* file that was distributed with this source code.
1212
*/
1313

14-
namespace RegexParser\Test\NodeVisitor;
14+
namespace RegexParser\Tests\NodeVisitor;
1515

1616
use PHPUnit\Framework\Attributes\DataProvider;
1717
use PHPUnit\Framework\TestCase;

tests/NodeVisitor/TestCaseGeneratorNodeVisitorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* file that was distributed with this source code.
1212
*/
1313

14-
namespace RegexParser\Test\NodeVisitor;
14+
namespace RegexParser\Tests\NodeVisitor;
1515

1616
use PHPUnit\Framework\TestCase;
1717
use RegexParser\NodeVisitor\TestCaseGeneratorNodeVisitor;

0 commit comments

Comments
 (0)