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

Как создать выражение калькулятора, которое может вычислять произвольное длинное выражение ?


образец
pi = 3,14 радиус = 3 sin( pi / 2 ) + pi * радиус ^ 2 + 0x1F - 1100b
это даст ошибку из-за двоичных и шестнадцатеричных чисел.

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

#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


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 (double) 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 toHex(char c)
{
    return (std::tolower(c) == 'x' && toInteger(c) <= 0xf);
}
double DoRandom (double arg)
  {
  return getrandom (static_cast <double> (arg));  // random number in range 0 to arg
  }




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 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 0xf :
        {
        if( type_==0xf){


         GetToken(true);
           return value_;
        }
        }
    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 ();
  }

0x01AA

Серьезно, тебе нравится, что кто-то здесь будет копаться в твоих вещах?

"это даст ошибку из-за двоичных и шестнадцатеричных чисел.":
1.) проверьте, готов ли ваш парсер распознавать шестнадцатеричные и двоичные числа.
2.) Используйте отладчик, чтобы проверить, где находится проблема.
2.a.) начните с простого выражения типа x= 0x1F и проверьте, какая проблема возникает с ним у вашего синтаксического анализатора.
2.б) сделайте то же самое для двоичных данных

Leigh Omar Alpha

ладно, мужик. но не думай обо мне так.

0x01AA

Что значит "но не думай обо мне так"? Кстати. У меня нет предубеждения против вас! Я только пытался внести свой вклад.

4 Ответов

Рейтинг:
9

0x01AA

Вам нужно расширить Parser::GetToken в том смысле, что он распознает шестнадцатеричные и двоичные числа. в данный момент Parser::GetToken обрабатывает только значения int и double.

Надеюсь, это поможет.


Leigh Omar Alpha

именно это я и пытаюсь сделать сейчас . но получить функцию для этого немного проблематично для меня

0x01AA

Цитата: "но получить функцию, чтобы сделать это немного проблема для меня"
Я чувствую себя с тобой! Я пытаюсь нырнуть в код и помочь, но не могу обещать ответа.

Leigh Omar Alpha

я перепробовал множество функций, но до сих пор безрезультатно.

0x01AA

Для меня большая путаница это параметр ignoreSign) о методе GetToken..С моей точки зрения GetToken следует вернуть знак как "фактор"... но пожалуйста. дай мне еще немного времени.
[Edit: почему знак должен быть возвращен как фактор]
допустимый номер также является "----+----1". И решить эту проблему не должна работа токенизатора... по крайней мере, я так понимаю.



Дело, которое нужно решить, это:
a.) токен, начинающийся с "0x" и следующий за потоком символов '0'-'F' и заканчивающийся белым символом=> Это шестнадцатеричное число
b.) токен содержит только '0' и '1' и заканчивается на 'b', за которым следует белый символ => это двоичный файл

[Edit1]
whitechar в приведенном выше не совсем корректен. Это также может быть оператор типа "+,-,*,/" и, возможно, другие

Leigh Omar Alpha

теперь я решил эту проблему, но у меня есть несколько строк, но только одна может быть прочитана, а другие строки не будут прочитаны.
файл ifstream ("input.txt");
ofstream file1;
file1.open("output.txt");

freopen("output.txt", "w", stdout);
если(файл.is_open()){
строка inputLine;
while (std::getline (file, inputLine))
{
if( strncmp ( "- - - - " , inputLine.c_str(), 4 ) == 0 )


продолжить;
Парсер p(str2);
двойное значение = стр. Оценить ();
с std::соиь<&ЛТ;"----------------------"&ЛТ;<епси;
с std::соиь <&ЛТ; "результате =" &ЛТ;&ЛТ; значение &ЛТ;&ЛТ; функция std::епси;

с std::соиь<&ЛТ;"----------------------"&ЛТ;<епси;

возвращает 0;
}
файл.рядом();
}

}
пожалуйста может ли любое тело помочь

0x01AA

Для меня это непонятно: Вы читаете в исходном тексте в переменную inputLine но вы вызываете парсер с помощью str2- Чего мне здесь не хватает?

Leigh Omar Alpha

извините, что я сделал некоторые вычисления с inputLine и поместил его в str2.
это полный код программы.

static char *stringtoken(char *str, char const *delims)
{
статический символ *src = NULL;
char *p, *ret = 0;

if (str != NULL)
src = str;

if (src == NULL || *src == '\0') // Fix 1
возвращать null;

ret = src; // Fix 2
if ((p = strpbrk(src, delims)) != NULL)
{
*p = 0;
//ret = src; // ненужно
src = ++p;
}
еще
src += strlen(src);

вернуться в отставке;
}


тап_п()
{



файл ifstream ("input.txt");
ofstream file1;
file1.open("output.txt");

freopen("output.txt", "w", stdout);
если(файл.is_open()){
строка inputLine;
while (std::getline (file, inputLine))
{
if( strncmp ( "- - - - " , inputLine.c_str(), 4 ) == 0 )


продолжить;


char *opr = "^+-/%*=,()";
с std::строки::итератор end_pos = с std::удалить(inputLine.начать(), inputLine.конец(), ' ');
inputLine.erase(end_pos, inputLine.end());
string str=inputLine;
строка str2="";
int длина пр.=.длина();
char t[длина];
str.copy(t, длина);
t[длина] = '\0';
char* tok;
char *cop=новый символ [длина];
str.copy(cop,длина);
char *w = stringtoken( t, opr );

while (w!=NULL)
{
строка w2=w;
std::stringstream tr;
tr << w2;
В2.понятно();
tr >> w2;
тип int х=Да2.длина();
int y=x-3;

проверка строки= w2.substr(0,3);
строка check1=w2.substr(0,x);

если(проверить.найти("0х") != СТД::строка::НКО)
{
unsigned int x= strtol(w2.c_str(), NULL, 0);
std::ostringstream s;
s << x;
константные СТД::строка второй(С. ул.());
стр2=стр2+ второй;
}
остальное, если (проверка1.найти("б")!=СТД::строка::НКО)
{
В2.pop_back();
long bin=std::strtol(w2.c_str(),0,2);
std::ostringstream s2;
s2<<bin;
const std::string t2(s2.str());
//inputLine.replace(inputLine.find(w2),(w2.length()+1),t2);
str2=str2+t2;
}
еще
{
str2=str2+w2;
}
char a =cop[w-t+strlen(w)];
строка s1="";
s1=s1+a;
std::stringstream tr1;
tr1 << s1;
С1.понятно();
tr1 >> s1;

str2=str2+s1;
w =stringtoken(NULL, opr);

}
Парсер p(str2);
двойное значение = стр. Оценить ();
с std::соиь<&ЛТ;"----------------------"&ЛТ;<епси;
с std::соиь <&ЛТ; "результате =" &ЛТ;&ЛТ; значение &ЛТ;&ЛТ; функция std::епси;

с std::соиь<&ЛТ;"----------------------"&ЛТ;<епси;

возвращает 0;
}
файл.рядом();
}

}

Рейтинг:
1

Patrice T

Цитата:
это даст ошибку из-за двоичных и шестнадцатеричных чисел.

Ваш код ведет себя не так, как вы ожидаете, или вы не понимаете, почему !

Существует почти универсальное решение: запускайте свой код на отладчике шаг за шагом, проверяйте переменные.
Отладчик здесь, чтобы показать вам, что делает ваш код, и ваша задача-сравнить с тем, что он должен делать.
В отладчике нет никакой магии, он не знает, что должен делать ваш код, он не находит ошибок, он просто помогает вам, показывая, что происходит. Когда код не делает того, что ожидается, вы близки к ошибке.
Чтобы увидеть, что делает ваш код: просто установите точку останова и посмотрите, как работает ваш код, отладчик позволит вам выполнять строки 1 на 1 и проверять переменные по мере их выполнения.

Отладчик - Википедия, свободная энциклопедия[^]

Освоение отладки в Visual Studio 2010 - руководство для начинающих[^]
Базовая отладка с помощью Visual Studio 2010 - YouTube[^]

1.11 — отладка программы (пошаговое выполнение и останова) | выучить C++[^]

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


Рейтинг:
0

OriginalGriff

Никто не собирается пробираться через такой большой код в поисках неопределенной ошибки типа "это даст ошибку из-за двоичных и шестнадцатеричных чисел."

Вы должны помнить, что компиляция не означает, что ваш код является правильным! :смеяться:
Подумайте о процессе разработки как о написании электронного письма: успешная компиляция означает, что вы написали письмо на правильном языке - например, на английском, а не на немецком, - а не то, что письмо содержало сообщение, которое вы хотели отправить.

Итак, теперь вы входите во вторую стадию разработки (на самом деле это четвертая или пятая, но вы перейдете к более ранним стадиям позже): тестирование и отладка.

Начните с рассмотрения того, что он делает, и как это отличается от того, что вы хотели. Это важно, потому что это дает вам информацию о том, почему он это делает. Например, если программа предназначена для того, чтобы позволить пользователю ввести число, а затем удвоить его и напечатать ответ, то если бы ввод / вывод был таким:

Input   Expected output    Actual output
  1            2                 1
  2            4                 4
  3            6                 9
  4            8                16
Тогда совершенно очевидно, что проблема заключается в бите, который удваивает его - он не прибавляет себя к себе или умножает его на 2, он умножает его на себя и возвращает квадрат входного сигнала.
Таким образом, вы можете посмотреть на код, и очевидно, что он находится где-то здесь:
int Double(int value)
   {
   return value * value;
   }

Как только у вас появится идея, что может пойти не так, начните использовать отладчик, чтобы выяснить, почему. Поместите точку останова в первую строку метода и запустите приложение. Когда он достигнет точки останова, отладчик остановится и передаст управление вам. Теперь вы можете запускать свой код построчно (так называемый "одноступенчатый") и просматривать (или даже изменять) содержимое переменных по мере необходимости (черт возьми, вы даже можете изменить код и повторить попытку, если вам это нужно).
Подумайте о том, что должна делать каждая строка кода перед ее выполнением, и сравните это с тем, что она действительно делала, когда вы использовали кнопку "Step over" для выполнения каждой строки по очереди. Он сделал то, что вы ожидали? Если да, то переходите к следующей строке.
Если нет, то почему? Чем это отличается?
Надеюсь, это поможет вам определить, в какой части этого кода есть проблема и в чем она заключается.
Это навык, и его стоит развивать, поскольку он помогает вам как в реальном мире, так и в развитии. И, как и все навыки, он только улучшается при использовании!


Рейтинг:
0

KarstenK

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

Пример: 3 * 2 + 5

первый шаг-это поиск чисел и операторов "3", "*", "2", "+" и "5"
чем вам нужно объединить их с правильной логикой:("3", "*", "2") и ( "+" ,"5")
чем вы можете начать свои операции.