Leigh Omar Alpha Ответов: 2

Я хочу свою parse.cpp файл для вычисления выражения в моем входном файле , который содержит десятичные, двоичные и шестнадцатеричные числа?


я хочу,чтобы мой parse.ccp вычислял выражение, которое будет иметь интергер, плавающую точку, двоичные и шестнадцатеричные значения, такие как.

е.г

когда я включаю это выражение оно работает :
радиус = 456 sin( pi / 2 ) + pi * radius *radius + 4500
но когда я включу это, это даст мне ошибку:
радиус = 456 sin( pi / 2 ) + pi * radius *radius + 0x1F - 1100b

это мое parse.cpp файл
#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
}

пожалуйста, ребята, помогите, так как это задание убивает меня.
это мой пример во входном файле
----
радиус = 456
sin( pi / 2 ) + pi * radius *radius + 0x1F - 1100b

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

Что я уже пробовал:

я должен написать а parse.cpp для вычисления выражений в text.cpp-да.

Richard MacCutchan

Пожалуйста, удалите весь избыточный код из вашего вопроса и просто покажите ту часть, которая анализирует значения.

2 Ответов

Рейтинг:
2

Rick York

Ричард, безусловно, правильно. Есть некоторые странные вещи, которые должны быть исправлены.

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.


Leigh Omar Alpha

пожалуйста, помогите мне получить токен, распознающий шестнадцатеричное значение, и преобразовать его в десятичное или плавающее. Борьба идет все последние дни и не может найти решения. пожалуйста ребята мне нужна ваша помощь

Rick York

Ищите его префикс 0x. Вы можете получить его значение с помощью strtoul() и передать ему основание 0, если вы передадите строку с 0x. Если вы не проходите в части "0x", то вам нужно вызвать ее с базой 16.

Подумайте о том, как работает компилятор. Он определяет тип токена, ища различные префиксные и суффиксные символы. Трейлинг f означает плавающее значение. Ведущий 0x означает шестнадцатеричный, а ведущий 0 (один) - восьмеричный. Десятичная точка означает, что это двойник. Если вы не хотите поддерживать экспоненциальную нотацию, то вы можете значительно упростить эту работу.

Leigh Omar Alpha

Иллюстрация кода или пример будут очень полезны Рику Йорку . Я новичок в C++, пожалуйста, Рик.

Rick York

У меня нет ни одного примера, который был бы полезен. На этом сайте есть много, очень много примеров оценщика выражений. Я рекомендую взглянуть на некоторые из них, чтобы вы могли увидеть, как они занимались своим синтаксическим анализом. Те, что я сделал, вероятно, очень неуклюжи по сравнению с ними.

Рейтинг:
1

Richard MacCutchan

Только одну вещь я заметил:

double DoInt (double arg)
  {
  return (int) arg;   // drop fractional part
  }

Вы определили DoInt как вернуть double значение, но вы на самом деле возвращаете int И следующий метод
double toInteger(char c)<br />
возвращает различные типы данных.

const int getrandom (const int x)

на самом деле возвращает двойник.

Я думаю, что вам нужно многое проверить в этом коде.