text: /*=============================================================================
      Copyright (c) 2001-2011 Joel de Guzman
  
      Distributed under the Boost Software License, Version 1.0. (See accompanying
      file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  =============================================================================*/
  #include "compiler.hpp"
  #include "vm.hpp"
  #include <boost/foreach.hpp>
  #include <boost/variant/apply_visitor.hpp>
  #include <boost/assert.hpp>
  #include <boost/lexical_cast.hpp>
  #include <set>
  
  namespace client { namespace code_gen
  {
      void program::op(int a)
      {
          code.push_back(a);
      }
  
      void program::op(int a, int b)
      {
          code.push_back(a);
          code.push_back(b);
      }
  
      void program::op(int a, int b, int c)
      {
          code.push_back(a);
          code.push_back(b);
          code.push_back(c);
      }
  
      int const* program::find_var(std::string const& name) const
      {
          std::map<std::string, int>::const_iterator i = variables.find(name);
          if (i == variables.end())
              return 0;
          return &i->second;
      }
  
      void program::add_var(std::string const& name)
      {
          std::size_t n = variables.size();
          variables[name] = n;
      }
  
      void program::print_variables(std::vector<int> const& stack) const
      {
          typedef std::pair<std::string, int> pair;
          BOOST_FOREACH(pair const& p, variables)
          {
              std::cout << "    " << p.first << ": " << stack[p.second] << std::endl;
          }
      }
  
      void program::print_assembler() const
      {
          std::vector<int>::const_iterator pc = code.begin();
  
          std::vector<std::string> locals(variables.size());
          typedef std::pair<std::string, int> pair;
          BOOST_FOREACH(pair const& p, variables)
          {
              locals[p.second] = p.first;
              std::cout << "      local       "
                  << p.first << ", @" << p.second << std::endl;
          }
  
          std::map<std::size_t, std::string> lines;
          std::set<std::size_t> jumps;
  
          while (pc != code.end())
          {
              std::string line;
              std::size_t address = pc - code.begin();
  
              switch (*pc++)
              {
                  case op_neg:
                      line += "      op_neg";
                      break;
  
                  case op_not:
                      line += "      op_not";
                      break;
  
                  case op_add:
                      line += "      op_add";
                      break;
  
                  case op_sub:
                      line += "      op_sub";
                      break;
  
                  case op_mul:
                      line += "      op_mul";
                      break;
  
                  case op_div:
                      line += "      op_div";
                      break;
  
                  case op_eq:
                      line += "      op_eq";
                      break;
  
                  case op_neq:
                      line += "      op_neq";
                      break;
  
                  case op_lt:
                      line += "      op_lt";
                      break;
  
                  case op_lte:
                      line += "      op_lte";
                      break;
  
                  case op_gt:
                      line += "      op_gt";
                      break;
  
                  case op_gte:
                      line += "      op_gte";
                      break;
  
                  case op_and:
                      line += "      op_and";
                      break;
  
                  case op_or:
                      line += "      op_or";
                      break;
  
                  case op_load:
                      line += "      op_load     ";
                      line += locals[*pc++];
                      break;
  
                  case op_store:
                      line += "      op_store    ";
                      line += locals[*pc++];
                      break;
  
                  case op_int:
                      line += "      op_int      ";
                      line += boost::lexical_cast<std::string>(*pc++);
                      break;
  
                  case op_true:
                      line += "      op_true";
                      break;
  
                  case op_false:
                      line += "      op_false";
                      break;
  
                  case op_jump:
                      {
                          line += "      op_jump     ";
                          std::size_t pos = (pc - code.begin()) + *pc++;
                          if (pos == code.size())
                              line += "end";
                          else
                              line += boost::lexical_cast<std::string>(pos);
                          jumps.insert(pos);
                      }
                      break;
  
                  case op_jump_if:
                      {
                          line += "      op_jump_if  ";
                          std::size_t pos = (pc - code.begin()) + *pc++;
                          if (pos == code.size())
                              line += "end";
                          else
                              line += boost::lexical_cast<std::string>(pos);
                          jumps.insert(pos);
                      }
                      break;
  
                  case op_stk_adj:
                      line += "      op_stk_adj  ";
                      line += boost::lexical_cast<std::string>(*pc++);
                      break;
              }
              lines[address] = line;
          }
  
          std::cout << "start:" << std::endl;
          typedef std::pair<std::size_t, std::string> line_info;
          BOOST_FOREACH(line_info const& l, lines)
          {
              std::size_t pos = l.first;
              if (jumps.find(pos) != jumps.end())
                  std::cout << pos << ':' << std::endl;
              std::cout << l.second << std::endl;
          }
  
          std::cout << "end:" << std::endl;
      }
  
      bool compiler::operator()(unsigned int x) const
      {
          program.op(op_int, x);
          return true;
      }
  
      bool compiler::operator()(bool x) const
      {
          program.op(x ? op_true : op_false);
          return true;
      }
  
      bool compiler::operator()(ast::variable const& x) const
      {
          int const* p = program.find_var(x.name);
          if (p == 0)
          {
              std::cout << x.id << std::endl;
              error_handler(x.id, "Undeclared variable: " + x.name);
              return false;
          }
          program.op(op_load, *p);
          return true;
      }
  
      bool compiler::operator()(ast::operation const& x) const
      {
          if (!boost::apply_visitor(*this, x.operand_))
              return false;
          switch (x.operator_)
          {
              case ast::op_plus: program.op(op_add); break;
              case ast::op_minus: program.op(op_sub); break;
              case ast::op_times: program.op(op_mul); break;
              case ast::op_divide: program.op(op_div); break;
  
              case ast::op_equal: program.op(op_eq); break;
              case ast::op_not_equal: program.op(op_neq); break;
              case ast::op_less: program.op(op_lt); break;
              case ast::op_less_equal: program.op(op_lte); break;
              case ast::op_greater: program.op(op_gt); break;
              case ast::op_greater_equal: program.op(op_gte); break;
  
              case ast::op_and: program.op(op_and); break;
              case ast::op_or: program.op(op_or); break;
              default: BOOST_ASSERT(0); return false;
          }
          return true;
      }
  
      bool compiler::operator()(ast::unary const& x) const
      {
          if (!boost::apply_visitor(*this, x.operand_))
              return false;
          switch (x.operator_)
          {
              case ast::op_negative: program.op(op_neg); break;
              case ast::op_not: program.op(op_not); break;
              case ast::op_positive: break;
              default: BOOST_ASSERT(0); return false;
          }
          return true;
      }
  
      bool compiler::operator()(ast::expression const& x) const
      {
          if (!boost::apply_visitor(*this, x.first))
              return false;
          BOOST_FOREACH(ast::operation const& oper, x.rest)
          {
              if (!(*this)(oper))
                  return false;
          }
          return true;
      }
  
      bool compiler::operator()(ast::assignment const& x) const
      {
          if (!(*this)(x.rhs))
              return false;
          int const* p = program.find_var(x.lhs.name);
          if (p == 0)
          {
              std::cout << x.lhs.id << std::endl;
              error_handler(x.lhs.id, "Undeclared variable: " + x.lhs.name);
              return false;
          }
          program.op(op_store, *p);
          return true;
      }
  
      bool compiler::operator()(ast::variable_declaration const& x) const
      {
          int const* p = program.find_var(x.assign.lhs.name);
          if (p != 0)
          {
              std::cout << x.assign.lhs.id << std::endl;
              error_handler(x.assign.lhs.id, "Duplicate variable: " + x.assign.lhs.name);
              return false;
          }
          bool r = (*this)(x.assign.rhs);
          if (r) // don't add the variable if the RHS fails
          {
              program.add_var(x.assign.lhs.name);
              program.op(op_store, *program.find_var(x.assign.lhs.name));
          }
          return r;
      }
  
      bool compiler::operator()(ast::statement const& x) const
      {
          return boost::apply_visitor(*this, x);
      }
  
      bool compiler::operator()(ast::statement_list const& x) const
      {
          BOOST_FOREACH(ast::statement const& s, x)
          {
              if (!(*this)(s))
                  return false;
          }
          return true;
      }
  
      bool compiler::operator()(ast::if_statement const& x) const
      {
          if (!(*this)(x.condition))
              return false;
          program.op(op_jump_if, 0);                  // we shall fill this (0) in later
          std::size_t skip = program.size()-1;        // mark its position
          if (!(*this)(x.then))
              return false;
          program[skip] = program.size()-skip;        // now we know where to jump to (after the if branch)
  
          if (x.else_)                                // We got an alse
          {
              program[skip] += 2;                     // adjust for the "else" jump
              program.op(op_jump, 0);                 // we shall fill this (0) in later
              std::size_t exit = program.size()-1;    // mark its position
              if (!(*this)(*x.else_))
                  return false;
              program[exit] = program.size()-exit;    // now we know where to jump to (after the else branch)
          }
  
          return true;
      }
  
      bool compiler::operator()(ast::while_statement const& x) const
      {
          std::size_t loop = program.size();          // mark our position
          if (!(*this)(x.condition))
              return false;
          program.op(op_jump_if, 0);                  // we shall fill this (0) in later
          std::size_t exit = program.size()-1;        // mark its position
          if (!(*this)(x.body))
              return false;
          program.op(op_jump,
              int(loop-1) - int(program.size()));     // loop back
          program[exit] = program.size()-exit;        // now we know where to jump to (to exit the loop)
          return true;
      }
  
      bool compiler::start(ast::statement_list const& x) const
      {
          program.clear();
          // op_stk_adj 0 for now. we'll know how many variables we'll have later
          program.op(op_stk_adj, 0);
  
          if (!(*this)(x))
          {
              program.clear();
              return false;
          }
          program[1] = program.nvars(); // now store the actual number of variables
          return true;
      }
  }}
  
  