package SMOP::RI::Writer;
use warnings;
use v5.10;
use strict;
use List::MoreUtils qw(uniq);
use Mildew::Frontend::M0ld;
use Mildew::Backend::OptC;
sub mangle {
my @list = @_;
for (@list) {
s/\`/Grave/g;
s/\~/Tilde/g;
s/\!/Bang/g;
s/\@/At/g;
s/\#/Sharp/g;
s/\$/Dollar/g;
s/\%/Percent/g;
s/\^/Caret/g;
s/\&/Amp/g;
s/\*/Star/g;
s/\(/Paren/g;
s/\)/Thesis/g;
s/\-/Minus/g;
s/\+/Plus/g;
s/\=/Equal/g;
s/\{/Cur/g;
s/\}/Ly/g;
s/\[/Bra/g;
s/\]/Ket/g;
s/\|/Vert/g;
s/\\/Back/g;
s/\:/Colon/g;
s/\;/Semi/g;
s/\'/Single/g;
s/\"/Double/g;
s/\</Lt/g;
s/\>/Gt/g;
s/\«/Fre/g;
s/\»/Nch/g;
s/\,/Comma/g;
s/\./Dot/g;
s/\?/Question/g;
s/\//Slash/g;
s/([^a-zA-Z_0-9])/sprintf("_%02x_",ord($1))/eg;
}
'SMOP__ID__' . (join '_', @list);
}
sub process_ri {
my ($ri_file,$c_file) = @_;
open(my $from,"<:encoding(UTF-8)",$ri_file);
open(my $to,">:encoding(UTF-8)",$c_file);
my @properties = qw(RI RI.id prefix include prototype singleton nagc.nofree prefix struct lowlevel);
my %properties;
my %methods;
my $method;
my %mold;
my $mold;
my %yeast;
my $yeast;
my @attrs;
my @getters;
my @getsetattr;
my @idconsts;
my @has;
my %raw;
sub debug {
}
while (my $line = <$from>) {
if ($line =~ /^\%\s*(\w*)\s*\{\s*$/) {
debug "raw.$1 start\n";
$raw{$1} = '';
until((my $raw_line = <$from>) =~ /^\%}\s*$/) {
debug "raw $raw_line";
$raw{$1} .= $raw_line;
}
debug "raw.R1 stop\n";
} elsif ($method) {
if ($line =~ /^%/) {
undef $method;
redo;
} else {
debug "in method $line";
$methods{$method} .= $line;
next;
}
} elsif ($mold) {
if ($line =~ /^%/) {
undef $mold;
redo;
} else {
debug "in mold $line";
$mold{$mold} .= $line;
next;
}
} elsif ($yeast) {
if ($line =~ /^%/) {
undef $yeast;
redo;
} else {
debug "in yeast $line";
$yeast{$yeast} .= $line;
next;
}
} elsif ($line =~ /^%method \s* (.*?) (?: \( ([^(]*) \) )?$/x) {
$method = $1;
my $sig = $2;
$methods{$method} = '';
if ($sig) {
my (@params) = split(/,/,$sig);
my $i = 1;
for my $param (@params) {
$methods{$method} .= " SMOP__Object* $param = SMOP__NATIVE__capture_positional(interpreter, capture, $i);\n";
$i++;
}
}
debug "method start:$method\n";
} elsif ($line =~ /^%mold\s*(.*)$/) {
$mold = $1;
debug "mold start:$mold\n";
} elsif ($line =~ /^%yeast\s*(.*)$/) {
$yeast = $1;
debug "mold start:$yeast\n";
} elsif ($line =~ /^%attr\s*(.*)\n$/) {
push(@attrs,$1);
} elsif ($line =~ /^%idconst\s*(.*)\n$/) {
push(@idconsts,$1);
} elsif ($line =~ /^%getter\s*(\S+)$/) {
push(@getters,$1);
} elsif ($line =~ /^%getset\s*(\S+)$/) {
push(@getsetattr,$1);
} elsif ($line =~ / ^\s*$ | ^\# | ^\/\/ /x) {
debug "ws/comment:$line";
} elsif ($line =~ /^%has\s*(\$|\@|\%)(\S+)(?:\s*=(.+))?\n$/) {
my $sigil = $1;
my $name = $2;
my $initial = $3;
my $id = $name;
$id =~ s/^\^\!/CaretBang/;
push @attrs, 'SMOP__Object* '.$id;
push @has, [$sigil, $id,$initial];
push(@idconsts,$name);
} else {
for my $property (@properties) {
if ($line =~ /^% \s* \Q$property\E \s+ (.*)$/x) {
$properties{$property} = $1;
debug "prop $property = $1\n";
goto FOUND;
}
}
die "unrecognised line: $line";
FOUND: ;
}
}
die "a %prefix is required\n" unless $properties{prefix};
my $struct = $properties{struct} // $properties{prefix} . '_struct';
print $to qq[/* generated by tools/ri - do not edit*/
#include <smop/base.h>
#include <smop/nagc.h>
#include <smop/mold.h>
#include <smop/yeast.h>
#include <smop/s0native.h>
#include <smop/util.h>
#include <stdlib.h>
#include <stdio.h>
static void DESTROYALL(SMOP__Object* interpreter,
SMOP__Object* invocant);
];
unless (defined $properties{lowlevel}) {
print $to "#include <smop/capture.h>\n";
print $to "#include <smop/native.h>\n";
}
if ($properties{prototype}) {
print $to "#include <smop/s1p.h>\n";
}
for (split (/,/,$properties{include} || '')) {
print $to "#include $_\n";
}
my $id = defined $properties{"RI.id"} ? $properties{"RI.id"} : "unknown RI";
if ($properties{prototype}) {
print $to "SMOP__Object* $properties{prototype};\n";
}
if ($properties{singleton}) {
print $to "SMOP__Object* $properties{singleton};\n";
}
if ($properties{RI}) {
print $to "SMOP__Object* $properties{RI};\n";
} else {
print $to "static SMOP__Object* RI;\n";
}
my $RI = $properties{RI} || 'RI';
for my $getter (@getters) {
$methods{$getter} = qq[ smop_nagc_rdlock((SMOP__NAGC__Object*)invocant);
ret = (($struct*)invocant)->$getter;
smop_nagc_unlock((SMOP__NAGC__Object*)invocant);
SMOP_REFERENCE(interpreter, ret);
];
}
for my $attr (@getsetattr) {
$methods{$attr} = "if ((($struct*)invocant)->$attr) ret = SMOP_REFERENCE(interpreter,(($struct*)invocant)->$attr);\n";
$methods{'set_' . $attr} = qq[
SMOP__Object* value = SMOP__NATIVE__capture_positional(interpreter, capture, 1);
if (!(($struct*)invocant)->$attr) {
(($struct*)invocant)->$attr = value;
} else {
printf("trying to set a new $attr\\n");
abort();
}
];
}
@idconsts = uniq(keys %methods,@idconsts);
for (@idconsts) {
print $to 'static SMOP__Object* ',mangle($_),";\n";
}
for (keys %mold) {
print $to 'static SMOP__Object* ',$_,";\n";
}
for (keys %yeast) {
print $to 'static SMOP__Object* ',$_,";\n";
}
unless ($properties{struct}) {
print $to qq[
typedef struct $properties{prefix}_struct {
SMOP__NAGC__Object__BASE
];
for my $attr (@attrs) {
print $to "$attr;\n";
}
print $to "} $properties{prefix}_struct;\n";
}
print $to $raw{''} || '';
for my $has (@has) {
my $sigil = $has->[0];
my $name = $has->[1];
my $initializer;
if ($has->[2]) {
$initializer = $has->[2];
} elsif ($sigil eq '$') {
$initializer = 'SMOP__S1P__Scalar_create(interpreter, SMOP__NATIVE__bool_false)';
} elsif ($sigil eq '@') {
$initializer = 'SMOP__S1P__Array_create(interpreter)';
} elsif ($sigil eq '%') {
$initializer = 'SMOP__S1P__Hash_create(interpreter)';
} else {
die 'unknown sigil in %has';
}
$methods{$name} = qq[
smop_nagc_rdlock((SMOP__NAGC__Object*)invocant);
SMOP__Object** pointer = &((($struct*)invocant)->$name);
smop_nagc_unlock((SMOP__NAGC__Object*)invocant);
if (!*pointer) {
smop_nagc_wrlock((SMOP__NAGC__Object*)invocant);
*pointer = $initializer;
smop_nagc_unlock((SMOP__NAGC__Object*)invocant);
}
ret = *pointer;
SMOP_REFERENCE(interpreter, ret);
];
}
print $to q[
static SMOP__Object* message(SMOP__Object* interpreter,
SMOP__ResponderInterface* self,
SMOP__Object* identifier,
SMOP__Object* capture) {
];
my $in_msg = $raw{inmessage} || '';
my $pre_msg = $raw{premessage} || '';
my $message = $raw{message} || q[
/*___NATIVE_CAPTURE_ONLY___;
___CONST_IDENTIFIER_ONLY___;*/
SMOP__Object* invocant = (SMOP__Object*) SMOP__NATIVE__capture_positional(interpreter, capture,0);
SMOP__Object* ret = SMOP__NATIVE__bool_false;
%%PREMESSAGE%%
%%METHODS%%
%%INMESSAGE%%
{
___UNKNOWN_METHOD___;
}
if (invocant) SMOP_RELEASE(interpreter,invocant);
SMOP_RELEASE(interpreter,capture);
return ret;
];
my $methods = '';
while (my ($method,$body) = each %methods) {
$methods .= "if (" . mangle($method) . " == identifier) {\n$body } else ";
}
$message =~ s/%%PREMESSAGE%%/$pre_msg/g;
$message =~ s/%%METHODS%%/$methods/g;
$message =~ s/%%INMESSAGE%%/$in_msg/g;
print $to $message;
print $to q[
}
];
my $destroyall = $raw{'DESTROYALL_ALL'} // '
static void DESTROYALL(SMOP__Object* interpreter,
SMOP__Object* invocant) {
%%INDESTROYALL%%
%%ATTRS%%
%%FREE%%
}
';
if ($raw{'DUMP'}) {
print $to '
static SMOP__Object* DUMP(SMOP__Object* interpreter,
SMOP__ResponderInterface* responder,
SMOP__Object* obj) {
';
print $to $raw{'DUMP'};
print $to ' }';
}
my $attrs = '';
if (@has) {
$attrs .= qq[
smop_nagc_wrlock((SMOP__NAGC__Object*)invocant);
];
for my $has (@has) {
my $name = $has->[1];
$attrs .= qq[
SMOP__Object* p_$name = ((($struct*)invocant)->$name);
((($struct*)invocant)->$name) = NULL;
];
}
$attrs .= qq[
smop_nagc_unlock((SMOP__NAGC__Object*)invocant);
];
for my $has (@has) {
my $name = $has->[1];
$attrs .= qq[
if (p_$name) SMOP_RELEASE(interpreter, p_$name);
];
}
}
$destroyall =~ s/%%INDESTROYALL%%/$raw{'DESTROYALL'} || ''/eg;
$destroyall =~ s/%%ATTRS%%/$attrs/g;
if ($properties{'nagc.nofree'}) {
$destroyall =~ s/%%FREE%%/smop_nagc_free(invocant);/g;
} else {
$destroyall =~ s/%%FREE%%//g;
}
print $to $destroyall;
my $release = $properties{'nagc.nofree'} ? 'smop_nagc_release_nofree' : 'smop_nagc_release';
my %yeasts_processed;
my $frontend = Mildew::Frontend::M0ld->new();
my $backend = Mildew::Backend::OptC->new();
while (my ($name,$m0ld) = each %yeast) {
$yeasts_processed{$name} = [$backend->yeast($frontend->parse($m0ld))];
}
while (my ($name,$yeast) = each %yeasts_processed) {
print $to $yeast->[0];
}
my $DUMP = "smop_nagc_dump";
if ($raw{'DUMP'}) {
$DUMP = "DUMP";
}
print $to qq[
void $properties{prefix}_init(SMOP__Object* interpreter) {
$RI = SMOP__NAGC__RI__create(
message,
smop_nagc_reference,
$release,
smop_nagc_weakref,
$DUMP,
DESTROYALL,
"$id");
];
for (@idconsts) {
print $to " ",mangle($_)," = ","SMOP__NATIVE__idconst_create(\"$_\");\n";
}
if ($properties{prototype}) {
print $to qq[
$properties{prototype} = SMOP__Proto__create($RI);
];
}
print $to $raw{'init'} || '';
while (my ($name,$mold) = each %mold) {
print $to " $name = ",preprocess_m0ld($mold),";\n";
}
while (my ($name,$yeast) = each %yeasts_processed) {
print $to " ",$yeast->[2],"\n";
print $to " $name = ",$yeast->[1],";\n";
}
print $to $raw{'init_post_mold'} || '';
print $to "}\n";
print $to qq[
void $properties{prefix}_destr(SMOP__Object* interpreter) {
];
while (my ($name,$mold) = each %mold) {
print $to "SMOP_RELEASE(interpreter,$name);\n";
}
if ($properties{prototype}) {
print $to qq[
SMOP_RELEASE(interpreter,$properties{prototype});
];
}
print $to $raw{'destr'} || '';
print $to qq[
free($RI);
}
];
print $to $raw{'bottom'} || '';
}
1;