#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>
#include "type.h"
/* Apparently we are expected to provide this type as named. */
typedef char *Anarres__Mud__Driver__Compiler__Type;
static HV *amd_typecache;
/* These are used as strings and must be allocated. */
static const char T_UNKNOWN[] = { C_UNKNOWN, 0 };
static const char T_INTEGER[] = { C_INTEGER, 0 };
static const char T_ARRAY[] = { C_M_ARRAY, C_UNKNOWN, 0 };
static const char T_MAPPING[] = { C_M_MAPPING, C_UNKNOWN, 0 };
static const char T_M_CLASS_END[] = { C_M_CLASS_END, 0 };
SV *
amd_type_new(const char *str)
{
SV **svp;
SV *sv;
SV *bsv;
STRLEN len;
len = strlen(str);
svp = hv_fetch(amd_typecache, str, len, FALSE);
if (svp)
return *svp;
// fprintf(stderr, "Creating new type %s\n", str);
sv = newSVpvn(str, len);
bsv = sv_bless(
newRV_noinc(sv),
gv_stashpv(_AMD "::Compiler::Type", TRUE));
hv_store(amd_typecache, str, len, bsv, 0);
return bsv;
}
#define EXPORT_TYPE(x) do { code[0] = C_ ## x; \
sv = amd_type_new(code); \
newCONSTSUB(stash, "T_" #x, sv); \
av_push(export, newSVpv("T_" #x, strlen(#x) + 2)); \
} while(0)
#define EXPORT_TYPE_MODIFIER(x) do { code[0] = C_ ## x; \
newCONSTSUB(stash, "T_" #x, newSVpvn(code, 1)); \
av_push(export, newSVpv("T_" #x, strlen(#x) + 2)); \
} while(0)
#define EXPORT_MODIFIER(x) do { \
newCONSTSUB(stash, #x, newSViv(x)); \
av_push(export, newSVpv(#x, strlen(#x))); \
} while(0)
MODULE = Anarres::Mud::Driver::Compiler PACKAGE = Anarres::Mud::Driver::Compiler
PROTOTYPES: ENABLE
BOOT:
{
{
amd_typecache = get_hv(_AMD "::Compiler::Type::CACHE", TRUE);
}
{
HV *stash;
AV *export;
SV *sv;
char code[2];
// fprintf(stderr, _AMD "::Compiler::Type: Building %%CACHE\n");
stash = gv_stashpv(_AMD "::Compiler::Type", TRUE);
export = get_av(_AMD "::Compiler::Type::EXPORT_OK", TRUE);
code[1] = '\0';
EXPORT_TYPE(VOID);
EXPORT_TYPE(NIL);
EXPORT_TYPE(UNKNOWN);
EXPORT_TYPE(BOOL);
EXPORT_TYPE(CLOSURE);
EXPORT_TYPE(INTEGER);
EXPORT_TYPE(OBJECT);
EXPORT_TYPE(STRING);
EXPORT_TYPE(FAILED);
sv = amd_type_new(T_ARRAY);
newCONSTSUB(stash, "T_ARRAY", sv);
av_push(export, newSVpv("T_ARRAY", strlen("T_ARRAY")));
sv = amd_type_new(T_MAPPING);
newCONSTSUB(stash, "T_MAPPING", sv);
av_push(export, newSVpv("T_MAPPING", strlen("T_MAPPING")));
EXPORT_TYPE_MODIFIER(M_ARRAY);
EXPORT_TYPE_MODIFIER(M_MAPPING);
EXPORT_TYPE_MODIFIER(M_CLASS_BEGIN);
EXPORT_TYPE_MODIFIER(M_CLASS_MID);
EXPORT_TYPE_MODIFIER(M_CLASS_END);
}
{
HV *stash;
AV *export;
stash = gv_stashpv(_AMD "::Compiler::Type", TRUE);
export = get_av(_AMD "::Compiler::Type::EXPORT_OK", TRUE);
EXPORT_MODIFIER(M_NOMASK);
EXPORT_MODIFIER(M_NOSAVE);
EXPORT_MODIFIER(M_STATIC);
EXPORT_MODIFIER(M_PRIVATE);
EXPORT_MODIFIER(M_PROTECTED);
EXPORT_MODIFIER(M_PUBLIC);
EXPORT_MODIFIER(M_VARARGS);
EXPORT_MODIFIER(M_EFUN);
EXPORT_MODIFIER(M_APPLY);
EXPORT_MODIFIER(M_INHERITED);
EXPORT_MODIFIER(M_HIDDEN);
EXPORT_MODIFIER(M_UNKNOWN);
EXPORT_MODIFIER(M_PURE);
}
}
MODULE = Anarres::Mud::Driver::Compiler::Type PACKAGE = Anarres::Mud::Driver::Compiler::Type
SV *
new(self, code)
SV * self
char * code
CODE:
RETVAL = amd_type_new(code);
/* This is automatically mortalised.
* We always have a ref to it through the hash anyway. */
SvREFCNT_inc(RETVAL);
OUTPUT:
RETVAL
void
compatible(self, arg)
Anarres::Mud::Driver::Compiler::Type self
Anarres::Mud::Driver::Compiler::Type arg
CODE:
{
/* This actually returns a boolean */
/* Can we assign type 'self' to type 'arg'? */
if (!*arg)
croak("arg is not a valid type: it is empty");
/* TODO: Make two compatible classes with different
* names be compatible. */
while (*self) {
if (*arg == *self) {
self++;
arg++;
continue;
}
else if (*arg == C_UNKNOWN) {
/* We can assign anything to a mixed. */
XSRETURN_YES;
}
else if (*self == C_NIL) {
/* We can assign a NIL to anything. */
XSRETURN_YES;
}
else if (*self == C_BOOL && *arg == C_INTEGER) {
/* We can assign a BOOL to INTEGER */
XSRETURN_YES;
}
else { /* Including !*arg, which should never happen */
XSRETURN_NO;
}
}
/* If we get here, then the two types were identical.
* However, a class name may be an initial substring of
* another class name, therefore we must check identity
* here. */
/* Actually, it can't be since classes are in braces */
if (*arg)
XSRETURN_NO;
XSRETURN_YES;
}
SV *
unify(self, arg)
Anarres::Mud::Driver::Compiler::Type self
Anarres::Mud::Driver::Compiler::Type arg
CODE:
{
SV *out;
int len;
int i;
char incomplete;
incomplete = 1; /* We haven't got anything yet */
for (len = 0; self[len]; len++) {
if (self[len] != arg[len])
break;
else if (self[len] == C_M_MAPPING)
incomplete = 1;
else if (self[len] == C_M_ARRAY)
incomplete = 1;
else if (self[len] == C_M_CLASS_BEGIN) {
/* XXX Really, if the two classes are strictly
* compatible, either one will do. */
for (i = len + 1; self[i]; i++) {
if (self[i] != arg[i]) {
incomplete = 1;
goto unify_endloop;
}
}
len = i;
incomplete = 0;
}
else
incomplete = 0;
}
unify_endloop:
/* Now we have to exploit that information. */
if (!self[len]) {
/* The two types were equal. */
out = newSVpvn(self, len);
}
else if (self[len] == C_NIL) {
/* Anything unifies with a 'nil' */
out = newSVpv(arg, 0);
}
else if (arg[len] == C_NIL) {
/* Anything unifies with a 'nil' */
out = newSVpv(self, 0);
}
else if ((arg[len] == C_BOOL || arg[len] == C_INTEGER) &&
(self[len] == C_BOOL || self[len] == C_INTEGER)) {
out = newSVpvn(self, len);
sv_catpvn(out, T_INTEGER, strlen(T_INTEGER));
}
else if (incomplete) {
out = newSVpvn(self, len);
sv_catpvn(out, T_UNKNOWN, strlen(T_UNKNOWN));
}
else {
out = newSVpvn(self, len);
}
RETVAL = amd_type_new(SvPV_nolen(out));
SvREFCNT_inc(RETVAL);
SvREFCNT_dec(out);
}
OUTPUT:
RETVAL