//============================================================================
// Name        :
// Author      : Avi
// Revision    : $Revision: #30 $ 
//
// Copyright 2009-2020 ECMWF.
// This software is licensed under the terms of the Apache Licence version 2.0 
// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
// In applying this licence, ECMWF does not waive the privileges and immunities 
// granted to it by virtue of its status as an intergovernmental organisation 
// nor does it submit to any jurisdiction. 
//
// Description :
//============================================================================

// Un-comment these for selective for debugging. At the moment because we have added
//            ast generation BOOST_SPIRIT_DEBUG is to verbose, making debugging a pain.
// Tpyically I will comment out ONLY BOOST_SPIRIT_DEBUG and enable all the others
//#define BOOST_SPIRIT_DEBUG
//#define BOOST_SPIRIT_DUMP_PARSETREE_AS_XML
//#define PRINT_TREE
//#define PRINT_AST_TRAVERSAL
//#define PRINT_AST

#include <boost/spirit/include/classic.hpp>
#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_actor.hpp>
#include <boost/spirit/include/classic_attribute.hpp>
#include <boost/spirit/include/classic_confix.hpp>
#include <boost/spirit/include/phoenix1_binders.hpp>
#include <boost/spirit/include/classic_ast.hpp>
#include <boost/spirit/include/classic_tree_to_xml.hpp>

#include "boost/lambda/lambda.hpp"
#include "boost/lambda/bind.hpp"
#include "boost/cast.hpp"

#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/trim.hpp>

#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <stack>
#include <sstream>
#include <map>

#include "ExprParser.hpp"
#include "ExprAst.hpp"
#include "ExprDuplicate.hpp"
#include "Indentor.hpp"
#include "Log.hpp"
#include "Str.hpp"
#include "NodeAttr.hpp"

// Reference
//‘*’   Zero or more
//‘!’   Zero or one
//‘+’   One or more
//‘>>’  Sequence/concatenation
//‘|’   alternate
//‘-‘   not

////////////////////////////////////////////////////////////////////////////
using namespace ecf;
using namespace std;
using namespace boost::spirit;
using namespace phoenix;
using namespace BOOST_SPIRIT_CLASSIC_NS;

/////////////////////////////////////////////////////////////////////////////
typedef tree_match<const char*>    treematch_t;
typedef treematch_t::tree_iterator tree_iter_t;

//////////////////////////////////////////////////////////////////////////////

struct ExpressionGrammer : public grammar<ExpressionGrammer>
{
   // The parser object is copied a lot, so only use reference/pointer variables as data members
   ExpressionGrammer()= default;
   ~ExpressionGrammer()= default;

   static const int integer_ID  = 1;
   static const int dot_path_ID = 2;
   static const int equal_1_ID  = 3;
   static const int equal_2_ID  = 4;
   static const int not_equal_1_ID = 5;
   static const int not_equal_2_ID = 6;
   static const int node_name_ID   = 7;
   static const int greater_equals_1_ID = 8;
   static const int greater_equals_2_ID = 9;
   static const int less_equals_1_ID   = 10;
   static const int less_equals_2_ID   = 11;
   static const int less_than_1_ID     = 12;
   static const int less_than_2_ID     = 13;
   static const int greater_than_1_ID  = 14;
   static const int greater_than_2_ID  = 15;
	static const int node_state_unknown_ID    = 16;

   static const int node_state_complete_ID   = 18;
   static const int node_state_queued_ID     = 19;
   static const int node_state_submitted_ID  = 20;
   static const int node_state_active_ID     = 21;
   static const int node_state_aborted_ID    = 22;

   static const int not1_ID           = 23;
   static const int not2_ID          = 24;
   static const int not3_ID          = 25;
   static const int and_ID           = 26;
   static const int or_ID            = 27;
   static const int dot_dot_path_ID     = 29;
   static const int base_trigger_ID     = 31;
   static const int sub_expression_ID   = 32;
   static const int node_path_state_ID  = 34;
   static const int absolute_path_ID    = 35;
	static const int event_state_ID      = 36;
   static const int variable_ID         = 37;
   static const int plus_ID      = 42;
   static const int minus_ID     = 43;
   static const int multiply_ID  = 44;
   static const int divide_ID    = 45;
   static const int modulo_ID    = 46;
   static const int calc_expression_ID    = 47;
   static const int calc_factor_ID    = 48;
   static const int calc_term_ID   = 49;
   static const int calc_grouping_ID   = 50;
   static const int calc_subexpression_ID   = 51;
   static const int basic_variable_path_ID   = 52;
   static const int compare_expression_ID   = 53;

   static const int cal_date_to_julian_ID   = 54;
   static const int cal_julian_to_date_ID   = 55;
   static const int cal_argument_ID         = 56;

   static const int flag_path_ID     = 57;
   static const int flag_late_ID     = 58;
   static const int flag_zombie_ID   = 59;
   static const int flag_archived_ID = 60;
   static const int flag_ID          = 61;
   static const int root_path_ID     = 62;
   static const int parent_variable_ID = 63;

    template <typename ScannerT>
    struct definition {
        rule<ScannerT,parser_tag<cal_date_to_julian_ID> > cal_date_to_julian;
        rule<ScannerT,parser_tag<cal_julian_to_date_ID> > cal_julian_to_date;
        rule<ScannerT,parser_tag<cal_argument_ID> > cal_argument;

        rule<ScannerT,parser_tag<integer_ID> > integer;
        rule<ScannerT,parser_tag<plus_ID> > plus;
        rule<ScannerT,parser_tag<minus_ID> > minus;
        rule<ScannerT,parser_tag<multiply_ID> > multiply;
        rule<ScannerT,parser_tag<divide_ID> > divide;
        rule<ScannerT,parser_tag<modulo_ID> > modulo;

        rule<ScannerT,parser_tag<equal_1_ID> > equal_1;
        rule<ScannerT,parser_tag<equal_2_ID> > equal_2;
        rule<ScannerT,parser_tag<not_equal_1_ID> > not_equal_1;
        rule<ScannerT,parser_tag<not_equal_2_ID> > not_equal_2;
        rule<ScannerT,parser_tag<node_name_ID> > nodename;

        rule<ScannerT,parser_tag<greater_equals_1_ID> > greater_equals_1;
        rule<ScannerT,parser_tag<greater_equals_2_ID> > greater_equals_2;
        rule<ScannerT,parser_tag<less_equals_1_ID> > less_equals_1;
        rule<ScannerT,parser_tag<less_equals_2_ID> > less_equals_2;

        rule<ScannerT,parser_tag<less_than_1_ID> > less_than_1;
        rule<ScannerT,parser_tag<less_than_2_ID> > less_than_2;
        rule<ScannerT,parser_tag<greater_than_1_ID> > greater_than_1;
        rule<ScannerT,parser_tag<greater_than_2_ID> > greater_than_2;

        rule<ScannerT,parser_tag<node_state_unknown_ID> >    node_state_unknown;
        rule<ScannerT,parser_tag<node_state_complete_ID> >   node_state_complete;
        rule<ScannerT,parser_tag<node_state_queued_ID> >     node_state_queued;
        rule<ScannerT,parser_tag<node_state_submitted_ID> >  node_state_submitted;
        rule<ScannerT,parser_tag<node_state_active_ID> >     node_state_active;
        rule<ScannerT,parser_tag<node_state_aborted_ID> >    node_state_aborted;

        rule<ScannerT,parser_tag<not1_ID> >   not1_r;
        rule<ScannerT,parser_tag<not2_ID> >   not2_r;
        rule<ScannerT,parser_tag<not3_ID> >   not3_r;
        rule<ScannerT,parser_tag<and_ID> >   and_r;
        rule<ScannerT,parser_tag<or_ID> >    or_r;

        rule<ScannerT,parser_tag<dot_path_ID> >      dotpath;
        rule<ScannerT,parser_tag<dot_dot_path_ID> >  dotdotpath;
        rule<ScannerT,parser_tag<absolute_path_ID> > absolutepath;
        rule<ScannerT,parser_tag<root_path_ID> > root_path;

        rule<ScannerT,parser_tag<node_path_state_ID> >  nodepathstate;

        rule<ScannerT,parser_tag<event_state_ID> >   event_state;
        rule<ScannerT,parser_tag<variable_ID> >      variable;

        rule<ScannerT,parser_tag<calc_expression_ID> >   calc_expression;
        rule<ScannerT,parser_tag<calc_factor_ID> >   calc_factor;
        rule<ScannerT,parser_tag<calc_term_ID> >   calc_term;
        rule<ScannerT,parser_tag<calc_grouping_ID> >   calc_grouping;
        rule<ScannerT,parser_tag<calc_subexpression_ID> >   calc_subexpression;
        rule<ScannerT,parser_tag<basic_variable_path_ID> >   basic_variable_path;
        rule<ScannerT,parser_tag<parent_variable_ID> >   parent_variable ;
        rule<ScannerT,parser_tag<compare_expression_ID> >   compare_expression;

        rule<ScannerT,parser_tag<flag_path_ID> >     flag_path;
        rule<ScannerT,parser_tag<flag_late_ID> >     flag_late;
        rule<ScannerT,parser_tag<flag_zombie_ID> >   flag_zombie;
        rule<ScannerT,parser_tag<flag_archived_ID> > flag_archived;
        rule<ScannerT,parser_tag<flag_ID> >   flag;

        rule<ScannerT> not_r,less_than_comparable,expression,andExpr,operators;
        rule<ScannerT> nodestate, equality_comparible, and_or, nodepath ;
  
        //        ‘*’   Zero or more
        //        ‘!’   Zero or one
        //        ‘+’   One or more
        //        ‘>>’  Sequence/concatenation
        //        ‘|’   alternate
        //        ‘-‘   not
        explicit definition(ExpressionGrammer const& /*self*/)
        {
          nodename
                = leaf_node_d[
                          lexeme_d [ (alnum_p || ch_p('_')) >> *(alnum_p || ch_p('_') || ch_p('.'))  ]
                             ]
                ;

          // Can be /suite/family/task ||  family/task ||   family

          absolutepath
             =    leaf_node_d[
                        !(str_p("/")) >> nodename
                                   >> *(
                                         + str_p("/")  >> nodename
                                       )
                             ]
            ;
          dotdotpath  // a kind of relative path
             =    leaf_node_d[
                           str_p("..")
                           >> *(
                                 + str_p("/") >> str_p("..")
                               )
                           >> +(
                                 + str_p("/")  >> nodename
                               )
                            ]
              ;

          dotpath = leaf_node_d[ str_p(".") >> +( str_p("/") >> nodename) ];
          nodepath =  absolutepath | dotdotpath | dotpath  ;

          // Integer is distinct from task/family names that are integers, since nodes with integer
          // names that occur in trigger/complete expression must have path ./0 ./1
          integer  =   leaf_node_d[  uint_p  ];
          plus = root_node_d [ str_p("+") ];
          minus = root_node_d [ str_p("-") ];
          divide = root_node_d [ str_p("/") ];
          multiply = root_node_d [ str_p("*") ];
          modulo = root_node_d [ str_p("%") ];
          operators = plus | minus | divide | multiply | modulo ;

          equal_1 = root_node_d [ str_p("==") ];
          equal_2 = root_node_d [ str_p("eq") ];
          not_equal_1 =  root_node_d [ str_p("!=") ] ;
          not_equal_2 = root_node_d [ str_p("ne") ];
          equality_comparible  = equal_1 | equal_2 | not_equal_2  | not_equal_1;

          greater_equals_1 = root_node_d [ str_p(">=") ];
          greater_equals_2 = root_node_d [ str_p("ge") ];
          less_equals_1 = root_node_d [ str_p("<=") ];
          less_equals_2 = root_node_d [ str_p("le") ];
          less_than_1 = root_node_d [ str_p("<") ];
          less_than_2 = root_node_d [ str_p("lt") ];
          greater_than_1 = root_node_d [ str_p(">") ];
          greater_than_2 = root_node_d [ str_p("gt") ];
          // Prioritise to most common first, to speed up parsing
          less_than_comparable
             =  greater_equals_2
                | less_equals_2
                | greater_than_2
                | less_than_2
                | greater_equals_1
                | less_equals_1
                | less_than_1
                | greater_than_1
              ;

          not1_r = root_node_d [ str_p("not ") ];
          not2_r = root_node_d [ str_p("~") ];
          not3_r = root_node_d [ str_p("!") ];
          not_r = not1_r | not3_r | not2_r;

          and_r = root_node_d [str_p("and")] || root_node_d [str_p("&&")] || root_node_d [str_p("AND")];
          or_r =  root_node_d [str_p("or") ] || root_node_d [str_p("||")] || root_node_d [str_p("OR")];
          and_or =  and_r | or_r;

          event_state = leaf_node_d[ str_p("set") ] || leaf_node_d[ str_p("clear")] ;

          node_state_unknown = root_node_d [ str_p("unknown") ];
          node_state_complete = root_node_d [ str_p("complete") ];
          node_state_queued = root_node_d [ str_p("queued") ];
          node_state_submitted = root_node_d [ str_p("submitted") ];
          node_state_active = root_node_d [ str_p("active") ];
          node_state_aborted = root_node_d [ str_p("aborted") ];
          nodestate
              =  node_state_complete
                | node_state_aborted
                | node_state_queued
                | node_state_active
                | node_state_submitted
                | node_state_unknown
                ;

          flag_late = root_node_d[ str_p("late") ];
          flag_zombie = root_node_d[ str_p("zombie") ];
          flag_archived = root_node_d[ str_p("archived") ];
          flag = flag_late | flag_zombie | flag_archived;

          variable = leaf_node_d [ nodename ];
          basic_variable_path = nodepath >> discard_node_d[ ch_p(':') ] >> variable ;
          parent_variable = ch_p(':') >> variable ; // if we discard_node, then we get just 'variable' and NOT parent_variable

          root_path = leaf_node_d[ (str_p("/")) ] ;
          flag_path = ( nodepath | root_path) >> discard_node_d[ str_p("<flag>") ] >> flag ;

          cal_argument = basic_variable_path | integer ;
          cal_date_to_julian
             = str_p("cal::date_to_julian")
                >>  discard_node_d[ ch_p('(')]
                >> cal_argument
                >> discard_node_d[ ch_p(')') ]
             ;
          cal_julian_to_date
             = str_p("cal::julian_to_date")
                >>  discard_node_d[ ch_p('(')]
                >> cal_argument
                >> discard_node_d[ ch_p(')') ]
             ;

          calc_factor
               = integer
                 | basic_variable_path
                 | discard_node_d[ ch_p('(') ] >>  calc_expression >> discard_node_d[ ch_p(')') ]
                 | flag_path
                 | parent_variable
                 | root_node_d[operators] >>  calc_factor
                 | cal_date_to_julian
                 | cal_julian_to_date
               ;
          calc_term
               = calc_factor
                   >> *( root_node_d[operators] >> calc_factor )
                 ;
          calc_expression
                 = calc_term
                 >> *( root_node_d[operators] >> calc_term )
                 ;

          nodepathstate = nodepath >> equality_comparible >> nodestate;

          compare_expression
                 = nodepathstate |
                   basic_variable_path >> equality_comparible >> event_state |
                   root_node_d[
                     calc_expression >> *(( equality_comparible | less_than_comparable) >> ( !not_r >> calc_expression))
                   ]
                 ;

          // We need to take special care so that 'and' has a higher priority than 'or'
          // (( This is done by have a custom rule for the and
          andExpr = !not_r >> compare_expression >> *(and_r >> !not_r >> compare_expression) ;

          calc_grouping = !not_r >> discard_node_d[ ch_p('(') ] >>  calc_subexpression >> discard_node_d[ ch_p(')') ];

          calc_subexpression = ( andExpr | calc_grouping ) >> *( (and_r | or_r) >> calc_subexpression) ;

          expression = calc_subexpression  >> end_p;

          BOOST_SPIRIT_DEBUG_NODE(cal_argument);
          BOOST_SPIRIT_DEBUG_NODE(cal_date_to_julian);
          BOOST_SPIRIT_DEBUG_NODE(andExpr);
          BOOST_SPIRIT_DEBUG_NODE(not_r);
          BOOST_SPIRIT_DEBUG_NODE(not1_r);
          BOOST_SPIRIT_DEBUG_NODE(not2_r);
          BOOST_SPIRIT_DEBUG_NODE(expression);
          BOOST_SPIRIT_DEBUG_NODE(nodename);
          BOOST_SPIRIT_DEBUG_NODE(nodepath);
          BOOST_SPIRIT_DEBUG_NODE(dotdotpath);
          BOOST_SPIRIT_DEBUG_NODE(dotpath);
          BOOST_SPIRIT_DEBUG_NODE(absolutepath);
          BOOST_SPIRIT_DEBUG_NODE(less_than_comparable);
          BOOST_SPIRIT_DEBUG_NODE(nodestate);
          BOOST_SPIRIT_DEBUG_NODE(equality_comparible);
          BOOST_SPIRIT_DEBUG_NODE(equal_1);
          BOOST_SPIRIT_DEBUG_NODE(equal_2);
          BOOST_SPIRIT_DEBUG_NODE(not_equal_1);
          BOOST_SPIRIT_DEBUG_NODE(not_equal_2);
          BOOST_SPIRIT_DEBUG_NODE(and_or);
          BOOST_SPIRIT_DEBUG_NODE(plus);
          BOOST_SPIRIT_DEBUG_NODE(minus);
          BOOST_SPIRIT_DEBUG_NODE(divide);
          BOOST_SPIRIT_DEBUG_NODE(multiply);
          BOOST_SPIRIT_DEBUG_NODE(modulo);
          BOOST_SPIRIT_DEBUG_NODE(nodepathstate);
          BOOST_SPIRIT_DEBUG_NODE(integer);
          BOOST_SPIRIT_DEBUG_NODE(event_state);
          BOOST_SPIRIT_DEBUG_NODE(variable);
          BOOST_SPIRIT_DEBUG_NODE(basic_variable_path);
          BOOST_SPIRIT_DEBUG_NODE(parent_variable);
          BOOST_SPIRIT_DEBUG_NODE(flag_path);
          BOOST_SPIRIT_DEBUG_NODE(calc_factor);
          BOOST_SPIRIT_DEBUG_NODE(calc_expression);
          BOOST_SPIRIT_DEBUG_NODE(calc_term);
          BOOST_SPIRIT_DEBUG_NODE(compare_expression);
        };

        rule<ScannerT> const& start() const { return expression; }
    };
};

/////////////////////////////////////////////////////////////////////////////////////////////

void print(tree_parse_info<> info,
           const std::string& expr,
           const std::map< parser_id, std::string >& rule_names);

AstTop* createTopAst(   tree_parse_info< > info,
               const std::string& expr,
					const std::map< parser_id, std::string >& rule_names,
					std::string& error_msg);

/////////////////////////////////////////////////////////////////////////////////////////////

ExprParser::ExprParser(  const std::string& expression ): expr_(expression) {}

static std::map< parser_id, std::string > rule_names;
static void populate_rule_names()
{
   if (rule_names.empty()) {
      rule_names[ExpressionGrammer::cal_date_to_julian_ID] = "cal_date_to_julian";
      rule_names[ExpressionGrammer::cal_julian_to_date_ID] = "cal_julian_to_date";
      rule_names[ExpressionGrammer::cal_argument_ID] = "cal_argument";
      rule_names[ExpressionGrammer::modulo_ID] = "MODULO";
      rule_names[ExpressionGrammer::equal_1_ID] = "EQUALS";
      rule_names[ExpressionGrammer::equal_2_ID] = "EQUALS";
      rule_names[ExpressionGrammer::not_equal_1_ID] = "NOT_EQUAL";
      rule_names[ExpressionGrammer::not_equal_2_ID] = "NOT_EQUAL";
      rule_names[ExpressionGrammer::greater_equals_1_ID] = "GREATER_THAN_OR_EQUALS";
      rule_names[ExpressionGrammer::greater_equals_2_ID] = "GREATER_THAN_OR_EQUALS";
      rule_names[ExpressionGrammer::less_equals_1_ID] = "LESS_THAN_OR_EQUALS";
      rule_names[ExpressionGrammer::less_equals_2_ID] = "LESS_THAN_OR_EQUALS";
      rule_names[ExpressionGrammer::less_than_1_ID] = "LESS_THAN";
      rule_names[ExpressionGrammer::less_than_2_ID] = "LESS_THAN";
      rule_names[ExpressionGrammer::greater_than_1_ID] = "GREATER_THAN";
      rule_names[ExpressionGrammer::greater_than_2_ID] = "GREATER_THAN";
      rule_names[ExpressionGrammer::not1_ID ] = "NOT";
      rule_names[ExpressionGrammer::not2_ID ] = "NOT";
      rule_names[ExpressionGrammer::not3_ID ] = "NOT";
      rule_names[ExpressionGrammer::and_ID ] = "AND";
      rule_names[ExpressionGrammer::or_ID ] = "OR";
      rule_names[ExpressionGrammer::node_name_ID] = "NODE_NAME";
      rule_names[ExpressionGrammer::node_state_unknown_ID ] = "UNKNOWN";
      rule_names[ExpressionGrammer::node_state_complete_ID ] = "COMPLETE";
      rule_names[ExpressionGrammer::node_state_queued_ID ] = "QUEUED";
      rule_names[ExpressionGrammer::node_state_submitted_ID ] = "SUBMITTED";
      rule_names[ExpressionGrammer::node_state_active_ID ] = "ACTIVE";
      rule_names[ExpressionGrammer::node_state_aborted_ID ] = "ABORTED";
      rule_names[ExpressionGrammer::integer_ID] = "INTEGER";
      rule_names[ExpressionGrammer::dot_path_ID ] = "DOT_PATH";
      rule_names[ExpressionGrammer::dot_dot_path_ID ] = "DOT_DOT_PATH";
      rule_names[ExpressionGrammer::absolute_path_ID ] = "ABSOLUTE_PATH";

      rule_names[ExpressionGrammer::base_trigger_ID  ] = "BASE_TRIGGER";
      rule_names[ExpressionGrammer::sub_expression_ID  ] = "SUB_EXPRESSION";
      rule_names[ExpressionGrammer::node_path_state_ID  ] = "NODE_PATH_STATE";
      rule_names[ExpressionGrammer::flag_path_ID  ] = "FLAG_PATH";

      rule_names[ExpressionGrammer::event_state_ID  ]   = "STRING";
      rule_names[ExpressionGrammer::variable_ID  ]      = "VARIABLE";

      rule_names[ExpressionGrammer::calc_expression_ID  ] = "calc_expression_ID";
      rule_names[ExpressionGrammer::calc_factor_ID  ] = "calc_factor_ID";
      rule_names[ExpressionGrammer::calc_term_ID  ] = "calc_term_ID";
      rule_names[ExpressionGrammer::calc_grouping_ID  ] = "calc_grouping_ID";
      rule_names[ExpressionGrammer::calc_subexpression_ID  ] = "calc_subexpression_ID";
      rule_names[ExpressionGrammer::basic_variable_path_ID  ] = "basic_variable_path_ID";
      rule_names[ExpressionGrammer::parent_variable_ID  ] = "parent_variable_ID";
      rule_names[ExpressionGrammer::compare_expression_ID  ] = "compare_expression_ID";
   }
}

bool ExprParser::doParse(std::string& errorMsg)
{
   if (expr_.empty()) {
      errorMsg = "Expression is empty";
      return false;
   }

   // =========================================================================
   // For large designs > 90% of triggers are identical.
   // We take advantage of this by only parsing the expression once
   // and storing then Abstract Syntax tree( via cloning ) using a map
   // This saves a huge amount of CPU time in re-parsing using spirit classic.
   // =========================================================================
   ast_ = ExprDuplicate::find(expr_);
   if (ast_.get()) {
      return true;
   }

   SimpleExprParser simpleParser(expr_);
   if (simpleParser.doParse()) {
      ast_ = simpleParser.ast();
      ExprDuplicate::add(expr_,ast_.get());  // bypass spirit if same expression used
      return true;
   }

   // SPIRIT CLASSIC parsing: very slooooow....
   ExpressionGrammer grammer;
   BOOST_SPIRIT_DEBUG_NODE(grammer);

   // Use parser that generates a abstract syntax tree
   tree_parse_info<> info = ast_parse( expr_.c_str(), grammer, space_p);
   if (info.full) {

      populate_rule_names();

#if defined(BOOST_SPIRIT_DUMP_PARSETREE_AS_XML)
      tree_to_xml( cout, info.trees, expr_.c_str(), rule_names );
#endif
#if defined(PRINT_TREE)
      print(info,expr_,rule_names);
#endif
      // Spirit has created a AST for us. However it is not use able as is
      // we will traverse the AST and create our OWN
 		ast_.reset( createTopAst(info,expr_,rule_names,errorMsg) );
      if (ast_.get() && errorMsg.empty()) {
        ExprDuplicate::add(expr_,ast_.get());
      }
      return errorMsg.empty();
   }
   else {
      std::stringstream ss;
      ss << "Parsing failed\n";
      ss << "length = " << std::dec << info.length << "\n";
      ss << "stopped at: \": " << info.stop << "\"\n";
      errorMsg = ss.str();
   }
   return false;
}


// The evaluation function for the AST
void do_print(const tree_iter_t& i,  const std::map< parser_id, std::string >& rule_names)
{
   Indentor in;
   auto iter = rule_names.find(i->value.id());
   if (iter != rule_names.end()) {
      Indentor::indent(cout) << "Rule " << (*iter).second << "(size:" << i->children.size() << ")"
         << "  " << string( i->value.begin(), i->value.end() ) << endl;
   }
   else {
      Indentor::indent(cout) << "Unknown rule(id:" << i->value.id().to_long() << ")" << "(size:" << i->children.size() << ")"
         << "  " << string( i->value.begin(), i->value.end() ) <<  endl;
   }

   Indentor in2;
   for (auto t = i->children.begin(); t != i->children.end(); ++t) { do_print( t, rule_names );  }
}

void print(tree_parse_info<> info,
           const std::string& expr,
           const std::map< parser_id, std::string >& rule_names)
{
   std::cout << "\nPRINT_TREE  " << expr << "\n";
   do_print(info.trees.begin(),rule_names);
}

////////////////////////////////////////////////////////////////////////////////////////////////

AstRoot* createRootNode(const tree_iter_t& i,  const std::map< parser_id, std::string >& rule_names)
{
#if defined(PRINT_AST_TRAVERSAL)
   Indentor in;
   std::map< parser_id, std::string >::const_iterator iter = rule_names.find(i->value.id());
   if (iter != rule_names.end()) {
      Indentor::indent(cout) << "Root Rule " << (*iter).second << "(" << i->children.size() << ")"
         << "  " << string( i->value.begin(), i->value.end() ) << endl;
   }
   else {
      Indentor::indent(cout) << "Unknown root rule " << "(" << i->children.size() << ")"
         << "  " << string( i->value.begin(), i->value.end() ) <<  endl;
   }
#endif

   if ( i->value.id() == ExpressionGrammer::equal_1_ID ) return new AstEqual();
   if ( i->value.id() == ExpressionGrammer::equal_2_ID ) return new AstEqual();
   if ( i->value.id() == ExpressionGrammer::and_ID ) return new AstAnd();
   if ( i->value.id() == ExpressionGrammer::or_ID ) return new AstOr();
   // Needed so that testing , when recreating expression uses same name for not.
   if ( i->value.id() == ExpressionGrammer::not1_ID ) { auto* astnot = new AstNot(); astnot->set_root_name("not "); return astnot;}
   if ( i->value.id() == ExpressionGrammer::not2_ID ) { auto* astnot = new AstNot(); astnot->set_root_name("~ "); return astnot;}
   if ( i->value.id() == ExpressionGrammer::not3_ID ) { auto* astnot = new AstNot(); astnot->set_root_name("! "); return astnot;}
   if ( i->value.id() == ExpressionGrammer::plus_ID ) return new AstPlus();

   if ( i->value.id() == ExpressionGrammer::not_equal_1_ID ) return new AstNotEqual();
   if ( i->value.id() == ExpressionGrammer::not_equal_2_ID ) return new AstNotEqual();
   if ( i->value.id() == ExpressionGrammer::greater_equals_1_ID ) return new AstGreaterEqual();
   if ( i->value.id() == ExpressionGrammer::greater_equals_2_ID ) return new AstGreaterEqual();
   if ( i->value.id() == ExpressionGrammer::less_equals_1_ID ) return new AstLessEqual();
   if ( i->value.id() == ExpressionGrammer::less_equals_2_ID ) return new AstLessEqual();
   if ( i->value.id() == ExpressionGrammer::less_than_1_ID ) return new AstLessThan();
   if ( i->value.id() == ExpressionGrammer::less_than_2_ID ) return new AstLessThan();
   if ( i->value.id() == ExpressionGrammer::greater_than_1_ID ) return new AstGreaterThan();
   if ( i->value.id() == ExpressionGrammer::greater_than_2_ID ) return new AstGreaterThan();

   if ( i->value.id() == ExpressionGrammer::minus_ID ) return new AstMinus();
   if ( i->value.id() == ExpressionGrammer::multiply_ID ) return new AstMultiply();
   if ( i->value.id() == ExpressionGrammer::divide_ID ) return new AstDivide();
   if ( i->value.id() == ExpressionGrammer::modulo_ID ) return new AstModulo();
   LOG_ASSERT(false,"");
   return nullptr;
}

//static bool is_node_state(const tree_iter_t& i) {
//   if ( i->value.id() == ExpressionGrammer::node_state_unknown_ID  ) return true;
//   if ( i->value.id() == ExpressionGrammer::node_state_complete_ID  ) return true;
//   if ( i->value.id() == ExpressionGrammer::node_state_queued_ID  ) return true;
//   if ( i->value.id() == ExpressionGrammer::node_state_submitted_ID  ) return true;
//   if ( i->value.id() == ExpressionGrammer::node_state_active_ID  ) return true;
//   if ( i->value.id() == ExpressionGrammer::node_state_aborted_ID  ) return true;
//   return false;
//}
//static bool has_child_node_state(const tree_iter_t& i) {
//   for (tree_iter_t t = i->children.begin(); t != i->children.end(); ++t)  {
//      if (is_node_state(t)) return true;
//   }
//   return false;
//}
//static bool has_child_event_state(const tree_iter_t& i) {
//   for (tree_iter_t t = i->children.begin(); t != i->children.end(); ++t)  {
//      if ( t->value.id() == ExpressionGrammer::event_state_ID) return true;
//   }
//   return false;
//}
//static bool child_has_path(const tree_iter_t& i) {
//   for (tree_iter_t t = i->children.begin(); t != i->children.end(); ++t)  {
//      if ( t->value.id() == ExpressionGrammer::dot_path_ID ) return true;
//      if ( t->value.id() == ExpressionGrammer::dot_dot_path_ID ) return true;
//      if ( t->value.id() == ExpressionGrammer::absolute_path_ID ) return true;
//   }
//   return false;
//}
//static bool is_comparable(const tree_iter_t& i) {
//   if ( i->value.id() == ExpressionGrammer::equal_1_ID ) return true;
//   if ( i->value.id() == ExpressionGrammer::equal_2_ID ) return true;
//   if ( i->value.id() == ExpressionGrammer::not_equal_1_ID ) return true;
//   if ( i->value.id() == ExpressionGrammer::not_equal_2_ID ) return true;
//   return false;
//}
//static bool has_child_not(const tree_iter_t& i) {
//   for (tree_iter_t t = i->children.begin(); t != i->children.end(); ++t)  {
//      if (is_not(t)) return true;
//   }
//   return false;
//}

static bool is_not(const tree_iter_t& i) {
   if ( i->value.id() == ExpressionGrammer::not1_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::not2_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::not3_ID ) return true;
   return false;
}
static bool child_has(const tree_iter_t& i, int id) {
   for (auto & t : i->children)  {
      if ( t.value.id() == id) return true;
   }
   return false;
}
static bool is_root_node(const tree_iter_t& i){
   if ( i->value.id() == ExpressionGrammer::equal_1_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::equal_2_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::and_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::or_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::not1_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::not2_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::not3_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::plus_ID ) return true;

   if ( i->value.id() == ExpressionGrammer::not_equal_1_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::not_equal_2_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::greater_equals_1_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::greater_equals_2_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::less_equals_1_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::less_equals_2_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::less_than_1_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::less_than_2_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::greater_than_1_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::greater_than_2_ID ) return true;

   if ( i->value.id() == ExpressionGrammer::minus_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::multiply_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::divide_ID ) return true;
   if ( i->value.id() == ExpressionGrammer::modulo_ID ) return true;
   return false;
}

Ast* createAst( const tree_iter_t& i, const std::map< parser_id, std::string >& rule_names ) {
#if defined(PRINT_AST_TRAVERSAL)
   Indentor in;
   std::map< parser_id, std::string >::const_iterator iter = rule_names.find(i->value.id());
   if (iter != rule_names.end()) {
      Indentor::indent(cout) << "Create AST Rule " << (*iter).second << "(" << i->children.size() << ")"
         << "  '" << string( i->value.begin(), i->value.end() ) << "'\n";
   }
   else {
      Indentor::indent(cout) << "Create AST Unknown rule " << "(" << i->children.size() << ")"
         << "  '" << string( i->value.begin(), i->value.end() ) <<  "'\n";
   }
#endif

   if ( i->value.id() == ExpressionGrammer::node_name_ID) {

      string thevalue( i->value.begin(), i->value.end() );
      boost::algorithm::trim(thevalue); // don't know why we get leading/trailing spaces
      LOG_ASSERT( !thevalue.empty(), "" );
      return new AstNode( thevalue );
   }
   else if ( i->value.id() == ExpressionGrammer::node_state_complete_ID) {

      return new AstNodeState( DState::COMPLETE );
   }
   else if ( i->value.id() == ExpressionGrammer::basic_variable_path_ID) {
      LOG_ASSERT((i->children.size() == 2), "");
      auto theNodePathIter = i->children.begin();
      auto theNameIter = i->children.begin()+1;

      string nodePath( theNodePathIter->value.begin(), theNodePathIter->value.end() );
      string name( theNameIter->value.begin(), theNameIter->value.end() );
      boost::algorithm::trim(nodePath); // don't know why we get leading/trailing spaces
      boost::algorithm::trim(name);     // don't know why we get leading/trailing spaces
      return new AstVariable( nodePath, name );
   }
   else if ( i->value.id() == ExpressionGrammer::parent_variable_ID) {

       // tree_iter_t the_colon = i->children.begin(); ignore
       auto the_variable_t = i->children.begin()+1;

       string the_variable(the_variable_t->value.begin(),the_variable_t->value.end() );
       boost::algorithm::trim(the_variable ); // don't know why we get leading/trailing spaces
       LOG_ASSERT( !the_variable.empty() , "");
       return new AstParentVariable( the_variable  );
    }
   else if ( i->value.id() == ExpressionGrammer::dot_dot_path_ID) {

      string thevalue( i->value.begin(), i->value.end() );
      boost::algorithm::trim(thevalue); // don't know why we get leading/trailing spaces
      LOG_ASSERT( !thevalue.empty() , "");
      return new AstNode( thevalue );
   }
   if ( i->value.id() == ExpressionGrammer::absolute_path_ID) {

      string thevalue( i->value.begin(), i->value.end() );
      boost::algorithm::trim(thevalue); // don't know why we get leading/trailing spaces
      LOG_ASSERT( !thevalue.empty() ,"");
      return new AstNode( thevalue );
   }
   else if ( i->value.id() == ExpressionGrammer::dot_path_ID) {

      string thevalue( i->value.begin(), i->value.end() );
      boost::algorithm::trim(thevalue); // don't know why we get leading/trailing spaces
      LOG_ASSERT( !thevalue.empty() , "");
      return new AstNode( thevalue );
   }
	else if ( i->value.id() == ExpressionGrammer::event_state_ID) {

      string thevalue( i->value.begin(), i->value.end() );
      boost::algorithm::trim(thevalue); // don't know why we get leading/trailing spaces
      if ( thevalue == Event::SET()) return new AstEventState(true);
      assert(thevalue == Event::CLEAR());
      return new AstEventState(false);
   }
   else if ( i->value.id() == ExpressionGrammer::integer_ID) {

      string thevalue( i->value.begin(), i->value.end() );
      boost::algorithm::trim(thevalue); // don't know why we get leading/trailing spaces
      auto theInt = boost::lexical_cast<int>(thevalue);
      return new AstInteger(theInt);
   }
   else if ( i->value.id() == ExpressionGrammer::node_state_aborted_ID) {

      return new AstNodeState( DState::ABORTED );
   }
   else if ( i->value.id() == ExpressionGrammer::node_state_active_ID) {

      return new AstNodeState( DState::ACTIVE );
   }
   else if ( i->value.id() == ExpressionGrammer::node_state_queued_ID) {

      return new AstNodeState( DState::QUEUED );
   }
   else if ( i->value.id() == ExpressionGrammer::node_state_submitted_ID) {

      return new AstNodeState( DState::SUBMITTED );
   }
   else if ( i->value.id() == ExpressionGrammer::node_state_unknown_ID) {

      return new AstNodeState( DState::UNKNOWN );
   }
   else if ( i->value.id() == ExpressionGrammer::cal_date_to_julian_ID) {

      LOG_ASSERT((i->children.size() == 2), ""); // 1st is function, 2nd is arg(integer | basic variable path)

      return new AstFunction(AstFunction::DATE_TO_JULIAN, createAst( i->children.begin()+1 , rule_names)  );
   }
   else if ( i->value.id() == ExpressionGrammer::cal_julian_to_date_ID) {

      LOG_ASSERT((i->children.size() == 2), ""); // 1st is function, 2nd is arg(integer | basic variable path)

      return new AstFunction(AstFunction::JULIAN_TO_DATE, createAst( i->children.begin()+1 , rule_names)  );
   }
   else if ( i->value.id() == ExpressionGrammer::flag_path_ID) {

      LOG_ASSERT((i->children.size() == 2), "");
      auto theNodePathIter = i->children.begin();
      auto theFlagIter = i->children.begin()+1;

      string nodePath( theNodePathIter->value.begin(), theNodePathIter->value.end() );
      string flag( theFlagIter->value.begin(), theFlagIter->value.end() );
      boost::algorithm::trim(nodePath); // don't know why we get leading/trailing spaces
      boost::algorithm::trim(flag);     // don't know why we get leading/trailing spaces

      return new AstFlag( nodePath, ecf::Flag::string_to_flag_type(flag) );
   }
   return nullptr;
}

// The evaluation function for the AST
Ast* doCreateAst(  const tree_iter_t& i,
                   const std::map< parser_id, std::string >& rule_names,
                   Ast* top)
{
#if defined(PRINT_AST_TRAVERSAL)
   Indentor in;
   std::map< parser_id, std::string >::const_iterator iter = rule_names.find(i->value.id());
   if (iter != rule_names.end()) {
      Indentor::indent(cout) << "Rule " << (*iter).second << " (" << i->children.size() << ")"
         << "  " << string( i->value.begin(), i->value.end() ) << endl;
   }
   else {
      Indentor::indent(cout) << "Unknown rule " << "(" << i->children.size() << ")"
         << "  " << string( i->value.begin(), i->value.end() ) <<  endl;
   }
#endif

   Indentor in2;
   if (i->children.size() == 3) {
      // child 1: left                0
      // child 2: root(i.e ==,!=)    +1
      // child 3: right              +2

      //cout << "i->children.size() == 3 ======================== \n";
      AstRoot* someRoot = createRootNode(  i->children.begin() + 1, rule_names  );
      if (someRoot ) {
         Ast* left  = doCreateAst(  i->children.begin(), rule_names, someRoot);
         if (left) someRoot->addChild(left);
         Ast* right = doCreateAst(  i->children.begin() + 2, rule_names,  someRoot);
         if (right) someRoot->addChild(right);

         if (top) top->addChild(someRoot);
         else return someRoot;
      }
   }
   else if ( is_root_node(i) && i->children.size() == 2) {

      //cout << "is_root_node(i) && i->children.size() == 2\n";
      AstRoot* someRoot = createRootNode(  i, rule_names  );

      Ast* left  = doCreateAst(  i->children.begin() , rule_names    ,someRoot  );
      if (left) someRoot->addChild(left);
      Ast* right = doCreateAst(  i->children.begin() + 1, rule_names , someRoot  );
      if (right) someRoot->addChild(right);

      if (top) top->addChild(someRoot);
      else return someRoot;
   }
   else if (i->children.size() == 4 && is_not(i->children.begin())) {
      // child 0: notRoot                 0
      // child 1: notChild               +1
      // child 2: someRoot(i.e ==,!=)    +2
      // child 3: right                  +3
      // Create as:         someRoot
      //              notRoot          right
      //      notChild
      //cout << "(i->children.size() == 4 && is_not(i->children.begin()))\n";

      LOG_ASSERT(is_not(i->children.begin()),"");
      AstRoot* notRoot = createRootNode(  i->children.begin(), rule_names  );

      Ast* notChild = doCreateAst(  i->children.begin() + 1, rule_names, notRoot/*top*/  );
      if (notChild) notRoot->addChild(notChild);

      AstRoot* someRoot = createRootNode(  i->children.begin() + 2, rule_names  );
      someRoot->addChild(notRoot); //left

      Ast* right = doCreateAst(  i->children.begin() + 3, rule_names,  someRoot/* top*/ );
      if (right) someRoot->addChild(right);
      if (top) top->addChild(someRoot);
      else return someRoot;
   }
   else if (i->children.size() == 4 && is_root_node(i->children.begin()+1) && is_not(i->children.begin()+2)  ) {
      // child 0: child                   0
      // child 1: someRoot(i.e ==,!=)    +1
      // child 2: NOT                    +2
      // child 3: integer | variable     +3
      //
      // Create as:         someRoot
      //              child          notRoot
      //                                    notChild
      //cout << "(i->children.size() == 4 && is_root_node(i->children.begin()+1) && is_not(i->children.begin()+2)  )\n";
      AstRoot* someRoot = createRootNode(  i->children.begin()+1, rule_names  );
      Ast* varPath = doCreateAst(  i->children.begin(), rule_names, someRoot/*top*/);
      if (varPath) someRoot->addChild(varPath); //left

      AstRoot* notRoot = createRootNode(  i->children.begin()+2, rule_names  );
      someRoot->addChild(notRoot); //right

      Ast* notChild = doCreateAst(  i->children.begin() + 3, rule_names, notRoot/*top*/);
      if (notChild) notRoot->addChild(notChild);

      if (top) top->addChild(someRoot);
      else return someRoot;
   }
   else if (i->children.size() == 2 && is_not(i->children.begin()) ) {
      // child 1: not     0
      // child 2: left   +1
      //cout << "(i->children.size() == 2 && is_not(i->children.begin()) )\n";

      AstRoot* notRoot = createRootNode(  i->children.begin(), rule_names  );

      if (child_has(i,ExpressionGrammer::basic_variable_path_ID)) {
         // special case where we treat as a event i.e ! ../../../prod2diss/operation_is_late:yes
         notRoot->addChild( createAst(i->children.begin() + 1, rule_names));
      }
      else {
         Ast* left  = doCreateAst(  i->children.begin() + 1, rule_names, notRoot);
         if (left) notRoot->addChild(left);
      }

      if (top) top->addChild(notRoot);
      else return notRoot;
   }
   else if (i->children.size() >=5 ) {
      // Must be multiple and's could have nots  Could have:
      //     !a and b
      //      a and !b
      // We always treat the not as *child*
      //cout << "(i->children.size() >=5 ) \n";
      stack<Ast*> childs;
      stack<Ast*> parents;
      Ast* not_ast = nullptr;
      for (auto t = i->children.begin(); t != i->children.end(); ++t) {
         if (is_root_node(t) && !is_not(t)) {
            Ast* and_ast = createRootNode(  t, rule_names  );
            assert(and_ast);
            assert(parents.empty());
            parents.push( and_ast );
         }
         else {
            if ( is_not(t)) {
                assert(!not_ast);
                not_ast = createRootNode(  t, rule_names  );
                assert(not_ast);
                childs.push( not_ast );
            }
            else {
               Ast* child_ast = doCreateAst(t, rule_names, /*Top*/nullptr);
               assert(child_ast);
               if (not_ast) {
                  not_ast->addChild(child_ast);
                  not_ast = nullptr;
               }
               else {
                  childs.push(child_ast );
               }
            }
         }

         if (parents.size() == 1 && childs.size() == 2) {
            Ast* parent = parents.top(); parents.pop();
            Ast* child1 = childs.top(); childs.pop();
            Ast* child2 = childs.top(); childs.pop();
            parent->addChild(child2);
            parent->addChild(child1);
            assert( parents.empty() && childs.empty());
            childs.push(parent);
         }
      }
      assert(top);
      assert( childs.size() == 1);
      if (top) top->addChild(childs.top());
   }
   else {
      //cout << "JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ\n";
      Ast* child = createAst(i,rule_names);
      if (top && child) top->addChild(child);
      else return child;
   }
   return nullptr;
}

AstTop* createTopAst(
      tree_parse_info< > info,
      const std::string& expr,
      const std::map< parser_id, std::string >& rule_names,
      std::string& error_msg
)
{
#if defined(PRINT_AST_TRAVERSAL)
   std::cout << "\nPRINT_AST_TRAVERSAL  " << expr << "\n";
#endif

   std::unique_ptr<AstTop> ast = std::make_unique<AstTop>();
   (void)doCreateAst(info.trees.begin(),rule_names,ast.get() );

   if (!ast->is_valid_ast(error_msg)) {
      return nullptr;
   }

#if defined(PRINT_AST)
   if (ast.get())  {
      std::stringstream s2;
      ast->print_flat(s2,true/*add_brackets*/);
      std::cout << "\nPRINT_AST  " << s2.str() << "\n";
      std::cout   << "PRINT_AST  " << expr     << "\n";
      std::cout << *ast.get();
   }
#endif
   return ast.release();
}

///////////////////////////////////////////////////////////////////////////
// SimpleExprParser
//////////////////////////////////////////////////////////////////////////

bool has_complex_expressions(const std::string& expr)
{
   // we allow . and /
   if (expr.find('(') != string::npos) return true;
   if (expr.find(':') != string::npos) return true;
   if (expr.find('.') != string::npos) return true;
   if (expr.find('/') != string::npos) return true;
   if (expr.find(" not ") != string::npos) return true;
   if (expr.find(" and ") != string::npos) return true;
   if (expr.find(" or ") != string::npos) return true;
   if (expr.find('!') != string::npos) return true;
   if (expr.find("&&") != string::npos) return true;
   if (expr.find("||") != string::npos) return true;
   if (expr.find('<') != string::npos) return true;
   if (expr.find('>') != string::npos) return true;
   if (expr.find('+') != string::npos) return true;
   if (expr.find('-') != string::npos) return true;
   if (expr.find('*') != string::npos) return true;
   if (expr.find('~') != string::npos) return true;
   if (expr.find(" ne ") != string::npos) return true;
   if (expr.find(" ge ") != string::npos) return true;
   if (expr.find("<=") != string::npos) return true;
   if (expr.find(">=") != string::npos) return true;
   if (expr.find(" le ") != string::npos) return true;
   if (expr.find(" gt ") != string::npos) return true;
   if (expr.find(" lt ") != string::npos) return true;
   return false;
}

bool SimpleExprParser::doParse()
{
   // 3199.def 57Mg
   // a == b, || a eq b i.e simple  76940
   // complex                       39240
   if (has_complex_expressions(expr_)) {
      return false;
   }

   // look for path == <state> || path eq state
   // This gets round issue with Str::split:
   //   "a = complete" will be split
   // since will split on *ANY* of character, and hence this would not be reported as an error
   std::vector<std::string> tokens;
   if (expr_.find("==") != string::npos) {
      Str::split(expr_, tokens, "==");
   }
   else if (expr_.find(" eq ") != string::npos) {
      Str::split(expr_, tokens, " eq ");
   }
   else {
      return false;
   }

   if (tokens.size() == 2) {

      boost::algorithm::trim(tokens[0]);
      boost::algorithm::trim(tokens[1]);

      if (tokens[0].find(' ') != string::npos) {
//         cout << "Found space " << expr_ << "\n";
         return false;
      }

      if (DState::isValid(tokens[1])) {

         ast_ = std::make_unique<AstTop>();
         Ast* someRoot = new AstEqual();
         someRoot->addChild(new AstNode(tokens[0]));
         someRoot->addChild(new AstNodeState(DState::toState(tokens[1])));
         ast_->addChild(someRoot);
//       cout << "simple expr : " << expr_ << " `" << tokens[0] << "' = '" << tokens[1] << "'\n";
         return true;
      }
      else {
         try {
            auto left = boost::lexical_cast<int>(tokens[0]);
            auto right = boost::lexical_cast<int>(tokens[1]);
            ast_ = std::make_unique<AstTop>();
            Ast* someRoot = new AstEqual();
            someRoot->addChild(new AstInteger(left));
            someRoot->addChild(new AstInteger(right));
            ast_->addChild(someRoot);
//               cout << "simple INT expr : " << expr_ << " `" << tokens[0] << "' = '" << tokens[1] << "'\n";
            return true;
         }
         catch ( boost::bad_lexical_cast& e ) {
//            cout << "simple INT FAILED expr : " << expr_ << " `" << tokens[0] << "' = '"
//                     << tokens[1] << "'\n";
         }
      }
   }
   return false;
}
