dfhack/plugins/ruby/codegen.pl

322 lines
8.0 KiB
Perl

2012-03-26 06:37:18 -06:00
#!/usr/bin/perl
use strict;
use warnings;
use XML::LibXML;
our @lines_rb;
our @lines_cpp;
sub indent_rb(&) {
my ($sub) = @_;
my @lines;
{
local @lines_rb;
$sub->();
@lines = map { " " . $_ } @lines_rb;
}
push @lines_rb, @lines
}
sub rb_ucase {
my ($name) = @_;
2012-03-31 17:33:30 -06:00
return $name if ($name eq uc($name));
2012-03-26 06:37:18 -06:00
return join("", map { ucfirst $_ } (split('_', $name)));
}
2012-04-02 11:00:38 -06:00
my %global_type_renderer = (
2012-03-31 17:33:30 -06:00
'enum-type' => \&render_global_enum,
'struct-type' => \&render_global_class,
'class-type' => \&render_global_class,
'bitfield-type' => \&render_global_bitfield,
);
2012-04-02 11:00:38 -06:00
my %item_renderer = (
2012-03-31 17:33:30 -06:00
'global' => \&render_item_global,
2012-04-02 11:00:38 -06:00
'number' => \&render_item_number,
2012-03-31 17:33:30 -06:00
'container' => \&render_item_container,
'compound' => \&render_item_compound,
2012-04-02 11:00:38 -06:00
'pointer' => \&render_item_pointer,
'static-array' => \&render_item_staticarray,
'primitive' => \&render_item_primitive,
'bytes' => \&render_item_bytes,
2012-03-26 06:37:18 -06:00
);
2012-04-02 11:00:38 -06:00
2012-03-26 06:37:18 -06:00
sub render_global_enum {
my ($name, $type) = @_;
my $rbname = rb_ucase($name);
push @lines_rb, "class $rbname";
indent_rb {
2012-03-31 17:33:30 -06:00
render_enum_fields($type);
2012-03-26 06:37:18 -06:00
};
push @lines_rb, "end";
}
2012-03-31 17:33:30 -06:00
sub render_enum_fields {
my ($type) = @_;
my $value = -1;
for my $item ($type->findnodes('child::enum-item')) {
$value = $item->getAttribute('value') || ($value+1);
my $elemname = $item->getAttribute('name'); # || "unk_$value";
if ($elemname) {
my $rbelemname = rb_ucase($elemname);
push @lines_rb, "$rbelemname = $value";
}
}
}
2012-04-02 11:00:38 -06:00
2012-03-26 06:37:18 -06:00
sub render_global_bitfield {
my ($name, $type) = @_;
my $rbname = rb_ucase($name);
push @lines_rb, "class $rbname < MemStruct";
indent_rb {
2012-03-31 17:33:30 -06:00
render_bitfield_fields($type);
2012-03-26 06:37:18 -06:00
};
push @lines_rb, "end";
}
2012-03-31 17:33:30 -06:00
sub render_bitfield_fields {
my ($type) = @_;
my $shift = 0;
for my $field ($type->findnodes('child::ld:field')) {
my $count = $field->getAttribute('count') || 1;
my $name = $field->getAttribute('name');
$name = $field->getAttribute('ld:anon-name') if (!$name);
print "bitfield $name !number\n" if (!($field->getAttribute('ld:meta') eq 'number'));
if ($count == 1) {
push @lines_rb, "bit :$name, $shift" if ($name);
} else {
push @lines_rb, "bits :$name, $shift, $count" if ($name);
}
$shift += $count;
}
}
2012-04-02 11:00:38 -06:00
2012-03-26 06:37:18 -06:00
sub render_global_class {
my ($name, $type) = @_;
#my $cppvar = 'v'; # for offsetof
my $rbname = rb_ucase($name);
my $parent = rb_ucase($type->getAttribute('inherits-from') || 'MemStruct');
push @lines_rb, "class $rbname < $parent";
indent_rb {
render_struct_fields($type);
};
push @lines_rb, "end";
}
2012-04-02 11:00:38 -06:00
sub render_struct_fields {
my ($type) = @_;
2012-03-26 06:37:18 -06:00
2012-04-02 11:00:38 -06:00
for my $field ($type->findnodes('child::ld:field')) {
my $name = $field->getAttribute('name');
$name = $field->getAttribute('ld:anon-name') if (!$name);
next if (!$name);
my $offset = "'TODOoffsetof_$name'";
2012-03-31 17:33:30 -06:00
2012-04-02 11:00:38 -06:00
push @lines_rb, "field(:$name, $offset) {";
2012-03-31 17:33:30 -06:00
indent_rb {
2012-04-02 11:00:38 -06:00
render_item($field);
2012-03-31 17:33:30 -06:00
};
2012-04-02 11:00:38 -06:00
push @lines_rb, "}";
2012-03-31 17:33:30 -06:00
}
2012-03-26 06:37:18 -06:00
}
2012-04-02 11:00:38 -06:00
sub render_item {
my ($item) = @_;
return if (!$item);
my $meta = $item->getAttribute('ld:meta');
my $renderer = $item_renderer{$meta};
if ($renderer) {
$renderer->($item);
2012-03-26 06:37:18 -06:00
} else {
2012-04-02 11:00:38 -06:00
print "no render item $meta\n";
2012-03-26 06:37:18 -06:00
}
}
2012-04-02 11:00:38 -06:00
sub render_item_global {
my ($item) = @_;
2012-03-31 17:33:30 -06:00
2012-04-02 11:00:38 -06:00
my $typename = $item->getAttribute('type-name');
my $rbname = rb_ucase($typename);
2012-03-31 17:33:30 -06:00
2012-04-02 11:00:38 -06:00
push @lines_rb, "global :$rbname";
2012-03-31 17:33:30 -06:00
}
2012-04-02 11:00:38 -06:00
sub render_item_number {
my ($item) = @_;
2012-03-31 17:33:30 -06:00
2012-04-02 11:00:38 -06:00
my $subtype = $item->getAttribute('ld:subtype');
$subtype = $item->getAttribute('base-type') if ($subtype eq 'enum');
$subtype = 'int32_t' if (!$subtype);
if ($subtype eq 'int64_t') {
push @lines_rb, 'number 64, true';
} elsif ($subtype eq 'uint32_t') {
push @lines_rb, 'number 32, false';
} elsif ($subtype eq 'int32_t') {
push @lines_rb, 'number 32, true';
} elsif ($subtype eq 'uint16_t') {
push @lines_rb, 'number 16, false';
} elsif ($subtype eq 'int16_t') {
push @lines_rb, 'number 16, true';
} elsif ($subtype eq 'uint8_t') {
push @lines_rb, 'number 8, false';
} elsif ($subtype eq 'int8_t') {
push @lines_rb, 'number 8, false';
} elsif ($subtype eq 'bool') {
push @lines_rb, 'number 8, true';
} elsif ($subtype eq 's-float') {
push @lines_rb, 'float';
2012-03-31 17:33:30 -06:00
} else {
2012-04-02 11:00:38 -06:00
print "no render number $subtype\n";
2012-03-31 17:33:30 -06:00
}
}
2012-04-02 11:00:38 -06:00
sub render_item_compound {
my ($item) = @_;
2012-03-31 17:33:30 -06:00
2012-04-02 11:00:38 -06:00
my $subtype = $item->getAttribute('ld:subtype');
if (!$subtype || $subtype eq 'bitfield') {
push @lines_rb, "compound {";
indent_rb {
if (!$subtype) {
render_struct_fields($item);
} else {
render_bitfield_fields($item);
}
};
push @lines_rb, "}"
} elsif ($subtype eq 'enum') {
# declare constants
render_enum_fields($item);
# actual field
render_item_number($item);
2012-03-31 17:33:30 -06:00
} else {
2012-04-02 11:00:38 -06:00
print "no render compound $subtype\n";
2012-03-31 17:33:30 -06:00
}
}
2012-04-02 11:00:38 -06:00
sub render_item_container {
my ($item) = @_;
2012-03-26 06:37:18 -06:00
2012-04-02 11:00:38 -06:00
my $subtype = $item->getAttribute('ld:subtype');
my $rbmethod = join('_', split('-', $subtype));
my $tg = $item->findnodes('child::ld:item')->[0];
if ($tg) {
my $tglen = get_tglen($tg);
push @lines_rb, "$rbmethod($tglen) {";
indent_rb {
render_item($tg);
};
push @lines_rb, "}";
} else {
push @lines_rb, "$rbmethod";
2012-03-26 06:37:18 -06:00
}
}
2012-04-02 11:00:38 -06:00
sub render_item_pointer {
2012-03-31 17:33:30 -06:00
my ($item) = @_;
2012-04-02 11:00:38 -06:00
my $tg = $item->findnodes('child::ld:item')->[0];
my $tglen = get_tglen($tg);
push @lines_rb, "pointer($tglen) {";
indent_rb {
render_item($tg);
};
push @lines_rb, "}";
2012-03-31 17:33:30 -06:00
}
2012-04-02 11:00:38 -06:00
sub render_item_staticarray {
2012-03-31 17:33:30 -06:00
my ($item) = @_;
2012-04-02 11:00:38 -06:00
my $count = $item->getAttribute('count');
my $tg = $item->findnodes('child::ld:item')->[0];
my $tglen = get_tglen($tg);
push @lines_rb, "static_array($count, $tglen) {";
indent_rb {
render_item($tg);
};
push @lines_rb, "}";
2012-03-31 17:33:30 -06:00
}
sub render_item_primitive {
my ($item) = @_;
my $subtype = $item->getAttribute('ld:subtype');
if ($subtype eq 'stl-string') {
2012-04-02 11:00:38 -06:00
push @lines_rb, "stl_string";
2012-03-31 17:33:30 -06:00
} else {
2012-04-02 11:00:38 -06:00
print "no render primitive $subtype\n";
2012-03-31 17:33:30 -06:00
}
}
2012-04-02 11:00:38 -06:00
sub render_item_bytes {
2012-03-31 17:33:30 -06:00
my ($item) = @_;
2012-03-26 06:37:18 -06:00
2012-04-02 11:00:38 -06:00
my $subtype = $item->getAttribute('ld:subtype');
if ($subtype eq 'padding') {
} elsif ($subtype eq 'static-string') {
my $size = $item->getAttribute('size');
push @lines_rb, "static_string($size)";
2012-03-31 17:33:30 -06:00
} else {
2012-04-02 11:00:38 -06:00
print "no render bytes $subtype\n";
2012-03-31 17:33:30 -06:00
}
}
2012-04-02 11:00:38 -06:00
sub get_tglen {
my ($tg) = @_;
if (!$tg) { return 'nil'; }
2012-03-31 17:33:30 -06:00
2012-04-02 11:00:38 -06:00
my $meta = $tg->getAttribute('ld:meta');
if ($meta eq 'number') {
return $tg->getAttribute('ld:bits')/8;
} elsif ($meta eq 'pointer') {
return 4;
2012-03-31 17:33:30 -06:00
} else {
2012-04-02 11:00:38 -06:00
# TODO
return "'TODOsizeof($meta)'";
2012-03-31 17:33:30 -06:00
}
}
2012-03-26 06:37:18 -06:00
my $input = $ARGV[0] || '../../library/include/df/codegen.out.xml';
my $output_rb = $ARGV[1] || 'ruby-autogen.rb';
my $output_cpp = $ARGV[2] || 'ruby-autogen.cpp';
my $doc = XML::LibXML->new()->parse_file($input);
my %global_types;
$global_types{$_->getAttribute('type-name')} = $_ foreach $doc->findnodes('/ld:data-definition/ld:global-type');
for my $name (sort { $a cmp $b } keys %global_types) {
my $type = $global_types{$name};
my $meta = $type->getAttribute('ld:meta');
2012-04-02 11:00:38 -06:00
my $renderer = $global_type_renderer{$meta};
2012-03-26 06:37:18 -06:00
if ($renderer) {
$renderer->($name, $type);
} else {
print "no render global type $meta\n";
}
}
for my $obj ($doc->findnodes('/ld:data-definition/ld:global-object')) {
my $name = $obj->getAttribute('name');
}
open FH, ">$output_rb";
print FH "$_\n" for @lines_rb;
close FH;
open FH, ">$output_cpp";
print FH "$_\n" for @lines_cpp;
close FH;