knowrob  2.1.0
A Knowledge Base System for Cognition-enabled Robots
PrologTerm.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of KnowRob, please consult
3  * https://github.com/knowrob/knowrob for license details.
4  */
5 
6 #include <list>
7 #include <stack>
8 #include "knowrob/integration/prolog/PrologTerm.h"
9 #include "knowrob/formulas/Predicate.h"
10 #include "knowrob/formulas/Negation.h"
11 #include "knowrob/formulas/Implication.h"
12 #include "knowrob/Logger.h"
13 #include "knowrob/terms/ListTerm.h"
14 #include "knowrob/formulas/Bottom.h"
15 #include "knowrob/formulas/Top.h"
16 #include "knowrob/formulas/Conjunction.h"
17 #include "knowrob/formulas/Disjunction.h"
18 #include "knowrob/queries/QueryError.h"
19 #include "knowrob/semweb/ImportHierarchy.h"
20 #include "knowrob/terms/Numeric.h"
21 #include "knowrob/terms/String.h"
22 #include "knowrob/terms/IRIAtom.h"
23 #include "knowrob/terms/Blank.h"
24 #include "knowrob/semweb/GraphQuery.h"
25 #include "knowrob/semweb/GraphSequence.h"
26 
27 using namespace knowrob;
28 
29 // NOTE: Prolog takes care of freeing the term references when returning from C++
30 // context to Prolog context. Therefore, we do not need to free the term here.
31 // Only for temporary terms that are not needed in Prolog context
32 // PL_reset_term_refs can be called, but it is not necessary.
33 // NOTE: term_t cannot be used in different threads as is. For now PrologTerm objects
34 // should be constructed in the same thread where the query is issued which is suboptimal.
35 // One solution could be to record a functional expression in the main thread and
36 // construct the term_t in the worker thread based on this expression.
37 
38 PrologList::PrologList(const std::vector<PrologTerm> &elements)
39  : PrologTerm() {
40  PL_put_nil(plTerm_); // initialize as empty list
41  for (auto &elem: elements) {
42  if (!PL_cons_list(plTerm_, elem(), plTerm_)) {
43  throw QueryError("Failed to put element into PrologList.");
44  }
45  unifyVars(elem);
46  }
47 }
48 
50  : plTerm_(PL_new_term_ref()) {
51  // the new term reference is initialized to a variable
53 }
54 
55 PrologTerm::PrologTerm(const Triple &triple, std::string_view functor)
56  : plTerm_(PL_new_term_ref()) {
57  putTriple(functor, triple);
58 }
59 
60 PrologTerm::PrologTerm(const std::shared_ptr<GraphTerm> &term) //NOLINT(misc-no-recursion)
61  : plTerm_(PL_new_term_ref()) {
62  putTerm(term);
63 }
64 
66  : plTerm_(PL_new_term_ref()) {
67  putTerm(query->term());
68 }
69 
70 PrologTerm::PrologTerm(const TriplePattern &pattern, const char *functor)
71  : plTerm_(PL_new_term_ref()) {
72  putTriplePattern(pattern, functor, plTerm_);
73 }
74 
75 PrologTerm::PrologTerm(const std::vector<PrologTerm> &args, std::string_view functor)
76  : plTerm_(PL_new_term_ref()) {
77  putCompoundTerm(plTerm_, functor, args);
78 }
79 
81  : plTerm_(PL_new_term_ref()) {
82  putFormula(kbFormula);
83 }
84 
86  : plTerm_(PL_new_term_ref()) {
87  putTerm(kbTerm);
88 }
89 
91  PrologTerm empty_list;
92  PL_put_nil(empty_list());
93  return empty_list;
94 }
95 
97  static const auto comma_f = ",";
98  if (PL_term_type(plTerm_) == PL_VARIABLE) {
99  return other;
100  } else if (PL_term_type(other.plTerm_) == PL_VARIABLE) {
101  return *this;
102  } else {
103  return PrologTerm(comma_f, *this, other);
104  }
105 }
106 
108  static const auto semicolon_f = ";";
109  if (PL_term_type(plTerm_) == PL_VARIABLE) {
110  return other;
111  } else if (PL_term_type(other.plTerm_) == PL_VARIABLE) {
112  return *this;
113  } else {
114  return PrologTerm(semicolon_f, *this, other);
115  }
116 }
117 
119  static const auto semicolon_f = "\\+";
120  return PrologTerm(semicolon_f, *this);
121 }
122 
123 void PrologTerm::unifyVars(const PrologTerm &other) {
124  for (auto &pair : other.vars_) {
125  if (vars_.find(pair.first) == vars_.end()) {
126  vars_[pair.first] = pair.second;
127  } else {
128  if(!PL_unify(vars_[pair.first], pair.second)) {
129  KB_WARN("failed to unify two variables.");
130  }
131  }
132  }
133 }
134 
135 qid_t PrologTerm::openQuery(int flags) const {
136  if (PL_term_type(plTerm_) != PL_TERM && PL_term_type(plTerm_) != PL_ATOM) {
137  throw QueryError("PrologTerm is not a compound nor atom (actual type: {}).", PL_term_type(plTerm_));
138  }
139  // Note that the context module only matters for meta-predicates
140  module_t ctxModule = module_.has_value() ?
141  PL_new_module(PL_new_atom(module_.value().data())) : nullptr;
142  // get name and arity of the Prolog term
143  size_t arity;
144  atom_t name_atom;
145  if (!PL_get_name_arity(plTerm_, &name_atom, &arity)) {
146  throw QueryError("Failed to get name and arity of Prolog term.");
147  }
148  // create argument term_t references
149  term_t args = PL_new_term_refs(arity);
150  for (std::size_t i = 0; i < arity; i++) {
151  if (!PL_get_arg(i + 1, plTerm_, args + i)) {
152  throw QueryError("Failed to get argument {} of Prolog term.", i);
153  }
154  }
155  // create a predicate_t object and open query
156  predicate_t pred = PL_predicate(
157  PL_atom_nchars(name_atom, nullptr),
158  static_cast<int>(arity),
159  nullptr);
160  return PL_open_query(ctxModule, flags, pred, args);
161 }
162 
163 bool PrologTerm::nextSolution(qid_t qid) {
164  if (PL_next_solution(qid)) {
165  return true;
166  } else {
167  auto pl_exception = PL_exception(qid);
168  if (pl_exception != (term_t) 0) {
169  // there was an exception
170  auto errorTerm = PrologTerm::toKnowRobTerm(pl_exception);
171  PL_clear_exception();
172  PL_close_query(qid);
173  throw QueryError("Prolog error: {}.", *errorTerm);
174  }
175  // no exception
176  return false;
177  }
178 }
179 
180 #ifdef KNOWROB_USE_PROLOG_RDF11
181 static inline bool putTypedLiteral(term_t pl_arg, const char *value, const std::string &xsdType) {
182  // with semweb/rdf11, literals are represented as compound terms of the form `Value^^Type`
183  static functor_t literalFunctor = PL_new_functor(PL_new_atom("^^"), 2);
184  term_t typedLiteral = PL_new_term_refs(2);
185  return PL_put_atom_chars(typedLiteral, value) &&
186  PL_put_atom_chars(typedLiteral + 1, xsdType.c_str()) &&
187  PL_cons_functor_v(pl_arg, literalFunctor, typedLiteral);
188 }
189 #endif
190 
191 #ifdef KNOWROB_USE_PROLOG_RDF_DB
192 
193 static inline bool putTypedLiteral(term_t pl_arg, std::string_view value, std::string_view xsdType) {
194  // with semweb/rdf_db, literals are represented as compound terms of the form `literal(type(Type,Value))`
195  static functor_t literalFunctor = PL_new_functor(PL_new_atom("literal"), 1);
196  static functor_t literalTypeFunctor = PL_new_functor(PL_new_atom("type"), 2);
197  term_t typedLiteral = PL_new_term_refs(3);
198  return PL_put_atom_chars(typedLiteral, xsdType.data()) &&
199  PL_put_atom_chars(typedLiteral + 1, value.data()) &&
200  PL_cons_functor_v(typedLiteral + 2, literalTypeFunctor, typedLiteral) &&
201  PL_cons_functor_v(pl_arg, literalFunctor, typedLiteral + 2);
202 }
203 
204 static inline bool putTyped(term_t pl_arg, std::string_view value, std::string_view xsdType) {
205  // with semweb/rdf_db, literals are represented as compound terms of the form `type(Type,Value)`
206  static functor_t literalTypeFunctor = PL_new_functor(PL_new_atom("type"), 2);
207  term_t typedLiteral = PL_new_term_refs(2);
208  return PL_put_atom_chars(typedLiteral, xsdType.data()) &&
209  PL_put_atom_chars(typedLiteral + 1, value.data()) &&
210  PL_cons_functor_v(pl_arg, literalTypeFunctor, typedLiteral);
211 }
212 #endif
213 
214 bool PrologTerm::putCompoundTerm(term_t plTerm, std::string_view functor, const std::vector<PrologTerm> &args) {
215  // construct argument terms
216  term_t plArgs = PL_new_term_refs(args.size());
217  for (uint32_t i = 0; i < args.size(); i++) {
218  auto &arg_i = args[i];
219  if (!PL_put_term(plArgs + i, arg_i())) {
220  PL_reset_term_refs(plArgs);
221  KB_WARN("Failed to put argument {} into PrologTerm.", i);
222  return false;
223  }
224  unifyVars(arg_i);
225  }
226  // construct compound term
227  if (!PL_cons_functor_v(plTerm,
228  PL_new_functor(PL_new_atom(functor.data()), args.size()),
229  plArgs)) {
230  PL_reset_term_refs(plArgs);
231  KB_WARN("Failed to put functor into PrologTerm.");
232  return false;
233  }
234  return true;
235 }
236 
237 bool PrologTerm::putTriple(std::string_view functor, const Triple &triple) {
238  term_t pl_arg = PL_new_term_refs(4);
239  if (!PL_put_atom_chars(pl_arg, triple.subject().data()) ||
240  !PL_put_atom_chars(pl_arg + 1, triple.predicate().data())) {
241  PL_reset_term_refs(pl_arg);
242  return false;
243  }
244  bool o_put = false;
245  if (triple.isObjectIRI() || triple.isObjectBlank()) {
246  o_put = PL_put_atom_chars(pl_arg + 2, triple.valueAsString().data());
247  } else if (triple.xsdType()) {
248  auto xsdType = xsdTypeToIRI(triple.xsdType().value());
249  switch (triple.xsdType().value()) {
250  case XSDType::STRING:
251  o_put = putTypedLiteral(pl_arg + 2, triple.valueAsString().data(), xsdType);
252  break;
253  default:
254  o_put = putTypedLiteral(pl_arg + 2, triple.createStringValue(), xsdType);
255  break;
256  }
257  }
258  if (!o_put) {
259  PL_reset_term_refs(pl_arg);
260  return false;
261  }
262 
263  if (triple.graph()) {
264  if (!PL_put_atom_chars(pl_arg + 3, (*triple.graph()).data())) {
265  PL_reset_term_refs(pl_arg);
266  return false;
267  }
268  } else {
269  // triple has no graph, using fallback origin ImportHierarchy::ORIGIN_USER
270  if (!PL_put_atom_chars(pl_arg + 3, ImportHierarchy::ORIGIN_USER.data())) {
271  PL_reset_term_refs(pl_arg);
272  return false;
273  }
274  }
275 
276  auto pl_functor = PL_new_functor(PL_new_atom(functor.data()), 4);
277  if (!PL_cons_functor_v(plTerm_, pl_functor, pl_arg)) {
278  PL_reset_term_refs(pl_arg);
279  throw QueryError("Failed to put triple into PrologTerm.");
280  }
281  vars_.clear();
282 
283  return true;
284 }
285 
287  // reset vars before putting a new formula
288  vars_.clear();
289  return putFormula(phi, plTerm_);
290 }
291 
292 bool PrologTerm::putFormula(const FormulaPtr &phi, term_t plTerm) { //NOLINT
293  static auto negationFun = PL_new_functor(PL_new_atom("\\+"), 1);
294  static auto implicationFun = PL_new_functor(PL_new_atom(":-"), 2);
295 
296  switch (phi->type()) {
298  return putTerm(Predicate::toFunction(std::static_pointer_cast<Predicate>(phi)), plTerm);
299 
300  case FormulaType::NEGATION: {
301  auto negated = std::static_pointer_cast<Negation>(phi)->negatedFormula();
302  auto negated_t = PL_new_term_ref();
303  return putFormula(negated, negated_t) &&
304  PL_cons_functor_v(plTerm, negationFun, negated_t);
305  }
306 
308  auto implication = std::static_pointer_cast<Implication>(phi);
309  auto args = PL_new_term_refs(2);
310  return putFormula(implication->consequent(), args) &&
311  putFormula(implication->antecedent(), args + 1) &&
312  PL_cons_functor_v(plTerm, implicationFun, args);
313  }
314 
316  return putCompound((CompoundFormula *) phi.get(), plTerm, PrologTerm::FUNCTOR_comma());
317 
319  return putCompound((CompoundFormula *) phi.get(), plTerm, PrologTerm::FUNCTOR_semicolon());
320 
321  case FormulaType::MODAL:
322  KB_WARN("Modal formula cannot be mapped to Prolog terms.");
323  return false;
324  }
325  return false;
326 }
327 
328 bool PrologTerm::putTerm(const TermPtr &kbTerm) {
329  // reset vars before putting a new term
330  vars_.clear();
331  return putTerm(kbTerm, plTerm_);
332 }
333 
334 bool PrologTerm::putTerm(const std::shared_ptr<GraphTerm> &graphTerm) { //NOLINT(misc-no-recursion)
335  // reset vars before putting a new term
336  vars_.clear();
337  return putTerm(graphTerm, plTerm_);
338 }
339 
340 bool PrologTerm::putFunction(Function *fn, term_t pl_term) { //NOLINT
341  if (fn->arity() > 0) {
342  // create a term reference for each argument of qa_pred
343  term_t pl_arg = PL_new_term_refs(fn->arity());
344  term_t pl_arg0 = pl_arg;
345  // construct argument terms
346  for (const auto &qa_arg: fn->arguments()) {
347  if (!putTerm(qa_arg, pl_arg)) {
348  return false;
349  }
350  pl_arg += 1;
351  }
352  // construct output term
353  return PL_cons_functor_v(pl_term,
354  PL_new_functor(
355  PL_new_atom(fn->functor()->stringForm().data()),
356  fn->arity()),
357  pl_arg0);
358  } else {
359  // 0-ary predicates are atoms
360  return PL_put_atom_chars(pl_term,
361  fn->functor()->stringForm().data());
362  }
363 }
364 
365 bool PrologTerm::putList(ListTerm *list, term_t pl_term) { //NOLINT
366  if (!PL_put_nil(pl_term)) return false;
367  term_t pl_elem = PL_new_term_ref();
368  for (auto &elem: list->elements()) {
369  if (!putTerm(elem, pl_elem) ||
370  !PL_cons_list(pl_term, pl_elem, pl_term)) {
371  return false;
372  }
373  }
374  return true;
375 }
376 
377 
378 bool PrologTerm::putTerm(const std::shared_ptr<GraphTerm> &kb_term, term_t pl_term) { //NOLINT(misc-no-recursion)
379  static const auto rdf_has_f = "rdf_has";
380 
381  switch (kb_term->termType()) {
383  return putTriplePattern(*std::static_pointer_cast<GraphPattern>(kb_term)->value(), rdf_has_f, pl_term);
384 
386  return putBuiltin(std::static_pointer_cast<GraphBuiltin>(kb_term), pl_term);
387 
388  case GraphTermType::Union: {
389  PrologTerm goal;
390  for (const auto &subTerm: std::static_pointer_cast<GraphConnective>(kb_term)->terms()) {
391  goal = (goal | PrologTerm(subTerm));
392  }
393  unifyVars(goal);
394  return PL_put_term(pl_term, goal());
395  }
397  PrologTerm goal;
398  for (const auto &subTerm: std::static_pointer_cast<GraphConnective>(kb_term)->terms()) {
399  goal = (goal & PrologTerm(subTerm));
400  }
401  unifyVars(goal);
402  return PL_put_term(pl_term, goal());
403  }
404  }
405  return false;
406 }
407 
408 bool PrologTerm::putTriplePattern(const TriplePattern &pat, const char *functor, term_t plTerm) {
409  static const auto less_f = "lt";
410  static const auto less_or_equal_f = "le";
411  static const auto greater_f = "gt";
412  static const auto greater_or_equal_f = "ge";
413  static const auto equal_f = "eq";
414 
415  PrologTerm objectTerm, patternTerm;
416  if (pat.objectOperator() == FilterType::EQ) {
417  // make sure that `literal(type(Type,Value))` is used for typed literals.
418  objectTerm.isRDFTerm_ = true;
419  objectTerm.putTerm(pat.objectTerm());
420  patternTerm = PrologTerm(functor, pat.subjectTerm(), pat.propertyTerm(), objectTerm);
421  if (pat.isNegated()) {
422  patternTerm = ~patternTerm;
423  }
424  if (pat.isOptional()) {
425  patternTerm = PrologTerm("ignore", patternTerm);
426  }
427  } else {
428  // make sure that `type(Type,Value)` is used for typed literals.
429  objectTerm.isRDFFilter_ = true;
430  objectTerm.putTerm(pat.objectTerm());
431  bool negate = pat.isNegated();
432  PrologTerm filter;
433  switch (pat.objectOperator()) {
434  case FilterType::EQ:
435  filter = PrologTerm(equal_f, objectTerm);
436  break;
437  case FilterType::NEQ:
438  filter = PrologTerm(equal_f, objectTerm);
439  negate = !negate;
440  break;
441  case FilterType::LT:
442  filter = PrologTerm(less_f, objectTerm);
443  break;
444  case FilterType::LEQ:
445  filter = PrologTerm(less_or_equal_f, objectTerm);
446  break;
447  case FilterType::GT:
448  filter = PrologTerm(greater_f, objectTerm);
449  break;
450  case FilterType::GEQ:
451  filter = PrologTerm(greater_or_equal_f, objectTerm);
452  break;
453  }
454  // Note: Object can be of the form literal(+Query, -Value)
455  PrologTerm objectVar = (pat.objectVariable() ? PrologTerm(pat.objectVariable()) : PrologTerm());
456  if (pat.objectVariable()) {
457  vars_[*pat.objectVariable()] = objectVar();
458  }
459  patternTerm = PrologTerm(functor,
460  pat.subjectTerm(),
461  pat.propertyTerm(),
462  PrologTerm("literal", filter, objectVar));
463  if (negate) {
464  patternTerm = ~patternTerm;
465  }
466  if (pat.isOptional()) {
467  PrologTerm negatedTerm(functor, pat.subjectTerm(), pat.propertyTerm(), PrologTerm());
468  patternTerm = PrologTerm("\\+", negatedTerm) | patternTerm;
469  }
470  }
471 
472  unifyVars(patternTerm);
473  return PL_put_term(plTerm, patternTerm());
474 }
475 
476 bool PrologTerm::putBuiltin(const std::shared_ptr<GraphBuiltin> &builtin, term_t plTerm) {
477  static const auto less_a = Atom::Tabled("<");
478  static const auto less_or_equal_a = Atom::Tabled("=<");
479  static const auto greater_a = Atom::Tabled(">");
480  static const auto greater_or_equal_a = Atom::Tabled(">=");
481  static const auto equal_a = Atom::Tabled("==");
482  static const auto not_equal_a = Atom::Tabled("\\==");
483  static const auto bind_f = "=";
484  static const auto min_f = "sw_literal_min";
485  static const auto max_f = "sw_literal_max";
486  static const auto compare_f = "sw_literal_compare";
487 
488  switch (builtin->builtinType()) {
490  if (builtin->arguments().size() == 2) {
491  PrologTerm t = PrologTerm(compare_f, less_a, builtin->arguments()[0], builtin->arguments()[1]);
492  unifyVars(t);
493  return PL_put_term(plTerm, t());
494  }
495  break;
497  if (builtin->arguments().size() == 2) {
498  PrologTerm t = PrologTerm(compare_f, less_or_equal_a, builtin->arguments()[0], builtin->arguments()[1]);
499  unifyVars(t);
500  return PL_put_term(plTerm, t());
501  }
502  break;
504  if (builtin->arguments().size() == 2) {
505  PrologTerm t = PrologTerm(compare_f, greater_a, builtin->arguments()[0], builtin->arguments()[1]);
506  unifyVars(t);
507  return PL_put_term(plTerm, t());
508  }
509  break;
511  if (builtin->arguments().size() == 2) {
512  PrologTerm t = PrologTerm(compare_f, greater_or_equal_a, builtin->arguments()[0], builtin->arguments()[1]);
513  unifyVars(t);
514  return PL_put_term(plTerm, t());
515  }
516  break;
518  if (builtin->arguments().size() == 2) {
519  PrologTerm t = PrologTerm(compare_f, equal_a, builtin->arguments()[0], builtin->arguments()[1]);
520  unifyVars(t);
521  return PL_put_term(plTerm, t());
522  }
523  break;
525  if (builtin->arguments().size() == 1 && builtin->bindVar() != nullptr) {
526  PrologTerm t(bind_f, builtin->bindVar(), builtin->arguments()[0]);
527  unifyVars(t);
528  return PL_put_term(plTerm, t());
529  }
530  break;
532  if (builtin->arguments().size() == 2 && builtin->bindVar() != nullptr) {
533  PrologTerm t(min_f, builtin->arguments()[0], builtin->arguments()[1], builtin->bindVar());
534  unifyVars(t);
535  return PL_put_term(plTerm, t());
536  }
537  break;
539  if (builtin->arguments().size() == 2 && builtin->bindVar() != nullptr) {
540  PrologTerm t(max_f, builtin->arguments()[0], builtin->arguments()[1], builtin->bindVar());
541  unifyVars(t);
542  return PL_put_term(plTerm, t());
543  }
544  break;
546  if (builtin->arguments().size() == 2) {
547  PrologTerm t = PrologTerm(compare_f, not_equal_a, builtin->arguments()[0], builtin->arguments()[1]);
548  unifyVars(t);
549  return PL_put_term(plTerm, t());
550  }
551  break;
552  }
553  return false;
554 }
555 
556 bool PrologTerm::putTerm(const TermPtr &kbTerm, term_t pl_term) { //NOLINT
557  switch (kbTerm->termType()) {
558  case TermType::FUNCTION: {
559  auto *fn = (Function *) kbTerm.get();
560  if (*fn->functor() == *ListTerm::listFunctor()) {
561  return putList((ListTerm *) fn, pl_term);
562  } else {
563  return putFunction(fn, pl_term);
564  }
565  }
566  case TermType::VARIABLE: {
567  auto *qa_var = (Variable *) kbTerm.get();
568  auto it = vars_.find(*qa_var);
569  if (it != vars_.end()) {
570  // try to use previously created term_t
571  return PL_put_term(pl_term, it->second);
572  } else if (PL_put_variable(pl_term)) {
573  // create a new variable
574  // Note: it is apparently impossible to assign a name to a variable.
575  // This is why we have to store the variable name in the map, and potentially create
576  // unneeded term_t references for variables that must be unified before querying anyway.
577  // the alternative would be to carry around a shared variable map which would make this
578  // interface slightly ugly
579  vars_[*qa_var] = pl_term;
580  return true;
581  } else {
582  return false;
583  }
584  }
585  case TermType::ATOMIC:
586  if (isRDFTerm_ || isRDFFilter_) {
587  return putRDFAtomic(std::static_pointer_cast<Atomic>(kbTerm), pl_term);
588  } else {
589  return putNativeAtomic(std::static_pointer_cast<Atomic>(kbTerm), pl_term);
590  }
591  }
592 
593  return false;
594 }
595 
596 bool PrologTerm::putRDFAtomic(const std::shared_ptr<Atomic> &atomic, term_t pl_term) const {
597  if (atomic->isIRI() || atomic->isBlank()) {
598  return PL_put_atom_chars(pl_term, atomic->stringForm().data());
599  } else if (atomic->isNumeric() || atomic->isString()) {
600  auto xsdAtomic = std::static_pointer_cast<XSDAtomic>(atomic);
601  auto xsdType = xsdTypeToIRI(xsdAtomic->xsdType());
602  if (isRDFFilter_) {
603  return putTyped(pl_term, atomic->stringForm(), xsdType);
604  } else {
605  return putTypedLiteral(pl_term, atomic->stringForm(), xsdType);
606  }
607  }
608  return false;
609 }
610 
611 bool PrologTerm::putNativeAtomic(const std::shared_ptr<Atomic> &atomic, term_t pl_term) {
612  switch (atomic->atomicType()) {
613  case AtomicType::ATOM:
614  return PL_put_atom_chars(pl_term, atomic->stringForm().data());
615 
616  case AtomicType::STRING:
617  return PL_put_string_chars(pl_term, atomic->stringForm().data());
618 
619  case AtomicType::NUMERIC: {
620  auto *numeric = (Numeric *) atomic.get();
621  switch (numeric->xsdType()) {
622  case XSDType::DOUBLE:
623  return PL_put_float(pl_term, numeric->asDouble());
624  case XSDType::FLOAT:
625  return PL_put_float(pl_term, numeric->asFloat());
627  case XSDType::INTEGER:
628  return PL_put_integer(pl_term, numeric->asInteger());
629  case XSDType::LONG:
630  return PL_put_int64(pl_term, numeric->asLong());
631  case XSDType::SHORT:
632  return PL_put_integer(pl_term, numeric->asShort());
634  return PL_put_uint64(pl_term, numeric->asUnsignedLong());
636  return PL_put_integer(pl_term, numeric->asUnsignedInteger());
638  return PL_put_integer(pl_term, numeric->asUnsignedShort());
639  case XSDType::BOOLEAN:
640  return PL_put_bool(pl_term, numeric->asBoolean());
641  case XSDType::STRING:
642  case XSDType::LAST:
643  break;
644  }
645  break;
646  }
647  }
648  return false;
649 }
650 
651 bool PrologTerm::putCompound(CompoundFormula *phi, term_t pl_term, const functor_t &pl_functor) { //NOLINT
652  if (phi->formulae().size() == 1) {
653  return putFormula(phi->formulae()[0], pl_term);
654  } else {
655  int counter = 1;
656  term_t last_head = PL_new_term_ref();
657 
658  for (auto i = phi->formulae().size(); i > 0; --i) {
659  auto j = i - 1;
660  if (counter == 1) {
661  // create term for last formula, remember in last_head term
662  if (!putFormula(phi->formulae()[j], last_head)) return false;
663  } else {
664  // create a 2-ary predicate using last_head as second argument
665  term_t pl_arg = PL_new_term_refs(2);
666  if (!putFormula(phi->formulae()[j], pl_arg) ||
667  !PL_put_term(pl_arg + 1, last_head) ||
668  !PL_cons_functor_v((j == 0 ? pl_term : last_head), pl_functor, pl_arg)) {
669  return false;
670  }
671 
672  }
673  counter += 1;
674  }
675 
676  return true;
677  }
678 }
679 
681  char *s;
682  if (PL_get_chars(plTerm, &s, CVT_VARIABLE|BUF_MALLOC)) {
683  std::string str(s);
684  PL_free(s);
685  return str;
686  } else {
687  throw QueryError("Failed to get variable name.");
688  }
689 }
690 
692  return toKnowRobTerm(plTerm_);
693 }
694 
695 static TermPtr toKnowRobLiteral1(const term_t &type_term) {
696  term_t xsd_type = PL_new_term_ref();
697  term_t xsd_value = PL_new_term_ref();
698  if (!PL_get_arg(1, type_term, xsd_type) || !PL_get_arg(2, type_term, xsd_value)) {
699  KB_WARN("failed to get argument of type term.");
700  return {};
701  }
702 
703  atom_t xsd_type_atom;
704  if (!PL_get_atom(xsd_type, &xsd_type_atom)) {
705  KB_WARN("Failed to get XSD type atom.");
706  return {};
707  }
708 
709  atom_t xsd_value_atom;
710  if (!PL_get_atom(xsd_value, &xsd_value_atom)) {
711  KB_WARN("Failed to get XSD value atom.");
712  return {};
713  }
714 
715  return XSDAtomic::create(PL_atom_chars(xsd_value_atom), PL_atom_chars(xsd_type_atom));
716 }
717 
718 static TermPtr toKnowRobLiteral(const term_t &literal_term) {
719  term_t type_term = PL_new_term_ref();
720  if (!PL_get_arg(1, literal_term, type_term)) {
721  KB_WARN("failed to get argument of literal term.");
722  return {};
723  }
724  if (PL_term_type(type_term) != PL_TERM) {
725  KB_WARN("argument of literal term must be a term.");
726  return {};
727  }
728 
729  size_t literal_arity;
730  atom_t literal_name;
731  if (!PL_get_name_arity(type_term, &literal_name, &literal_arity) ||
732  literal_arity != 2 || std::string_view(PL_atom_chars(literal_name)) != "type") {
733  KB_WARN("argument of literal term must be a 2-ary type term.");
734  return {};
735  }
736 
737  return toKnowRobLiteral1(type_term);
738 }
739 
740 TermPtr PrologTerm::toKnowRobTerm(const term_t &t) { //NOLINT
741  switch (PL_term_type(t)) {
742  case PL_TERM: {
743  size_t arity;
744  atom_t name;
745  if (!PL_get_name_arity(t, &name, &arity)) break;
746  std::string functorName(PL_atom_chars(name));
747 
748  // Special handling for typed literals. whenever a term is of the form `literal(type(Type,Value))`
749  // it is considered a typed literal, and converted to a corresponding Term type.
750  if (functorName == "literal" && arity == 1) {
751  auto xsdLiteral = toKnowRobLiteral(t);
752  if (xsdLiteral) return xsdLiteral;
753  }
754  if (functorName == "type" && arity == 2) {
755  auto xsdLiteral = toKnowRobLiteral1(t);
756  if (xsdLiteral) return xsdLiteral;
757  }
758 
759  // construct arguments
760  std::vector<TermPtr> arguments(arity);
761  term_t arg = PL_new_term_ref();
762  for (std::size_t n = 1; n <= arity; n++) {
763  if (PL_get_arg(n, t, arg)) {
764  arguments[n - 1] = toKnowRobTerm(arg);
765  } else {
766  KB_WARN("Failed to construct argument {} of predicate {}.", n, functorName);
767  }
768  }
769  // construct Predicate object
770  return std::make_shared<Function>(functorName, arguments);
771  }
772 
773  case PL_VARIABLE:
774  return std::make_shared<Variable>(getVarName(t));
775 
776  case PL_ATOM: {
777  atom_t atom;
778  if (!PL_get_atom(t, &atom)) break;
779  // map `fail/0` and `false/0` to BottomTerm
781  return Bottom::get()->functor();
782  }
783  // map `true/0` to TopTerm
784  else if (atom == PrologTerm::ATOM_true()) {
785  return Top::get()->functor();
786  } else {
787  std::string_view charForm = PL_atom_chars(atom);
788  switch (rdfNodeTypeGuess(charForm)) {
789  case RDFNodeType::BLANK:
790  return Blank::Tabled(charForm);
791  case RDFNodeType::IRI:
792  return IRIAtom::Tabled(charForm);
794  return Atom::Tabled(charForm);
795  }
796  }
797  break;
798  }
799  case PL_INTEGER: {
800  long val = 0;
801  if (!PL_get_long(t, &val)) break;
802  return std::make_shared<Long>(val);
803  }
804  case PL_FLOAT: {
805  double val = 0.0;
806  if (!PL_get_float(t, &val)) break;
807  return std::make_shared<Double>(val);
808  }
809  case PL_STRING: {
810  char *s;
811  if (!PL_get_chars(t, &s, CVT_ALL|BUF_MALLOC)) break;
812  auto str = std::make_shared<String>(s);
813  PL_free(s);
814  return str;
815  }
816  case PL_NIL:
817  return ListTerm::nil();
818  case PL_LIST_PAIR: {
819  term_t head = PL_new_term_ref();
820  std::list<TermPtr> elements;
821  while (PL_get_list(t, head, t)) {
822  elements.push_back(toKnowRobTerm(head));
823  }
824  return std::make_shared<ListTerm>(
825  std::vector<TermPtr>(elements.begin(), elements.end()));
826  }
827  default:
828  KB_WARN("Unknown Prolog term type {}.", PL_term_type(t));
829  break;
830  }
831 
832  KB_WARN("Failed to read Prolog term of type {}.", PL_term_type(t));
833  return Bottom::get()->functor();
834 }
835 
838 }
839 
841 {
842  static std::string comma_functor = ",";
843  static std::string semicolon_functor = ";";
844 
845  std::shared_ptr<Predicate> p = (
846  t->termType() == TermType::FUNCTION ?
847  Predicate::fromFunction(std::static_pointer_cast<Function>(t)) :
848  Bottom::get());
849 
850  if (p->functor()->stringForm() == comma_functor) {
851  std::vector<FormulaPtr> formulas(p->arity());
852  for (std::size_t i = 0; i < formulas.size(); i++) {
853  formulas[i] = toKnowRobFormula(p->arguments()[i]);
854  }
855  return std::make_shared<Conjunction>(formulas);
856  } else if (p->functor()->stringForm() == semicolon_functor) {
857  std::vector<FormulaPtr> formulas(p->arity());
858  for (std::size_t i = 0; i < formulas.size(); i++) {
859  formulas[i] = toKnowRobFormula(p->arguments()[i]);
860  }
861  return std::make_shared<Disjunction>(formulas);
862  } else {
863  return p;
864  }
865 }
866 
867 /******************************************/
868 /*********** static constants *************/
869 /******************************************/
870 
871 const atom_t &PrologTerm::ATOM_fail() {
872  static atom_t a = PL_new_atom("fail");
873  return a;
874 }
875 
876 const atom_t &PrologTerm::ATOM_false() {
877  static atom_t a = PL_new_atom("false");
878  return a;
879 }
880 
881 const atom_t &PrologTerm::ATOM_true() {
882  static atom_t a = PL_new_atom("true");
883  return a;
884 }
885 
886 const atom_t &PrologTerm::ATOM_comma() {
887  static atom_t a = PL_new_atom(",");
888  return a;
889 }
890 
891 const atom_t &PrologTerm::ATOM_semicolon() {
892  static atom_t a = PL_new_atom(";");
893  return a;
894 }
895 
896 const functor_t &PrologTerm::FUNCTOR_comma() {
897  static atom_t a = PL_new_functor(PrologTerm::ATOM_comma(), 2);
898  return a;
899 }
900 
901 const functor_t &PrologTerm::FUNCTOR_semicolon() {
902  static atom_t a = PL_new_functor(PrologTerm::ATOM_semicolon(), 2);
903  return a;
904 }
905 
906 const predicate_t &PrologTerm::PREDICATE_comma() {
907  static predicate_t a = PL_pred(PrologTerm::FUNCTOR_comma(), nullptr);
908  return a;
909 }
910 
911 const predicate_t &PrologTerm::PREDICATE_semicolon() {
912  static predicate_t a = PL_pred(PrologTerm::FUNCTOR_semicolon(), nullptr);
913  return a;
914 }
915 
916 bool PrologTerm::display(std::ostream &os, term_t t, const std::string &indent) { //NOLINT(misc-no-recursion)
917  std::size_t n;
918  size_t len;
919  char *s;
920 
921  switch (PL_term_type(t)) {
922  case PL_ATOM:
923  case PL_INTEGER:
924  case PL_FLOAT:
925  if (PL_get_chars(t, &s, CVT_ALL|BUF_MALLOC)) {
926  os << s;
927  PL_free(s);
928  return true;
929  }
930  break;
931  case PL_VARIABLE:
932  if (PL_get_chars(t, &s, CVT_VARIABLE|BUF_MALLOC)) {
933  os << s;
934  PL_free(s);
935  return true;
936  }
937  break;
938  case PL_STRING:
939  if (PL_get_string_chars(t, &s, &len)) {
940  os << s;
941  return true;
942  }
943  break;
944  case PL_TERM: {
945  term_t a = PL_new_term_ref();
946  atom_t name;
947  size_t arity;
948  if (!PL_get_name_arity(t, &name, &arity)) break;
949  std::string_view functor(PL_atom_chars(name));
950 
951  if (functor == "," || functor == ";") {
952  os << '(' << ' ';
953  for (n = 1; n <= arity; n++) {
954  if (!PL_get_arg(n, t, a)) break;
955  if (n > 1) {
956  os << '\n' << indent << functor << ' ';
957  }
958  if (!display(os, a, indent+'\t')) os << "_";
959  }
960  os << ' ' << ')';
961  } else {
962  os << functor << '(';
963  for (n = 1; n <= arity; n++) {
964  if (!PL_get_arg(n, t, a)) break;
965  if (n > 1) os << ", ";
966  if (!display(os, a)) os << "_";
967  }
968  os << ')';
969  }
970  return true;
971  }
972  }
973  return false;
974 }
975 
976 void PrologTerm::write(std::ostream &os) const {
977  display(os, plTerm_);
978 }
#define KB_WARN
Definition: Logger.h:27
static std::shared_ptr< knowrob::Atom > Tabled(std::string_view stringForm)
Definition: Atom.cpp:40
static std::shared_ptr< Blank > Tabled(std::string_view stringForm)
Definition: Blank.cpp:12
static const std::shared_ptr< Bottom > & get()
Definition: Bottom.cpp:12
const std::vector< FormulaPtr > & formulae() const
auto arity() const
Definition: Function.h:52
auto & arguments() const
Definition: Function.h:47
auto & functor() const
Definition: Function.h:42
static std::shared_ptr< IRIAtom > Tabled(std::string_view stringForm)
Definition: IRIAtom.cpp:25
static constexpr std::string_view ORIGIN_USER
auto & elements() const
Definition: ListTerm.h:39
static std::shared_ptr< ListTerm > nil()
Definition: ListTerm.cpp:22
static const AtomPtr & listFunctor()
Definition: ListTerm.cpp:17
static FunctionPtr toFunction(const std::shared_ptr< Predicate > &predicate)
Definition: Predicate.cpp:85
static std::shared_ptr< Predicate > fromFunction(const FunctionPtr &fn)
Definition: Predicate.cpp:89
static const atom_t & ATOM_false()
Definition: PrologTerm.cpp:876
bool putFunction(Function *fn, term_t pl_term)
Definition: PrologTerm.cpp:340
void unifyVars(const PrologTerm &other)
Definition: PrologTerm.cpp:123
static PrologTerm nil()
Definition: PrologTerm.cpp:90
bool putTriple(std::string_view functor, const Triple &triple)
Definition: PrologTerm.cpp:237
FormulaPtr toKnowRobFormula() const
Definition: PrologTerm.cpp:836
qid_t openQuery(int flags) const
Definition: PrologTerm.cpp:135
static bool nextSolution(qid_t qid)
Definition: PrologTerm.cpp:163
bool putRDFAtomic(const std::shared_ptr< Atomic > &atomic, term_t pl_term) const
Definition: PrologTerm.cpp:596
static const atom_t & ATOM_true()
Definition: PrologTerm.cpp:881
friend class PrologList
Definition: PrologTerm.h:305
static const predicate_t & PREDICATE_semicolon()
Definition: PrologTerm.cpp:911
static bool putNativeAtomic(const std::shared_ptr< Atomic > &atomic, term_t pl_term)
Definition: PrologTerm.cpp:611
static const functor_t & FUNCTOR_comma()
Definition: PrologTerm.cpp:896
void write(std::ostream &os) const override
Definition: PrologTerm.cpp:976
TermPtr toKnowRobTerm() const
Definition: PrologTerm.cpp:691
bool putFormula(const FormulaPtr &phi)
Definition: PrologTerm.cpp:286
std::map< Variable, term_t, std::less<> > vars_
Definition: PrologTerm.h:266
static const atom_t & ATOM_fail()
Definition: PrologTerm.cpp:871
PrologTerm operator~() const
Definition: PrologTerm.cpp:118
static const atom_t & ATOM_semicolon()
Definition: PrologTerm.cpp:891
bool putCompound(CompoundFormula *phi, term_t pl_term, const functor_t &pl_functor)
Definition: PrologTerm.cpp:651
PrologTerm operator&(const PrologTerm &other) const
Definition: PrologTerm.cpp:96
static std::string getVarName(term_t plTerm)
Definition: PrologTerm.cpp:680
bool putCompoundTerm(term_t plTerm, std::string_view functor, const std::vector< PrologTerm > &args)
Definition: PrologTerm.cpp:214
static bool display(std::ostream &os, term_t t, const std::string &indent="")
Definition: PrologTerm.cpp:916
std::optional< std::string_view > module_
Definition: PrologTerm.h:267
bool putBuiltin(const std::shared_ptr< GraphBuiltin > &builtin, term_t plTerm)
Definition: PrologTerm.cpp:476
static const atom_t & ATOM_comma()
Definition: PrologTerm.cpp:886
bool putTerm(const TermPtr &kbTerm)
Definition: PrologTerm.cpp:328
bool putTriplePattern(const TriplePattern &pattern, const char *functor, term_t plTerm)
Definition: PrologTerm.cpp:408
PrologTerm operator|(const PrologTerm &other) const
Definition: PrologTerm.cpp:107
static const predicate_t & PREDICATE_comma()
Definition: PrologTerm.cpp:906
static const functor_t & FUNCTOR_semicolon()
Definition: PrologTerm.cpp:901
bool putList(ListTerm *list, term_t pl_term)
Definition: PrologTerm.cpp:365
static const std::shared_ptr< Top > & get()
Definition: Top.cpp:11
virtual std::string_view valueAsString() const =0
auto xsdType() const
Definition: Triple.h:64
virtual std::optional< std::string_view > graph() const =0
virtual std::string_view subject() const =0
virtual std::string_view predicate() const =0
std::string createStringValue() const
Definition: Triple.cpp:71
bool isObjectIRI() const
Definition: Triple.h:54
bool isObjectBlank() const
Definition: Triple.h:49
auto objectOperator() const
auto & propertyTerm() const
Definition: TriplePattern.h:92
auto & subjectTerm() const
Definition: TriplePattern.h:81
auto & objectVariable() const
auto & objectTerm() const
Definition: TriplePattern.h:97
static std::shared_ptr< XSDAtomic > create(std::string_view lexicalForm, std::string_view xsdTypeIRI)
Definition: XSDAtomic.cpp:17
GraphTermRule & graphTerm()
Definition: graph.cpp:77
GraphTermRule & pattern()
Definition: graph.cpp:23
StringRule & xsd_value()
Definition: strings.cpp:70
StringRule & atom()
Definition: strings.cpp:54
TermRule & string()
Definition: terms.cpp:63
TermRule & atomic()
Definition: terms.cpp:79
TermRule & term()
Definition: terms.cpp:136
std::string_view xsdTypeToIRI(XSDType type)
Definition: XSDAtomic.cpp:70
RDFNodeType rdfNodeTypeGuess(std::string_view str)
Definition: RDFNode.cpp:11
std::shared_ptr< Term > TermPtr
Definition: Term.h:117
std::shared_ptr< Formula > FormulaPtr
Definition: Formula.h:99
std::shared_ptr< GraphQuery > GraphQueryPtr
Definition: GraphQuery.h:65