| 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 ; }