/*
* SchemeEval.h
*
* Scheme expression evaluator for OpenCog
* Copyright (c) 2008, 2014 Linas Vepstas <linas@linas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License v3 as
* published by the Free Software Foundation and including the exceptions
* at http://opencog.org/wiki/Licenses
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, write to:
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef OPENCOG_SCHEME_EVAL_H
#define OPENCOG_SCHEME_EVAL_H
#ifdef HAVE_GUILE
#include <condition_variable>
#include <mutex>
#include <string>
#include <sstream>
#include <cstddef>
#include <libguile.h>
#include <opencog/atomspace/AtomSpace.h>
#include <opencog/atoms/base/Atom.h>
#include <opencog/atoms/base/Handle.h>
#include <opencog/eval/GenericEval.h>
namespace opencog {
// Forward declaration for friend
template <typename T> class EvaluatorPool;
/** \addtogroup grp_smob
* @{
*
* The evaluator provides two modes of usage: synchronous and
* asynchronous. The synchronous API is provided by the following
* methods: eval(), eval_h(), apply(), and apply_generic(). The last
* three are special-purpose wrappers around the first, returning
* handles, and applying functions to argument lists.
*
* The eval() method returns a string, holding any output that was
* printed during evaluation. e.g. output printed using the scheme
* 'display' function. If the code to be evaluated is long-running,
* then nothing can be returned until the evaluation completes. This
* presents a problem when the thing to be evaluated is perhaps an
* infinite loop.
*
* Thus, the asynchronous interface is provided. This is implemented
* with three methods: begin_eval(), eval_expr(), and poll_result().
* If the expression that needs to be evaluated is long-running, or
* even if it is an infinite loop, yet which periodically prints, then
* it can be run in one thread, and the print output can be collected in
* another thread, with poll_result(). The poll_result() method will
* block until there is printed output. It can be called repeatedly.
* When evaluation is completed, it will return any pending printed
* output, and subsequent calls will return the empty string
* immediately, without any further blocking. Be sure to call
* begin_eval() first, to set things up.
*
* The synchronous implementation is built on top of the async one,
* and runs entirely within the same thread; see the code below; it
* should show that:
*
* std::string eval(const std::string& expr)
* { begin_eval(); eval_expr(expr); return poll_result(); }
*
*/
class SchemeEval : public GenericEval
{
private:
// Initialization stuff
void init(void);
static void * c_wrap_init(void *);
void per_thread_init(void);
// Destructor stuff
void finish(void);
static void * c_wrap_finish(void *);
// Things related to (async) cogserver shell-evaluation
const std::string *_pexpr;
std::string _answer;
void save_rc(SCM);
SCM _rc;
bool _eval_done;
bool _poll_done;
std::mutex _poll_mtx;
std::condition_variable _wait_done;
SCM _pipe;
int _pipeno;
void do_eval(const std::string &);
std::string do_poll_result();
std::string poll_port();
static void * c_wrap_eval(void *);
static void * c_wrap_poll(void *);
// Support for interruption from a shell.
SCM _eval_thread;
static void * c_wrap_interrupt(void *);
// Output port, for any printing done by scheme code.
SCM _outport;
SCM _saved_outport;
bool _in_shell;
bool _in_server;
int _in_redirect;
void capture_port();
void redirect_output();
void restore_output();
void drain_output();
// Straight-up evaluation
SCM do_scm_eval(SCM, SCM (*)(void *));
static void * c_wrap_eval_v(void *);
static void * c_wrap_eval_as(void *);
// Apply function to arguments, returning Handle or TV
ValuePtr _hargs;
ValuePtr _retval;
AtomSpacePtr _retas;
SCM do_apply_scm(const std::string& func, const ValuePtr& varargs);
static void * c_wrap_apply_v(void *);
// Exception and error handling stuff
SCM _scm_error_string;
std::string _error_msg;
SCM _captured_stack;
void set_error_string(SCM);
void set_captured_stack(SCM);
static SCM preunwind_handler_wrapper(void *, SCM, SCM);
static SCM catch_handler_wrapper(void *, SCM, SCM);
SCM preunwind_handler(SCM, SCM);
SCM catch_handler(SCM, SCM);
// Printing of basic types
static std::string prt(SCM);
static void * c_wrap_set_atomspace(void *);
static void * c_wrap_get_atomspace(void *);
bool _in_eval;
protected:
AtomSpacePtr _atomspace;
friend class EvaluatorPool<SchemeEval>;
SchemeEval(void);
public:
// Call before first use. Cogserver needs this!
static void init_scheme(void);
// Set AtomSpace that this evaluator will be using.
virtual void set_atomspace(const AtomSpacePtr&);
virtual AtomSpacePtr get_atomspace(void);
virtual ~SchemeEval();
virtual std::string get_name(void) const { return "SchemeEval"; }
// Return per-thread, per-atomspace singleton
static SchemeEval* get_scheme_evaluator(AtomSpace*);
static SchemeEval* get_scheme_evaluator(const AtomSpacePtr&);
// The async-output interface.
void begin_eval(void);
void eval_expr(const std::string&);
std::string poll_result(void);
void interrupt(void);
// The synchronous-output interfaces.
std::string eval(const std::string& expr)
{ begin_eval(); eval_expr(expr); return poll_result(); }
std::string eval(const std::stringstream& ss)
{ return eval(ss.str()); }
// Evaluate expression, returning value.
ValuePtr eval_v(const std::string&);
ValuePtr eval_v(const std::stringstream& ss) { return eval_v(ss.str()); }
// Evaluate expression, returning handle.
Handle eval_h(const std::string& str) { return HandleCast(eval_v(str)); }
Handle eval_h(const std::stringstream& ss) { return eval_h(ss.str()); }
// Evaluate expression, returning AtomSpace.
AtomSpacePtr eval_as(const std::string&);
// Apply expression to args, returning Handle or TV
virtual ValuePtr apply_v(const std::string& func, ValuePtr varargs);
Handle apply(const std::string& func, Handle varargs) {
return HandleCast(apply_v(func, varargs)); }
// Nested invocations
bool recursing(void) { return _in_eval; }
};
/** @}*/
}
extern "C" {
// For shared-library loading
opencog::SchemeEval* get_scheme_evaluator(opencog::AtomSpace*);
};
#endif/* HAVE_GUILE */
#endif /* OPENCOG_SCHEME_EVAL_H */