fork download
  1. /*
  2.  * Кaлькулятор. Пример из учебника "С++ Programming Language" (основа).
  3.  * Применяется алгоритм "рекурсивный спуск" (recursive descent).
  4.  * (Примечание: калькулятор может работать с выражениями. Например, если
  5.  * на входе подать r = 2.5; area = pi * r * r; то на выходе будет
  6.  * 2.5; 19.635)
  7.  */
  8.  
  9. #include <cctype>
  10. #include <iostream>
  11. #include <map>
  12. #include <sstream>
  13. #include <string>
  14.  
  15. enum Token_value : char {
  16. NAME, NUMBER, END,
  17. PLUS='+', MINUS='-', MUL='*', DIV='/',
  18. PRINT=';', ASSIGN='=', LP='(', RP=')'
  19. };
  20.  
  21. enum Number_value : char {
  22. NUM0='0', NUM1='1', NUM2='2',
  23. NUM3='3', NUM4='4', NUM5='5',
  24. NUM6='6', NUM7='7', NUM8='8',
  25. NUM9='9', NUMP='.',
  26. };
  27.  
  28. Token_value curr_tok = PRINT; // Хранит последний возврат функции get_token().
  29. double number_value; // Хранит целый литерал или литерал с плавающей запятой.
  30. std::string string_value; // Хранит имя.
  31. std::map<std::string, double> table; // Таблица имён.
  32. int no_of_errors; // Хранит количество встречаемых ошибок.
  33.  
  34. double expr(std::istream*, bool); // Обязательное объявление.
  35.  
  36. /****************************************************************************/
  37.  
  38. // Функция error() имеет тривиальный характер: инкрементирует счётчик ошибок.
  39. double error(const std::string& error_message) {
  40. ++no_of_errors;
  41. std::cerr << "error: " << error_message << std::endl;
  42. return 1;
  43. }
  44.  
  45. Token_value get_token(std::istream* input) {
  46. char ch;
  47.  
  48. do { // Пропустить все пробельные символы кроме '\n'.
  49. if (!input->get(ch)) {
  50. return curr_tok = END; // Завершить работу калькулятора.
  51. }
  52. } while (ch != '\n' && isspace(ch));
  53.  
  54. switch (ch) {
  55. case 0: // При вводе символа конца файла, завершить работу калькулятора.
  56. return curr_tok = END;
  57. case PRINT:
  58. case '\n':
  59. return curr_tok = PRINT;
  60. case MUL:
  61. case DIV:
  62. case PLUS:
  63. case MINUS:
  64. case LP:
  65. case RP:
  66. case ASSIGN:
  67. return curr_tok = Token_value(ch); // Приведение к целому и возврат.
  68. case NUM0: case NUM1: case NUM2: case NUM3: case NUM4:
  69. case NUM5: case NUM6: case NUM7: case NUM8: case NUM9:
  70. case NUMP:
  71. input->putback(ch); // Положить назад в поток...
  72. *input >> number_value; // И считать всю лексему.
  73. return curr_tok = NUMBER;
  74. default:
  75. if (isalpha(ch)) {
  76. string_value = ch;
  77. while (input->get(ch) && isalnum(ch)) {
  78. string_value.push_back(ch);
  79. }
  80. input->putback(ch);
  81. return curr_tok = NAME;
  82. }
  83. error("Bad Token");
  84. return curr_tok = PRINT;
  85. }
  86. }
  87.  
  88. /* Каждая функция синтаксического анализа принимает аргумент типа bool
  89.  * указывающий, должна ли функция вызывать get_token() для получения
  90.  * очередной лексемы. */
  91.  
  92. // prim() - обрабатывает первичные выражения.
  93. double prim(std::istream* input, bool get) {
  94. if (get) {
  95. get_token(input);
  96. }
  97.  
  98. switch (curr_tok) {
  99. case NUMBER: {
  100. double v = number_value;
  101. get_token(input);
  102. return v;
  103. }
  104. case NAME: {
  105. double& v = table[string_value];
  106. if (get_token(input) == ASSIGN) {
  107. v = expr(input, true);
  108. }
  109. return v;
  110. }
  111. case MINUS:
  112. return -prim(input, true);
  113. case LP: {
  114. double e = expr(input, true);
  115. if (curr_tok != RP) {
  116. return error("')' expected");
  117. }
  118. get_token(input);
  119. return e;
  120. }
  121. default:
  122. return error("primary expected");
  123. }
  124. }
  125.  
  126. // term() - умножение и деление.
  127. double term(std::istream* input, bool get) {
  128. double left = prim(input, get);
  129.  
  130. for ( ; ; ) {
  131. switch (curr_tok) {
  132. case MUL:
  133. left *= prim(input, true);
  134. break;
  135. case DIV:
  136. if (double d = prim(input, true)) {
  137. left /= d;
  138. break;
  139. }
  140. return error("Divide by 0");
  141. default:
  142. return left;
  143. }
  144. }
  145. }
  146.  
  147. // expr() - сложение и вычитание.
  148. double expr(std::istream* input, bool get) {
  149. double left = term(input, get);
  150.  
  151. for ( ; ; ) {
  152. switch (curr_tok) {
  153. case PLUS:
  154. left += term(input, true);
  155. break;
  156. case MINUS:
  157. left -= term(input, true);
  158. break;
  159. default:
  160. return left;
  161. }
  162. }
  163. }
  164.  
  165. int main(int argc, char* argv[]) {
  166. std::istream* input = nullptr; // Указатель на поток.
  167.  
  168. switch (argc) {
  169. case 1:
  170. input = &std::cin;
  171. break;
  172. case 2:
  173. input = new std::istringstream(argv[1]);
  174. break;
  175. default:
  176. error("Too many arguments");
  177. return 1;
  178. }
  179.  
  180. table["pi"] = 3.1415926535897932385;
  181. table["e"] = 2.7182818284590452354;
  182.  
  183. while (*input) {
  184. get_token(input);
  185. if (curr_tok == END) {
  186. break;
  187. }
  188.  
  189. // Снимает ответственность expr() за обработку пустых выражений.
  190. if (curr_tok == PRINT) {
  191. continue;
  192. }
  193.  
  194. // expr() -> term() -> prim() -> expr() ...
  195. std::cout << expr(input, false) << std::endl;
  196. }
  197.  
  198. if (input != &std::cin) {
  199. delete input;
  200. }
  201.  
  202. return no_of_errors;
  203. }
Success #stdin #stdout 0.01s 5268KB
stdin
((10+5)*3)/(4-10)
stdout
-7.5