#ifndef _SIMPLE_EVAL_H
#define _SIMPLE_EVAL_H

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <map>
#define _USE_MATH_DEFINES 
#include <corecrt_math_defines.h>
#include <cmath>
#include <complex>
#include "str.h"
#include "var.h"
#ifdef USE_WIN_STRAEM
#include "st.h"
#endif

//#define var double

#ifndef NO_GETPASS
extern "C" {
extern char* my_getpass(const char * msg);
}
#endif

using namespace std;
using namespace string_tools;

//namespace simple_eval {
//#define var double

using namespace string_tools;

var shell_eval(const string & str);
var eval(const string & str);

class Instruction {
public:
   string label;
   string opcode;
   string arg1;
   string arg2;

   Instruction() {

   }
 
   Instruction(const string & label, const string & opcode, const string & arg1, const string & arg2) {
      this->label = label;
      this->opcode = opcode;
      this->arg1 = arg1;
      this->arg2 = arg2;
   }

   Instruction(const Instruction & ins) {
      label = ins.label;
      opcode = ins.opcode;
      arg1 = ins.arg1;
      arg2 = ins.arg2;
   }

   ~Instruction() {

   }

   const Instruction & operator=(const Instruction & cp) {
      label = cp.label;
      opcode = cp.opcode;
      arg1 = cp.arg1;
      arg2 = cp.arg2;
      return *this;
   }
};

inline
ostream & 
operator<<(ostream & out, const Instruction & ins) {
   if(ins.label.size()) {
      out << "." << ins.label << ":" << endl;
   }
   if(ins.opcode.size() && ins.arg1.size() && ins.arg2.size()) {
      out << "\t" << ins.opcode << " " << ins.arg1 << "," << ins.arg2;
   }
   else if(ins.opcode.size() && ins.arg1.size()) {
      out << "\t" << ins.opcode << " " << ins.arg1;
   }
   else if(ins.opcode.size()) {
      out << "\t" << ins.opcode;
   }
}

class Symbol {
public:
   enum pointer_type {
      DOUBLE_TYPE,
      FLOAT_TYPE,
      INT_TYPE,
      LONG_TYPE,
      CHAR_TYPE,
      SHORT_TYPE,
      BOOL_TYPE,
      STRING_TYPE,
      VAR_TYPE,
      PTR_FUNCTION_TYPE,
   };
private:
   void * pointer;
   pointer_type type;
public:
    Symbol() {
       pointer = 0;
       type = INT_TYPE;
    }
    Symbol(const Symbol & cp) {
       pointer = cp.pointer;
       type = cp.type;
    }
    Symbol(void * pointer, pointer_type type) {
       this->pointer = pointer;
       this->type = type;
    }
    Symbol & operator=(const Symbol & cp) {
       pointer = cp.pointer;
       type = cp.type;
       return *this;
    }
    void * get_pointer() { 
       return pointer; 
    }
    void set_value(const var & val) {
       if(pointer == 0) throw "null pointer...";
       switch(type) {
          case DOUBLE_TYPE:
            *((double*)pointer) = val.to_double();
          break;
          case FLOAT_TYPE:
            *((float*)pointer) = val.to_float();
          break;
          case LONG_TYPE:
            *((long*)pointer) = val.to_long();
          break;
          case CHAR_TYPE:
            *((char*)pointer) = val.to_char();
          break;
          case SHORT_TYPE:
            *((short*)pointer) = val.to_short();
          break;
          case STRING_TYPE:
            *((string*)pointer) = val.to_string();
          break;
          case VAR_TYPE:
            *((var*)pointer) = val;
          break;
          case PTR_FUNCTION_TYPE:
            *((void**)pointer) = val.to_pointer();
          break;
          case BOOL_TYPE:
            *((bool*)pointer) = val.to_bool();
          break;
       }
    }
    var get_value() {
       var ret = 0.0;
       if(pointer == 0) throw "null pointer...";
       switch(type) {
          case DOUBLE_TYPE:
            ret = var(*((double*)pointer));
          break;
          case FLOAT_TYPE:
            ret = var(*((float*)pointer));
          break;
          case LONG_TYPE:
            ret = var(*((long*)pointer));
          break;
          case CHAR_TYPE:
            ret = var(*((char*)pointer));
          break;
          case SHORT_TYPE:
            ret = var(*((short*)pointer));
          break;
          case STRING_TYPE:
            ret = var(*((string*)pointer));
          break;
          case VAR_TYPE:
            ret = var(*((var*)pointer));
          break;
          case PTR_FUNCTION_TYPE:
            ret = var(*(void**)pointer);
          break;
          case BOOL_TYPE:
            ret = var(*(bool*)pointer);
          break;
       }
       return ret;
    }
};

class SimpleEval {
public:
   typedef var (*pf_t)(...);
   typedef void (*proc_t)(...);

   class Function {
   public:
      string name;
      pf_t pf;
      int arg_count;
      Function() {
         pf = 0;
         arg_count = 0;
      }
      Function(const Function & fun) {
         name = fun.name;
         pf = fun.pf;
         arg_count = fun.arg_count;
      }
      Function(const string & name, pf_t pf, int arg_count) {
         this->name = name;
         this->pf = pf;
         this->arg_count = arg_count;
      }
      Function & operator=(const Function & fun) {
         name = fun.name;
         pf = fun.pf;
         arg_count = fun.arg_count;
         return *this;
      }
   };

   class Procedure {
   public:
      string name;
      proc_t pf;
      int arg_count;
      Procedure() {
         pf = 0;
         arg_count = 0;
      }
      Procedure(const Procedure & fun) {
         name = fun.name;
         pf = fun.pf;
         arg_count = fun.arg_count;
      }
      Procedure(const string & name, proc_t pf, int arg_count) {
         this->name = name;
         this->pf = pf;
         this->arg_count = arg_count;
      }
      Procedure & operator=(const Procedure & fun) {
         name = fun.name;
         pf = fun.pf;
         arg_count = fun.arg_count;
         return *this;
      }
   };   

   typedef map< string, Procedure > procedure_map_type;
   typedef map< string, Function > function_map_type;
   typedef map< string, Symbol > symbol_map_type;
   typedef vector< Instruction > instruction_vector_type;
private:
   function_map_type fun_map;
   procedure_map_type proc_map;
   symbol_map_type sym_tbl;
   bool gen_code;
   instruction_vector_type code;
   double math_pi;
   double math_inf;
   double math_e;
   pf_t pf;
   bool debug;
public:
   SimpleEval(bool gen_code = false);
   ~SimpleEval();
   instruction_vector_type & get_code();
   void register_function(const string & name, pf_t fun, int nparam);
   void register_variable(const string & name, const Symbol & pointer);
   void register_variable(const string & name, double & val) { register_variable(name, Symbol(&val, Symbol::DOUBLE_TYPE)); }
   void register_variable(const string & name, float & val) { register_variable(name, Symbol(&val, Symbol::FLOAT_TYPE)); }
   void register_variable(const string & name, char & val) { register_variable(name, Symbol(&val, Symbol::CHAR_TYPE)); }
   void register_variable(const string & name, short & val) { register_variable(name, Symbol(&val, Symbol::SHORT_TYPE)); }
   void register_variable(const string & name, int & val) { register_variable(name, Symbol(&val, Symbol::INT_TYPE)); }
   void register_variable(const string & name, long & val) { register_variable(name, Symbol(&val, Symbol::LONG_TYPE)); }
   void register_variable(const string & name, string & val) { register_variable(name, Symbol(&val, Symbol::STRING_TYPE)); }
   void register_variable(const string & name, var & val) { register_variable(name, Symbol(&val, Symbol::VAR_TYPE)); }
   void register_variable(const string & name, pf_t & pf) { register_variable(name, Symbol(&pf, Symbol::PTR_FUNCTION_TYPE)); }
   void register_variable(const string & name, bool & bval) { register_variable(name, Symbol(&bval, Symbol::BOOL_TYPE)); }
   void add_code(const string & op);
   void add_code(const string & op, const string & arg1);
   void add_label(const string & label);
   void clear_code() { code.clear(); }
   void register_procedure(const string & name, proc_t pf, int nargs);
   var call_function(const string & id, vector< var > & args);
   void call_procedure(const string & id, vector< var > & args);
   var call_ptr_function(const string & id, vector< var > & args);
   void arg_list(const vector< string > & token_vec, int & offset, vector< var > & args);
   bool lex(const string & str, vector< string > & token_vec);
   var eval(const string & str);
   var expression(const vector< string > & token_vec, int & offset);
   var cond(var & start, const vector< string > & token_vec, int & offset);
   var boolop(const vector< string > & token_vec, int & offset);
   var relop(const vector< string > & token_vec, int & offset);
   var term(const vector< string > & token_vec, int & offset);
   var factor(const vector< string > & token_vec, int & offset);
   var bitop(const vector< string > & token_vec, int & offset);
   var primary(const vector< string > & token_vec, int & offset);
   var call(const string & id, const vector< string > & token_vec, int & offset);
   void set_gen_code(bool val) { gen_code = val; }
};

inline
void SimpleEval::add_code(const string & op) { 
   if(debug) cout << op << endl; 
   if(gen_code) code.push_back(Instruction("",op,"","")); 
}

inline
void SimpleEval::add_code(const string & op, const string & arg1) { 
   if(debug) cout << op << " " << arg1 << endl; 
   if(gen_code) code.push_back(Instruction("",op,arg1,"")); 
}

inline
void SimpleEval::add_label(const string & label) { 
   if(debug) cout << "." << label << ":" << endl; 
   if(gen_code) code.push_back(Instruction(label,"NOP","","")); 
}

inline
SimpleEval::instruction_vector_type & 
SimpleEval::get_code()
{
   return code;
}

inline
var
Random() 
{
   return var((long)rand());
}

inline
var
Seed() 
{
   srand(time(NULL));
   return var(0);
}

inline
var
Sin(var * arg)
{
   return var(sin(arg->to_complex()));
}

inline
var
Cos(var * arg)
{
   return var(cos(arg->to_complex()));
}

inline
var
Pow(var * n, var * ex)
{
   return var(pow(n->to_double(), ex->to_double()));
}

inline
var
Eval(SimpleEval * expr, var * str)
{
   return expr->eval(str->to_string());
}

inline
var
Min(var * arg1, var * arg2)
{
   return min(arg1->to_double(),arg2->to_double());
}

inline
var
Max(var * arg1, var * arg2)
{
   return max(arg1->to_double(),arg2->to_double());
}

inline
var
Abs(var * arg1)
{
   return abs(arg1->to_double());
}

inline
var
Sqrt(var * arg)
{
   return var(sqrt(arg->to_complex()));
}

inline
var
Input()
{
   string str;
   char ch;
   ch = cin.get();
   while(ch != '\n') {
      str += ch;
      ch = cin.get();
   }
   return str;
}

inline
var
Strcmp(var * left, var * right)
{
   return var(*left == *right);
}

#ifndef NO_GETPASS
inline
var
Getpass(var * arg)
{
   return var(my_getpass(arg->to_string().c_str()));
}

int MyRunShell(void * ptr);

inline
var
ShellEval(var * str)
{
   return MyRunShell((void*)(str->to_string().c_str()));
}
#endif

inline
SimpleEval::SimpleEval(bool gen_code)
{
   this->gen_code = gen_code;
   register_function("sin", (pf_t)Sin, 1);
   register_function("cos", (pf_t)Cos, 1);
   register_function("pow", (pf_t)Pow, 2);
   register_function("sqrt", (pf_t)Sqrt, 1);
   register_function("eval", (pf_t)Eval, 1);
   register_function("abs", (pf_t)Abs, 1);
   register_function("seed", (pf_t)Seed, 0);
   register_function("random", (pf_t)Random, 0);
   register_function("min", (pf_t)Min, 2);
   register_function("max", (pf_t)Max, 2);
   register_function("input", (pf_t)Input, 0);
   register_function("strcmp", (pf_t)Strcmp, 2);
#ifndef NO_GETPASS
   register_function("getpass", (pf_t)Getpass, 1);
   register_function("system", (pf_t)ShellEval, 1);
#endif
   register_variable("M_PI", math_pi);
   register_variable("M_E", math_e);
   register_variable("INFINITY", math_inf);
   register_variable("pf", pf);
   register_variable("debug", debug);
   math_pi = M_PI;
   math_e = M_E;
   math_inf = INFINITY;
   pf = 0;
   debug = false;
}

inline
void SimpleEval::register_procedure(const string & name, proc_t pf, int nargs)
{
   proc_map[name] = Procedure(name, pf, nargs);
}

inline
SimpleEval::~SimpleEval()
{
}

inline
void
SimpleEval::register_function(const string & name, pf_t fun, int arg_count)
{
   fun_map[name] = Function(name, fun, arg_count);
}

inline
void
SimpleEval::register_variable(const string & name, const Symbol & pointer)
{
   sym_tbl[name] = pointer;
}

inline
var shell_eval(const string & str)
{
   SimpleEval expr;
   string temp = replace(str, "\'", "\"");
   //temp = replace(temp, "=", "==");
   //temp = replace(temp, "<>", "!=");
   return expr.eval(temp);
}

inline
var eval(const string & str)
{
   SimpleEval expr;
   return expr.eval(str);
}

inline
bool SimpleEval::lex(const string & str, vector< string > & token_vec) {
   string tok;
   bool ret = true;
   for(size_t i = 0; i < str.size(); i++) {
      if(is_digit(str[i])) {
         if(tok.size()) {
            token_vec.push_back(tok);
            tok = "";
         }
         if(str[i] == '0') {
            if((i+1) < str.size()) {
               if(str[i+1] == 'x') {
                  i += 2;
                  while(is_hex(str[i])) {
                     tok += str[i];
                     i++;
                     if(i >= str.size()) {
                        break;
                     }
                  }
                  i--;
                  stringstream ss;
                  ss << hex << tok;
                  long long val;
                  ss >> val;
                  stringstream ss2;
                  ss2 << val;
                  token_vec.push_back(ss2.str());
                  continue;
               }
               else if(is_oct(str[i+1])) {
                  i++;
                  while(is_oct(str[i])) {
                     tok += str[i];
                     i++;
                     if(i >= str.size()) {
                        break;
                     }
                  }
                  i--;
                  stringstream ss;
                  ss << oct << tok;
                  long long val;
                  ss >> val;
                  stringstream ss2;
                  ss2 << val;
                  token_vec.push_back(ss2.str());
                  continue;
               }
            }
         }
         int count = 0;
         while(is_digit(str[i]) || str[i] == '.') {
            tok += str[i];
            if(str[i] == '.') {
               count++;
               if(count > 1) {
                  throw "exception: lex error...";
               }
            }
            i++;
            if(i >= str.size()) {
               break;
            }
         }
         i--;
         token_vec.push_back(tok);
         tok = "";
      }
      else if(is_letter(str[i]) || str[i] == '_') {
         while(is_letter(str[i]) || str[i] == '_' || is_digit(str[i])) {
            tok += str[i];
            i++;
            if(i >= str.size()) {
               break;
            }
         }
         i--;
         token_vec.push_back(tok);
         tok = "";
      }
      else if(str[i] == '(') {
         if(tok.size()) {
            token_vec.push_back(tok);
            tok = "";
         }
         tok += str[i];
         token_vec.push_back(tok);
         tok = "";
      }
      else if(str[i] == ')') {
         if(tok.size()) {
            token_vec.push_back(tok);
            tok = "";
         }
         tok += str[i];
         token_vec.push_back(tok);
         tok = "";
      }
      else if(str[i] == '\"') {
         if(tok.size()) {
            token_vec.push_back(tok);
            tok = "";
         }
         tok += "\"";
         i++;
         while(i < str.size()) {
            tok += str[i];
            if(str[i] == '\"') {
               if(i) {
                  if(str[i-1]== '\\') {

                  }
                  else {
                     break;
                  }
               }
               else {
                  break;
               }
            }
            i++;
         }
         token_vec.push_back(tok);
         tok = "";
      }
      else if(str[i] == ' ' || str[i] == '\t') {
         if(tok.size()) {
            token_vec.push_back(tok);
            tok = "";
         }
      }
      else {
         if(tok.size()) {
            token_vec.push_back(tok);
            tok = "";
         }
         char ch = str[i];
         if((i+1) < str.size()) {
            char ch2 = str[i+1];
            if((i+2) < str.size()) {
               char ch3 = str[i+2];
               if((ch == '<' && ch2 == '<' && ch3 == '=')
                  || (ch == '>' && ch2 == '>' && ch3 == '=')) { 
                  tok += ch;
                  tok += ch2;
                  tok += ch3;
                  token_vec.push_back(tok);
                  tok = "";
                  i += 2;
                  continue;
               }
            }
            if((ch == '<' && ch2 == '=') 
               || (ch == '>' && ch2 == '=')
               || (ch == '>' && ch2 == '>')
               || (ch == '<' && ch2 == '<')
               || (ch == '!' && ch2 == '=')
               || (ch == '=' && ch2 == '=')
               || (ch == '|' && ch2 == '=')
               || (ch == '&' && ch2 == '=')
               || (ch == '^' && ch2 == '=')
               || (ch == '~' && ch2 == '=')
               || (ch == '/' && ch2 == '=')
               || (ch == '*' && ch2 == '=')
               || (ch == '%' && ch2 == '=')
               || (ch == '+' && ch2 == '=')
               || (ch == '-' && ch2 == '=')
               || (ch == '+' && ch2 == '+')
               || (ch == '-' && ch2 == '-')
               || (ch == '&' && ch2 == '&')
               || (ch == '|' && ch2 == '|')) {
               tok += ch;
               tok += ch2;
               token_vec.push_back(tok);
               tok = "";
               i++;
            }
            else {
               tok += ch;
               token_vec.push_back(tok);
               tok = "";
            }
         }
         else {
            tok += ch;
            token_vec.push_back(tok);
            tok = "";
         }
      }
   }
   if(tok.size()) {
      token_vec.push_back(tok);
   }
   return ret;
}

inline
var SimpleEval::eval(const string & str) {
   vector< string > token_vec;
   int offset = 0;
   var ret;
   string str0 = replace(str,"\'", "\"");
   try {
      if(lex(str0,token_vec)) {
         ret = expression(token_vec,offset);
      }
   }
   catch(const char * message) {
      cout << message << endl;
      ret = 1;
   }
   catch(...) {
      cout << "unknown exception..." << endl;
      ret = 1;
   }
   return ret;
}

inline
var SimpleEval::expression(const vector< string > & token_vec, int & offset)
{
   var ret = 0.0;
   ret = boolop(token_vec,offset);
   if(offset < token_vec.size()) {
      if(token_vec[offset] == "?") {
         ret = cond(ret,token_vec,offset);
      }
   }
   return ret;
}

inline
var SimpleEval::boolop(const vector< string > & token_vec, int & offset) {
   var ret = 0.0;
   if(offset < token_vec.size()) {
      ret = relop(token_vec, offset);
      if(offset < token_vec.size()) {
         if(token_vec[offset] == "||") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <boolop>";
            ret = ret || boolop(token_vec,offset);
            add_code("OR");
         }
         else if(token_vec[offset] == "&&") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <boolop>";
            ret = ret && boolop(token_vec,offset);
            add_code("AND");
         }
      }
   }
   return ret;
}

inline
var SimpleEval::relop(const vector< string > & token_vec, int & offset) {
   var ret = 0.0;
   ///cout << "<relop>" << endl;
   if(offset < token_vec.size()) {
      ret = term(token_vec, offset);
      if(offset < token_vec.size()) {
         if(token_vec[offset] == "<") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <relop>";
            ret = ret < relop(token_vec,offset);
            add_code("LT");
         }
         else if(token_vec[offset] == ">") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <relop>";
            ret = ret > relop(token_vec,offset);
            add_code("GT");
         }
         else if(token_vec[offset] == "<=") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <relop>";
            ret = ret <= relop(token_vec,offset);
            add_code("LTEQ");
         }
         else if(token_vec[offset] == ">=") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <relop>";
            ret = ret >= relop(token_vec,offset);
            add_code("GTEQ");
         }
         else if(token_vec[offset] == "==") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <relop>";
            ret = ret == relop(token_vec,offset);
            add_code("EQ");
         }
         else if(token_vec[offset] == "!=") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <relop>";
            ret = ret != relop(token_vec,offset);
            add_code("NEQ");
         }
      }
   }
   return ret;
}

inline
var SimpleEval::term(const vector< string > & token_vec, int & offset) {
   var ret = 0.0;
   //cout << "<term>" << endl;
   if(offset < token_vec.size()) {
      ret = factor(token_vec, offset);
      if(offset < token_vec.size()) {
         if(token_vec[offset] == "+") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <term>";
            var temp = term(token_vec,offset);
            ret = ret + temp;
            add_code("ADD");
         }
         else if(token_vec[offset] == "-") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <term>";
            ret = ret + term(token_vec,offset);
            add_code("SUB");
         }
      }
   }
   return ret;
}

inline
var SimpleEval::factor(const vector< string > & token_vec, int & offset) {
   var ret = 0.0;
   if(offset < token_vec.size()) {
      ret = bitop(token_vec, offset);
      if(offset < token_vec.size()) {
         if(token_vec[offset] == "/") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <factor>";
            var temp = factor(token_vec,offset);
            if(temp == var(0)) {
               throw "divide by zero...";
            }
            ret = ret / temp;
            add_code("DIV");
         }
         else if(token_vec[offset] == "*") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <factor>";
            ret = ret * factor(token_vec,offset);
            add_code("MUL");
         }
         else if(token_vec[offset] == "%") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <factor>";
            ret = ret % factor(token_vec,offset);
            add_code("MOD");
         }
      }
   }
   return ret;
}

inline
var SimpleEval::bitop(const vector< string > & token_vec, int & offset) {
   var ret = 0.0;
   if(offset < token_vec.size()) {
      ret = primary(token_vec, offset);
      if(offset < token_vec.size()) {
         if(token_vec[offset] == "<<") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <bitop>";
            ret = ret << bitop(token_vec,offset);
            add_code("LSH");
         }
         else if(token_vec[offset] == ">>") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <bitop>";
            ret = ret >> bitop(token_vec,offset);
            add_code("RSH");
         }
         else if(token_vec[offset] == "|") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <bitop>";
            ret = ret | bitop(token_vec,offset);
            add_code("BOR");
         }
         else if(token_vec[offset] == "&") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <bitop>";
            ret = ret & bitop(token_vec,offset);
            add_code("BAND");
         }
         else if(token_vec[offset] == "^") {
            offset++;
            if(offset >= token_vec.size()) throw "expected: <bitop>";
            ret = ret ^ bitop(token_vec,offset);
            add_code("XOR");
         }
      }
   }
   return ret;
}

inline
void SimpleEval::arg_list(const vector< string > & token_vec, int & offset, vector< var > & args)
{
   while(token_vec[offset] != ")") {
      args.push_back(boolop(token_vec,offset));
      if(token_vec[offset] == ",") {
         offset++;
         if(offset >= token_vec.size()) {
            throw "expected: <boolop>";
         }
      }
      else if(token_vec[offset] != ")") {
         throw "expected: \")\"";
      }
   }
}

inline
var SimpleEval::call_function(const string & id, vector< var > & args)
{
   pf_t fun = fun_map[id].pf;
   var ret;
   if(args.size() != fun_map[id].arg_count) {
      throw "function argument count not correct...";
   }
   switch(args.size()) {
      case 0:
         ret = fun();
      break;

      case 1:
      if(id == "eval")
         ret = fun(this, &args[0]);
      else
         ret = fun(&args[0]);
      break;

      case 2:
         ret = fun(&args[0], &args[1]);
      break;

      case 3:
         ret = fun(&args[0], &args[1], &args[2]);
      break;

      default:
         throw "only three function arguments allowed...";
   }
   add_code("CALL", id);
   return ret;
}

inline
void SimpleEval::call_procedure(const string & id, vector< var > & args)
{
   Procedure * proc = &proc_map[id];
   proc_t fun = proc->pf;
   if(args.size() == proc->arg_count);
      throw "function call argument miss-match...";
   switch(args.size()) {
      case 0:
         fun();
      break;

      case 1:
         fun(&args[0]);
      break;

      case 2:
         fun(&args[0], &args[1]);
      break;

      case 3:
         fun(&args[0], &args[1], &args[2]);
      break;

      default:
         throw "only three function arguments allowed...";
   }
   add_code("CALL", id);
}


inline
var SimpleEval::cond(var & start, const vector< string > & token_vec, int & offset) 
{
   var ret = start;
   if(offset < token_vec.size()) {
      if(token_vec[offset] == "?") {
         offset++;
         if(offset < token_vec.size()) {
            var first,second;
            first = boolop(token_vec,offset);
            if(token_vec[offset] == ":") {
               offset++;
               if(offset < token_vec.size()) {
                  second = boolop(token_vec,offset);
                  if(ret != var(0)) {
                     ret = first;
                  }
                  else {
                     ret = second;
                  }
               }
               else throw "expected: <boolop>";
            }
            else throw "expected: \":\"";
         }
         else throw "expected <boolop>";
      }
   }
   return ret;
}

inline
var SimpleEval::call_ptr_function(const string & id, vector< var > & args)
{
   var ret = 0.0;
   pf_t fun = (pf_t)(*(void**)(sym_tbl[id].get_pointer()));
   //fun = (pf_t)Sin;
   if(fun == 0) {
      throw "exception: null function pointer...";
   }
   switch(args.size()) {
      case 0:
         ret = fun();
      break;

      case 1:
      if(id == "eval")
         ret = fun(this, &args[0]);
      else
         ret = fun(&args[0]);
      break;

      case 2:
         ret = fun(&args[0], &args[1]);
      break;

      case 3:
         ret = fun(&args[0], &args[1], &args[2]);
      break;

      default:
         throw "only three function arguments allowed...";
   }
   add_code("CALL", id);
   return ret;
}

inline
var SimpleEval::call(const string & id, const vector< string > & token_vec, int & offset) {
   var ret = 0.0;
   if(offset < token_vec.size()) {
      vector< var > args;
      arg_list(token_vec,offset,args);
      if(token_vec[offset] == ")") {
         offset++;
         if(fun_map.find(id) != fun_map.end()) {
            ret = call_function(id,args);
         }
         else if(proc_map.find(id) != proc_map.end()) {
            call_procedure(id,args);
         }
         else {
            if(sym_tbl.find(id) != sym_tbl.end()) {
               ret = call_ptr_function(id,args);
            }
            else {
               throw "exception: unknown function...";
            }
         }
      }
      else {
         throw "expected: \")\"";
      }
   }
   else {
      throw "expected: \")\"";
   }
   return ret;
}

inline
var SimpleEval::primary(const vector< string > & token_vec, int & offset) {
   var ret = 0.0;
   if(offset < token_vec.size()) {
      if(token_vec[offset] == "!") {
         offset++;
         if(offset >= token_vec.size()) {
            throw "expected: <primary>";
         }
         else {
            ret = !primary(token_vec,offset);
            add_code("NOT");
         }
      }
      else if(token_vec[offset] == "~") {
         offset++;
         if(offset >= token_vec.size()) {
            throw "expected: <primary>";
         }
         else {
            ret = ~primary(token_vec,offset);
            add_code("BNOT");
         }
      }
      else if(token_vec[offset] == "++") {
         offset++;
         if(offset >= token_vec.size()) {
            throw "expected: <primary>";
         }
         else {
            ret = ++primary(token_vec,offset);
            add_code("PUSH", "1");
            add_code("ADD");
         }
      }
      else if(token_vec[offset] == "--") {
         if(offset >= token_vec.size()) {
            throw "expected: <primary>";
         }
         else {
            add_code("PUSH", "1");
            ret = --primary(token_vec,offset);
            add_code("SUB");
         }
      }
      else if(token_vec[offset] == "true") {
         offset++;
         ret = var(true);
         add_code("PUSH", "1");
      }
      else if(token_vec[offset] == "false") {
         offset++;
         ret = var(false);
         add_code("PUSH", "0");
      }
      else if(token_vec[offset] == "-") {
         offset++;
         if(offset >= token_vec.size()) {
            throw "expected: <primary>";
         }
         else {
            ret = -primary(token_vec,offset);
            add_code("UMINUS");
         }
      }
      else if(token_vec[offset][0] == '_' || is_letter(token_vec[offset][0])) {
         string id = token_vec[offset];
         offset++;
         if(offset < token_vec.size()) {
            while(offset < token_vec.size()) {
               if(token_vec[offset] == ".") {
                  offset++;
                  if(token_vec[offset][0] == '_' || is_letter(token_vec[offset][0])) {
                     string id2 = token_vec[offset];
                     offset++;
                  }
                  else {
                     throw "expected: <identifier>";
                  }
               }
               else if(token_vec[offset] == "->") {
                  offset++;
                  if(offset < token_vec.size()) {
                     if(token_vec[offset][0] == '_' || is_letter(token_vec[offset][0])) {
                        string id2 = token_vec[offset];
                        offset++;
                     }
                     else {
                        throw "expected: <identifier>";
                     }
                  }
                  else {
                     throw "expected: <identifier>";
                  }
               }
               else {
                  break;
               }
            }
            bool flag = false;
            if(token_vec[offset] == "++") {
               offset++;
               sym_tbl[id].set_value(sym_tbl[id].get_value() + var(1));
               ret = sym_tbl[id].get_value();
               add_code("PUSH", "1");
               add_code("APUSH", id);
               add_code("ADD");
               add_code("APOP", id);
               flag = true;
            }
            else if(token_vec[offset] == "--") {
               offset++;
               sym_tbl[id].set_value(sym_tbl[id].get_value() - var(1));
               ret = sym_tbl[id].get_value();
               add_code("PUSH", "1");
               add_code("APUSH", id);
               add_code("SUB");
               add_code("APOP", id);
               flag = true;
            }
            if(offset >= token_vec.size()) {

            }
            else if(token_vec[offset] == "(" && !flag) {
               offset++;
               ret = call(id,token_vec,offset);
            }
            else if(token_vec[offset] == "=") {
               offset++;
               if(offset >= token_vec.size()) throw "expected: <boolop>";
               var value = boolop(token_vec, offset);
               sym_tbl[id].set_value(value);
               ret = sym_tbl[id].get_value();
               add_code("APOP", id);
               add_code("APUSH", id);
            }
            else if(token_vec[offset] == "+=") {
               offset++;
               if(offset >= token_vec.size()) throw "expected: <boolop>";
               sym_tbl[id].set_value(sym_tbl[id].get_value() + boolop(token_vec, offset));
               ret = sym_tbl[id].get_value();
               add_code("APUSH", id);
               add_code("ADD");
               add_code("APOP", id);
               add_code("APUSH", id);
            }
            else if(token_vec[offset] == "-=") {
               offset++;
               if(offset >= token_vec.size()) throw "expected: <boolop>";
               sym_tbl[id].set_value(sym_tbl[id].get_value() - boolop(token_vec, offset));
               ret = sym_tbl[id].get_value();
               add_code("APUSH", id);
               add_code("SUB");
               add_code("APOP", id);
               add_code("APUSH", id);
            }
            else if(token_vec[offset] == "*=") {
               offset++;
               if(offset >= token_vec.size()) throw "expected: <boolop>";
               sym_tbl[id].set_value(sym_tbl[id].get_value() * boolop(token_vec, offset));
               ret = sym_tbl[id].get_value();
               add_code("APUSH", id);
               add_code("MUL");
               add_code("APOP", id);
               add_code("APUSH", id);
            }
            else if(token_vec[offset] == "/=") {
               offset++;
               if(offset >= token_vec.size()) throw "expected: <boolop>";
               sym_tbl[id].set_value(sym_tbl[id].get_value() / boolop(token_vec, offset));
               ret = sym_tbl[id].get_value();
               add_code("APUSH", id);
               add_code("DIV");
               add_code("APOP", id);
               add_code("APUSH", id);
            }
            else if(token_vec[offset] == "<<=") {
               offset++;
               if(offset >= token_vec.size()) throw "expected: <boolop>";
               sym_tbl[id].set_value(sym_tbl[id].get_value() << boolop(token_vec, offset));
               ret = sym_tbl[id].get_value();
               add_code("APUSH", id);
               add_code("LSH");
               add_code("APOP", id);
               add_code("APUSH", id);
            }
            else if(token_vec[offset] == ">>=") {
               offset++;
               if(offset >= token_vec.size()) throw "expected: <boolop>";
               sym_tbl[id].set_value(sym_tbl[id].get_value() >> boolop(token_vec, offset));
               ret = sym_tbl[id].get_value();
               add_code("APUSH", id);
               add_code("RSH");
               add_code("APOP", id);
               add_code("APUSH", id);
            }
            else if(token_vec[offset] == "|=") {
               offset++;
               if(offset >= token_vec.size()) throw "expected: <boolop>";
               sym_tbl[id].set_value(sym_tbl[id].get_value() | boolop(token_vec, offset));
               ret = sym_tbl[id].get_value();
               add_code("APUSH", id);
               add_code("BOR");
               add_code("APOP", id);
               add_code("APUSH", id);
            }
            else if(token_vec[offset] == "&=") {
               offset++;
               if(offset >= token_vec.size()) throw "expected: <boolop>";
               sym_tbl[id].set_value(sym_tbl[id].get_value() & boolop(token_vec, offset));
               ret = sym_tbl[id].get_value();
               add_code("APUSH", id);
               add_code("BAND");
               add_code("APOP", id);
               add_code("APUSH", id);
            }
            else if(token_vec[offset] == "^=") {
               offset++;
               if(offset >= token_vec.size()) throw "expected: <boolop>";
               sym_tbl[id].set_value(sym_tbl[id].get_value() ^ boolop(token_vec, offset));
               ret = sym_tbl[id].get_value();
               add_code("APUSH", id);
               add_code("XOR");
               add_code("APOP", id);
               add_code("APUSH", id);
            }
            else if(token_vec[offset] == "~=") {
               offset++;
               if(offset >= token_vec.size()) throw "expected: <boolop>";
               sym_tbl[id].set_value(~boolop(token_vec, offset));
               ret = sym_tbl[id].get_value();
               add_code("BNOT");
               add_code("APOP", id);
               add_code("APUSH", id);
            }
            else if(fun_map.find(id) != fun_map.end()) {
               ret = var((void*)(fun_map[id].pf));
               add_code("APUSH", id);
            }
            else if(proc_map.find(id) != proc_map.end()) {
               ret = var((void*)(proc_map[id].pf));
               add_code("APUSH", id);
            }
            else {
               ret = sym_tbl[id].get_value();
               add_code("APUSH", id);
            }
         }
         else if(fun_map.find(id) != fun_map.end()) {
            ret = var((void*)(fun_map[id].pf));
            add_code("APUSH", id);
         }
         else if(proc_map.find(id) != proc_map.end()) {
            ret = var((void*)(proc_map[id].pf));
            add_code("APUSH", id);
         }
         else {
            ret = sym_tbl[id].get_value();
            add_code("APUSH", id);
         }
      }
      else if(token_vec[offset] == "(") {
         offset++;
         ret = expression(token_vec,offset);
         if(token_vec[offset] == ")") {
            offset++;
         }
         else {
            throw "expected: \")\"";
         }
      }
      else if((token_vec[offset].size())?(token_vec[offset][0] == '\"'):(false)) {
         string str = token_vec[offset];
         str = str.substr(1,str.size()-2);
         str = replace(str, "\\n", "\n");
         str = replace(str, "\\r", "\r");
         str = replace(str, "\\t", "\t");
         str = replace(str, "\\\'", "\'");
         str = replace(str, "\\\"", "\"");
         ret = str;
         offset++;
         add_code("SPUSH", str);
      }
      else {
         stringstream str;
         str << token_vec[offset];
         double temp;
         str >> temp;
         ret = temp;
         offset++;
         add_code("PUSH", str.str());
      }
   }
   return ret;
}

//}

#endif
