1
0
forked from 0ad/0ad
0ad/build/errorlist/errorlist.pl

244 lines
5.7 KiB
Perl
Executable File

#!perl -w
++$|;
END { print "\n\nPress enter to exit.\n"; <STDIN> }
use strict;
use warnings;
my ($source, $output);
for (@ARGV) {
# Note to self: "perl errorlist.pl --source=../../source/i18n --output=../../source/i18n/tests/ps/Errors.cpp"
if (/[-\/]\?|--\?|--help/) {
print <<EOH;
$0 parameters:
--source=../../source - root directory for source code
--output=../../source/ps/Errors.cpp - output file to generate
--help - route to enlightenment
EOH
exit;
} elsif (/^--source="?(.*?)"?$/) {
$source = $1;
} elsif (/^--output="?(.*?)"?$/) {
$output = $1;
}
}
$source ||= '../../source';
$output ||= "$source/ps/Errors.cpp";
print "Reading files from $source... ";
my (%topgroups, %groups, %types);
my @files = cpp_files("$source/");
my $loc = 0;
for (@files) {
open my $f, $_ or die "Error opening file '$_' ($!)";
while (<$f>) {
if (/^ERROR_/) {
if (/^ERROR_GROUP\((.+?)\)/) {
$topgroups{$1} = 1;
} elsif (/^ERROR_SUBGROUP\((.+?)\)/) {
$groups{join '~', split /,\s*/, $1} = 1;
} elsif (/^ERROR_TYPE\((.+?)\)/) {
$types{join '~', split /,\s*/, $1} = 1;
}
}
++$loc;
}
}
# Add commas to number in groups of three
1 while $loc =~ s/(\d+)(\d{3})/$1,$2/;
print "(".@files." files read - $loc lines of code)\n";
print "Generating $output... ";
# Add "PSERROR_Error_InvalidError", so that an error to throw when being
# told to throw an error that doesn't exist exists.
$topgroups{Error} = 1;
$types{'Error~InvalidError'} = 1;
open my $out, '>', "$output" or die "Error opening $output ($!)";
print $out <<'.';
// Auto-generated by errorlist.pl - do not edit.
#include "precompiled.h"
#include "Errors.h"
.
for (sort keys %topgroups) {
print $out "class PSERROR_$_ : public PSERROR {};\n";
}
print $out "\n";
for (sort { $a->[1] cmp $b->[1] } map [$_, do{(my $c=$_)=~s/~/_/;$c} ], keys %groups) {
my ($base, $name) = split /~/, $_->[0];
print $out "class PSERROR_${base}_$name : public PSERROR_$base {};\n";
}
print $out "\n";
for (sort { $a->[1] cmp $b->[1] } map [$_, do{(my $c=$_)=~s/~/_/;$c} ], keys %types) {
my ($base, $name) = split /~/, $_->[0];
print $out "class PSERROR_${base}_$name : public PSERROR_$base { public: PSRETURN getCode() const; };\n";
}
print $out "\n";
# The difficult bit:
=pod
mask
**** PSERROR
0001 PSERROR_ Err1
1*** PSERROR_Sec1
1001 PSERROR_Sec1_ Err1
1002 PSERROR_Sec1_ Err2
1003 PSERROR_Sec1_ Err3
11** PSERROR_Sec1_Sec1
1101 PSERROR_Sec1_Sec1_Err1
1102 PSERROR_Sec1_Sec1_Err2
2*** PSERROR_Sec2
2001 PSERROR_Sec2_ Err1
...so split into three sections (0 if null) plus final code...
=cut
my @sec_codes;
$sec_codes[$_]{''} = 1 for 0..2;
for (keys %types) {
my (@secs) = split /[~_]/;
my $err = pop @secs;
$sec_codes[$_]{$secs[$_] || ''} = 1 for 0..2;
}
for my $n (0..2) {
@{$sec_codes[$n]}{sort keys %{$sec_codes[$n]}} = 0 .. keys(%{$sec_codes[$n]})-1;
}
my ($last_sec, $last_err) = ('', 0);
for (sort keys %types) {
my (@secs) = split /[~_]/;
my $err = pop @secs;
my $id = join '', map chr $sec_codes[$_]{$secs[$_] || ''}, 0..2;
if ($id eq $last_sec) {
$id .= chr(++$last_err);
} else {
$last_sec = $id;
$id .= chr($last_err=1);
}
$types{$_} = $id;
}
for (sort keys %types) {
my ($base, $name) = split /~/;
print $out "extern const PSRETURN PSRETURN_${base}_${name} = 0x".unpack('H*', $types{$_}).";\n";
}
print $out "\n";
for (sort keys %topgroups) {
my (@secs) = $_;
my $id = join '', map chr $sec_codes[$_]{$secs[$_] || ''}, 0..2;
my $code = unpack 'H*', $id;
(my $mask = $code) =~ s/(\d\d)/$1+0 ? 'ff' : '00'/ge;
print $out "extern const PSRETURN MASK__PSRETURN_".join('_', @secs)." = 0x${mask}00;\n";
print $out "extern const PSRETURN CODE__PSRETURN_".join('_', @secs)." = 0x${code}00;\n";
}
for (sort keys %groups) {
my (@secs) = split /[_~]/;
my $id = join '', map chr $sec_codes[$_]{$secs[$_] || ''}, 0..2;
my $code = unpack 'H*', $id;
(my $mask = $code) =~ s/(\d\d)/$1+0 ? 'ff' : '00'/ge;
print $out "extern const PSRETURN MASK__PSRETURN_".join('_', @secs)." = 0x${mask}00;\n";
print $out "extern const PSRETURN CODE__PSRETURN_".join('_', @secs)." = 0x${code}00;\n";
}
print $out "\n";
for (sort keys %types) {
my $code = unpack 'H*', $types{$_};
s/~/_/;
print $out "extern const PSRETURN MASK__PSRETURN_$_ = 0xffffffff;\n";
print $out "extern const PSRETURN CODE__PSRETURN_$_ = 0x$code;\n";
}
# End of difficult bit.
print $out "\n";
for (sort keys %types) {
my ($base, $name) = split /~/;
print $out qq~PSRETURN PSERROR_${base}_${name}::getCode() const { return 0x~.unpack('H*',$types{$_}).qq~; }\n~;
}
print $out <<".";
const char* PSERROR::what() const throw ()
{
return GetErrorString(getCode());
}
const char* GetErrorString(const PSERROR& err)
{
return GetErrorString(err.getCode());
}
const char* GetErrorString(PSRETURN code)
{
switch (code)
{
.
for (sort keys %types) {
(my $name = $_) =~ s/~/_/;
print $out qq{\tcase 0x}.unpack('H*',$types{$_}).qq{: return "$name";\n};
}
print $out <<".";
default: return "Unrecognised error";
}
}
void ThrowError(PSRETURN code)
{
switch (code) // Use 'break' in case someone tries to continue from the exception
{
.
for (sort keys %types) {
(my $name = $_) =~ s/~/_/;
print $out qq{\tcase 0x}.unpack('H*',$types{$_}).qq{: throw PSERROR_$name(); break;\n};
}
print $out <<".";
default: throw PSERROR_Error_InvalidError(); // Hmm...
}
}
.
print "Finished.\n";
sub cpp_files {
opendir my $d, $_[0] or die "Error opening directory '$_[0]' ($!)";
my @f = readdir $d;
my @files = map "$_[0]/$_", grep /\.(?:cpp|h)$/, @f;
push @files, cpp_files("$_[0]/$_") for grep { !/^(?:workspaces|tools)$/ and /^[a-zA-Z0-9]+$/ and -d "$_[0]/$_" } @f;
return @files;
}