#include #define TYPEPKG "Language::Prolog::Types" #define TYPEINTPKG TYPEPKG "::Internal" #define PKG "Language::XSB::Base" static SV *converter; /* prototypes */ static SV *term2sv(prolog_term t); static void perl2p_sv(SV *sv, prolog_term t, AV *refs, AV *cells); /* some functions to easily call simple methods on Perl refs: */ static SV *call_method__sv(SV *object, char *method) { dSP; SV *result; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(object); PUTBACK; call_method(method, G_SCALAR); SPAGAIN; result=POPs; SvREFCNT_inc(result); PUTBACK; FREETMPS; LEAVE; return sv_2mortal(result); } static int call_method__int(SV *object, char *method) { dSP; int result; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(object); PUTBACK; call_method(method, G_SCALAR); SPAGAIN; result=POPi; PUTBACK; FREETMPS; LEAVE; return result; } static SV *call_method_int__sv(SV *object, char *method, int i) { dSP; SV *result; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(object); XPUSHs(sv_2mortal(newSViv(i))); PUTBACK; call_method(method, G_SCALAR); SPAGAIN; result=POPs; SvREFCNT_inc(result); PUTBACK; FREETMPS; LEAVE; return sv_2mortal(result); } static SV *call_method_sv__sv(SV *object, char *method, SV *arg) { dSP; SV *result; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(object); XPUSHs(arg); PUTBACK; call_method(method, G_SCALAR); SPAGAIN; result=POPs; SvREFCNT_inc(result); PUTBACK; FREETMPS; LEAVE; return sv_2mortal(result); } static int regtype(int index) { prolog_term t=reg_term(index); if (is_int(t)) return 2; if (is_string(t)) return 3; if (is_float(t)) return 4; if (is_list(t)) return 5; if (is_nil(t)) return 6; if (is_functor(t)) return 7; if (is_var(t)) return 1; return 0; } static SV *term2sv(prolog_term t) { /* fprintf(stderr, "term \%u: ", t); */ /* printterm(stderr, t, 100); */ /* fprintf(stderr, "\n"); */ if (is_int(t)) return newSViv(p2c_int(t)); if (is_string(t)) return newSVpv(p2c_string(t),0); if (is_float(t)) return newSVnv(p2c_float(t)); if (is_nil(t)) { AV *array=newAV(); SV *ref=newRV_noinc((SV *)array); sv_bless(ref, gv_stashpv(TYPEINTPKG "::nil",1)); return ref; } if (is_list(t)) { AV *array=newAV(); SV *ref=newRV_noinc((SV *)array); while(is_list(t)) { av_push(array, term2sv(p2p_car(t))); t=p2p_cdr(t); } if(is_nil(t)) { sv_bless(ref, gv_stashpv(TYPEINTPKG "::list",1)); } else { av_push(array, term2sv(t)); sv_bless(ref, gv_stashpv(TYPEINTPKG "::ulist",1)); } return ref; } if (is_functor(t)) { int arity=p2c_arity(t); int i; AV *functor=newAV(); SV *ref=newRV_noinc((SV*)functor); sv_bless(ref, gv_stashpv(TYPEINTPKG "::functor",1)); av_extend(functor,arity+1); av_store(functor,0,newSVpv(p2c_functor(t),0)); for(i=1; i<=arity; i++) av_store(functor,i,term2sv(p2p_arg(t,i))); return ref; } if (is_var(t)) { SV *var=newSVuv(t); SV *ref=newRV_noinc(var); /* SV *ref=newRV_noinc(term2sv(p2p_deref(t))); */ sv_bless(ref, gv_stashpv(TYPEINTPKG "::variable",1)); return ref; } if(1) { SV *var=newSVuv(t); SV *ref=newRV_noinc(var); warn ("unknow type for XSB term \%u", t); sv_bless(ref, gv_stashpv(TYPEINTPKG "::unknow",1)); return ref; } die("unknow/unsupported term type"); return NULL; } static int remap_result(int result, char *sub_name) { if (result==0) return 1; if (result==1) return 0; die ("\%s failed with error \%d", sub_name, result); } static SV *my_fetch (AV *av, int i) { SV **sv_p=av_fetch(av, i, 0); return (sv_p ? *sv_p : &PL_sv_undef); } static void perl2p_ifunctor(SV *o, prolog_term t, AV *refs, AV *cells) { if(SvTYPE(o)==SVt_PVAV) { AV *array=(AV *)o; int arity=av_len(array); int i; /* fprintf(stderr, "creating functor arity %d\n", arity); */ if(!c2p_functor(SvPV_nolen(my_fetch(array,0)), arity, t)) die("unable to convert functor to XSB"); for(i=1;i<=arity;i++) perl2p_sv(my_fetch(array, i), p2p_arg(t,i), refs, cells); } else die ("implementation mismatch, " TYPEINTPKG "::functor object is not an array ref"); } static void perl2p_array(AV *array, int u, prolog_term list, AV *refs, AV *cells) { int i; int len=av_len(array); if(u) { if (len<0) die ("implementation mismatch, " TYPEINTPKG "::ulist object is an array with less than one element\n"); --len; } for(i=0; i<=len; i++, list=p2p_cdr(list)) { if(!c2p_list(list)) die ("internal error, unable to create XSB list\n"); perl2p_sv(my_fetch(array, i), p2p_car(list), refs, cells); } if(u) { /* warn ("setting tail, index: %d, tail: %s, term: %x type: %d", i, SvPV_nolen(my_fetch(array, i)), list, regtype(list)); */ perl2p_sv(my_fetch(array, i), list, refs, cells); } else if(!c2p_nil(list)) die ("internal error, unable to create XSB list tail\n"); } static void perl2p_nil(prolog_term t, AV *refs, AV *cells) { if(!c2p_nil(t)) die ("internal error, unable to create XSB nil\n"); } static void perl2p_ilist(SV *o, prolog_term t, AV *refs, AV *cells) { if(SvTYPE(o)==SVt_PVAV) perl2p_array((AV *)o, 0, t, refs, cells); else die ("implementation mismatch, " TYPEINTPKG "::list object is not an array ref"); } static void perl2p_iulist(SV *o, prolog_term t, AV *refs, AV *cells) { if(SvTYPE(o)==SVt_PVAV) perl2p_array((AV *)o, 1, t, refs, cells); else die ("implementation mismatch, " TYPEINTPKG "::ulist object is not an array ref"); } static void perl2p_list(SV *o, prolog_term list, AV *refs, AV *cells) { dSP; int i; int len; SV *el; ENTER; SAVETMPS; len=call_method__int(o, "length"); for (i=0; i XSB term\n"); */ perl2p_sv( call_method_sv__sv(converter, "perl_ref2prolog", ref), t, refs, cells); } static void perl2p_object(SV *sv, prolog_term t, AV *refs, AV *cells) { if (sv_derived_from(sv, TYPEPKG "::Term")) { if (sv_isa(sv,TYPEINTPKG "::list")) { perl2p_ilist(SvRV(sv), t, refs, cells); } else if(sv_isa(sv, TYPEINTPKG "::ulist")) { perl2p_iulist(SvRV(sv), t, refs, cells); } else if (sv_isa(sv, TYPEINTPKG "::functor")) { perl2p_ifunctor(SvRV(sv), t, refs, cells); } else if (sv_isa(sv, TYPEINTPKG "::nil")) { perl2p_nil(t, refs, cells); } else if (sv_derived_from(sv, TYPEPKG "::UList")) { perl2p_list(sv, t, refs, cells); } else if (sv_derived_from(sv, TYPEPKG "::List")) { perl2p_list(sv, t, refs, cells); } else if (sv_derived_from(sv, TYPEPKG "::Functor")) { perl2p_functor(sv, t, refs, cells); } else if (sv_derived_from(sv, TYPEPKG "::Nil")) { perl2p_nil(t, refs, cells); } else { warn ("unable to convert "TYPEPKG"::Term object '%s' to XSB term", SvPV_nolen(sv)); perl2p_any_ref(sv, t, refs, cells); } } else perl2p_any_ref(sv, t, refs, cells); } int lookup_ref(SV *sv, prolog_term t, AV *refs, AV *cells) { int i; int len=av_len(refs); if(sv_isobject(sv) && sv_derived_from(sv, TYPEPKG "::Variable")) { /* variables are the same if they have the same name, even if * they have different references */ dSP; SV *name; ENTER; SAVETMPS; name=call_method__sv(sv, "name"); for (i=0; i<=len; i++) { SV *ref=my_fetch(refs, i); if ( sv_isobject(ref) && sv_derived_from(ref, TYPEPKG "::Variable") && !sv_cmp(name, call_method__sv(ref, "name"))) { break; } } FREETMPS; LEAVE; } else { SV *new_ref=SvRV(sv); for (i=0; i<=len; i++) { SV **ref_p=av_fetch(refs, i, 0); if(!ref_p) die ("internal error, unable to fetch reference pointer from references cache"); if (new_ref==SvRV(*ref_p)) break; } } if (i<=len) { SV **cell_p=av_fetch(cells, i, 0); if(!cell_p || *cell_p==&PL_sv_undef) { warn ("cycled reference passed to XSB as nil\n"); perl2p_nil(t, refs, cells); return 1; } if(!p2p_unify(t, SvIV(*cell_p))) die ("internal error, unable to unify multiple instances of Perl object '%s'", SvPV_nolen(sv)); return 1; } return 0; } static void perl2p_rv(SV *sv, prolog_term t, AV *refs, AV *cells) { if (!lookup_ref(sv, t, refs, cells)) { /* store object reference in cache */ SV *cell; int cell_index; SvREFCNT_inc(sv); av_push(refs, sv); cell_index=av_len(refs); if(sv_isobject(sv)) { /* if it is a variable we have to do nothing */ if(!sv_derived_from(sv, TYPEPKG "::Variable")) perl2p_object(sv, t, refs, cells); } else { SV *val=SvRV(sv); if(SvTYPE(val)==SVt_PVAV) perl2p_array((AV *)val, 0, t, refs, cells); else perl2p_any_ref(sv, t, refs, cells); } /* store term in cache */ cell=newSViv(t); SvREADONLY_on(cell); if(!av_store(cells, cell_index, cell)) { die("unable to store cell in cell cache\n"); } } } static void perl2p_sv(SV *sv, prolog_term t, AV *refs, AV *cells) { if (!is_var(t)) die ("unable to convert perl value to XSB, term is not a free variable"); if (!SvOK(sv)) { if(!c2p_nil(t)) die ("unable to convert undef to XSB nil term"); } else if (SvIOK(sv)) { if (!c2p_int(SvIV(sv),t)) die ("unable to convert integer to XSB term"); } else if (SvNOK(sv)) { if(!c2p_float(SvNV(sv),t)) die ("unable to convert float to XSB term"); } else if (SvPOK(sv)) { if (!c2p_string(SvPV_nolen(sv),t)) die ("unable to convert string to XSB term"); } else if (SvROK(sv)) { perl2p_rv(sv, t, refs, cells); } else { warn ("unable to convert unknow type '%s' to XSB term", SvPV_nolen(sv)); perl2p_any_ref(sv, t, refs, cells); } } static SV *setreg(int index, SV *pt) { dSP; AV *refs, *cells; SV *ref; prolog_term t; t=reg_term(index); if(!is_var(t)) die ("unable to set register %d, it isn't a free variable\n", index); ENTER; SAVETMPS; perl2p_sv( pt, t, refs=(AV *)sv_2mortal((SV *)newAV()), (AV *)sv_2mortal((SV *)newAV())); ref=newRV_inc((SV *)refs); FREETMPS; LEAVE; sv_bless(ref, gv_stashpv(TYPEINTPKG "::list",1)); return ref; } static void *setreg_int(int index, int value) { prolog_term t=reg_term(index); if(!is_var(t)) die ("unable to set register %d, it isn't a free variable\n", index); if(!c2p_int(value, t)) { die ("conversion from int to XSB term failed\n"); } } SV *getreg_int(int index) { prolog_term t=reg_term(index); if(!is_int(t)) return &PL_sv_undef; return newSViv(p2c_int(t)); }