/* Interface to Perl from OCaml. * Copyright (C) 2003 Merjis Ltd. * $Id: perl_c.c,v 1.3 2003-10-12 11:56:26 rich Exp $ */ #include #include #include #include #include #include #include #include #include #include /* XXX This was required to avoid an error on my machine when loading the Perl * headers. Not clear why this is missing. */ #define off64_t __off64_t #include #include /* Perl requires the interpreter to be called literally 'my_perl'! */ static PerlInterpreter *my_perl; /* Wrap up an arbitrary void pointer in an opaque OCaml object. */ static value Val_voidptr (void *ptr); /* Get the concrete value from an optional field. */ static value unoption (value option, value deflt); /* Unwrap an arbitrary void pointer from an opaque OCaml object. */ #define Voidptr_val(type,rv) ((type *) Field ((rv), 0)) /* Hide Perl types in opaque OCaml objects. */ #define Val_perl(pl) (Val_voidptr ((pl))) #define Perl_val(plv) (Voidptr_val (PerlInterpreter, (plv))) #define Val_sv(sv) (Val_voidptr ((sv))) #define Sv_val(svv) (Voidptr_val (SV, (svv))) CAMLprim value perl4caml_init (value unit) { PERL_SYS_INIT3 (NULL, NULL, NULL); return Val_unit; } CAMLprim value perl4caml_create (value optargs, value unit) { CAMLparam2 (optargs, unit); CAMLlocal1 (args); int argc, i; char **argv; static char *no_args[] = { "", "-e", "0" }; /* Arguments given? */ if (optargs == Val_int (0)) /* "None" */ { argc = 3; argv = no_args; } else /* "Some args" where args is a string array. */ { args = Field (optargs, 0); argc = Wosize_val (args); argv = alloca (argc * sizeof (char *)); for (i = 0; i < argc; ++i) argv[i] = String_val (Field (args, i)); } my_perl = perl_alloc (); perl_construct (my_perl); PL_exit_flags |= PERL_EXIT_DESTRUCT_END; perl_parse (my_perl, NULL, argc, argv, NULL); perl_run (my_perl); CAMLreturn (Val_perl (my_perl)); } CAMLprim value perl4caml_destroy (value plv) { CAMLparam1 (plv); PerlInterpreter *pl = Perl_val (plv); perl_destruct (pl); perl_free (pl); CAMLreturn (Val_unit); } CAMLprim value perl4caml_set_context (value plv) { CAMLparam1 (plv); PerlInterpreter *pl = Perl_val (plv); PERL_SET_CONTEXT (pl); my_perl = pl; CAMLreturn (Val_unit); } CAMLprim value perl4caml_int_of_sv (value svv) { CAMLparam1 (svv); SV *sv = Sv_val (svv); CAMLreturn (Val_int (SvIV (sv))); } CAMLprim value perl4caml_sv_of_int (value iv) { CAMLparam1 (iv); CAMLreturn (Val_sv (newSViv (Int_val (iv)))); } CAMLprim value perl4caml_float_of_sv (value svv) { CAMLparam1 (svv); SV *sv = Sv_val (svv); CAMLlocal1 (f); f = copy_double (SvNV (sv)); CAMLreturn (f); } CAMLprim value perl4caml_sv_of_float (value fv) { CAMLparam1 (fv); CAMLreturn (Val_sv (newSViv (Double_val (fv)))); } CAMLprim value perl4caml_string_of_sv (value svv) { CAMLparam1 (svv); SV *sv = Sv_val (svv); char *str; STRLEN len; CAMLlocal1 (strv); str = SvPV (sv, len); /* XXX This won't work if the string contains NUL. */ strv = copy_string (str); CAMLreturn (strv); } CAMLprim value perl4caml_sv_of_string (value strv) { CAMLparam1 (strv); CAMLreturn (Val_sv (newSVpv (String_val (strv), string_length (strv)))); } CAMLprim value perl4caml_sv_is_true (value svv) { CAMLparam1 (svv); SV *sv = Sv_val (svv); CAMLreturn (SvTRUE (sv) ? Val_true : Val_false); } CAMLprim value perl4caml_sv_is_undef (value svv) { CAMLparam1 (svv); SV *sv = Sv_val (svv); CAMLreturn (sv == &PL_sv_undef ? Val_true : Val_false); } CAMLprim value perl4caml_sv_get_undef (value unit) { CAMLparam1 (unit); CAMLreturn (Val_sv (&PL_sv_undef)); } CAMLprim value perl4caml_sv_get_yes (value unit) { CAMLparam1 (unit); CAMLreturn (Val_sv (&PL_sv_yes)); } CAMLprim value perl4caml_sv_get_no (value unit) { CAMLparam1 (unit); CAMLreturn (Val_sv (&PL_sv_no)); } CAMLprim value perl4caml_get_sv (value optcreate, value name) { CAMLparam2 (optcreate, name); CAMLlocal1 (create); SV *sv; create = unoption (optcreate, Val_false); sv = get_sv (String_val (name), create == Val_true ? TRUE : FALSE); if (sv == NULL) raise_not_found (); CAMLreturn (Val_sv (sv)); } CAMLprim value perl4caml_call (value optsv, value optfnname, value arglist) { CAMLparam3 (optsv, optfnname, arglist); dSP; int count; SV *sv; CAMLlocal3 (errv, svv, fnname); ENTER; SAVETMPS; /* Push the parameter list. */ PUSHMARK (SP); /* Iteration over the linked list. */ for (; arglist != Val_int (0); arglist = Field (arglist, 1)) { svv = Field (arglist, 0); sv = Sv_val (svv); XPUSHs (sv_2mortal (newSVsv (sv))); } PUTBACK; if (optsv != Val_int (0)) { svv = unoption (optsv, Val_false); sv = Sv_val (svv); count = call_sv (sv, G_EVAL|G_SCALAR); } else if (optfnname != Val_int (0)) { fnname = unoption (optfnname, Val_false); count = call_pv (String_val (fnname), G_EVAL|G_SCALAR); } else { fprintf (stderr, "Perl.call: must supply either 'sv' or 'fn' parameters."); abort (); } SPAGAIN; assert (count == 1); /* Pretty sure it should never be anything else. */ /* Pop return value off the stack. Note that the return value on the * stack is mortal, so we need to take a copy. */ sv = newSVsv (POPs); PUTBACK; FREETMPS; LEAVE; /* Died with an error? * XXX Actually this doesn't work for some reason. */ if (SvTRUE (ERRSV)) { STRLEN n_a; const char *err = SvPV (ERRSV, n_a); /* Equivalent of $@ in Perl. */ errv = copy_string (err); raise_with_arg (*caml_named_value ("perl4caml_perl_failure"), errv); } else { svv = Val_sv (sv); CAMLreturn (svv); } } CAMLprim value perl4caml_call_array (value optsv, value optfnname, value arglist) { CAMLparam3 (optsv, optfnname, arglist); dSP; int i, count; SV *sv; CAMLlocal5 (errv, svv, fnname, list, cons); ENTER; SAVETMPS; /* Push the parameter list. */ PUSHMARK (SP); /* Iteration over the linked list. */ for (; arglist != Val_int (0); arglist = Field (arglist, 1)) { svv = Field (arglist, 0); sv = Sv_val (svv); XPUSHs (sv_2mortal (newSVsv (sv))); } PUTBACK; if (optsv != Val_int (0)) { svv = unoption (optsv, Val_false); sv = Sv_val (svv); count = call_sv (sv, G_EVAL|G_ARRAY); } else if (optfnname != Val_int (0)) { fnname = unoption (optfnname, Val_false); count = call_pv (String_val (fnname), G_EVAL|G_ARRAY); } else { fprintf (stderr, "Perl.call_array: must supply either 'sv' or 'fn' parameters."); abort (); } SPAGAIN; /* Pop all the return values off the stack into a list. Values on the * stack are mortal, so we must copy them. */ list = Val_int (0); for (i = 0; i < count; ++i) { cons = alloc (2, 0); Field (cons, 1) = list; list = cons; Field (cons, 0) = Val_sv (newSVsv (POPs)); } /* Restore the stack. */ PUTBACK; FREETMPS; LEAVE; /* Died with an error? * XXX Actually this doesn't work for some reason. */ if (SvTRUE (ERRSV)) { STRLEN n_a; const char *err = SvPV (ERRSV, n_a); /* Equivalent of $@ in Perl. */ errv = copy_string (err); raise_with_arg (*caml_named_value ("perl4caml_perl_failure"), errv); } else CAMLreturn (list); } CAMLprim value perl4caml_call_void (value optsv, value optfnname, value arglist) { CAMLparam3 (optsv, optfnname, arglist); dSP; int count; SV *sv; CAMLlocal3 (errv, svv, fnname); ENTER; SAVETMPS; /* Push the parameter list. */ PUSHMARK (SP); /* Iteration over the linked list. */ for (; arglist != Val_int (0); arglist = Field (arglist, 1)) { svv = Field (arglist, 0); sv = Sv_val (svv); XPUSHs (sv_2mortal (newSVsv (sv))); } PUTBACK; if (optsv != Val_int (0)) { svv = unoption (optsv, Val_false); sv = Sv_val (svv); count = call_sv (sv, G_EVAL|G_VOID); } else if (optfnname != Val_int (0)) { fnname = unoption (optfnname, Val_false); count = call_pv (String_val (fnname), G_EVAL|G_VOID); } else { fprintf (stderr, "Perl.call_void: must supply either 'sv' or 'fn' parameters."); abort (); } SPAGAIN; assert (count == 0); /* Pretty sure it should never be anything else. */ /* Restore the stack. */ PUTBACK; FREETMPS; LEAVE; /* Died with an error? * XXX Actually this doesn't work for some reason. */ if (SvTRUE (ERRSV)) { STRLEN n_a; const char *err = SvPV (ERRSV, n_a); /* Equivalent of $@ in Perl. */ errv = copy_string (err); raise_with_arg (*caml_named_value ("perl4caml_perl_failure"), errv); } else CAMLreturn (Val_unit); } CAMLprim value perl4caml_eval (value expr) { CAMLparam1 (expr); dSP; SV *sv; CAMLlocal2 (errv, svv); ENTER; SAVETMPS; PUSHMARK (SP); eval_pv (String_val (expr), G_SCALAR); SPAGAIN; sv = newSVsv (POPs); PUTBACK; FREETMPS; LEAVE; /* Died with an error? * XXX Actually this doesn't work for some reason. */ if (SvTRUE (ERRSV)) { STRLEN n_a; const char *err = SvPV (ERRSV, n_a); /* Equivalent of $@ in Perl. */ errv = copy_string (err); raise_with_arg (*caml_named_value ("perl4caml_perl_failure"), errv); } else { svv = Val_sv (sv); CAMLreturn (svv); } } CAMLprim value perl4caml_call_method (value ref, value name, value arglist) { CAMLparam3 (ref, name, arglist); dSP; int count; SV *sv; CAMLlocal2 (errv, svv); ENTER; SAVETMPS; /* Push the parameter list. */ PUSHMARK (SP); sv = Sv_val (ref); XPUSHs (sv_2mortal (newSVsv (sv))); /* Iteration over the linked list. */ for (; arglist != Val_int (0); arglist = Field (arglist, 1)) { svv = Field (arglist, 0); sv = Sv_val (svv); XPUSHs (sv_2mortal (newSVsv (sv))); } PUTBACK; count = call_method (String_val (name), G_EVAL|G_SCALAR); SPAGAIN; assert (count == 1); /* Pretty sure it should never be anything else. */ /* Pop return value off the stack. Note that the return value on the * stack is mortal, so we need to take a copy. */ sv = newSVsv (POPs); PUTBACK; FREETMPS; LEAVE; /* Died with an error? * XXX Actually this doesn't work for some reason. */ if (SvTRUE (ERRSV)) { STRLEN n_a; const char *err = SvPV (ERRSV, n_a); /* Equivalent of $@ in Perl. */ errv = copy_string (err); raise_with_arg (*caml_named_value ("perl4caml_perl_failure"), errv); } else { svv = Val_sv (sv); CAMLreturn (svv); } } CAMLprim value perl4caml_call_method_array (value ref, value name, value arglist) { CAMLparam3 (ref, name, arglist); dSP; int count, i; SV *sv; CAMLlocal4 (errv, svv, list, cons); ENTER; SAVETMPS; /* Push the parameter list. */ PUSHMARK (SP); sv = Sv_val (ref); XPUSHs (sv_2mortal (newSVsv (sv))); /* Iteration over the linked list. */ for (; arglist != Val_int (0); arglist = Field (arglist, 1)) { svv = Field (arglist, 0); sv = Sv_val (svv); XPUSHs (sv_2mortal (newSVsv (sv))); } PUTBACK; count = call_method (String_val (name), G_EVAL|G_ARRAY); SPAGAIN; /* Pop all return values off the stack. Note that the return values on the * stack are mortal, so we need to take a copy. */ list = Val_int (0); for (i = 0; i < count; ++i) { cons = alloc (2, 0); Field (cons, 1) = list; list = cons; Field (cons, 0) = Val_sv (newSVsv (POPs)); } /* Restore the stack. */ PUTBACK; FREETMPS; LEAVE; /* Died with an error? * XXX Actually this doesn't work for some reason. */ if (SvTRUE (ERRSV)) { STRLEN n_a; const char *err = SvPV (ERRSV, n_a); /* Equivalent of $@ in Perl. */ errv = copy_string (err); raise_with_arg (*caml_named_value ("perl4caml_perl_failure"), errv); } else CAMLreturn (list); } CAMLprim value perl4caml_call_method_void (value ref, value name, value arglist) { CAMLparam3 (ref, name, arglist); dSP; int count; SV *sv; CAMLlocal2 (errv, svv); ENTER; SAVETMPS; /* Push the parameter list. */ PUSHMARK (SP); sv = Sv_val (ref); XPUSHs (sv_2mortal (newSVsv (sv))); /* Iteration over the linked list. */ for (; arglist != Val_int (0); arglist = Field (arglist, 1)) { svv = Field (arglist, 0); sv = Sv_val (svv); XPUSHs (sv_2mortal (newSVsv (sv))); } PUTBACK; count = call_method (String_val (name), G_EVAL|G_VOID); SPAGAIN; assert (count == 0); /* Pretty sure it should never be anything else. */ /* Restore the stack. */ PUTBACK; FREETMPS; LEAVE; /* Died with an error? * XXX Actually this doesn't work for some reason. */ if (SvTRUE (ERRSV)) { STRLEN n_a; const char *err = SvPV (ERRSV, n_a); /* Equivalent of $@ in Perl. */ errv = copy_string (err); raise_with_arg (*caml_named_value ("perl4caml_perl_failure"), errv); } else CAMLreturn (Val_unit); } CAMLprim value perl4caml_call_class_method (value classname, value name, value arglist) { CAMLparam3 (classname, name, arglist); dSP; int count; SV *sv; CAMLlocal2 (errv, svv); ENTER; SAVETMPS; /* Push the parameter list. */ PUSHMARK (SP); XPUSHs (sv_2mortal (newSVpv (String_val (classname), 0))); /* Iteration over the linked list. */ for (; arglist != Val_int (0); arglist = Field (arglist, 1)) { svv = Field (arglist, 0); sv = Sv_val (svv); XPUSHs (sv_2mortal (newSVsv (sv))); } PUTBACK; count = call_method (String_val (name), G_EVAL|G_SCALAR); SPAGAIN; assert (count == 1); /* Pretty sure it should never be anything else. */ /* Pop return value off the stack. Note that the return value on the * stack is mortal, so we need to take a copy. */ sv = newSVsv (POPs); PUTBACK; FREETMPS; LEAVE; /* Died with an error? * XXX Actually this doesn't work for some reason. */ if (SvTRUE (ERRSV)) { STRLEN n_a; const char *err = SvPV (ERRSV, n_a); /* Equivalent of $@ in Perl. */ errv = copy_string (err); raise_with_arg (*caml_named_value ("perl4caml_perl_failure"), errv); } else { svv = Val_sv (sv); CAMLreturn (svv); } } CAMLprim value perl4caml_call_class_method_array (value classname, value name, value arglist) { CAMLparam3 (classname, name, arglist); dSP; int count, i; SV *sv; CAMLlocal4 (errv, svv, list, cons); ENTER; SAVETMPS; /* Push the parameter list. */ PUSHMARK (SP); XPUSHs (sv_2mortal (newSVpv (String_val (classname), 0))); /* Iteration over the linked list. */ for (; arglist != Val_int (0); arglist = Field (arglist, 1)) { svv = Field (arglist, 0); sv = Sv_val (svv); XPUSHs (sv_2mortal (newSVsv (sv))); } PUTBACK; count = call_method (String_val (name), G_EVAL|G_ARRAY); SPAGAIN; /* Pop all return values off the stack. Note that the return values on the * stack are mortal, so we need to take a copy. */ list = Val_int (0); for (i = 0; i < count; ++i) { cons = alloc (2, 0); Field (cons, 1) = list; list = cons; Field (cons, 0) = Val_sv (newSVsv (POPs)); } /* Restore the stack. */ PUTBACK; FREETMPS; LEAVE; /* Died with an error? * XXX Actually this doesn't work for some reason. */ if (SvTRUE (ERRSV)) { STRLEN n_a; const char *err = SvPV (ERRSV, n_a); /* Equivalent of $@ in Perl. */ errv = copy_string (err); raise_with_arg (*caml_named_value ("perl4caml_perl_failure"), errv); } else CAMLreturn (list); } CAMLprim value perl4caml_call_class_method_void (value classname, value name, value arglist) { CAMLparam3 (classname, name, arglist); dSP; int count; SV *sv; CAMLlocal2 (errv, svv); ENTER; SAVETMPS; /* Push the parameter list. */ PUSHMARK (SP); XPUSHs (sv_2mortal (newSVpv (String_val (classname), 0))); /* Iteration over the linked list. */ for (; arglist != Val_int (0); arglist = Field (arglist, 1)) { svv = Field (arglist, 0); sv = Sv_val (svv); XPUSHs (sv_2mortal (newSVsv (sv))); } PUTBACK; count = call_method (String_val (name), G_EVAL|G_VOID); SPAGAIN; assert (count == 0); /* Pretty sure it should never be anything else. */ /* Restore the stack. */ PUTBACK; FREETMPS; LEAVE; /* Died with an error? * XXX Actually this doesn't work for some reason. */ if (SvTRUE (ERRSV)) { STRLEN n_a; const char *err = SvPV (ERRSV, n_a); /* Equivalent of $@ in Perl. */ errv = copy_string (err); raise_with_arg (*caml_named_value ("perl4caml_perl_failure"), errv); } else CAMLreturn (Val_unit); } static value Val_voidptr (void *ptr) { value rv = alloc (1, Abstract_tag); /* XXX Is this correct? */ Field(rv, 0) = (value) ptr; return rv; } static value unoption (value option, value deflt) { if (option == Val_int (0)) /* "None" */ return deflt; else /* "Some 'a" */ return Field (option, 0); }