/*
* interpret.h
*
* This file is part of NEST.
*
* Copyright (C) 2004 The NEST Initiative
*
* NEST is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* NEST 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 General Public License
* along with NEST. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef INTERPRETER_H
#define INTERPRETER_H
/*
interpret.h defines the SLI Interpreter class
*/
#include <typeinfo>
#include <list>
#include "token.h"
#include "slitype.h"
#include "tokenstack.h"
#include "sliactions.h"
#include "slibuiltins.h"
#include "slimodule.h"
/**
* @defgroup SLIOutput How to notify the SLI user
*/
/**
* @defgroup SLIMessaging Displaying messages to the user
* @ingroup SLIOutput
*/
/**
* @defgroup SLIError Raising (and displaying) errors
* @ingroup SLIOutput
*/
class Parser;
class Scanner;
class DictionaryStack;
class Dictionary;
class FunctionDatum;
class BoolDatum;
class DynModule;
namespace nest {
class Network;
}
extern "C"{
void SLIthrowsignal(int s);
}
class SLIInterpreter
{
std::list<SLIModule *> modules;
/* Flags and variables to control debugging and
* optimizations.
*/
bool debug_mode_; //!< True, if SLI level debugging is enabled.
bool show_stack_; //!< Show stack in debug mode.
bool show_backtrace_; //!< Show stack-backtrace on error.
bool catch_errors_; //!< Enter debugger on error.
bool opt_tailrecursion_;//!< Optimize tailing recursion.
int call_depth_; //!< Current depth of procedure calls.
int max_call_depth_; //!< Depth until which procedure calls are debugged.
unsigned long cycle_count;
bool cycle_guard;
unsigned long cycle_restriction;
int verbositylevel;
void inittypes(void);
void initdictionaries(void);
void initbuiltins(void);
void initexternals(void);
public:
unsigned long code_accessed; // for code coverage analysis.
unsigned long code_executed; // ration should be coverage
Dictionary *statusdict;
Dictionary *errordict;
DictionaryStack *DStack;
Parser *parse;
// Names of basics functions
Name ilookup_name;
Name ipop_name;
Name isetcallback_name;
Name iiterate_name;
Name iloop_name;
Name irepeat_name;
Name ifor_name;
Name iforallarray_name;
Name iforalliter_name;
Name iforallindexedarray_name;
Name iforallindexedstring_name;
Name iforallstring_name;
Name pi_name;
Name e_name;
Name iparse_name;
Name stop_name;
Name end_name;
// Names of symbols and objects
Name null_name;
Name true_name;
Name false_name;
Name mark_name;
Name istopped_name;
Name systemdict_name;
Name userdict_name;
Name errordict_name;
Name quitbyerror_name;
Name newerror_name;
Name errorname_name;
Name commandname_name;
Name signo_name;
Name recordstacks_name;
Name estack_name;
Name ostack_name;
Name dstack_name;
Name commandstring_name;
Name interpreter_name;
// Names of basic errors
Name ArgumentTypeError;
Name StackUnderflowError;
Name UndefinedNameError;
Name WriteProtectedError;
Name DivisionByZeroError;
Name RangeCheckError;
Name PositiveIntegerExpectedError;
Name BadIOError;
Name StringStreamExpectedError;
Name CycleGuardError;
Name SystemSignal;
Name BadErrorHandler;
Name KernelError;
Name InternalKernelError;
Token execbarrier_token;
// Debug Message levels as static consts
/** @ingroup SLIMessaging
* @name Predefined error levels
* @{
*/
static const int M_ALL; //!< Predefined error level for turning on
//!< the display of all messages; for use with verbosity(int).
static const int M_DEBUG; //!< Predefined error level for debugging messages
static const int M_STATUS;//!< Predefined error level for status messages
static const int M_INFO; //!< Predefined error level for informational messages
static const int M_WARNING; //!< Predefined error level for warning messages
static const int M_ERROR; //!< Predefined error level for error messages
static const int M_FATAL; //!< Predefined error level for failure messages
static const int M_QUIET; //!< An error level above all others. Use to turn off messages completely.
/** @} */
private:
static char const * const M_ALL_NAME;
static char const * const M_DEBUG_NAME;
static char const * const M_STATUS_NAME;
static char const * const M_INFO_NAME;
static char const * const M_WARNING_NAME;
static char const * const M_ERROR_NAME;
static char const * const M_FATAL_NAME;
static char const * const M_QUIET_NAME;
public:
// These static members must be accessible from
// the Datum constructors
static SLIType Integertype;
static SLIType Doubletype;
static SLIType Stringtype;
static SLIType Nametype;
static SLIType Booltype;
static SLIType Literaltype;
static SLIType Arraytype;
static SLIType Proceduretype;
static SLIType Litproceduretype;
static SLIType Dictionarytype;
static SLIType Symboltype;
static SLIType Functiontype;
static SLIType Trietype;
static SLIType Callbacktype;
static SLIType Istreamtype;
static SLIType XIstreamtype;
static SLIType Ostreamtype;
static SLIType IntVectortype;
static SLIType DoubleVectortype;
static SLIType Iteratortype;
// SLIType default actions
static DatatypeFunction datatypefunction;
static NametypeFunction nametypefunction;
static ProceduretypeFunction proceduretypefunction;
static LitproceduretypeFunction litproceduretypefunction;
static FunctiontypeFunction functiontypefunction;
static TrietypeFunction trietypefunction;
static CallbacktypeFunction callbacktypefunction;
static XIstreamtypeFunction xistreamtypefunction;
// Basic Operations needed to run the default actions
static const IlookupFunction ilookupfunction;
static const IsetcallbackFunction isetcallbackfunction;
static const IiterateFunction iiteratefunction;
static const IloopFunction iloopfunction;
static const IrepeatFunction irepeatfunction;
static const IforFunction iforfunction;
static const IforallarrayFunction iforallarrayfunction;
static const IforalliterFunction iforalliterfunction;
static const IforallindexedarrayFunction iforallindexedarrayfunction;
static const IforallindexedstringFunction iforallindexedstringfunction;
static const IforallstringFunction iforallstringfunction;
// State variables of the Interpreter
Token ct; // callback; see comments in execute(void)
TokenStack OStack;
TokenStack EStack;
// public member functions:
SLIInterpreter(void);
~SLIInterpreter();
int startup(); //!< Initialise the interpreter by reading in the startup files.
/**
* Execute the supplied command string.
*/
int execute(const std::string &);
/**
* Start the interpreter and run the startup code.
*/
int execute(int v=0);
// int execute_protected(void);
/**
* Run the interpreter with a prepared execution stack.
* The function returns, if the execution stack has reached the given level.
*/
int execute_(size_t exitlevel=0);
int execute_debug_(size_t exitlevel=0);
void createdouble(Name const&, double);
void createcommand(Name const&, SLIFunction const *);
void createcommand(Name const&, Name const&, SLIFunction const *);
void createconstant(Name const&, const Token&);
/** Lookup a name searching all dictionaries on the stack.
* The first occurrence is reported. If the Name is not found,
* @a VoidToken is returned.
*/
const Token & lookup(const Name &n) const;
/** Lookup a name searching all dictionaries on the stack.
* The first occurrence is reported. If the Name is not found,
* an UndefinedName exceptiopn is thrown.
*/
const Token & lookup2(const Name &n) const;
/** Lookup a name searching only the bottom level dictionary.
* If the Name is not found,
* @a VoidToken is returned.
*/
const Token & baselookup(const Name &n) const; // lookup in a specified
/** Test for a name searching all dictionaries on the stack.
*/
bool known(const Name &n) const;
/** Test for a name in the bottom level dictionary.
*/
bool baseknown(const Name &n) const;
/** Bind a Token to a Name.
* The token is copied. This can be an expensive operation for large
* objects. Also, if the token is popped off one of the stacks after
* calling def, it is more reasonable to use SLIInterpreter::def_move.
*/
void def(Name const&, Token const &);
/** Unbind a previously bound Token from a Name.
* Throws UnknownName Exception.
*/
void undef(Name const&);
/** Bind a Token to a Name in the bottom level dictionary.
* The Token is copied.
*/
void basedef(const Name &n, const Token &t);
/** Bind a Token to a Name.
* like def, however, the Datum object is moved from the token into the
* dictionary, thus, no memory allocation or copying is needed.
*/
void def_move(Name const&, Token &);
/** Bind a Token to a Name in the bottom level dictionary.
* The Token is moved.
*/
void basedef_move(const Name &n, Token &t);
void setcycleguard(Index);
void removecycleguard(void);
/**
* Increment call depth level.
* The value of call_depth_ is used to control
* the step mode.
* Step mode is disabled for call_depth_ >= max_call_depth_.
* This gives the user the opportunity to skip over nested
* calls during debugging.
*/
void inc_call_depth()
{
++call_depth_;
}
/**
* Decrement call depth level.
* The value of call_depth_ is used to control
* the step mode.
* Step mode is disabled for call_depth_ >= max_call_depth_.
* This gives the user the opportunity to skip over nested
* calls during debugging.
*/
void dec_call_depth()
{
--call_depth_;
}
/**
* Set call depth level to a specific value.
* The value of call_depth_ is used to control
* the step mode.
* Step mode is disabled for call_depth_ >= max_call_depth_.
* This gives the user the opportunity to skip over nested
* calls during debugging.
*/
void set_call_depth(int l)
{
call_depth_=l;
}
/**
* Return current call depth level.
* The value of call_depth_ is used to control
* the step mode.
* Step mode is disabled for call_depth_ >= max_call_depth_.
* This gives the user the opportunity to skip over nested
* calls during debugging.
*/
int get_call_depth() const
{
return call_depth_;
}
/**
* Set maximal call depth level to a specific value.
* The value of call_depth_ is used to control
* the step mode.
* Step mode is disabled for call_depth_ >= max_call_depth_.
* This gives the user the opportunity to skip over nested
* calls during debugging.
*/
void set_max_call_depth(int d)
{
max_call_depth_=d;
}
/**
* Return value of maximal call depth level.
* The value of call_depth_ is used to control
* the step mode.
* Step mode is disabled for call_depth_ >= max_call_depth_.
* This gives the user the opportunity to skip over nested
* calls during debugging.
*/
int get_max_call_depth() const
{
return max_call_depth_;
}
/**
* Returns true, if step mode is active.
* The step mode is active in debug mode if
* call_depth_ < max_call_depth_
*/
bool step_mode() const
{
return debug_mode_ && (call_depth_ < max_call_depth_);
}
/**
* Returns true, if debug mode is turned on.
*/
bool get_debug_mode() const
{
return debug_mode_;
}
/**
* Turn debug mode on.
*/
void debug_mode_on()
{
debug_mode_=true;
}
/**
* Turn debug mode off.
*/
void debug_mode_off()
{
debug_mode_=false;
}
/**
* Switch stack display on or off in debug mode.
*/
void toggle_stack_display();
/**
* Show Debug options.
*/
void debug_options() const;
/**
* Prompt user for commands during debug mode.
* In this function, the user can enter simple commands
* to debug code executed by the interpreter.
*/
char debug_commandline(Token &);
/**
* Returns true, if tailing recursion optimization is done.
*/
bool optimize_tailrecursion() const
{
return opt_tailrecursion_;
}
/**
* Enable tail-recursion optimization.
* Tail-recursion can be optimizes in such a way
* that the execution stack is not growing with each
* recursion level.
* This optimization may improve performance for
* applications which heavily rely on deep recusions.
* However, during debugging, tail-recursion
* optimization removes important information from the
* execution stack.
*/
void optimize_tailrecursion_on()
{
opt_tailrecursion_=true;
}
/**
* Disable tail-recursion optimization.
* Tail-recursion can be optimizes in such a way
* that the execution stack is not growing with each
* recursion level.
* This optimization may improve performance for
* applications which heavily rely on deep recusions.
* However, during debugging, tail-recursion
* optimization removes important information from the
* execution stack.
*/
void optimize_tailrecursion_off()
{
opt_tailrecursion_=false;
}
/**
* True, if a stack backtrace should be shown on error.
* Whenever an error or stop is raised, the execution stack is
* unrolled up to the nearest stopped context.
* In this process it is possible to display a stack backtrace
* which allows the user to diagnose the origin and possible
* cause of the error.
* For applications which handle themselfs, this backtrace may be
* disturbing. So it is possible to switch this behavior on and
* off.
*/
bool show_backtrace() const
{
return show_backtrace_;
}
/**
* Switch stack backtrace on.
* Whenever an error or stop is raised, the execution stack is
* unrolled up to the nearest stopped context.
* In this process it is possible to display a stack backtrace
* which allows the user to diagnose the origin and possible
* cause of the error.
* For applications which handle themselfs, this backtrace may be
* disturbing. So it is possible to switch this behavior on and
* off.
*/
void backtrace_on();
/**
* Switch stack backtrace off.
* Whenever an error or stop is raised, the execution stack is
* unrolled up to the nearest stopped context.
* In this process it is possible to display a stack backtrace
* which allows the user to diagnose the origin and possible
* cause of the error.
* For applications which handle themselfs, this backtrace may be
* disturbing. So it is possible to switch this behavior on and
* off.
*/
void backtrace_off();
bool catch_errors() const
{
return catch_errors_;
}
void catch_errors_on()
{
catch_errors_=true;
}
void catch_errors_off()
{
catch_errors_=false;
}
void stack_backtrace(int n);
/** Cause the SLI interpreter to raise an error.
* This function is used by classes derived from SLIFunction to raise
* an error.
* \n
* raiseerror() is an interface to the SLI interpreter's error
* handling mechanism (see The Red Book for details). If an error
* is raised, the following actions are performed:
* - the value of errordict /newerror is set to true
* - the value of errordict /command is set to the name of the command
* which raised the error
* - If the value of errordict /recorstack is true,
* the state of the interpreter is saved:
* - the operand stack is copied to errordict /ostack
* - the execution stack is copied to errordict /estack
* - the dictionary stack is copied to errordict /dstack
* - the dictionary stack is cleared.
* - stop is called. Stop then tries to find an enclosing stopped
* context and calls the associated function.
*
* This mechanism is explained in detail in The PostScript Reference Manual.
* \n
* If the user did not establish any stopped context, the default
* stopped context for the SLI interpreter will be executed, which
* includes display of an error message and stopping program
* execution.
* \n
* Please note that before raiserror() is called, the state of the
* operand and execution stack shall be restored to their initial
* state.
*
* @param err The argument is the name of the error, specified as
* a string.
* The name of the currently active function will be used as the
* function name.
*
* @ingroup SLIError
* @see raiseerror(Name),
* raiseerror(Name,Name), raiseagain()
*/
void raiseerror(const char* err) { raiseerror(Name(err));}
/** Cause the SLI interpreter to raise an error.
* This function is used by classes derived from SLIFunction to raise
* an error.
* \n
* raiseerror() is an interface to the SLI interpreter's error
* handling mechanism (see The Red Book for details). If an error
* is raised, the following actions are performed:
* - the value of errordict /newerror is set to true
* - the value of errordict /command is set to the name of the command
* which raised the error
* - If the value of errordict /recorstack is true,
* the state of the interpreter is saved:
* - the operand stack is copied to errordict /ostack
* - the execution stack is copied to errordict /estack
* - the dictionary stack is copied to errordict /dstack
* - the dictionary stack is cleared.
* - stop is called. Stop then tries to find an enclosing stopped
* context and calls the associated function.
*
* This mechanism is explained in detail in The PostScript Reference Manual.
* \n
* If the user did not establish any stopped context, the default
* stopped context for the SLI interpreter will be executed, which
* includes display of an error message and stopping program
* execution.
* \n
* Please note that before raiserror() is called, the state of the
* operand and execution stack shall be restored to their initial
* state.
*
* @param err The argument is the name of the error.
* For conveniency, there is also a variant of this function that takes a string as
* the argument.
*
* @ingroup SLIError
* @see raiseerror(const char*),
* raiseerror(Name,Name), raiseagain()
*/
void raiseerror(Name err);
/**
* Handle exceptions thrown by any execute().
* This raiseerror is the first step in handling C++ exceptions
* thrown by an execute() call. In particular,
* - the name of the calling function is recorded;
* - the command is popped from the execution stack;
* - the error message is extracted from those exceptions that are
* derived from SLIException.
* - handling is forwarded to raiserror(Name, Name).
*/
void raiseerror(std::exception &err);
/** Cause the SLI interpreter to raise an error.
* This function is used by classes derived from SLIFunction to raise
* an error.
* \n
* raiseerror() is an interface to the SLI interpreter's error
* handling mechanism (see The Red Book for details). If an error
* is raised, the following actions are performed:
* - the value of errordict /newerror is set to true
* - the value of errordict /command is set to the name of the command
* which raised the error
* - If the value of errordict /recorstack is true,
* the state of the interpreter is saved:
* - the operand stack is copied to errordict /ostack
* - the execution stack is copied to errordict /estack
* - the dictionary stack is copied to errordict /dstack
* - the dictionary stack is cleared.
* - stop is called. Stop then tries to find an enclosing stopped
* context and calls the associated function.
*
* This mechanism is explained in detail in The PostScript Reference Manual.
* \n
* If the user did not establish any stopped context, the default
* stopped context for the SLI interpreter will be executed, which
* includes display of an error message and stopping program
* execution.
* \n
* Please note that before raiserror() is called, the state of the
* operand and execution stack shall be restored to their initial
* state.
*
* @param cmd The first argument is the name of the calling function.
* @param err The second argument is the name of the error.
*
* @ingroup SLIError
* @see raiseerror(const char*), raiseerror(Name),
* raiseagain()
*/
void raiseerror(Name cmd, Name err);
/** Print a description of a raised error.
* The errordict members errorname, command and message together
* with the function input parameters decides the nature of the
* output message. The function use the message() function to
* print the error.
* Replaces the SLI :print_error function.
*
* @param cmd The name of the function that raised the error.
*
* @see raiseerror(const char*), raiseerror(Name),
* raiseerror(Name,Name)
*/
void print_error(Token cmd);
/** Re-raise the last error.
* raiseagain re-raises a previously raised error. This is useful
* if an error handler cannot cope with a particular error (e.g. a signal)
* and wants to pass it to an upper level handler. Thus, nestet error handlers
* are possible.
*
* @ingroup SLIError
* @see raiseerror(const char*), raiseerror(Name),
* raiseerror(Name,Name)
*/
void raiseagain(void);
/** TO BE DOCUMENTED.
* @todo Document this function.
*
* @ingroup SLIError
*/
void raisesignal(int);
// Message loging mechanism
/** Set the verbosity level of the SLI messaging mechanism.
* Only messages having an error level that is equal to or greater
* than this level will be displayed by the interpreter.
*
* @see verbosity(void), message(), debug(), status(), info(), warning(),
* error(), fatal()
* @ingroup SLIMessaging
*/
void verbosity(int);
/** Retrieve the current verbosity level of the SLI messaging mechanism.
* Only messages having an error level that is equal to or greater
* than this level will be displayed by the interpreter.
* \n
* You may use any positive integer here. For conveniency,
* there exist five predifined error levels: \n
* SLIInterpreter::M_ALL=0, display all messages \n
* SLIInterpreter::M_DEBUG=5, display debugging messages and above \n
* SLIInterpreter::M_DEBUG=7, display status messages and above \n
* SLIInterpreter::M_INFO=10, display information messages and above \n
* SLIInterpreter::M_WARNING=20, display warning messages and above \n
* SLIInterpreter::M_ERROR=30, display error messages and above \n
* SLIInterpreter::M_FATAL=40, display failure messages and above \n
* SLIInterpreter::M_QUIET=100, suppress all messages \n
* Thus, by calling verbosity(SLIInterpreter::M_WARNING) you
* indicate that you are interested in seeing error messages and
* more important messages only.
*
* @see verbosity(void), message(), debug(), status(), info(), warning(),
* error(), fatal()
* @ingroup SLIMessaging
*/
int verbosity(void) const;
/** Display a message.
* @param level The error level that shall be associated with the
* message. You may use any poitive integer here. For conveniency,
* there exist five predifined error levels: \n
* (SLIInterpreter::M_ALL=0, for use with verbosity(int) only, see there), \n
* SLIInterpreter::M_DEBUG=5, a debugging message \n
* SLIInterpreter::M_DEBUG=7, a status message \n
* SLIInterpreter::M_INFO=10, an informational message \n
* SLIInterpreter::M_WARNING=20, a warning message \n
* SLIInterpreter::M_ERROR=30, an error message \n
* SLIInterpreter::M_FATAL=40, a failure message.
* (SLIInterpreter::M_QUIET=100, for use with verbosity(int) only, see there), \n
* @param from A string specifying the name of the function that
* sends the message.
* @param test A string specifying the message text.
*
* The message will ony be displayed if the current verbosity level
* is greater than or equal to the specified level.
* \n
* If two or more messages are issued after each other, that have
* the same <I>from</I> and <I>level</I> argument, the messages will
* be grouped toghether in the output.
*
* @see verbosity(void), verbosity(int)
* @ingroup SLIMessaging
*/
void message(int level, const char from[], const char text[],
const char errorname[] = "") const;
/** Function used by the message(int, const char*, const char*) function.
* Prints a message to the specified output stream.
* @param out output stream
* @param levelname name associated with input level
*/
void message(std::ostream& out, const char levelname[],
const char from[], const char text[],
const char errorname[] = "") const;
void terminate(int returnvalue=-1);
//*******************************************************
Name getcurrentname(void) const;
unsigned long cycles(void) const
{
return cycle_count;
}
template<class T> void addmodule(void);
void addmodule(SLIModule *);
/*
* Add a linked dynamic module to the interpreter.
* Initializers (commandstrings) for linked dynamic modules are executed
* by sli-init.sli after all C++ initialization is done.
* Do not use this for modules loaded at runtime!
*/
void addlinkeddynmodule(DynModule *, nest::Network *);
FunctionDatum * Ilookup(void) const;
FunctionDatum * Iiterate(void) const;
/**
* Throw StackUnderflow exception if too few elements on stack.
* @param n Minimum number of elements required on stack.
* @throw StackUnderflow if fewer that @c n elements on stack.
*/
void assert_stack_load(size_t n);
};
// This function template is a workaround for the parameterless
// template member function, given below. As of
// egcs-2.91.16 980328 (gcc-2.8.0 release)
// the compiler is not able to parse a call like
// engine.addmodule<ModuleX>();
// (Stroustrup97), Sec 13.3.1 (p335)
template<class T>
void addmodule(SLIInterpreter &i )
{
i.addmodule(new T);
}
template<class T>
void SLIInterpreter::addmodule(void)
{
SLIModule *m=new T();
modules.push_back(m);
m->install(std::cout,this);
}
inline
void SLIInterpreter::assert_stack_load(size_t n)
{
if ( OStack.load() < n)
throw StackUnderflow(n, OStack.load());
}
#endif