Advertisement
mk17ru

Untitled

Apr 1st, 2020
407
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.83 KB | None | 0 0
  1. #include "calc.h"
  2.  
  3. #include <cctype> // for std::isspace
  4. #include <cmath> // various math functions
  5. #include <iostream> // for error reporting via std::cerr
  6. namespace {
  7.  
  8. const std::size_t max_decimal_digits = 10;
  9.  
  10. enum class Op {
  11. ERR
  12. , SET
  13. , ADD
  14. , SUB
  15. , MUL
  16. , DIV
  17. , REM
  18. , NEG
  19. , POW
  20. , SQRT
  21. };
  22.  
  23. std::size_t arity(const Op op)
  24. {
  25. switch (op) {
  26. // error
  27. case Op::ERR: return 0;
  28. // unary
  29. case Op::NEG: return 1;
  30. case Op::SQRT: return 1;
  31. // binary
  32. case Op::SET: return 2;
  33. case Op::ADD: return 2;
  34. case Op::SUB: return 2;
  35. case Op::MUL: return 2;
  36. case Op::DIV: return 2;
  37. case Op::REM: return 2;
  38. case Op::POW: return 2;
  39. }
  40. return 0;
  41. }
  42.  
  43. Op parse_op(const std::string & line, std::size_t & i)
  44. {
  45. const auto rollback = [&i, &line] (const std::size_t n) {
  46. i -= n;
  47. std::cerr << "Unknown operation " << line << std::endl;
  48. return Op::ERR;
  49. };
  50. switch (line[i++]) {
  51. case '0': [[fallthrough]];
  52. case '1': [[fallthrough]];
  53. case '2': [[fallthrough]];
  54. case '3': [[fallthrough]];
  55. case '4': [[fallthrough]];
  56. case '5': [[fallthrough]];
  57. case '6': [[fallthrough]];
  58. case '7': [[fallthrough]];
  59. case '8': [[fallthrough]];
  60. case '9':
  61. --i; // a first digit is a part of op's argument
  62. return Op::SET;
  63. case '+':
  64. return Op::ADD;
  65. case '-':
  66. return Op::SUB;
  67. case '*':
  68. return Op::MUL;
  69. case '/':
  70. return Op::DIV;
  71. case '%':
  72. return Op::REM;
  73. case '_':
  74. return Op::NEG;
  75. case '^':
  76. return Op::POW;
  77. case 'S':
  78. switch (line[i++]) {
  79. case 'Q':
  80. switch (line[i++]) {
  81. case 'R':
  82. switch (line[i++]) {
  83. case 'T':
  84. return Op::SQRT;
  85. default:
  86. return rollback(4);
  87. }
  88. default:
  89. return rollback(3);
  90. }
  91. default:
  92. return rollback(2);
  93. }
  94. default:
  95. return rollback(1);
  96. }
  97. }
  98.  
  99. std::size_t skip_ws(const std::string & line, std::size_t i)
  100. {
  101. while (i < line.size() && std::isspace(line[i])) {
  102. ++i;
  103. }
  104. return i;
  105. }
  106.  
  107. double parse_arg(const std::string & line, std::size_t & i)
  108. {
  109.  
  110. if (line[i] < '0' || line[i] > '9') {
  111. std::cerr << "Invalid start of argument" << line << std::endl;
  112. }
  113. double res = 0;
  114. std::size_t count = 0;
  115. bool good = true;
  116. bool integer = true;
  117. int base = 10;
  118. bool have_base_zero = false;
  119. double fraction = 1;
  120. size_t start = i;
  121. while (good && i < line.size() && count < max_decimal_digits) {
  122. char cur_char = (char)toupper(line[i]);
  123. switch (cur_char) {
  124. case 'B':
  125. if (have_base_zero) {
  126. base = 2;
  127. have_base_zero = false;
  128. ++i;
  129. break;
  130. } else {
  131. [[fallthrough]];
  132. }
  133. case '0':
  134. if (i == start) {
  135. have_base_zero = true;
  136. ++i;
  137. break;
  138. } else {
  139. [[fallthrough]];
  140. }
  141. case '1': [[fallthrough]];
  142. case '2': [[fallthrough]];
  143. case '3': [[fallthrough]];
  144. case '4': [[fallthrough]];
  145. case '5': [[fallthrough]];
  146. case '6': [[fallthrough]];
  147. case '7':
  148. if (have_base_zero) {
  149. base = 8;
  150. have_base_zero = false;
  151. }
  152. [[fallthrough]];
  153. case '8': [[fallthrough]];
  154. case '9': [[fallthrough]];
  155. case 'A': [[fallthrough]];
  156. case 'C': [[fallthrough]];
  157. case 'D': [[fallthrough]];
  158. case 'E': [[fallthrough]];
  159. case 'F':
  160. if (!((cur_char >= '0' && cur_char <= '0' + base - 1)
  161. || (base == 16 && cur_char >= 'A' && cur_char <= 'F'))) {
  162. good = false;
  163. break;
  164. }
  165. if (integer) {
  166. res *= base;
  167. if (cur_char >= '0' && cur_char <= '9') {
  168. res += cur_char - '0';
  169. } else {
  170. res += cur_char - 'A' + 10;
  171. }
  172. }
  173. else {
  174. fraction /= base;
  175. if (cur_char >= '0' && cur_char <= '9') {
  176. res += (cur_char - '0') * fraction;
  177. } else {
  178. res += (cur_char - 'A' + 10) * fraction;
  179. }
  180. }
  181. ++i;
  182. ++count;
  183. break;
  184. case '.':
  185. integer = false;
  186. ++i;
  187. have_base_zero = false;
  188. break;
  189. case 'X':
  190. if (i != start + 1) {
  191. good = false;
  192. break;
  193. }
  194. base = 16;
  195. ++i;
  196. have_base_zero = false;
  197. break;
  198. default:
  199. good = false;
  200. break;
  201. }
  202. }
  203. if (i < line.size()) {
  204. std::cerr << "Argument isn't fully parsed, suffix left: '" << line.substr(i) << "'" << std::endl;
  205. }
  206.  
  207. return res;
  208. }
  209.  
  210. double unary(const double current, const Op op)
  211. {
  212. switch (op) {
  213. case Op::NEG:
  214. return -current;
  215. case Op::SQRT:
  216. if (current > 0) {
  217. return std::sqrt(current);
  218. }
  219. else {
  220. std::cerr << "Bad argument for SQRT: " << current << std::endl;
  221. [[fallthrough]];
  222. }
  223. default:
  224. return current;
  225. }
  226. }
  227.  
  228. double binary(const Op op, const double left, const double right)
  229. {
  230. switch (op) {
  231. case Op::SET:
  232. return right;
  233. case Op::ADD:
  234. return left + right;
  235. case Op::SUB:
  236. return left - right;
  237. case Op::MUL:
  238. return left * right;
  239. case Op::DIV:
  240. if (right != 0) {
  241. return left / right;
  242. }
  243. else {
  244. std::cerr << "Bad right argument for division: " << right << std::endl;
  245. return left;
  246. }
  247. case Op::REM:
  248. if (right != 0) {
  249. return std::remainder(left, right);
  250. }
  251. else {
  252. std::cerr << "Bad right argument for remainder: " << right << std::endl;
  253. return left;
  254. }
  255. case Op::POW:
  256. return std::pow(left, right);
  257. default:
  258. return left;
  259. }
  260. }
  261.  
  262. } // anonymous namespace
  263.  
  264. double process_line(const double current, const std::string & line)
  265. {
  266. std::size_t i = 0;
  267. const auto op = parse_op(line, i);
  268. switch (arity(op)) {
  269. case 2: {
  270. i = skip_ws(line, i);
  271. const auto arg = parse_arg(line, i);
  272. return binary(op, current, arg);
  273. }
  274. case 1: return unary(current, op);
  275. default: break;
  276. }
  277. return current;
  278. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement