jeudi 28 octobre 2021

The C++ Programming Language 4th edition A desk calculator return ct={Kind::name;} the output is wrong when i input pi

when i copy the code from the book The C++ Programming Language Fourth Edition ,Bjarne Stroustrup I found out that when i input a pi(which has been defined in the code) the output is wrong so i spent 1 day to find out the problem. i just share it

#include<iostream>
#include<map>
#include<string>
#include<sstream>
#include<cctype>

enum class Kind:char{
  name,number,end,
  plus='+',minus='-',mul='*',div='/',print=';',assign='=',lp='(',rp=')'
};

struct Token{
  Kind kind;
  std::string string_value;
  double number_value;
};

int no_of_errors;

double error(const std::string& s)
{
  no_of_errors++;
  std::cerr<<"error: "<<s<<'\n';
  return 1;
}

std::map<std::string,double> table;

double expr(bool get);
double term(bool get);
double prim(bool get);

class Token_stream{
public:
  Token_stream(std::istream& s):ip{&s},owns{false}{}
  Token_stream(std::istream* p):ip{p},owns{true}{}

  ~Token_stream(){close();}

  Token get();
  Token& current(){return ct;}

  void set_input(std::istream& s){close();ip=&s;owns=false;}
  void set_input(std::istream* p){close();ip=p;owns=true;}

private:
  void close(){if(owns) delete ip;}

  std::istream* ip;
  bool owns;
  Token ct{Kind::end};
};

Token_stream ts{std::cin};

//Token& Token_stream::current()
//{
//  return ct;
//}

Token Token_stream::get()
{
  char ch;

  do{
    if(!ip->get(ch)){
      return ct={Kind::end};
    }
  }while(ch!='\n' && std::isspace(ch));

  switch(ch){
    case ';':
    case '\n':
      return ct={Kind::print};
    case '*':
    case '/':
    case '+':
    case '-':
    case '(':
    case ')':
    case '=':
      return ct={static_cast<Kind>(ch)};
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
    case '.':{
      ip->putback(ch);
      *ip>>ct.number_value;
      ct.kind=Kind::number;
      return ct;
    }
    default:
      if(std::isalpha(ch)){
        ct.string_value = ch;
        while(ip->get(ch) && std::isalnum(ch)){
          ct.string_value +=ch;
        }
        //test
        //std::cout<<ts.current().string_value<<'\n';
        //std::cout<<table[ct.string_value]<<'\n';
        ip->putback(ch);
        return ct={Kind::name,ct.string_value};
      }
      error("bad token");
      return ct={Kind::print};
  }
}

double expr(bool get)
{
//test  
//  std::cout<<ts.current().string_value<<'\n';
//  std::cout<<table[ts.current().string_value]<<'\n';
  double left=term(get);

  for(;;){
    switch(ts.current().kind){
      case Kind::plus:
        left += term(true);
        break;
      case Kind::minus:
        left-= term(true);
        break;
      default:
        return left;
    }
  }
}

double term(bool get)
{
  double left = prim(get);

  for(;;){
    switch(ts.current().kind){
      case Kind::mul:
        left *= prim(true);
        break;
      case Kind::div:
        if(auto d = prim(true)){
          left /= d;
          break;
        }
        return error("divide by 0");
      default:
        return left;
    }
  }
}

double prim(bool get)
{
  if(get){
    ts.get();
  }

  switch(ts.current().kind){
    case Kind::number:
    {
      double v=ts.current().number_value;
      ts.get();
      return v;
    }
    case Kind::name:
    {
      //test
      //std::cout<<ts.current().string_value<<'\n';
      double& v=table[ts.current().string_value];
      //std::cout<<table[ts.current().string_value]<<'\n';
      if(ts.get().kind == Kind::assign){
        v=expr(true);
      }
      //std::cout<<ts.current().string_value<<'\n';
      //std::cout<<v<<'\n';
      //std::cout<<table[ts.current().string_value]<<'\n';
      return v;
    }
    case Kind::lp:
    {
      auto e = expr(true);
      if(ts.current().kind!=Kind::rp){
        return error("')' expected");
      }
      void(ts.get());
      return e;
    }
    default:
      return error("primary expected");
  }
}

void calculate()
{
  for(;;){
    ts.get();
    //test
    //if(ts.current().kind != Kind::number){
    //  std::cout<<"number"<<'\n';
    //}
    if(ts.current().kind == Kind::end)break;
    if(ts.current().kind == Kind::print)continue;
    std::cout<<expr(false)<<'\n';
  }
}


int main(int arg,char* argv[])
{
  switch(arg){
    case 1:
      break;
    case 2:
      ts.set_input(new std::istringstream{argv[1]});
      break;
    default:
      error("too many arguments");
      return 1;
  }

  table["pi"] = 3.1415926535897932385;
  table["e"] = 2.7182818284590452354;

  calculate();
  return no_of_errors;

}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         233,1         Bot


the most important for correct the error is as follows:

//in member function Token Token_stream::get()
//old code:
return ct={Kind::name;}

we should change the code like this:

//in member function Token Token_stream::get()
//new code:
return ct={Kind::name,ct.string_value}

Aucun commentaire:

Enregistrer un commentaire