Download | Plain Text | Line Numbers


/**
 * @module cprogram
 * @author Guenther Neuwirth (0626638), Manuel Mausz (0728348)
 * @brief  CProgram extends std::vector and adds a method for parsing programfile
 * @date   26.05.2009
 */
 
#ifndef CPROGRAM_H
#define CPROGRAM_H 1
 
#include <vector>
#include <set>
#include <map>
#include <stdexcept>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/split.hpp>
#ifdef DEBUG
# include <iostream>
# include <iomanip>
#endif
 
/**
 * @class CProgramError
 *
 * Exception thrown by implemententations of CProgram
 */
class CProgramError
 : public std::invalid_argument
{
  public:
    /**
     * @method CProgramError
     * @brief  Default exception ctor
     * @param  what  message to pass along
     * @return -
     * @globalvars none
     * @exception  none
     * @pre  none
     * @post none
     */
    CProgramError(const std::string& what)
      : std::invalid_argument(what)
    {}
};
 
#include "cinstruction.h"
#include "instructions.h"
 
/* forward declare CInstruction */
template <class T>
class CInstruction;
 
/**
 * @class CProgram
 *
 * CProgram extends std::vector and adds a method for parsing
 * programfile. This adds instances of CInstruction to CProgram itself.
 */
template <class T>
class CProgram
  : public std::vector<CInstruction<T> *>
{
  typedef typename std::set<CInstruction<T> *>::iterator setiterator;
  typedef std::vector<CInstruction<T> *> super;
  typedef typename super::iterator iterator;
  using super::begin;
  using super::end;
  using super::size;
 
  public:
    /**
     * @method CProgram
     * @brief  Default ctor
     * @param  -
     * @return -
     * @globalvars none
     * @exception  none
     * @pre  none
     * @post none
     */
    CProgram();
 
    /**
     * @method ~CProgram
     * @brief  Default dtor
     * @param  -
     * @return -
     * @globalvars none
     * @exception  none
     * @pre  none
     * @post none
     */
    ~CProgram();
 
    /**
     * @method getLabels
     * @brief  get reference to labels map
     * @param  -
     * @return reference to labels map
     * @globalvars none
     * @exception  none
     * @pre  none
     * @post none
     */
    const std::map<std::string, unsigned>& getLabels() const
    {
      return m_labels;
    }
 
    /**
     * @method findLabel
     * @brief  search for label
     * @param  label  name of label to search for
     * @return index of found label in program
     * @globalvars none
     * @exception  CProgramError
     * @pre  none
     * @post none
     */
    unsigned findLabel(const std::string& label) const;
 
    /**
     * @method compile
     * @brief  create instructions from parsing stream
     * @param  in  inputstream to read from
     * @return void
     * @globalvars none
     * @exception  CProgramError
     * @pre  none
     * @post none
     */
    void compile(std::istream& in);
 
#if DEBUG
    /**
     * @method dump
     * @brief  dumps contents to outputstream
     * @param  out  outputstream to write to
     * @return void
     * @globalvars none
     * @exception  none
     * @pre  none
     * @post none
     */
    void dump(std::ostream& out);
#endif
 
  private:
    /* members */
    /** set of known instructions */
    std::set<CInstruction<T> *> m_instrset;
    std::map<std::string, unsigned> m_labels;
};
 
/*----------------------------------------------------------------------------*/
 
template <class T>
CProgram<T>::CProgram()
{
  m_instrset.insert(new CInstructionInc<T>);
  m_instrset.insert(new CInstructionDec<T>);
  m_instrset.insert(new CInstructionAdd<T>);
  m_instrset.insert(new CInstructionSub<T>);
  m_instrset.insert(new CInstructionMul<T>);
  m_instrset.insert(new CInstructionDiv<T>);
  m_instrset.insert(new CInstructionLoad<T>);
  m_instrset.insert(new CInstructionStore<T>);
  m_instrset.insert(new CInstructionTest<T>);
  m_instrset.insert(new CInstructionLabel<T>);
  m_instrset.insert(new CInstructionJumpA<T>);
  m_instrset.insert(new CInstructionJumpZ<T>);
  m_instrset.insert(new CInstructionJumpS<T>);
  m_instrset.insert(new CInstructionWrite<T>);
}
 
/*----------------------------------------------------------------------------*/
 
template <class T>
CProgram<T>::~CProgram()
{
  /* free instruction set */
  for (setiterator it = m_instrset.begin(); it != m_instrset.end(); ++it)
    delete *it;
 
  /* free instruction */
  for (iterator it2 = begin(); it2 != end(); ++it2)
    delete *it2;
}
 
/*----------------------------------------------------------------------------*/
 
template <class T>
void CProgram<T>::compile(std::istream& in)
{
  if (!in.good())
    return;
 
  std::string line;
  unsigned i = 0;
  while (!in.eof() && in.good())
  {
    ++i;
 
    /* read stream per line */
    std::getline(in, line);
    if (line.empty())
      continue;
 
    boost::trim(line);
    boost::to_lower(line);
 
    /* ignore comments */
    if (line.find_first_of('#') == 0)
      continue;
 
    /* get instruction name */
    size_t pos = line.find_first_of(' ');
    std::string instrname(line.substr(0, pos));
 
    /* search and create instruction */
    CInstruction<T> *instrptr = NULL;
    setiterator it;
    for (it = m_instrset.begin(); it != m_instrset.end(); ++it)
    {
      if (*(*it) == instrname)
      {
        instrptr = *it;
        break;
      }
    }
    if (instrptr == NULL)
    {
      std::stringstream sstr;
      sstr << "Unknown instruction '" << instrname << "' on line " << i << ".";
      throw CProgramError(sstr.str());
    }
 
    /* create instruction */
    CInstruction<T> *instr = instrptr->factory();
 
    /* parse instruction parameters */
    std::string params = (pos == std::string::npos) ? "" : line.substr(pos + 1);
    boost::trim(params);
    std::list<std::string> instrparams;
    boost::split(instrparams, params, boost::is_any_of(", \t"), boost::token_compress_on);
 
    /* let instruction parse the parameters. catch+throw exception */
    try
    {
      /* handle label instruction ourself, but still add a dummy instruction */
      if (instrname == "label")
      {
        if (instrparams.size() != 1)
          throw CInstructionError("Invalid paramater count - must be 1");
        std::string label(instrparams.front());
        if (label.length() < 2 || label[ label.length() - 1] != ':')
          throw CInstructionError("Label has invalid syntax");
        m_labels[ label.substr(0, label.length() - 1) ] = size();
      }
      instr->compile(instrparams);
    }
    catch(CInstructionError& ex)
    {
      std::stringstream sstr;
      sstr << "Unable to compile instruction '" << instrname
           << "' (line " << i << "): " << ex.what();
      throw CProgramError(sstr.str());
    }
 
    push_back(instr);
  }
}
 
/*----------------------------------------------------------------------------*/
 
template <class T>
unsigned CProgram<T>::findLabel(const std::string& label) const
{
  std::map<std::string, unsigned>::const_iterator it;
  it = m_labels.find(label);
  if (it == m_labels.end())
    throw CProgramError("Unknown label '" + label + "'");
  return it->second;
}
 
/*----------------------------------------------------------------------------*/
 
#if DEBUG
template <class T>
void CProgram<T>::dump(std::ostream& out)
{
  out << "[PROGRAM DUMP]" << std::endl;
  unsigned i = 0;
  for(iterator it = begin(); it != end(); ++it)
  {
    out << "[" << std::setw(4) << std::setfill('0') << i << "]  "
        << *(*it) << std::endl;
    ++i;
  }
}
#endif
 
#endif
 
/* vim: set et sw=2 ts=2: */