Click here to Skip to main content
15,882,055 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
i want my parse.ccp to calculate an expression which will have an interger,floating point, binary and hexacimal values like.

e.g

when i include this expression it works :
radius = 456 sin( pi / 2 ) + pi * radius *radius + 4500
but when i include this it will give me error:
radius = 456 sin( pi / 2 ) + pi * radius *radius + 0x1F - 1100b

this is my parse.cpp file
C++
#include "parser.h"

// returns a number from 0 up to, but excluding x
const int getrandom (const int x)
{
	if (x <= 0)
		return 0;

	// r will be between 0 and 1 (but below 1 as we are dividing by RAND_MAX+1)
	double r = static_cast<double> (std::rand () % RAND_MAX) / (static_cast<double> (RAND_MAX) + 1.0);
	return floor (r * x);

}   // end of getrandom

const int roll (const int howmany, const int die)
{
	int count;
	int total = 0;

	for (count = 0; count < howmany; ++count)
		total += getrandom (die) + 1;

	return total;

} // end of roll


// returns true if a x% probability exists
// eg. percent (80) will be true 80% of the time
const bool percent (const int prob)
  {
  if (prob <= 0)
    return false;
  if (prob >= 100)
    return true;

  return getrandom (100) > (100 - prob);

  }

static int initRandom ()
  {
  srand   (time (NULL));
#ifndef WIN32
  srand48 (time (NULL));
#endif
  return 0;
  }

// initialise random number generator
static int someNumber = initRandom ();



// functions we can call from an expression

double DoInt (double arg)
  {
  return (int) arg;   // drop fractional part
  }
double toInteger(char c)
{
    if (c >= '0' && c <= '9') return c -'0';
    if (c >= 'a' && c <= 'f') return c -'a' + 0xa;
    if (c >= 'A' && c <= 'F') return c -'A' + 0xa;
    double noDigit = 0xf + 1;
    return noDigit;
}

double DoRandom (double arg)
  {
  return getrandom (static_cast <int> (arg));  // random number in range 0 to arg
  }

double DoPercent (double arg)
  {
  if (percent (static_cast <int> (arg)))  // true x% of the time
    return 1.0;
  else
    return 0.0;
  }

const double DoMin (const double arg1, const double arg2)
  {
  return (arg1 < arg2 ? arg1 : arg2);
  }

const double DoMax (const double arg1, const double arg2)
  {
  return (arg1 > arg2 ? arg1 : arg2);
  }

const double DoFmod (const double arg1, const double arg2)
  {
  if (arg2 == 0.0)
    throw std::runtime_error ("Divide by zero in mod");

  return fmod (arg1, arg2);
  }

const double DoPow (const double arg1, const double arg2)
  {
  return pow (arg1, arg2);
  }

const double DoRoll (const double arg1, const double arg2)
  {
  return roll (static_cast <int> (arg1), static_cast <int> (arg2));
  }

const double DoIf (const double arg1, const double arg2, const double arg3)
  {
  if (arg1 != 0.0)
    return arg2;
  else
    return arg3;
  }

typedef double (*OneArgFunction)  (double arg);
typedef const double (*TwoArgFunction)  (const double arg1, const double arg2);
typedef const double (*ThreeArgFunction)  (const double arg1, const double arg2, const double arg3);

// maps of function names to functions
static std::map<std::string, OneArgFunction>    OneArgumentFunctions;
static std::map<std::string, TwoArgFunction>    TwoArgumentFunctions;
static std::map<std::string, ThreeArgFunction>  ThreeArgumentFunctions;

// for standard library functions
#define STD_FUNCTION(arg) OneArgumentFunctions [#arg] = arg

static int LoadOneArgumentFunctions ()
  {
  OneArgumentFunctions ["abs"] = fabs;
  STD_FUNCTION (acos);
  STD_FUNCTION (asin);
  STD_FUNCTION (atan);
#ifndef WIN32   // doesn't seem to exist under Visual C++ 6
  STD_FUNCTION (atanh);
#endif
  //STD_FUNCTION (ceil);
  STD_FUNCTION (cos);
  //STD_FUNCTION (cosh);
  STD_FUNCTION (exp);
  STD_FUNCTION (exp);
 // STD_FUNCTION (floor);
  STD_FUNCTION (log);
  STD_FUNCTION (log10);
  STD_FUNCTION (sin);
  //STD_FUNCTION (sinh);
  STD_FUNCTION (sqrt);
  STD_FUNCTION (tan);
  //STD_FUNCTION (tanh);

  OneArgumentFunctions ["int"] = DoInt;
  OneArgumentFunctions ["rand"] = DoRandom;
  OneArgumentFunctions ["rand"] = DoRandom;
  OneArgumentFunctions ["percent"] = DoPercent;
  return 0;
  } // end of LoadOneArgumentFunctions

static int LoadTwoArgumentFunctions ()
  {
  TwoArgumentFunctions ["min"]  = DoMin;
  TwoArgumentFunctions ["max"]  = DoMax;
  TwoArgumentFunctions ["mod"]  = DoFmod;
  TwoArgumentFunctions ["pow"]  = DoPow;     //   x to the power y
  TwoArgumentFunctions ["roll"] = DoRoll;   // dice roll
  return 0;
  } // end of LoadTwoArgumentFunctions

static int LoadThreeArgumentFunctions ()
  {
  ThreeArgumentFunctions ["if"]  = DoIf;
  return 0;
  } // end of LoadThreeArgumentFunctions

const Parser::TokenType Parser::GetToken (const bool ignoreSign)
  {
  word_.erase (0, std::string::npos);

  // skip spaces
  while (*pWord_ && isspace (*pWord_))
    ++pWord_;

  pWordStart_ = pWord_;   // remember where word_ starts *now*

  // look out for unterminated statements and things
  if (*pWord_ == 0 &&  // we have EOF
      type_ == END)  // after already detecting it
    throw std::runtime_error ("Unexpected end of expression.");

  unsigned char cFirstCharacter = *pWord_;        // first character in new word_

  if (cFirstCharacter == 0)    // stop at end of file
    {
    word_ = "<end of expression>";
    return type_ = END;
    }

  unsigned char cNextCharacter  = *(pWord_ + 1);  // 2nd character in new word_

  // look for number
  // can be: + or - followed by a decimal point
  // or: + or - followed by a digit
  // or: starting with a digit
  // or: decimal point followed by a digit
  if ((!ignoreSign &&
	   (cFirstCharacter == '+' || cFirstCharacter == '-') &&
	   (isdigit (cNextCharacter) || cNextCharacter == '.')
	   )
	  || isdigit (cFirstCharacter)
	  // allow decimal numbers without a leading 0. e.g. ".5"
	  
	  || (cFirstCharacter == '.' && isdigit (cNextCharacter)) )
	  {
    // skip sign for now
    if ((cFirstCharacter == '+' || cFirstCharacter == '-'))
      pWord_++;
    while (isdigit (*pWord_) || *pWord_ == '.')
      pWord_++;

    // allow for 1.53158e+15
    if (*pWord_ == 'e' || *pWord_ == 'E')
      {
      pWord_++; // skip 'e'
      if ((*pWord_  == '+' || *pWord_  == '-'))
        pWord_++; // skip sign after e
      while (isdigit (*pWord_))  // now digits after e
        pWord_++;
      }

    word_ = std::string (pWordStart_, pWord_ - pWordStart_);

    std::istringstream is (word_);
    // parse std::string into double value
    is >> value_;

    if (is.fail () && !is.eof ())
      throw std::runtime_error ("Bad numeric literal: " + word_);
    return type_ = NUMBER;
    }   // end of number found

  // special test for 2-character sequences: <= >= == !=
  // also +=, -=, /=, *=
  if (cNextCharacter == '=')
    {
    switch (cFirstCharacter)
      {
      // comparisons
      case '=': type_ = EQ;   break;
      case '<': type_ = LE;   break;
      case '>': type_ = GE;   break;
      case '!': type_ = NE;   break;
      // assignments
      case '+': type_ = ASSIGN_ADD;   break;
      case '-': type_ = ASSIGN_SUB;   break;
      case '*': type_ = ASSIGN_MUL;   break;
      case '/': type_ = ASSIGN_DIV;   break;
      // none of the above
      default:  type_ = NONE; break;
      } // end of switch on cFirstCharacter

    if (type_ != NONE)
      {
      word_ = std::string (pWordStart_, 2);
      pWord_ += 2;   // skip both characters
      return type_;
      } // end of found one
    } // end of *=

  switch (cFirstCharacter)
    {
    case '&': if (cNextCharacter == '&')    // &&
                {
                word_ = std::string (pWordStart_, 2);
                pWord_ += 2;   // skip both characters
                return type_ = AND;
                }
              break;
   case '|': if (cNextCharacter == '|')   // ||
                {
                word_ = std::string (pWordStart_, 2);
                pWord_ += 2;   // skip both characters
                return type_ = OR;
                }
              break;
    // single-character symboles
    case '=':
    case '<':
    case '>':
    case '+':
    case '-':
    case '/':
    case '*':
    case '(':
    case ')':
    case ',':
    case '!':
      word_ = std::string (pWordStart_, 1);
      ++pWord_;   // skip it
      return type_ = TokenType (cFirstCharacter);
    } // end of switch on cFirstCharacter

  if (!isalpha (cFirstCharacter))
    {
    if (cFirstCharacter < ' ')
      {
      std::ostringstream s;
      s << "Unexpected character (decimal " << int (cFirstCharacter) << ")";
      throw std::runtime_error (s.str ());
      }
    else
      throw std::runtime_error ("Unexpected character: " + std::string (1, cFirstCharacter));
    }

  // we have a word (starting with A-Z) - pull it out
  while (isalnum (*pWord_) || *pWord_ == '_')
    ++pWord_;

  word_ = std::string (pWordStart_, pWord_ - pWordStart_);
  return type_ = NAME;
  }   // end of Parser::GetToken

// force load of functions at static initialisation time
static int doLoadOneArgumentFunctions = LoadOneArgumentFunctions ();
static int doLoadTwoArgumentFunctions = LoadTwoArgumentFunctions ();
static int doLoadThreeArgumentFunctions = LoadThreeArgumentFunctions ();

const double Parser::Primary (const bool get)   // primary (base) tokens
  {

  if (get)
    GetToken ();    // one-token lookahead

  switch (type_)
    {
    case NUMBER:
      {
      double v = value_;
      GetToken (true);  // get next one (one-token lookahead)
      return v;
      }

    case NAME:
      {
      std::string word = word_;
      GetToken (true);
      if (type_ == LHPAREN)
        {
        // might be single-argument function (eg. abs (x) )
        std::map<std::string, OneArgFunction>::const_iterator si;
        si = OneArgumentFunctions.find (word);
        if (si != OneArgumentFunctions.end ())
          {
          double v = Expression (true);   // get argument
          CheckToken (RHPAREN);
          GetToken (true);        // get next one (one-token lookahead)
          return si->second (v);  // evaluate function
          }

        // might be double-argument function (eg. roll (6, 2) )
        std::map<std::string, TwoArgFunction>::const_iterator di;
        di = TwoArgumentFunctions.find (word);
        if (di != TwoArgumentFunctions.end ())
          {
          double v1 = Expression (true);   // get argument 1 (not commalist)
          CheckToken (COMMA);
          double v2 = Expression (true);   // get argument 2 (not commalist)
          CheckToken (RHPAREN);
          GetToken (true);            // get next one (one-token lookahead)
          return di->second (v1, v2); // evaluate function
          }

       // might be double-argument function (eg. roll (6, 2) )
        std::map<std::string, ThreeArgFunction>::const_iterator ti;
        ti = ThreeArgumentFunctions.find (word);
        if (ti != ThreeArgumentFunctions.end ())
          {
          double v1 = Expression (true);   // get argument 1 (not commalist)
          CheckToken (COMMA);
          double v2 = Expression (true);   // get argument 2 (not commalist)
          CheckToken (COMMA);
          double v3 = Expression (true);   // get argument 3 (not commalist)
          CheckToken (RHPAREN);
          GetToken (true);  // get next one (one-token lookahead)
          return ti->second (v1, v2, v3); // evaluate function
          }

        throw std::runtime_error ("Function '" + word + "' not implemented.");
        }

      // not a function? must be a symbol in the symbol table
      double & v = symbols_ [word];  // get REFERENCE to symbol table entry
      // change table entry with expression? (eg. a = 22, or a = 22)
      switch (type_)
        {
        // maybe check for NaN or Inf here (see: isinf, isnan functions)
        case ASSIGN:     v  = Expression (true); break;
        case ASSIGN_ADD: v += Expression (true); break;
        case ASSIGN_SUB: v -= Expression (true); break;
        case ASSIGN_MUL: v *= Expression (true); break;
        case ASSIGN_DIV:
            {
            double d = Expression (true);
            if (d == 0.0)
              throw std::runtime_error ("Divide by zero");
            v /= d;
            break;   // change table entry with expression
            } // end of ASSIGN_DIV
        default: break;   // do nothing for others
        } // end of switch on type_
      return v;               // and return new value
      }

    case MINUS:               // unary minus
      return - Primary (true);

    case NOT:   // unary not
      return (Primary (true) == 0.0) ? 1.0 : 0.0;;

    case LHPAREN:
      {
      double v = CommaList (true);    // inside parens, you could have commas
      CheckToken (RHPAREN);
      GetToken (true);                // eat the )
      return v;
      }

    default:
      throw std::runtime_error ("Unexpected token: " + word_);

    } // end of switch on type

  } // end of Parser::Primary

const double Parser::Term (const bool get)    // multiply and divide
  {
  double left = Primary (get);
  while (true)
    {
    switch (type_)
      {
      case MULTIPLY:
        left *= Primary (true); break;
      case DIVIDE:
          {
          double d = Primary (true);
          if (d == 0.0)
            throw std::runtime_error ("Divide by zero");
          left /= d;
          break;
          }
      default:    return left;
      } // end of switch on type
    }   // end of loop
  } // end of Parser::Term

const double Parser::AddSubtract (const bool get)  // add and subtract
  {
  double left = Term (get);
  while (true)
    {
    switch (type_)
      {
      case PLUS:  left += Term (true); break;
      case MINUS: left -= Term (true); break;
      default:    return left;
      } // end of switch on type
    }   // end of loop
  } // end of Parser::AddSubtract

const double Parser::Comparison (const bool get)  // LT, GT, LE, EQ etc.
  {
  double left = AddSubtract (get);
  while (true)
    {
    switch (type_)
      {
      case LT:  left = left <  AddSubtract (true) ? 1.0 : 0.0; break;
      case GT:  left = left >  AddSubtract (true) ? 1.0 : 0.0; break;
      case LE:  left = left <= AddSubtract (true) ? 1.0 : 0.0; break;
      case GE:  left = left >= AddSubtract (true) ? 1.0 : 0.0; break;
      case EQ:  left = left == AddSubtract (true) ? 1.0 : 0.0; break;
      case NE:  left = left != AddSubtract (true) ? 1.0 : 0.0; break;
           default:    return left;
      } // end of switch on type
    }   // end of loop
  } // end of Parser::Comparison

const double Parser::Expression (const bool get)  // AND and OR
  {
  double left = Comparison (get);
  while (true)
    {
    switch (type_)
      {
      case AND:
            {
            double d = Comparison (true);   // don't want short-circuit evaluation
            left = (left != 0.0) && (d != 0.0);
            }
          break;
      case OR:
            {
            double d = Comparison (true);   // don't want short-circuit evaluation
            left = (left != 0.0) || (d != 0.0);
            }
          break;
      default:    return left;
      } // end of switch on type
    }   // end of loop
  } // end of Parser::Expression

const double Parser::CommaList (const bool get)  // expr1, expr2
  {
  double left = Expression (get);
  while (true)
    {
    switch (type_)
      {
      case COMMA:  left = Expression (true); break; // discard previous value
      default:    return left;
      } // end of switch on type
    }   // end of loop
  } // end of Parser::CommaList

const double Parser::Evaluate ()  // get result
  {
  pWord_    = program_.c_str ();
  type_     = NONE;
  double v = CommaList (true);
  if (type_ != END)
    throw std::runtime_error ("Unexpected text at end of expression: " + std::string (pWordStart_));
  return v;
  }

// change program and evaluate it
const double Parser::Evaluate (const std::string & program)  // get result
  {
  program_  = program;
  return Evaluate ();
  }
<


this is my main.cpp
<pre>#include "parser.h"

#include <iostream>
#include <fstream>
#include <string.h>
using namespace std;

static const std::string SPACES = " \t\r\n";           // what gets removed when we trim

// get rid of leading and trailing spaces from a std::string
const std::string trim (const std::string & sInput, const std::string & t = SPACES)
{
  std::string s = sInput;
  std::string::size_type i = s.find_last_not_of (t);
  if (i == std::string::npos)
    return "";
  else
   return s.erase (i + 1).erase (0, sInput.find_first_not_of (t)) ;
} // end of trim

// remove backspaces from a std::string
const std::string removeBackspaces (const std::string & sInput)
  {
  std::string s = sInput;
  std::string::size_type i;
  while ((i = s.find ('\b')) != std::string::npos)
    if (i == 0)
      s.erase (0, 1);  // backspace at start, just erase it
    else
      s.erase (i - 1, 2);   // erase character before backspace (and backspace)
  return s;
  }     // end of removeBackspaces

int main ()

  {
  string inputLine;
  ifstream file ("input.txt");
  ofstream file1;
       file1.open("output.txt");


  //try
    //{
    while (std::getline (file, inputLine))
      {
       if( strncmp( "----", inputLine.c_str(), 4 ) == 0 )
               continue;
      inputLine = trim (removeBackspaces (inputLine));

    

      // create instance of parser, using entered input line
      Parser p (inputLine);

      // example of pre-loading a symbol
      p ["abc"] = 42;

      // parse entered expression, return result into "value
      double value = p.Evaluate ();

      // display result

       file1 <<std::cout << "Result = " << value << std::endl;
       double abc = p ["abc"];
       
       file1<<std::cout<<"----------------------"<<endl;




    

    } // end of try block
}

please guys help as this assignment is killing me.
this is my example in the input file
----
radius = 456
sin( pi / 2 ) + pi * radius *radius + 0x1F - 1100b

----
10 * (20 +45)
----
2+3

What I have tried:

i have write a parse.cpp to calculate the expressions in the text.cpp.
Posted
Updated 20-Dec-18 10:17am
v3
Comments
Richard MacCutchan 16-Dec-18 4:03am    
Please remove all the redundant code from your question and just show the part that parses the values.

Just one thing I noticed:
C++
double DoInt (double arg)
  {
  return (int) arg;   // drop fractional part
  }

You have defined DoInt as returning a double value, but you actually return an int. And the following method
double toInteger(char c)<br />
returns different types.

C++
const int getrandom (const int x)

actually returns a double.

I think you have a lot of checking to do in that code.
 
Share this answer
 
v2
Richard is definitely correct. There are some weird things that should be straightened out.

I think you have to revise GetToken() to accept leading "0x" and interpret the token as hexadecimal and trailing 'b', maybe 'B' also, and interpret the token as binary. That whole function should probably be re-written. It needs to find the end of token boundary in a different way. It gets a little tricky when doubles and exponential notation are thrown into the mix because the decimal point and possible negative sign are not part of a single token. Typically, a standard variable token can have alpha-numeric characters plus the underscore. For literals the rule is different and varies according to type such as integers can't have a decimal point. I recommend coming up with a parser that takes these rules into account but works in a bit different way. First separate out the token, based on boundaries and delimiters, and then interpret it.
 
Share this answer
 
Comments
Leigh Omar Alpha 18-Dec-18 14:53pm    
please can you help me get the token recognizing hexadecimal value and convert it to decimal or float. A struggle all the past days and cannot get solutions. please guys i need your help
Rick York 18-Dec-18 14:59pm    
Look for the 0x prefix of it. You can obtain its value using strtoul() and pass it a base of 0 if you pass in the string with 0x. If you don't pass in the "0x" part then you need to call it with a base of 16.

Think about how the compiler works. It determines the type of a token by looking for various prefix and suffix characters. A trailing f means a float value. A leading 0x means hex and leading 0 (alone) means octal. A decimal point means it's a double. If you don't want to support exponential notation then you can simplify the work considerably.
Leigh Omar Alpha 18-Dec-18 15:52pm    
A code illustration or example will be very helpful Rick York . I am new to C++ please Rick.
Rick York 18-Dec-18 16:26pm    
I don't have any examples that would be helpful. There are many, many expression evaluator samples on this site. I recommend taking a look at a few so you can see how they went about their parsing. The ones I have made are probably very clumsy in comparison.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900