Rewrite the code generator and parser for the database metalanguage to perform less (or no) parsing in the code generators.

Instead we now have a two phase process:
 * First parse and build an abstract syntax tree (AST)
 * Secondly loop over the generated AST to produce useable code for the target language.

NOTE: Currently only the C struct definition generator is rewritten to this new architecture.

git-svn-id: svn+ssh://svn.gna.org/svn/warzone/trunk@5359 4a71c877-e1ca-e34f-864e-861f7616d084
master
Giel van Schijndel 2008-07-03 18:55:06 +00:00
parent 9db4235480
commit 3df34aeaa7
3 changed files with 275 additions and 119 deletions

View File

@ -5,147 +5,178 @@ use strict;
# Code generator for C struct definitions
my @structQualifierCount;
my @structQualifiers;
my @nestFieldCount;
my @fieldDecls;
my @nestNames;
my @curComment;
sub blankLine()
sub printStructFieldType
{
print "\n";
my $field = $_[0];
$_ = ${$field}{"type"};
if (/count/) { print "unsigned int "; }
elsif (/string/) { print "const char* "; }
elsif (/real/) { print "float "; }
elsif (/bool/) { print "bool "; }
elsif (/set/) { print "bool "; }
elsif (/enum/) { print "${$field}{\"enum\"} "; }
else { die "UKNOWN TYPE: $_"; }
}
sub beginStructDef()
sub preProcessQualifiers
{
push @nestNames, $_[0];
push @nestFieldCount, 0;
push @structQualifierCount, 0;
my ($field, $comments) = @_;
$_ = ${$field}{"qualifier"};
$_ = "" unless $_;
if (/unique/)
{
# Separate this notice from the rest of the comment if there's any
push @{$comments}, "" if @{$comments};
push @{$comments}, " Unique across all instances";
}
elsif (/set/)
{
# Separate this notice from the rest of the comment if there's any
push @{$comments}, "" if @{$comments};
push @{$comments}, " \@see ${$field}{\"enum\"} for the meaning of index values";
}
}
sub endStructDef()
sub postProcessQualifiers
{
my $name = pop(@nestNames);
my $fieldCount = pop(@nestFieldCount);
my $qualifierCount = pop(@structQualifierCount);
my $field = $_[0];
my $enumMap = $_[1];
$_ = ${$field}{"qualifier"};
$_ = "" unless $_;
# Fetch qualifier list from stack and reverse it
my @qualifiers;
while ($qualifierCount)
if (/set/)
{
push @qualifiers, pop(@structQualifiers);
my $enumName = ${$field}{"enum"};
my $enum = ${$enumMap}{$enumName};
my $enumSize = @{${$enum}{"values"}};
$qualifierCount--;
print "\[${enumSize}]" if (/set/);
}
}
sub printComments
{
my ($comments, $indent) = @_;
return unless @{$comments};
print "\t" if $indent;
print "/**\n";
foreach my $comment (@{$comments})
{
print "\t" if $indent;
print " *${comment}\n";
}
# Fetch field list from stack and reverse it
my @fields;
while ($fieldCount)
{
push @fields, pop(@fieldDecls);
print "\t" if $indent;
print " */\n";
}
$fieldCount--;
}
sub printStructFields
{
my @fields = @{${$_[0]}{"fields"}};
my $enumMap = $_[1];
# Start printing the structure
print "typedef struct\n{\n";
# Process struct qualifiers
foreach (@qualifiers)
{
if (/^prefix\s+\"([^\"]+)\"$/)
{
$name = $1 . $name;
}
elsif (/^abstract$/) {}
else
{
die "Unknown qualifier: `$_'\n";
}
}
# Print fields
while (@fields)
{
print pop(@fields) . "\n";
my $field = shift(@fields);
my @comments = @{${$field}{"comment"}};
preProcessQualifiers $field, \@comments;
printComments \@comments, 1;
print "\t";
printStructFieldType $field;
print ${$field}{"name"};
postProcessQualifiers $field, $enumMap;
print ";\n";
# Seperate field defintions by blank lines
print "\n" if @fields;
}
print "} ${name};\n";
}
sub addStructQualifier()
sub printStruct
{
push @structQualifiers, $_[0];
$structQualifierCount[@structQualifierCount - 1]++;
}
my ($struct, $name, $prefix, $structMap, $enumMap) = @_;
sub fieldDeclaration()
{
my ($type, $qualifier, $name) = @_;
my $fieldDecl = "";
$_ = $type;
if (/count/) { $type = "unsigned int "; }
elsif (/string/) { $type = "const char* "; }
elsif (/real/) { $type = "float "; }
elsif (/bool/) { $type = "bool "; }
else { die "UKNOWN TYPE: $_"; }
my $set = "";
if ($qualifier)
foreach (keys %{${$struct}{"qualifiers"}})
{
foreach ($qualifier)
$$prefix = ${${$struct}{"qualifiers"}}{$_} if /prefix/ and not $$prefix;
if (/inherit/)
{
if (/set/)
{
$set = "\[]";
}
elsif (/unique/)
{
# Separate this notice from the rest of the comment if there's any
push @curComment, "" if @curComment;
push @curComment, " Unique across all instances";
}
else
{
die "UNKNOWN QUALIFIER: $_";
}
my $inheritName = ${${$struct}{"qualifiers"}}{"inherit"};
my $inheritStruct = ${$structMap}{$inheritName};
print "\t/* BEGIN of inherited \"$inheritName\" definition */\n";
printStruct($inheritStruct, $name, $prefix, $structMap, $enumMap) if /inherit/;
print "\t/* END of inherited \"$inheritName\" definition */\n\n";
}
}
# If there's a comment, "open" it
$fieldDecl .= "\t/**" if @curComment;
$$name = ${$struct}{"name"};
while (@curComment)
{
$fieldDecl .= shift(@curComment) . "\n";
if (@curComment)
{
$fieldDecl .= "\t * ";
}
else
{
$fieldDecl .= "\t */\n";
}
}
$fieldDecl .= "\t${type}${name}${set};";
push @fieldDecls, $fieldDecl;
$nestFieldCount[@nestFieldCount - 1]++;
printStructFields($struct, $enumMap);
}
sub pushComment()
sub printEnums()
{
push @curComment, substr($_[0], 1);
foreach my $enum (@{$_[0]})
{
printComments ${$enum}{"comment"}, 0;
print "typedef enum\n{\n";
my @values = @{${$enum}{"values"}};
while (@values)
{
my $value = shift(@values);
my $name = ${$value}{"name"};
printComments ${$value}{"comment"}, 1;
print "\t${$enum}{\"name\"}_${name},\n";
print "\n" if @values;
}
print "} ${$enum}{\"name\"};\n\n";
}
}
sub printStructs()
{
my ($structList, $structMap, $enumMap) = @_;
my @structs = @{$structList};
while (@structs)
{
my $struct = shift(@structs);
my $name;
my $prefix = "";
printComments ${$struct}{"comment"}, 0;
# Start printing the structure
print "typedef struct\n{\n";
printStruct($struct, \$name, \$prefix, $structMap, $enumMap);
$name = $prefix . $name;
print "} ${name};\n";
print "\n" if @structs;
}
}
1;

View File

@ -1,19 +1,140 @@
#!/usr/bin/perl -w
# vim: set et sts=4 sw=4:
my $out_lang = shift or die "Missing output language";
$out_lang .= "_cg.pm";
require $out_lang or die "Couldn't load $out_lang";
my %enumMap;
my @enumList;
my %structMap;
my @structList;
sub parseEnum
{
my %curEnum = (name => $_[0]);
my @curComment = ();
@{$curEnum{"comment"}} = @{$_[1]};
@{$_[1]} = ();
while (<>)
{
chomp;
if (/^\s*(\#.*)?$/)
{
push @curComment, substr($1, 1) if $1;
}
elsif (/^\s*(\w+)\s*$/)
{
my %value = (name=>$1);
@{$value{"comment"}} = @curComment;
@curComment = ();
push @{$curEnum{"values"}}, \%value;
}
elsif (/^\s*end\s*;\s*$/)
{
# Put the enum at the end of the array
push @enumList, \%curEnum;
# Also place it in the hash
$enumMap{$curEnum{"name"}} = \%curEnum;
return;
}
else { print "Unmatched line: $_\n"; }
}
}
sub parseStruct
{
my %curStruct = (name => $_[0]);
my @curComment = ();
@{$curStruct{"comment"}} = @{$_[1]};
@{$_[1]} = ();
while (<>)
{
chomp;
if (/^\s*(\#.*)?$/)
{
push @curComment, substr($1, 1) if $1;
}
elsif (/^\s*end\s*;\s*$/)
{
# Put the struct at the end of the array
push @structList, \%curStruct;
# Also place it in the hash
$structMap{$curStruct{"name"}} = \%curStruct;
return;
}
# Parse struct-level qualifiers
elsif (/^\s*%(.*)\s*;\s*$/)
{
$_ = $1;
if (/^prefix\s+\"([^\"]+)\"$/)
{
${$curStruct{"qualifiers"}}{"prefix"} = $1;
}
elsif (/^abstract$/)
{
${$curStruct{"qualifiers"}}{"abstract"} = 1;
}
elsif (/^inherit\s+(\w+)$/)
{
${$curStruct{"qualifiers"}}{"inherit"} = $1;
}
}
# Parse regular field declarations
elsif (/^\s*(count|string|real|bool)\s+(unique\s+)?(\w+)\s*;\s*$/)
{
my %field = (type=>$1, qualifier=>$2, name=>$3);
@{$field{"comment"}} = @curComment;
@curComment = ();
push @{$curStruct{"fields"}}, \%field;
}
# Parse set and enum field declarations
elsif (/^\s*(set|enum)\s+(\w+)\s+(\w+)\s*;\s*$/)
{
my %field = (type=>$1, enum=>$2, name=>$3);
@{$field{"comment"}} = @curComment;
@curComment = ();
$field{"qualifier"} = "set" if ($1 =~ /set/);
push @{$curStruct{"fields"}}, \%field;
}
else { print "Unmatched line: $_\n"; }
}
}
my @curComment = ();
# Read and parse the file
my $name;
while (<>)
{
chomp;
if (/^\s*(\#.*)?$/) { CG::blankLine(); if ($1) { CG::pushComment($1); } }
elsif (/^\s*struct\s+(\w+)\s*$/) { CG::beginStructDef($1); }
elsif (/^\s*end\s+struct\s*;\s*$/) { CG::endStructDef() }
elsif (/^\s*%(.*)\s*;\s*$/) { CG::addStructQualifier($1); }
elsif (/^\s*(count|string|real|bool)\s+(unique\s+|set\s+)?(\w+)\s*;\s*$/) { CG::fieldDeclaration($1, $2, $3); }
# Process blank lines, possibly with comments on them
if (/^\s*(\#.*)?$/)
{
push @curComment, substr($1, 1) if $1;
}
elsif (/^\s*struct\s+(\w+)\s*$/) { parseStruct($1, \@curComment); }
elsif (/^\s*enum\s+(\w+)\s*$/) { parseEnum($1, \@curComment); }
else { print "Unmatched line: $_\n"; }
}
CG::printEnums(\@enumList);
CG::printStructs(\@structList, \%structMap, \%enumMap);

View File

@ -8,14 +8,16 @@ struct BASE
# short name, describing the component, must be translateable
string name;
end struct;
end;
# Enumerates the different technology levels
enum TECH_LEVEL
ONE
TWO
THREE
end enum;
end;
# Represents a researchable component statistic
struct COMPONENT
%inherit BASE;
@ -42,9 +44,11 @@ struct COMPONENT
# We'll name it IMD_model for now.
#
# The "base" IMD model representing this component in 3D space.
IMD_model baseModel;
end struct;
#### IMD_model baseModel;
string baseModel;
end;
# Will contain all data associated with a body
struct BODY
%inherit COMPONENT;
@ -53,4 +57,4 @@ struct BODY
# Engine output of this body's engine
real powerOutput;
end struct;
end;