Did you know ... | Search Documentation: |
Pack rolog -- src/rolog_cpp2.txt |
#include <SWI-cpp2.h> #include "Rcpp.h" using namespace Rcpp ;
// Translate prolog expression to R // // [] -> NULL // real -> NumericVector // #(r1, r2, r3) -> NumericVector (# is a default, see option realvec) // ##(#(row1), #(row2), ...) -> Matrix // integer -> IntegerVector // %(i1, i2, i3) -> IntegerVector (see option intvec for the name) // %%(%(row1), %(row2), ...) -> Matrix // string -> CharacterVector // $$(s1, s2, s3) CharacterVector // $$$($$(row1), $$(row2), ...) -> Matrix // na (atom) -> NA // true, false (atoms) -> LogicalVector // !(l1, l2, l3) -> LogicalVector (see option boolvec) // !!(!(row1), !(row2), ...) -> Matrix // the empty atom -> "" // other atoms -> symbol/name // variable -> expression(variable name) // compound -> call (aka. "language") // list -> list // RObject pl2r(PlTerm pl, CharacterVector& names, PlTerm& vars, List options) ;
// Translate R expression to prolog // // NULL -> [] // numeric vector of length 1 -> real (unless rolog.scalar == FALSE) // numeric vector of length > 1 -> e.g., #(1.0, 2.0, 3.0) (see rolog.realvec) // integer vector of length 1 -> integer // integer vector of length > 1 -> %(1, 2, 3) // character vector of length 1 -> string // character vector of length > 1 -> $("a", "b", "c") // logical vector of length 1 -> the atoms true, false or na // logical vector of length > 1 -> $(true, false, na) // other symbols/name -> atom // expression -> variable // call/language -> compound // list -> list // PlTerm r2pl(SEXP r, CharacterVector& names, PlTerm& vars, List options) ;
// Consult one or more files. If something fails, the procedure stops, and
// will not try to consult the remaining files.
//
// [[Rcpp::export(.consult)]]
LogicalVector consult_(CharacterVector files)
{
for(R_xlen_t i=0; i<files.size()
; i++)
{
try
{
PlCall("consult", PlTermv(PlTerm_string(files(i)
)));
}
catch(PlException& ex)
{
String err(ex.as_string(PlEncoding::Locale));
PL_clear_exception() ;
stop("failed to consult %s: %s", (char*) files(i)
, err.get_cstring()
) ;
}
}
return true ; }
// Prolog -> R
RObject pl2r_null()
{
return R_NilValue ;
}
// TODO: use this and pl.eq_if_atom(ATOM_na)
instead of
// pl.is_atom()
&& pl == "na"
// PlAtom ATOM_na("na");
// This helper function checks for na and then translates an individual PlTerm
// to a double.
double pl2r_double(PlTerm pl)
{
if(pl.is_atom()
&& pl.as_string()
== "na")
return NA_REAL ;
try
{
return pl.as_float()
;
}
catch(PlException& ex)
{
warning("cannot convert %s to float: %s",
pl.as_string(PlEncoding::Locale).c_str()
, ex.as_string(PlEncoding::Locale).c_str()
) ;
PL_clear_exception() ;
return NA_REAL ;
}
}
// Convert scalar real to DoubleVector of length 1
DoubleVector pl2r_real(PlTerm pl)
{
return DoubleVector::create(pl2r_double(pl))
;
}
// Convert vector of reals (e.g., #(1.0, 2.0, na)) to DoubleVector
DoubleVector pl2r_realvec(PlTerm pl)
{
DoubleVector r(pl.arity())
;
for(size_t i=0; i<pl.arity()
; i++)
r(i)
= pl2r_double(pl[i+1]) ;
return r ; }
// Convert matrix of reals (e.g., ##(#(1.0, 2.0), #(na, ...), ...))
NumericMatrix pl2r_realmat(PlTerm pl)
{
size_t nrow = pl.arity()
;
size_t ncol = 0 ;
if(nrow > 0)
{
for(size_t i=0; i<pl.arity()
; i++)
if(i == 0)
ncol = pl[1].arity()
;
else
{
if(pl[i+1].arity()
!= ncol)
stop("cannot convert PlTerm to Matrix, inconsistent rows")
;
}
}
NumericMatrix r(nrow, ncol)
;
for(size_t i=0; i<nrow; i++)
r.row(i)
= pl2r_realvec(pl[i+1]) ;
return r ; }
// See above for pl2r_double
long pl2r_int(PlTerm pl)
{
if(pl.is_atom()
&& pl.as_string()
== "na")
return NA_INTEGER ;
try
{
return pl.as_long()
;
}
catch(PlException& ex)
{
warning("Cannot convert %s to integer: %s",
pl.as_string(PlEncoding::Locale).c_str()
, ex.as_string(PlEncoding::Locale).c_str()
) ;
PL_clear_exception() ;
return NA_INTEGER ;
}
}
IntegerVector pl2r_integer(PlTerm pl)
{
return IntegerVector::create(pl2r_int(pl))
;
}
IntegerVector pl2r_intvec(PlTerm pl)
{
IntegerVector r(pl.arity())
;
for(size_t i=0; i<pl.arity()
; i++)
r(i)
= pl2r_int(pl[i+1]) ;
return r ; }
IntegerMatrix pl2r_intmat(PlTerm pl)
{
size_t nrow = pl.arity()
;
size_t ncol = 0 ;
if(nrow > 0)
{
for(size_t i=0; i<pl.arity()
; i++)
if(i == 0)
ncol = pl[1].arity()
;
else
{
if(pl[i+1].arity()
!= ncol)
stop("cannot convert PlTerm to Matrix, inconsistent rows")
;
}
}
IntegerMatrix r(nrow, ncol)
;
for(size_t i=0; i<nrow; i++)
r.row(i)
= pl2r_intvec(pl[i+1]) ;
return r ; }
// See above for pl2r_double
String pl2r_string(PlTerm pl)
{
if(pl.is_atom()
&& pl.as_string()
== "na")
return NA_STRING ;
return pl.as_string(PlEncoding::Locale) ; }
CharacterVector pl2r_char(PlTerm pl)
{
return CharacterVector::create(pl2r_string(pl))
;
}
CharacterVector pl2r_charvec(PlTerm pl)
{
CharacterVector r(pl.arity())
;
for(size_t i=0; i<pl.arity()
; i++)
r(i)
= pl2r_string(pl[i+1]) ;
return r ; }
CharacterMatrix pl2r_charmat(PlTerm pl)
{
size_t nrow = pl.arity()
;
size_t ncol = 0 ;
if(nrow > 0)
{
for(size_t i=0; i<pl.arity()
; i++)
if(i == 0)
ncol = pl[1].arity()
;
else
{
if(pl[i+1].arity()
!= ncol)
stop("cannot convert PlTerm to Matrix, inconsistent rows")
;
}
}
CharacterMatrix r(nrow, ncol)
;
for(size_t i=0; i<nrow; i++)
r.row(i)
= pl2r_charvec(pl[i+1]) ;
return r ; }
// Convert prolog atom to R symbol (handle na, true, false)
RObject pl2r_symbol(PlTerm pl)
{
if(pl.as_string() == "na")
return wrap(NA_LOGICAL)
;
if(pl.as_string() == "true")
return wrap(true)
;
if(pl.as_string() == "false")
return wrap(false)
;
// Empty symbols
if(pl.as_string() == "")
return Function("substitute")() ;
return as<RObject>(Symbol(pl.as_string(PlEncoding::UTF8))) ; // TODO: PlEncoding::Locale? }
// Forward declaration, needed below RObject pl2r_compound(PlTerm pl, CharacterVector& names, PlTerm& vars, List options) ;
// Convert prolog neck to R function RObject pl2r_function(PlTerm pl, CharacterVector& names, PlTerm& vars, List options) { PlTerm plhead = pl[1] ; PlTerm plbody = pl[2] ;
Language head("alist")
;
for(unsigned int i=1 ; i<=plhead.arity()
; i++)
{
PlTerm arg = plhead[i] ;
// Compounds like mean=100 are translated to named function arguments
if(arg.is_compound()
&& arg.name()
.as_string()
== "=" && arg.arity()
== 2)
{
PlTerm a1 = arg[1] ;
PlTerm a2 = arg[2] ;
if(a1.is_atom())
{
head.push_back(Named(a1.as_string(PlEncoding::UTF8)) = pl2r(a2, names, vars, options)
) ;
continue ;
}
}
// the argument is the name head.push_back(Named(arg.as_string(PlEncoding::UTF8)) = pl2r_symbol(PlTerm_atom(""))) ; }
RObject body = pl2r_compound(plbody, names, vars, options)
;
head.push_back(body)
;
Function as_function("as.function")
;
return wrap(as_function(head))
;
}
LogicalVector pl2r_boolvec(PlTerm pl)
{
LogicalVector r(pl.arity())
;
for(size_t i=0; i<pl.arity()
; i++)
{
PlTerm t = pl[i+1] ;
if(t.is_atom())
{
if(t.as_string() == "na")
{
r(i)
= NA_LOGICAL ;
continue ;
}
if(t.as_string() == "true")
{
r(i)
= 1 ;
continue ;
}
if(t.as_string() == "false")
{
r(i)
= 0 ;
continue ;
}
}
warning("r2pl_logical: invalid item %s, returning NA", t.as_string(PlEncoding::Locale).c_str()
) ;
r(i)
= NA_LOGICAL ;
}
return r ; }
LogicalMatrix pl2r_boolmat(PlTerm pl)
{
size_t nrow = pl.arity()
;
size_t ncol = 0 ;
if(nrow > 0)
{
for(size_t i=0; i<pl.arity()
; i++)
if(i == 0)
ncol = pl[1].arity()
;
else
{
if(pl[i+1].arity()
!= ncol)
stop("cannot convert PlTerm to Matrix, inconsistent rows")
;
}
}
LogicalMatrix r(nrow, ncol)
;
for(size_t i=0; i<nrow; i++)
r.row(i)
= pl2r_boolvec(pl[i+1]) ;
return r ; }
// Translate prolog variables to R expressions.
RObject pl2r_variable(PlTerm pl, CharacterVector& names, PlTerm& vars)
{
// names and vars is a list of all the variables from the R query,
// a typical member of names is something like X, a member of vars
// is something like _1545.
//
// Search for the variable (e.g., _1545) in names and return its R name as an
// expression (say, X).
PlTerm_tail tail(vars)
;
PlTerm_var v ;
for(int i=0 ; i<names.length()
; i++)
{
PlCheckFail(tail.next(v)
) ;
if(v == pl)
return ExpressionVector::create(Symbol(names(i)
)) ;
}
// If the variable is not found, it's a new one created by Prolog, e.g., in
// queries like member(1, Y)
, Y is unified with [1 | _NewVar ]. This variable
// cannot be translated to a human-readable name, so it is returned as _1545.
return ExpressionVector::create(Symbol(pl.as_string(PlEncoding::UTF8))) ; // TODO: PlEncoding::Locale?
}
// Translate prolog compound to R call
//
// This function takes care of special compound names (#, %, $, !) for vector
// objects in R, as well as "named" function arguments like "mean=100", in
// rnorm(10, mean=100, sd=15)
.
RObject pl2r_compound(PlTerm pl, CharacterVector& names, PlTerm& vars, List options)
{
// This function does not (yet) work for cyclic terms
if(!PL_is_acyclic(pl.C_))
stop("pl2r: Cannot convert cyclic term %s", pl.as_string(PlEncoding::Locale).c_str()
) ;
// Convert ##(#(...), ...) to NumericMatrix
if(!strcmp(pl.name()
.as_string(PlEncoding::UTF8).c_str()
, options("realmat")
))
return pl2r_realmat(pl)
;
// Convert #(1.0, 2.0, 3.0) to DoubleVector (# given by options("realvec")
)
if(!strcmp(pl.name()
.as_string(PlEncoding::UTF8).c_str()
, options("realvec")
))
return pl2r_realvec(pl)
;
// Convert %%(%(...), ...) to IntegerMatrix
if(!strcmp(pl.name()
.as_string(PlEncoding::UTF8).c_str()
, options("intmat")
))
return pl2r_intmat(pl)
;
// Convert %(1.0, 2.0, 3.0) to IntegerVector
if(!strcmp(pl.name()
.as_string(PlEncoding::UTF8).c_str()
, options("intvec")
))
return pl2r_intvec(pl)
;
// Convert $$$($$(...), ...) to StringMatrix
if(!strcmp(pl.name()
.as_string(PlEncoding::UTF8).c_str()
, options("charmat")
))
return pl2r_charmat(pl)
;
// Convert $$(1.0, 2.0, 3.0) to CharacterVector
if(!strcmp(pl.name()
.as_string(PlEncoding::UTF8).c_str()
, options("charvec")
))
return pl2r_charvec(pl)
;
// Convert !!(!(...), ...) to LogicalMatrix
if(!strcmp(pl.name()
.as_string(PlEncoding::UTF8).c_str()
, options("boolmat")
))
return pl2r_boolmat(pl)
;
// Convert !(1.0, 2.0, 3.0) to LogicalVector
if(!strcmp(pl.name()
.as_string(PlEncoding::UTF8).c_str()
, options("boolvec")
))
return pl2r_boolvec(pl)
;
// Convert :- to function
if(pl.name().as_string() == ":-")
return pl2r_function(pl, names, vars, options)
;
// Other compounds
Language r(pl.name()
.as_string(PlEncoding::UTF8).c_str()
) ;
for(unsigned int i=1 ; i<=pl.arity()
; i++)
{
PlTerm arg = pl[i] ;
// Compounds like mean=100 are translated to named function arguments
if(arg.is_compound()
&& !strcmp(arg.name()
.as_string(PlEncoding::UTF8).c_str()
, "=") && arg.arity()
== 2)
{
PlTerm a1 = arg[1] ;
PlTerm a2 = arg[2] ;
if(a1.is_atom())
{
r.push_back(Named(a1.name()
.as_string(PlEncoding::UTF8).c_str()
) = pl2r(a2, names, vars, options)
) ;
continue ;
}
}
// argument has no name
r.push_back(pl2r(arg, names, vars, options))
;
}
return as<RObject>(r) ; }
// Translate prolog list to R list
//
// This code allows for lists like [1, 2 | Tail] with variable tail. These
// cannot be processed by PlTerm_tail, therefore, the code is a bit more
// complicated, also because it can handle named arguments.
//
// Examples:
// [1, 2, 3] -> list(1, 2, 3)
// [1, 2 | X] -> `[|]`(1, `[|]`(2, expression(X)
))
// [a-1, b-2, c-3] -> list(a=1, b=2, c=3)
//
RObject pl2r_list(PlTerm pl, CharacterVector& names, PlTerm& vars, List options)
{
PlTerm head = pl[1] ;
// if the tail is a list or empty, return a normal list RObject tail = pl2r(pl[2], names, vars, options) ; if(TYPEOF(tail) == VECSXP || TYPEOF(tail) == NILSXP) { List r = as<List>(tail) ;
// convert prolog pair a-X to named list element
if(head.is_compound()
&& !strcmp(head.name()
.as_string(PlEncoding::UTF8).c_str()
, "-") && head.arity()
== 2)
{
PlTerm a1 = head[1] ;
PlTerm a2 = head[2] ;
if(a1.is_atom())
{
r.push_front(pl2r(a2, names, vars, options)
, a1.name()
.as_string(PlEncoding::UTF8).c_str()
) ;
return r ;
}
}
// element has no name
r.push_front(pl2r(head, names, vars, options))
;
return r ;
}
// if the tail is something else, return [|](head, tail)
Language r(pl.name()
.as_string(PlEncoding::UTF8).c_str()
) ;
// convert prolog pair a-X to named list element
if(head.is_compound()
&& !strcmp(head.name()
.as_string(PlEncoding::UTF8).c_str()
, "-") && head.arity()
== 2)
{
PlTerm a1 = head[1] ;
PlTerm a2 = head[2] ;
if(a1.is_atom())
{
r.push_back(Named(a1.name()
.as_string(PlEncoding::UTF8).c_str()
) = pl2r(a2, names, vars, options)
) ;
r.push_back(tail)
;
return as<RObject>(r) ;
}
}
// element has no name
r.push_back(pl2r(head, names, vars, options))
;
r.push_back(tail)
;
return as<RObject>(r) ;
}
RObject pl2r(PlTerm pl, CharacterVector& names, PlTerm& vars, List options)
{
if(pl.type() == PL_NIL)
return pl2r_null()
;
if(pl.is_integer())
return pl2r_integer(pl)
;
if(pl.is_float())
return pl2r_real(pl)
;
if(pl.is_string())
return pl2r_char(pl)
;
if(pl.is_atom())
return pl2r_symbol(pl)
;
if(pl.is_list())
return pl2r_list(pl, names, vars, options)
;
if(pl.is_compound())
return pl2r_compound(pl, names, vars, options)
;
if(pl.is_variable())
return pl2r_variable(pl, names, vars)
;
stop("pl2r: Cannot convert %s", pl.as_string(PlEncoding::Locale).c_str()
) ;
}
// Translate R expression to prolog // // Forward declarations PlTerm r2pl_real(NumericVector r, List options) ; PlTerm r2pl_logical(LogicalVector r, List options) ; PlTerm r2pl_integer(IntegerVector r, List options) ; PlTerm r2pl_string(CharacterVector r, List options) ;
// This returns an empty list
PlTerm r2pl_null()
{
PlTerm_var pl ;
PlCheckFail(PlTerm_tail(pl).close()
) ;
return pl ;
}
// Prolog representation of R's NA.
PlTerm r2pl_na()
{
return PlTerm_atom("na") ;
}
// Translate to matrix ##(#(1.0, 2.0, 3.0), #(4.0, 5.0, 6.0))
PlTerm r2pl_matrix(Matrix<REALSXP> r, List aoptions)
{
List options(aoptions)
;
options("scalar")
= false ;
PlTermv rows(r.nrow())
;
for(int i=0 ; i<r.nrow()
; i++)
PlCheckFail(rows[i].unify_term(r2pl_real(r.row(i), options))
) ;
return PlCompound((const char*) options("realmat")
, rows) ;
}
// Translate to (scalar) real or compounds like #(1.0, 2.0, 3.0) PlTerm r2pl_real(NumericVector r, List options) { if(Rf_isMatrix(r)) return r2pl_matrix(as<Matrix<REALSXP>>(r), options) ;
if(r.length() == 0)
return r2pl_null()
;
LogicalVector nan = is_nan(r)
;
LogicalVector na = is_na(r)
;
// Translate to scalar
if(as<LogicalVector>(options("scalar")
)(0) && r.length()
== 1)
{
if(na[0] && !nan[0])
return r2pl_na()
;
return PlTerm_float(r[0]); }
// Translate to vector #(1.0, 2.0, 3.0)
size_t len = (size_t) r.length()
;
PlTermv args(len)
;
for(size_t i=0 ; i<len ; i++)
{
if(na[i] && !nan[i])
PlCheckFail(args[i].unify_term(r2pl_na())
) ;
else
PlCheckFail(args[i].unify_float(r[i])) ;
}
return PlCompound((const char*) options("realvec")
, args) ;
}
// Translate to matrix !!(!(true, false), !(false, true))
PlTerm r2pl_matrix(Matrix<LGLSXP> r, List aoptions)
{
List options(aoptions)
;
options("scalar")
= false ;
PlTermv rows(r.nrow())
;
for(int i=0 ; i<r.nrow()
; i++)
PlCheckFail(rows[i].unify_term(r2pl_logical(r.row(i), options))
) ;
return PlCompound((const char*) options("boolmat")
, rows) ;
}
// Translate to (scalar) boolean or compounds like !(true, false, na) PlTerm r2pl_logical(LogicalVector r, List options) { if(Rf_isMatrix(r)) return r2pl_matrix(as<Matrix<LGLSXP>>(r), options) ;
if(r.length() == 0)
return r2pl_null()
;
LogicalVector na = is_na(r)
;
// scalar boolean
if(as<LogicalVector>(options("scalar")
)(0) && r.length()
== 1)
{
if(na[0])
return r2pl_na()
;
return PlTerm_atom(r[0] ? "true" : "false") ; }
// LogicalVector !(true, false, na)
size_t len = (size_t) r.length()
;
PlTermv args(len)
;
for(size_t i=0 ; i<len ; i++)
{
if(na[i])
PlCheckFail(args[i].unify_term(r2pl_na())
) ;
else
PlCheckFail(args[i].unify_atom(r[i] ? "true" : "false")) ; // TODO: unify_bool()
}
return PlCompound((const char*) options("boolvec")
, args) ;
}
// Translate to matrix %%(%(1, 2), %(3, 4))
PlTerm r2pl_matrix(Matrix<INTSXP> r, List aoptions)
{
List options(aoptions)
;
options("scalar")
= false ;
PlTermv rows(r.nrow())
;
for(int i=0 ; i<r.nrow()
; i++)
PlCheckFail(rows[i].unify_term(r2pl_integer(r.row(i), options))
) ;
return PlCompound((const char*) options("intmat")
, rows) ;
}
// Translate to (scalar) integer or compounds like %(1, 2, 3) PlTerm r2pl_integer(IntegerVector r, List options) { if(Rf_isMatrix(r)) return r2pl_matrix(as<Matrix<INTSXP>>(r), options) ;
if(r.length() == 0)
return r2pl_null()
;
LogicalVector na = is_na(r)
;
// scalar integer
if(as<LogicalVector>(options("scalar")
)(0) && r.length()
== 1)
{
if(na[0])
return r2pl_na()
;
return PlTerm_integer(r(0)
) ;
}
// IntegerVector %(1, 2, 3)
size_t len = (size_t) r.length()
;
PlTermv args(len)
;
for(size_t i=0 ; i<len ; i++)
{
if(na[i])
PlCheckFail(args[i].unify_term(r2pl_na())
) ;
else
PlCheckFail(args[i].unify_integer(r[i])) ;
}
return PlCompound((const char*) options("intvec")
, args) ;
}
// Translate R expression to prolog variable
//
// This function keeps a record of the names of the variables in
// use (e.g., _1545) as well as the corresponding R names (e.g., X). If a new
// variable is encountered, its name is looked up in the list of known
// variables, and it is unified with it if the name is found. Otherwise, a new
// variable is created.
//
// If options("atomize")
is true, no variable is created, but an atom is created
// with the variable name from R. This is only used for pretty printing.
PlTerm r2pl_var(ExpressionVector r, CharacterVector& names, PlTerm& vars, List options)
{
// Variable name in R
Symbol n = as<Symbol>(r[0]) ;
// If the variable should be "atomized" for pretty printing
if(as<LogicalVector>(options("atomize")
)(0))
return PlTerm_atom(n.c_str()
) ; // TODO:
// Do not map the anonymous variable to a known variable name
if(n == "_")
return PlTerm_var() ;
// Unify with existing variable of the same name
PlTerm_tail tail(vars)
;
PlTerm_var v ;
for(R_xlen_t i=0 ; i<names.length()
; i++)
{
PlCheckFail(tail.next(v)
) ;
if(n == names(i))
return v ;
}
// If no such variable exists, create a new one and remember the name
names.push_back(n.c_str())
;
PlTerm_var pl ;
PlCheckFail(tail.append(pl)
) ;
return pl ;
}
// Translate R symbol to prolog atom
PlTerm r2pl_atom(Symbol r)
{
return PlTerm_atom(r.c_str()
) ;
}
// Translate to matrix $$$($$(1, 2), $$(3, 4))
PlTerm r2pl_matrix(Matrix<STRSXP> r, List aoptions)
{
List options(aoptions)
;
options("scalar")
= false ;
PlTermv rows(r.nrow())
;
for(int i=0 ; i<r.nrow()
; i++)
PlCheckFail(rows[i].unify_term(r2pl_string(r.row(i), options))
) ;
return PlCompound((const char*) options("charmat")
, rows) ;
}
// Translate CharacterVector to (scalar) string or things like $("a", "b", "c") PlTerm r2pl_string(CharacterVector r, List options) { if(Rf_isMatrix(r)) return r2pl_matrix(as<Matrix<STRSXP>>(r), options) ;
if(r.length() == 0)
return r2pl_null()
;
LogicalVector na = is_na(r)
;
// scalar string
if(as<LogicalVector>(options["scalar"])(0) && r.length()
== 1)
{
if(na[0])
return r2pl_na()
;
return PlTerm_string(r(0)
) ;
}
// compound like $("a", "b", "c")
size_t len = (size_t) r.length()
;
PlTermv args(len)
;
for(size_t i=0 ; i<len ; i++)
{
if(na[i])
PlCheckFail(args[i].unify_term(r2pl_na())
) ;
else
PlCheckFail(args[i].unify_term(PlTerm_string(r(i)
))) ; // DO NOT SUBMIT - unify_string()
}
return PlCompound((const char*) options("charvec")
, args) ;
}
// Translate R call to prolog compound, taking into account the names of the
// arguments, e.g., rexp(50, rate=1)
-> rexp(50, =(rate, 1))
PlTerm r2pl_compound(Language r, CharacterVector& names, PlTerm& vars, List options)
{
// For convenience, collect arguments in a list
List l = as<List>(CDR(r)) ;
// R functions with no arguments are translated to compounds (not atoms)
size_t len = (size_t) l.size()
;
if(len == 0)
{
PlTermv pl(3)
;
PlCheckFail(pl[1].unify_atom(as<Symbol>(CAR(r)).c_str()
)) ;
PlCheckFail(pl[2].unify_integer(0)
) ;
PlCall("compound_name_arity", pl) ;
return pl[0] ;
}
// Extract names of arguments
CharacterVector n ;
// if there are no names, l.names()
returns NULL and n has length 0
if(TYPEOF(l.names()
) == STRSXP)
n = l.names()
;
PlTermv pl(len)
;
for(size_t i=0 ; i<len ; i++)
{
PlTerm arg = r2pl(l(i), names, vars, options)
;
// Convert named arguments to prolog compounds a=X
if(n.length()
&& n(i)
!= "")
PlCheckFail(pl[i].unify_term(PlCompound("=", PlTermv(PlTerm_atom(n(i)
), arg)))) ;
else
PlCheckFail(pl[i].unify_term(arg)
) ; // no name
}
return PlCompound(as<Symbol>(CAR(r)).c_str()
, pl) ;
}
// Translate R list to prolog list, taking into account the names of the
// elements, e.g., list(a=1, b=2)
-> [a-1, b-2]. This may change, since the
// minus sign is a bit specific to prolog, and the conversion in the reverse
// direction may be ambiguous.
//
PlTerm r2pl_list(List r, CharacterVector& names, PlTerm& vars, List options)
{
// Names of list elements (empty vector if r.names()
== NULL)
CharacterVector n ;
if(TYPEOF(r.names()
) == STRSXP)
n = as<CharacterVector>(r.names()
) ;
PlTerm_var pl ;
PlTerm_tail tail(pl)
;
for(R_xlen_t i=0; i<r.size()
; i++)
{
PlTerm arg = r2pl(r(i), names, vars, options)
;
// Convert named argument to prolog pair a-X.
if(n.length()
&& n(i)
!= "")
PlCheckFail(tail.append(PlCompound("-", PlTermv(PlTerm_atom(n(i)
), arg)))) ;
else
PlCheckFail(tail.append(arg)
) ; // no name
}
PlCheckFail(tail.close()
) ;
return pl ;
}
// Translate R function to :- ("neck")
PlTerm r2pl_function(Function r, CharacterVector& names, PlTerm& vars, List options)
{
PlTermv fun(2)
;
PlCheckFail(fun[1].unify_term(r2pl_compound(BODY(r), names, vars, options))) ;
List formals = as<List>(FORMALS(r)) ;
size_t len = (size_t) formals.size()
;
if(len == 0)
{
PlTermv pl(3)
;
PlCheckFail(pl[1].unify_atom("$function")
) ;
PlCheckFail(pl[2].unify_integer(0)
) ;
PlCall("compound_name_arity", pl) ;
PlCheckFail(fun[0].unify_term(pl[0])) ; return PlCompound(":-", fun) ; }
CharacterVector n = formals.names()
;
PlTermv pl(len)
;
for(size_t i=0 ; i<len ; i++)
PlCheckFail(pl[i].unify_atom(n(i))
) ;
PlCheckFail(fun[0].unify_term(PlCompound("$function", pl))) ;
return PlCompound(":-", fun) ;
}
PlTerm r2pl(SEXP r, CharacterVector& names, PlTerm& vars, List options)
{
if(TYPEOF(r) == LANGSXP)
return r2pl_compound(r, names, vars, options)
;
if(TYPEOF(r) == REALSXP)
return r2pl_real(r, options)
;
if(TYPEOF(r) == LGLSXP)
return r2pl_logical(r, options)
;
if(TYPEOF(r) == INTSXP)
return r2pl_integer(r, options)
;
if(TYPEOF(r) == EXPRSXP)
return r2pl_var(r, names, vars, options)
;
if(TYPEOF(r) == SYMSXP)
return r2pl_atom(r)
;
if(TYPEOF(r) == STRSXP)
return r2pl_string(r, options)
;
if(TYPEOF(r) == VECSXP)
return r2pl_list(r, names, vars, options)
;
if(TYPEOF(r) == NILSXP)
return r2pl_null()
;
if(TYPEOF(r) == CLOSXP)
return r2pl_function(r, names, vars, options)
;
return r2pl_na()
;
}
class RlQuery { CharacterVector names ; PlTerm_var vars ; List options ; PlQuery* qid ;
public: RlQuery(RObject aquery, List aoptions) ; ~RlQuery() ;
int next_solution()
;
List bindings()
;
const List& get_options()
const
{
return options ;
}
} ;
RlQuery::RlQuery(RObject aquery, List aoptions)
: names()
,
vars()
,
options(aoptions)
,
qid(NULL)
{
options("atomize")
= false ;
PlTerm pl = r2pl(aquery, names, vars, options)
;
qid = new PlQuery("call", PlTermv(PlTerm(pl))) ;
}
RlQuery::~RlQuery()
{
if(qid)
delete qid ;
}
int RlQuery::next_solution()
{
if(qid == NULL)
stop("next_solution: no open query.")
;
int q ;
try
{
q = qid->next_solution()
;
}
catch(PlException& ex)
{
warning(ex.as_string(PlEncoding::Locale).c_str()
) ;
PL_clear_exception() ;
stop("Query failed")
;
}
return q ; }
List RlQuery::bindings()
{
List l ;
PlTerm_tail tail(vars)
;
PlTerm_var v ;
for(int i=0 ; i<names.length()
; i++)
{
PlCheckFail(tail.next(v)
) ;
RObject r = pl2r(v, names, vars, options)
;
if(TYPEOF(r) == EXPRSXP && names[i] == as<Symbol>(as<ExpressionVector>(r)[0]).c_str()
)
continue ;
l.push_back(r, (const char*) names[i]) ; }
return l ; }
RlQuery* query_id = NULL ;
// Open a query for later use.
// [[Rcpp::export(.query)]]
RObject query_(RObject query, List options)
{
if(PL_current_query() != 0)
{
warning("Cannot raise simultaneous queries. Please invoke clear()")
;
return wrap(false)
;
}
query_id = new RlQuery(query, options) ;
return wrap(true)
;
}
// Clear query (and invoke cleanup handlers, see PL_close_query)
// [[Rcpp::export(.clear)]]
RObject clear_()
{
if(query_id)
delete query_id ;
query_id = NULL ;
return wrap(true)
;
}
// Submit query
// [[Rcpp::export(.submit)]]
RObject submit_()
{
if(query_id == NULL)
{
warning("submit: no open query.")
;
return wrap(false)
;
}
if(!query_id->next_solution()
)
{
delete query_id ;
query_id = NULL ;
return wrap(false)
;
}
return query_id->bindings()
;
}
// Execute a query once and return conditions
//
// Examples:
//
// once(call("=", 1, 2))
-> FALSE
// once(call("=", 1, 1))
-> empty list
// once(call("member", 1, list(2, expression(X))))
-> list stating that X = 1
// once(call("=", list(expression(X), expression(Y)), list(1, expression(Z))))
// -> list stating that X = 1 and Z = Y
// once(call("member", 1, expression(X)))
-> list stating that X = [1 | _];
// e.g., something like [|]`(1, expression(`_6330`)
). This is cumbersome, any
// better ideas are welcome.
//
// [[Rcpp::export(.once)]]
RObject once_(RObject query, List options)
{
PlFrame f ;
if(!query_(query, options))
stop("Could not create query.")
;
RObject l = submit_() ; clear_() ; return l ; }
// Same as once_ above, but return all solutions to a query.
// [[Rcpp::export(.findall)]]
List findall_(RObject query, List options)
{
PlFrame f ;
if(!query_(query, options))
stop("Could not create query.")
;
List results ;
while(true)
{
RObject l = submit_() ;
if(TYPEOF(l) == LGLSXP)
break ;
results.push_back(l)
;
}
clear_() ; return results ; }
// Pretty print query. Maybe simplify to something like this:
// with_output_to(string(S), write_term(member(X), [variable_names(['X'=X])]))
.
//
// [[Rcpp::export(.portray)]]
RObject portray_(RObject query, List options)
{
if(PL_current_query() != 0)
{
warning("Closing the current query.")
;
clear_() ;
}
CharacterVector names ;
PlTerm_var vars ;
options("atomize")
= true ; // translate variables to their R names
PlTermv pl(3)
;
PlCheckFail(pl[0].unify_term(r2pl(query, names, vars, options))
) ;
PlTerm_tail tail(pl[2]) ;
PlCheckFail(tail.append(PlCompound("quoted", PlTermv(PlTerm_atom("false"))))) ;
PlCheckFail(tail.append(PlCompound("spacing", PlTermv(PlTerm_atom("next_argument"))))) ;
PlCheckFail(tail.close()
) ;
PlFrame f ;
PlQuery q("term_string", pl)
;
try
{
if(!q.next_solution()
)
return wrap(false)
;
}
catch(PlException& ex)
{
warning(ex.as_string(PlEncoding::Locale).c_str()
) ;
PL_clear_exception() ;
stop("portray of %s failed.", pl[0].as_string(PlEncoding::Locale).c_str()
) ;
}
return pl2r(pl[1], names, vars, options) ; }
// Execute a query given as a string
//
// Example:
// once("use_module(library(http/html_write))")
//
// [[Rcpp::export(.call)]]
RObject call_(String query)
{
if(PL_current_query() != 0)
{
warning("Closing the current query.")
;
clear_() ;
}
bool r = false ;
try
{
r = PlCall(query.get_cstring()
) ;
}
catch(PlException& ex)
{
warning(ex.as_string(PlEncoding::Locale).c_str()
) ;
PL_clear_exception() ;
stop("query failed: %s", query.get_cstring())
;
}
return wrap(r)
;
}
// Call R expression from Prolog
PREDICATE(r_eval, 1)
{
CharacterVector names ;
PlTerm_var vars ;
List options ;
if(query_id)
options = query_id->get_options()
;
else
options = List::create(Named("realvec") = "#", Named("realmat") = "##",
Named("boolvec") = "!", Named("boolmat") = "!!",
Named("charvec") = "$$", Named("charmat") = "$$$",
Named("intvec") = "%", Named("intmat") = "%%",
Named("atomize") = false, Named("scalar") = true) ;
RObject Expr = pl2r(A1, names, vars, options)
;
RObject Res = Expr ;
try
{
Language id("identity")
;
id.push_back(Expr)
;
Res = id.eval()
;
}
catch(std::exception& ex)
{
throw PlException(PlCompound("r_eval", PlTermv(A1, PlTerm_atom(ex.what()
)))) ;
}
return true ; }
// Evaluate R expression from Prolog
PREDICATE(r_eval, 2)
{
CharacterVector names ;
PlTerm_var vars ;
List options ;
if(query_id)
options = query_id->get_options()
;
else
options = List::create(Named("realvec") = "#", Named("realmat") = "##",
Named("boolvec") = "!", Named("boolmat") = "!!",
Named("charvec") = "$$", Named("charmat") = "$$$",
Named("intvec") = "%", Named("intmat") = "%%",
Named("atomize") = false, Named("scalar") = true) ;
RObject Expr = pl2r(A1, names, vars, options)
;
RObject Res = Expr ;
try
{
Language id("identity")
;
id.push_back(Expr)
;
Res = id.eval()
;
}
catch(std::exception& ex)
{
throw PlException(PlCompound("r_eval", PlTermv(A1, PlTerm_atom(ex.what()
)))) ;
}
PlTerm_var pl ;
try
{
PlCheckFail(pl.unify_term(r2pl(Res, names, vars, options))
) ;
}
catch(std::exception& ex)
{
throw PlException(PlCompound("r_eval", PlTermv(A1, PlTerm_atom(ex.what()
)))) ;
}
return A2.unify_term(pl)
;
}
// The SWI system should not be initialized twice; therefore, we keep track of // its status. bool pl_initialized = false ;
// Initialize SWI-prolog. This needs a list of the command-line arguments of
// the calling program, the most important being the name of the main
// executable, argv[0]. I added "-q" to suppress SWI prolog's welcome message
// which is shown in .onAttach anyway.
// [[Rcpp::export(.init)]]
LogicalVector init_(String argv0)
{
if(pl_initialized)
warning("Please do not initialize SWI-prolog twice in the same session.")
;
// Prolog documentation requires that argv is accessible during the entire
// session. I assume that this pointer is valid during the whole R session,
// and that I can safely cast it to const.
const int argc = 2 ;
const char* argv[argc] ;
argv[0] = argv0.get_cstring()
;
argv[1] = "-q" ;
if(!PL_initialise(argc, (char**) argv))
stop("rolog_init: initialization failed.")
;
pl_initialized = true ; return true ; }
// [[Rcpp::export(.done)]]
LogicalVector done_()
{
if(!pl_initialized)
{
warning("rolog_done: swipl has not been initialized")
;
return true ;
}
// Just in case there are open queries clear_() ;
PL_cleanup(0) ; pl_initialized = false ; return true ; }