// ---------------------------------------------------------------------------
// - Interp.hpp                                                              -
// - afnix engine - interpreter class definition                             -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - 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.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2008 amaury darsch                                   -
// ---------------------------------------------------------------------------

#ifndef  AFNIX_INTERP_HPP
#define  AFNIX_INTERP_HPP

#ifndef  AFNIX_STACK_HPP
#include "Stack.hpp"
#endif

#ifndef  AFNIX_LOADER_HPP
#include "Loader.hpp"
#endif

#ifndef  AFNIX_OPTIONS_HPP
#include "Options.hpp"
#endif

#ifndef  AFNIX_TERMINAL_HPP
#include "Terminal.hpp"
#endif

#ifndef  AFNIX_SUPERSET_HPP
#include "Superset.hpp"
#endif

#ifndef  AFNIX_RESOLVER_HPP
#include "Resolver.hpp"
#endif

#ifndef  AFNIX_RUNNABLE_HPP
#include "Runnable.hpp"
#endif

namespace afnix {

  /// The Interp class is the main afnix interpreter engine. The interpreter
  /// operates on three streams, the standard input, the standard output and
  /// the standard error stream. By default, the interpreter install a 
  /// terminal object, unless the three streams are specified at construction.
  /// The interpreter holds a series of registers. For each basic types,
  /// like integer, real, character and boolean, the interpreter has 256 
  /// local thread registers. The operations associated with the registers
  /// are type dependant. For integer and real, these are mostly arithmetic 
  /// operations. For boolean, these are logical operations. The interpreter 
  /// has a top level (or global) nameset. The global nameset is shared 
  /// between all interpreters and is referenced as '...'. On top of this, 
  /// the interpreter provides a 'compile' method which takes a form and 
  /// generates a new one. The sole purpose of the compile method is to
  /// perform static checking and generate a new form ready for execution.
  /// @author amaury darsch

  class Interp : public Runnable {
  public:
    /// @return an interpreter option object
    static Options* getopts (void);
    
  private:
    /// the standard input stream
    mutable Input* p_is;
    /// the standard output stream
    mutable Output* p_os;
    /// the standard error stream
    mutable Output* p_es;
    /// the assert flag
    bool    d_assert;
    /// the cloned interpreter flag
    bool    d_cloned;
    /// the posted object
    Object* p_posted;
    /// the super global nameset
    Superset* p_gset;
    /// the execution stack
    Stack*  p_stk;
    /// the vector arguments
    Vector* p_argv;
    /// the runnable form
    Object* p_rform;
    /// the library loader
    Loader* p_shld;

  protected:
    /// the default terminal
    Terminal* p_term;
    /// the path resolver
    Resolver* p_rslv;
    /// the encoding mode
    String d_emod;
    /// the next flag
    bool   d_next;

  public:
    /// create a default interpreter
    Interp (void);

    /// create a new interpreter with or without a terminal
    /// @param tflg the terminal flag
    Interp (const bool tflg);

    /// create a new interpreter
    /// @param is the standard input stream
    /// @param os the standard output stream
    /// @param es the standard error stream
    Interp (Input* is, Output* os, Output* es);

    /// copy constructor for this interpreter
    /// @param that the interpreter to copy
    Interp (const Interp& that);

    /// destroy this interpreter
    ~Interp (void);

    /// @return the class name
    String repr (void) const;

    /// post an object in this interpreter
    void post (Object* object);

    /// @return a clone of this interpreter
    Object* clone (void);

    /// clone this interpreter and set the runnable form
    /// @param form the runnable form to set
    Interp* dup (Object* form) const;

    /// duplicate this interpreter by seeting the terminal
    /// @param term the teminal stream to set
    Interp* dup (Terminal* term) const;

    /// duplicate this interpreter by updating the streams
    /// @param is the input  stream to update
    /// @param os the output stream to update
    /// @param es the error  stream to update
    Interp* dup (Input* is, Output* os, Output* es) const;

    /// evaluate the runnable form
    Object* run (void);

    /// evaluate a form in a thread by cloning this interpreter
    /// @param form the form to evaluate
    Object* launch (Object* form);

    /// evaluate a form in a daemon thread by cloning this interpreter
    /// @param form the form to evaluate
    Object* daemon (Object* form);

    /// @return the interpreter input stream
    Input* getis (void) const;

    /// @return the interpreter output stream
    Output* getos (void) const;

    /// @return the interpreter error stream
    Output* getes (void) const;

    /// set the primary prompt
    /// @param value the prompt to set
    void setpp (const String& value);

    /// set the secondary prompt
    /// @param value the prompt to set
    void setsp (const String& value);

    /// @return the primary prompt
    String getpp (void) const;

    /// @return the secondary prompt
    String getsp (void) const;

    /// set the interpreter encoding mode
    /// @param emod the encoding mode
    void setemod (const String& emod);

    /// @return the interpreter stack
    Stack* getstk (void) const;

    /// @return the interpreter global set
    Nameset* getgset (void) const;

    /// create a new reserved name in the global nameset
    /// @param name the reserved name
    /// @param object the object to bind
    void mkrsv (const String& name, Object* object);

    /// create a child nameset in the superset
    /// @param name the nameset name to create
    Nameset* mknset (const String& name);

    /// set the interpreter arguments
    /// @param args the arguments to set
    void setargs (const Strvec& args);

    /// @return the interpreter arguments
    Strvec getargs (void) const;

    /// add a path to the resolver
    /// @param path the path to add
    void addpath (const String& path);

    /// set the resolver with a list of path
    /// @param path the path to set
    void setpath (const Strvec& path);

    /// set the assert flag
    /// @param flag the flag to set
    void setasrt (const bool flag);

    /// @return the assert flag
    bool getasrt (void) const;

    /// @return the interpreter loader
    Loader* getld (void) const;

    /// register a library by name and handle
    /// @param name the library name to register
    /// @param hand the library handle
    void reglib (const String& name, void* hand);

    /// open a new dynamic library by name
    /// @param name the library name
    /// @param argv the vector arguments
    Object* library (const String& name, Vector* argv);
 
    /// set the next flag
    /// @param flag the flag to set
    void setnext (const bool flag);

    /// @return the next flag
    bool getnext (void) const;

    /// break the runnable in a nameset with an object
    /// @param nset   the nameset to loop
    /// @param object the object to break on
    bool bpt (Nameset* nset, Object* object);

    /// run the read-eval loop on the standard streams
    /// @return false if something bad happen
    bool loop (void);

    /// loop in the context of a nameset and an input stream
    /// @param nset the nameset context
    /// @param is   the input stream to use
    bool loop (Nameset* nset, Input* is);

    /// run the read-eval loop with a file
    /// @param fname the file name to read
    /// @return false if something bad happen
    bool loop (const String& fname);

    /// run the read-eval loop with a file
    /// @param fname the file name to read
    void load (const String& fname);

    /// compile from an input stream to an output stream
    /// @param the file to compile
    /// @param os the output stream to write
    void compile (const String& name, Output& os);

  private:
    // make the assignment operator private
    Interp& operator = (const Interp&);

  public:
    /// @return true if the given quark is defined
    bool isquark (const long quark, const bool hflg) const;

    /// evaluate an object in this object
    /// @param object the object to evaluate
    Object* eval (Object* object);

    /// evaluate an object method by quark
    /// @param robj  the current runnable
    /// @param nset  the current nameset    
    /// @param quark the quark to evaluate
    Object* eval (Runnable* robj, Nameset* nset, const long quark);

    /// apply this object with a set of arguments and a quark
    /// @param robj  the current runnable
    /// @param nset  the current nameset    
    /// @param quark the quark to apply these arguments
    /// @param argv  the arguments to apply
    Object* apply (Runnable* robj, Nameset* nset, const long quark,
		   Vector* argv);
  };
}

#endif
