/*
 * 
 * Copyright (c) 2001-2007 Scott E. Johnston
 * Copyright (c) 2000 IET Inc.
 * Copyright (c) 1994-1999 Vectaport Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and
 * its documentation for any purpose is hereby granted without fee, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the names of the copyright holders not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  The copyright holders make
 * no representations about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 *
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 * 
 */

/*
 * ComTerp is a command interpreter derived from the Parser
 */

#if !defined(_comterp_h)
#define _comterp_h

// #define POSTEVAL_EXPERIMENT

#include <ComTerp/parser.h>

#include <OS/table.h>
declareTable(ComValueTable,int,void*)


class AttributeList;
class AttributeValue;
class AttributeValueList;
class ComFunc;
class ComFuncState;
class ComTerpState;
class ComValue;
#include <iosfwd>

class ComterpHandler;

//: extendable command interpreter.
// ComTerp is an extendable command interpreter with a simple C-like expression
// syntax and support for commands with fixed-location and keyword-prefixed 
// arguments.  The underlying architecture of this interpreter is patterned
// after Fischer and LeBlanc's "Crafting a Compiler with C", with their pipeline
// of scanner --> parser --> code_conversion --> code_generation retrofitted into 
// one of scanner --> parser --> code_conversion --> interpreter.
// <p>
// You use a ComTerp by first constructing one with a default set of commands
// and operators, then add any commands (derived ComFunc objects) needed for a
// particular application, then start it running on either stdin or 
// some alternate input file described by arguments to the constructor.
// To embed a ComTerp in another application you'll probably want to use
// a ComTerpServ, which has extensions to ComTerp for passing input and output
// strings from/to the interpreter by buffers.
class ComTerp : public Parser {
public:
    ComTerp();
    // construct with default set of ComFunc objects.
    ComTerp(const char* path);
    // construct and run on commands read from 'path'.
    ComTerp(void*, char*(*)(char*,int,void*), int(*)(void*), int(*)(void*));
    // construct with IO system based on a void* pointer and three function
    // pointers to receive that void* as their first argument, with
    // signatures that match fgets, feof, and ferror.
    ~ComTerp();

    void init();
    // initialize memory for the regular stack, the ComFuncState stack,
    // and the symbol tables.

    boolean read_expr();
    // read expression from the input, return true if all ok.
    boolean eof();
    // return true when end-of-file found on the input.
    void increment_linenum();
    // increment line number when empty line is read

    virtual int eval_expr(boolean nested=false);
    // evaluate topmost expression on the stack, with a flag to
    // indicate if this call to eval_expr() is nested inside
    // another, so that initialization doesn't get repeated.
    virtual int eval_expr(ComValue* pfvals, int npfvals);
    // evaluate postfix expression stored in ComValue objects.
    virtual int post_eval_expr(int tokcnt, int offtop, int pedepth 
#ifdef POSTEVAL_EXPERIMENT
			       , int nolookup=false
#endif
);
    // copy unevaluated expression to the stack and evaluate.
    void print_post_eval_expr(int tokcnt, int offtop, int pedepth);
    // print unevaluated expression
    postfix_token* copy_post_eval_expr(int tokcnt, int offtop);
    // copy unevaluated expression
    boolean top_expr();
    // return true if the topmost expression is currently being evaluated

    int print_stack_top() const;
    // print the top of the stack to stdout.
    int print_stack_top(ostream& out) const;
    // print the top of the stack to an ostream.
    int print_stack() const;
    // print the entire stack to stdout.
    int print_stack(std::ostream& out) const;
    // print the entire stack to stdout.
    int stack_height() { return _stack_top+1; }
    // return current height of the stack.
    boolean brief() const;
    // return brief mode flag.
    void brief(boolean flag) { _brief = flag; }
    // set brief mode flag.

    int add_command(const char* name, ComFunc*, const char* alias = nil, const char* docstring2 = nil);
    // add a derived ComFunc to be known by 'name'.
    void list_commands(ostream& out, boolean sorted = false);
    // print an optionally sorted list of commands to an ostream.
    int* get_commands(int &ncommands, boolean sorted = false);
    // return an optionally sorted list of command names.

    ComValue pop_stack(boolean lookupsym=true);
    // return a reference (on the stack) to what was the top of the stack,
    // if 'lookupsym' is false, don't look up ComValue objects in 
    // the local or global symbol table to replace a symbol, just
    // return the symbol itself.
    ComValue& lookup_symval(ComValue&);
    // look up a ComValue associated with a symbol (specified in the
    // input ComValue) in the local or global symbol tables.
    AttributeValue* lookup_symval(ComValue*);
    // look up a pointer to an AttributeValue associated with a symbol
    // (specified in the input ComValue) in the local or global symbol
    // tables.  Do not alter the input ComValue.
    ComValue& lookup_symval(int symid);
    // look up a ComValue associated with a symbol (specified with a
    // symbol id) in the local or global symbol tables.

    ComValue& stack_top(int n=0);
    // return reference to top of the stack, offset by 'n' (usually negative).
    ComValue& pop_symbol();
    // return a reference (on the stack) to what was the top of the stack,
    void push_stack(ComValue&);
    // copy the state of a ComValue onto the stack, incrementing the
    // reference count of any AttributeValueList, otherwise replicating data.
    void push_stack(AttributeValue&);
    // copy the state of an AttributeValue into a ComValue on the stack, 
    // incrementing the reference count of any AttributeValueList, 
    // otherwise replicating data.
    boolean stack_empty();

    ComValue& expr_top(int n=0);
    // top of currently evaluating expression buffer.

    static ComTerp& instance();
    // instance of ComTerp for global use.

    void quit(boolean quitflag=true);
    // set flag that will cause a quit to happen as soon as possible.
    void quitflag(boolean flag);
    // set flag that will cause a quit to happen as soon as possible.
    boolean quitflag();
    // return value of flag that will cause a quit to happen as soon as possible.
    virtual void exit(int status=0);
    // call _exit().

    virtual int run(boolean one_expr=false, boolean nested=false);
    // run interpreter until end-of-file or quit command, unless 
    // 'one_expr' is true.  Leave 'one_expr' false when using a ComTerpServ.
    // Return Value:  -1 if eof, 0 if normal operation, 1 if 
    // partial expression parsed, 2 if no result computed, 3 if error in parsing.  
    // 'nested' indicates contents of stack should be preserved.

    virtual int runfile(const char* filename, boolean popen_flag=0);
    // run interpreter on contents of 'filename'.
    void add_defaults();
    // add default commands (ComFunc objects).

    ComValueTable* localtable() const { return _localtable; }
    // local symbol table associated with an individual ComTerp.
    ComValueTable* globaltable() const 
      { if (!_globaltable) _globaltable = new ComValueTable(100); return _globaltable; }
    // global symbol table associated with every ComTerp.
    ComValue* localvalue(int symid);
    // value associated with a symbol id in the local symbol table.
    ComValue* globalvalue(int symid);
    // value associated with a symbol id in the global symbol table.
    ComValue* eithervalue(int symid, boolean globalfirst=false);
    // value associated with a symbol id in either symbol table.

    const char* errmsg() { return _errbuf; }
    // current error message buffer.

    void set_attributes(AttributeList*);
    // set AttributeList to be used as an additional local symbol table.
    AttributeList* get_attributes();
    // return pointer to AttributeList being used as an additional 
    // local symbol table.

    void handler(ComterpHandler* h );
    // set handler for invoking ComFunc execute methods.
    ComterpHandler* handler();
    // return pointer to handler that can read_expressions from
    // a connection and interpret them.

    void disable_prompt();
    // disable '>' prompting sent in response to an unfinished input expression.
    void enable_prompt();
    // enable '>' prompting sent in response to an unfinished input expression.

    unsigned int& pfnum() { return _pfnum; }
    // number of arguments in the input postfix buffer of a tokenized 
    // tree like expression ready to be evaluated, but not yet
    // converted to ComValue objects.

    virtual boolean is_serv() { return false; } 
    // flag to test if ComTerp or ComTerpServ

    void func_for_next_expr(ComFunc* func);
    // set ComFunc to use on subsequent expression
    ComFunc* func_for_next_expr();
    // get ComFunc to use on subsequent expression

    void val_for_next_func(ComValue& val);
    // set ComValue to pass to subequent command
    ComValue& val_for_next_func();
    // get ComValue to pass to subequent command
    void clr_val_for_next_func();
    // clear out ComValue to pass to subequent command

    unsigned int& linenum() { return _linenum; }
    // count of lines processed

    ComTerpState* top_servstate();
    // return pointer to top state on ComTerpServ state stack

    void push_servstate();
    // push ComTerpServ state for later retrieval

    void pop_servstate();
    // pop ComTerpServ state that was saved earlier

    void trace_mode(int mode) { _trace_mode = mode; }
    // set trace mode

    int trace_mode() { return _trace_mode; }
    // return trace mode

    int& npause() { return _npause; }
    // return (reference to) number of pauses

    int& stepflag() { return _stepflag; }
    // return flag that controls stepwise execution

    void echo_postfix(boolean flag) { _echo_postfix = flag; }
    // set flag that indicates whether to echo postfix or not

    boolean echo_postfix() const { return _echo_postfix; }
    // return flag that indicates whether to echo contents of postfix buffer

    void postfix_echo();
    // echo the postfix tokens
    void postfix_echo(postfix_token* pfbuf, int pfnum);
    // echo the postfix tokens

    void delim_func(boolean flag) { _delim_func = flag; }
    // set flag that indicates whether to run a delimiter-selected func.
 
    boolean delim_func() const { return _delim_func; }
    // return flag that indicates whether to run a delimiter-selected func.

    void ignore_commands(boolean flag) { _ignore_commands = flag; }
    // set flag that indicates whether to ignore all built-in commands
 
    boolean ignore_commands() const { return _ignore_commands; }
    // return flag that indicates whether to ignore all built-in commands

    void autostream(boolean flag) { _autostream = flag; }
    // set flag that indicates whether to run a delimiter-selected func.
 
    boolean autostream() const { return _autostream; }
    // return flag that indicates whether to run a delimiter-selected func.

    void running(boolean flag) { _running = flag; }
    // set flag that indicates whether to run a delimiter-selected func.
 
    boolean running() const { return _running; }
    // return flag that indicates whether to run a delimiter-selected func.

    void muted(boolean flag) { _muted = flag; }
    // set flag that indicates whether to mute output
 
    boolean muted() const { return _muted; }
    // return flag that indicates whether to mute output

    void force_nested(boolean flag) { _force_nested = flag; }
    // set flag to ensure subsequent calls are nested

    boolean force_nested() { return _force_nested; }
    // get flag to ensure subsequent calls are nested

    int arg_str(int n);
    // return nth command line argument

    int narg_str();
    // return number of command line argument

    void set_args(int argc, char** argv);
    // set command line arguments

    void set_args(const char* argstr);
    // set command line arguments

    void clear_top_commands(); 
    // clear list used to collect list of top-level commands

    AttributeValueList* top_commands() { return _top_commands; }
    // list of top-most commands for this derived interpreter


protected:
    void incr_stack();
    void incr_stack(int n);
    void decr_stack(int n=1);

    boolean skip_func(ComValue* topval, int& offset, int offlimit);
    boolean skip_key(ComValue* topval, int& offset, int offlimit, int& argcnt);
    boolean skip_arg(ComValue* topval, int& offset, int offlimit, int& argcnt);

    void push_stack(postfix_token*);
    void token_to_comvalue(postfix_token*, ComValue*);
    const ComValue* stack(unsigned int &top) const;
    void load_sub_expr();
    void load_postfix(postfix_token*, int toklen, int tokoff);
    void eval_expr_internals(int pedepth=0);

    ComFuncState* top_funcstate();
    // return top ComFuncState on stack
    virtual void push_funcstate(ComFuncState& funcstate);
    // push ComFuncState onto stack
    virtual void pop_funcstate();
    // pop ComFuncState off stack

    ComValue* _stack;  // stack of multi-value objects, central to the interpreter
    int _stack_top; // current top of stack, -1 indicates empty
    unsigned int _stack_siz; // current maximum stack size
    boolean _quitflag; // flag that can be set to terminate interpreter
    char* _errbuf; // buffer used for rendering error messages
    char* _errbuf2; // ancillary buffer for rendering error messages
    int _pfoff; // current offset in _pfbuf
    boolean _brief; // when used to produce ComValue output
    boolean _just_reset; // flag that gets set after call to ::reset_stack()
    boolean _defaults_added; // flag for base set of commands added 

    ComValueTable* _localtable; // per interpreter symbol table
    static ComValueTable* _globaltable; // interpreter shared symbol table
    AttributeList* _alist; // extends symbol tables with names in an AttributeList

    ComFuncState* _fsstack;  // stack of func-status (nargs/nkeys/...) 
    int _fsstack_top;
    unsigned int _fsstack_siz;

    ComTerpState* _ctsstack;  // stack of ComTerpServ state
    int _ctsstack_top;
    unsigned int _ctsstack_siz;

    ComValue* _pfcomvals; 
    // postfix buffer of ComValue's converted from postfix_token

    static ComTerp* _instance;
    // default instance of a ComTerp

    ComterpHandler* _handler;
    // I/O handler for this ComTerp.

    ComFunc* _func_for_next_expr;
    // ComFunc to run on next expression

    ComValue* _val_for_next_func;
    // ComValue to pass to next command

    int _trace_mode;
    // trace mode

    int _npause;
    // depth of pause

    int _stepflag;
    // true if single-stepping interpreter

    boolean _echo_postfix;
    // echos postfix tokens if true

    boolean _delim_func;
    // use delimiter-selected func, passing symbol in ::command_symid()

    boolean _ignore_commands;
    // ignore any built-in commands.

    boolean _autostream;
    // automatically iterates over stream if left on stack

    boolean _running;
    // flag to inform others this ComTerp in use

    boolean _muted;
    // flag to mute any response from a ComTerp

    boolean _force_nested;
    // flag to ensure subsequent calls are nested

    int _fd;
    // handle for I/O

    int* _arg_strs;
    // array of string ids for command line arguments

    int _narg_strs;
    // size of array of string ids for command line arguments

    AttributeValueList* _top_commands;
    // list of top-most commands for this derived comterp

    friend class ComFunc;
    friend class ComterpHandler;
    friend class ComTerpIOHandler;
};

//: object for holding ComTerp state
// object that holds the state of a ComTerp
// which allows for nested and recursive use of a singular ComTerp
class ComTerpState {
public:
  ComTerpState() {}
  ComTerpState(ComTerpState& ctss) { *this = ctss; }
  // copy constructor.

  postfix_token*& pfbuf() { return _pfbuf; }
  int& pfsiz() { return _pfsiz;; }
  int& pfnum() { return _pfnum; }  
  int& pfoff() { return _pfoff; }
  int& bufptr() { return _bufptr; }
  int& bufsiz() { return _bufsiz; }
  int& linenum() { return _linenum; }
  //  int& just_reset() { return _just_reset; }
  char*& buffer() { return _buffer; }
  ComValue*& pfcomvals() { return _pfcomvals; }
  infuncptr& infunc() { return _infunc; }
  eoffuncptr& eoffunc() { return _eoffunc; }
  errfuncptr& errfunc() { return _errfunc; }
  void*& inptr() { return _inptr; }
  AttributeList*& alist() { return _alist; }

protected:

  postfix_token* _pfbuf;
  int _pfsiz;
  int _pfnum;
  int _pfoff;
  int _bufptr;
  int _linenum;
  // int _just_reset;
  char* _buffer;
  int _bufsiz;
  ComValue* _pfcomvals;
  infuncptr _infunc;
  eoffuncptr _eoffunc;
  errfuncptr _errfunc;
  void* _inptr;
  AttributeList* _alist;

};

#endif /* !defined(_comterp_h) */
