Compiler-Parser/gen/testcode_generator.pl
use strict;
use warnings;
use Compiler::Lexer;
use Compiler::Parser;
use Term::ANSIColor qw(:constants);
use String::CamelCase qw/decamelize/;
use Data::Dumper;
my $template = do { local $/; <DATA> };
my $body;
sub generate {
my ($ast) = @_;
my $root = $ast->root;
my $depth = 1;
if ($root->{next}) {
$depth++;
$body .= "[\n";
}
my $node_name = print_block_start($root, $depth);
__generate($_ => $root->{$_}, $depth+1) foreach grep { $_ !~ /next/ } @{$root->branches};
print_block_end($node_name, $depth);
__generate_next($root->{next}, $depth) if $root->{next};
if ($root->{next}) {
$body .= indent($depth-1) . "]";
}
}
sub __generate_next {
my ($node, $depth) = @_;
my $node_name = print_block_start($node, $depth);
__generate($_ => $node->{$_}, $depth+1) foreach grep { $_ !~ /next/ } @{$node->branches};
print_block_end($node_name, $depth);
__generate_next($node->{next}, $depth) if $node->{next};
}
sub indent {
my ($depth) = @_;
return ' ' x 4 x $depth;
}
sub print_block_start {
my ($node, $depth) = @_;
my ($name) = ref($node) =~ /.*::(.*)/;
my $node_name = decamelize($name);
$node_name = 'Test::Compiler::Parser::package' if ($node_name eq 'package');
$node_name = 'Test::Compiler::Parser::return' if ($node_name eq 'return');
if ($node_name eq 'leaf') {
$body .= indent($depth) . sprintf("%s '%s',\n", $node_name, $node->data);
} else {
$body .= indent($depth) . sprintf("%s { '%s',\n", $node_name, $node->data);
}
return $node_name;
}
sub print_block_start_with_branch {
my ($node, $branch_name, $depth, $multiple) = @_;
my ($name) = ref($node) =~ /.*::(.*)/;
my $node_name = decamelize($name);
$node_name = 'Test::Compiler::Parser::package' if ($node_name eq 'package');
$node_name = 'Test::Compiler::Parser::return' if ($node_name eq 'return');
if ($node_name eq 'leaf' && $multiple) {
$body .= indent($depth) . sprintf("%s => [\n%s%s '%s',\n", $branch_name, indent($depth+1), $node_name, $node->data);
} elsif ($node_name eq 'leaf') {
$body .= indent($depth) . sprintf("%s => %s '%s',\n", $branch_name, $node_name, $node->data);
} elsif ($multiple) {
$body .= indent($depth) . sprintf("%s => [\n%s%s { '%s',\n", $branch_name, indent($depth+1), $node_name, $node->data);
} else {
$body .= indent($depth) . sprintf("%s => %s { '%s',\n", $branch_name, $node_name, $node->data);
}
return $node_name;
}
sub print_block_end {
my ($node_name, $depth) = @_;
$body .= indent($depth) . "},\n" unless $node_name eq 'leaf';
}
sub print_node {
my ($branch_name, $node, $depth) = @_;
my $multiple = 0;
if ($node->{next}) {
$multiple = 1;
}
my $node_name = print_block_start_with_branch($node, $branch_name, $depth, $multiple);
$depth++ if ($multiple);
__generate($_ => $node->{$_}, $depth+1) foreach grep { $_ !~ /next/ } @{$node->branches};
print_block_end($node_name, $depth);
__generate_next($node->{next}, $depth) if $node->{next};
if ($node->{next}) {
$body .= indent($depth-1) . "],\n";
}
}
sub __generate_array {
my ($node, $depth) = @_;
my $node_name;
foreach my $arg (@$node) {
if ($arg->{next}) {
$depth++;
$body .= indent($depth) . "[\n";
}
my $node_name = print_block_start($arg, $depth+1);
__generate($_ => $arg->{$_}, $depth+2) foreach grep { $_ !~ /next/ } @{$arg->branches};
print_block_end($node_name, $depth + 1);
__generate_next($arg->{next}, $depth + 1) if $arg->{next};
if ($arg->{next}) {
$body .= indent($depth) . "],\n";
$depth--;
}
}
return $node_name;
}
sub __generate {
my ($branch_name, $node, $depth) = @_;
return unless $node;
if (ref($node) eq 'ARRAY') {
$body .= indent($depth) . "$branch_name => [\n";
my $node_name = __generate_array($node, $depth);
print_block_end('', $depth+1) if ($node_name && $node_name ne 'leaf');
$body .= indent($depth) . "],\n";
} else {
print_node($branch_name, $node, $depth);
}
}
foreach my $filename (@ARGV) {
print "... generate $filename\n";
open my $fh, '<', $filename or die "Cannot load $filename";
my $code = do { local $/; <$fh> };
if (my $pid = fork()) {
waitpid($pid, 0);
} else {
my $ast = Compiler::Parser->new->parse(Compiler::Lexer->new->tokenize($code));
$body = '';
generate($ast);
open $fh, '>', 'test.t';
print $fh sprintf $template, $filename, $body, $code;
close $fh;
exit;
}
}
__DATA__
use strict;
use warnings;
use Test::More;
use Compiler::Lexer;
use Compiler::Parser;
use Compiler::Parser::AST::Renderer;
use Test::Compiler::Parser;
subtest 'parse %s' => sub {
my $script = do { local $/; <DATA> };
my $tokens = Compiler::Lexer->new('')->tokenize($script);
my $ast = Compiler::Parser->new->parse($tokens);
Compiler::Parser::AST::Renderer->new->render($ast);
node_ok($ast->root, %s);
};
done_testing;
__DATA__
%s