The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#ifdef __cplusplus
extern "C" {
#endif

#define PERL_NO_GET_CONTEXT /* we want efficiency */
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>

#ifdef __cplusplus
} /* extern "C" */
#endif

#define NEED_newSVpvn_flags
#include "ppport.h"

#include <string.h>
#include "pvip.h"
#include "const.h"


#define XS_STATE(type, x)     (INT2PTR(type, SvROK(x) ? SvIV(SvRV(x)) : SvIV(x)))

#define XS_STRUCT2OBJ(sv, class, obj, is_root) \
    sv = newSViv(PTR2IV(obj));  \
    sv = newRV_noinc(sv); \
    sv_magic(sv, sv_2mortal(newSViv(is_root)), PERL_MAGIC_ext, NULL, 0); \
    sv_bless(sv, gv_stashpv(class, 1)); \
    SvREADONLY_on(sv);

MODULE = Perl6::PVIP    PACKAGE = Perl6::PVIP

PROTOTYPES: DISABLE

BOOT:
    setup_pvip_const();

void
_parse_string(code)
    SV *code;
PREINIT:
    size_t len;
    const char *buf;
    PVIPNode *node;
    SV *sv;
    SV *errpv;
PPCODE:
    buf = SvPV(code, len);
    PVIPString* err;
    node = PVIP_parse_string(buf, len, 0, &err);
    if (node) {
        XS_STRUCT2OBJ(sv, "Perl6::PVIP::Node", node, 1);
        XPUSHs(sv);
    } else {
        XPUSHs(&PL_sv_undef);
        errpv = newSVpv(err->buf, err->len);
        XPUSHs(errpv);
        PVIP_string_destroy(err);
    }

MODULE = Perl6::PVIP    PACKAGE = Perl6::PVIP::Node

int
type(self)
    SV *self;
CODE:
    PVIPNode *node = XS_STATE(PVIPNode*, self);
    RETVAL = node->type;
OUTPUT:
    RETVAL

int
line_number(self)
    SV *self;
CODE:
    PVIPNode *node = XS_STATE(PVIPNode*, self);
    RETVAL = node->line_number;
OUTPUT:
    RETVAL

SV*
as_sexp(self)
    SV *self;
PREINIT:
    SV *ret;
CODE:
    PVIPNode *node = XS_STATE(PVIPNode*, self);
    PVIPString * buf = PVIP_string_new();
    PVIP_node_as_sexp(node, buf);
    ret = newSVpv(buf->buf, buf->len);
    PVIP_string_destroy(buf);
    RETVAL = ret;
OUTPUT:
    RETVAL

int
category(self)
    SV *self;
CODE:
    PVIPNode *node = XS_STATE(PVIPNode*, self);
    RETVAL = PVIP_node_category(node->type);
OUTPUT:
    RETVAL

const char*
name(self)
    SV *self;
CODE:
    PVIPNode *node = XS_STATE(PVIPNode*, self);
    RETVAL = PVIP_node_name(node->type);
OUTPUT:
    RETVAL

SV*
value(self)
    SV *self;
PREINIT:
    int i;
    AV *av;
CODE:
    PVIPNode *node = XS_STATE(PVIPNode*, self);
    PVIP_category_t cat = PVIP_node_category(node->type);
    switch (cat) {
    case PVIP_CATEGORY_STRING:
        RETVAL = newSVpvn(node->pv->buf, node->pv->len);
        break;
    case PVIP_CATEGORY_INT:
        RETVAL = newSViv(node->iv);
        break;
    case PVIP_CATEGORY_NUMBER:
        RETVAL = newSVnv(node->nv);
        break;
    case PVIP_CATEGORY_CHILDREN:
        av=newAV();
        for (i=0;i<node->children.size; i++) {
            SV *sv;
            XS_STRUCT2OBJ(sv, "Perl6::PVIP::Node", node->children.nodes[i], 0);
            av_push(av, sv);
        }
        RETVAL = newRV_noinc((SV*)av);
        break;
    default:
        abort();
    }
OUTPUT:
    RETVAL

void
DESTROY(self)
    SV *self;
PREINIT:
    MAGIC *mg;
CODE:
    PVIPNode *node = XS_STATE(PVIPNode*, self);
    mg = mg_find(SvRV(self), PERL_MAGIC_ext);
    if (mg && SvIV(mg->mg_obj)==1) {
        /* release if it's root node. */
        PVIP_node_destroy(node);
    }