-
Notifications
You must be signed in to change notification settings - Fork 0
/
CalcParser.hpp
117 lines (95 loc) · 4.02 KB
/
CalcParser.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#pragma once
#include <cstdint>
#include <limits>
#include <stdexcept>
inline void check_mlt_overflow(std::int64_t a, std::int64_t b) {
if (b != 0 && (a > std::numeric_limits<std::int64_t>::max() / b
|| a < std::numeric_limits<std::int64_t>::min() / b)) {
throw std::overflow_error("");
}
}
inline void check_div_overflow(std::int64_t a, std::int64_t b) {
if (b == 0) {
throw std::overflow_error("");
}
if (a == std::numeric_limits<std::int64_t>::min() && b == -1) {
throw std::overflow_error("");
}
}
inline void check_plus_overflow(std::int64_t a, std::int64_t b) {
if ((b > 0 && a > std::numeric_limits<std::int64_t>::max() - b)
|| (b < 0 && a < std::numeric_limits<std::int64_t>::min() - b)) {
throw std::overflow_error("");
}
}
inline void check_minus_overflow(std::int64_t a, std::int64_t b) {
if ((b < 0 && a > std::numeric_limits<std::int64_t>::max() + b)
|| (b > 0 && a < std::numeric_limits<std::int64_t>::min() + b)) {
throw std::overflow_error("");
}
}
#include "Parsec/Parsec.hpp"
#include "RomanNumeralsParser.hpp"
namespace CalcParser {
namespace Internal {
using namespace Parsec;
Parser<int64_t> roman_expr();
Parser<int64_t> roman_atom();
Parser<int64_t> roman_numeral() {
return RomanNumerals::roman_numeral();
}
Parser<int64_t> roman_unary_minus_atom() {
return map_parser(char_parser('-') >> lazy_parser<int64_t>(roman_atom), [](int64_t a) { return -a; });
}
Parser<int64_t> roman_brackets() {
// На самом деле, исходя из грамматики, здесь должен быть только один второй случай,
// Но если сделать так, то слишком просто построить пример, на котором парсер работает медленно
// А так начинают быстрее работать всякие ((((((I)))))) и похожие примеры
return brackets_parser(char_parser('('), lazy_parser<int64_t>(roman_brackets), char_parser(')'))
| brackets_parser(char_parser('('), lazy_parser<int64_t>(roman_expr), char_parser(')'));
}
Parser<int64_t> roman_atom() {
return roman_numeral() | roman_unary_minus_atom() | roman_brackets();
}
Parser<int64_t> roman_mlt_div() {
return fold(
seq_save(roman_atom(), char_parser('*') | char_parser('/')),
{
{'*', [](int64_t a, int64_t b) {
check_mlt_overflow(a, b);
return a * b;
}},
{'/', [](int64_t a, int64_t b) {
check_div_overflow(a, b);
return a / b - (((a < 0) ^ (b < 0)) && (a % b != 0));
}}
// -5/-2 = 2, -5/2 = -3, 5/-2 = -3, 5/2 = 2 НО! 2/-2 = -1
}
);
}
Parser<int64_t> roman_expr() {
return fold(
seq_save(roman_mlt_div(), char_parser('+') | char_parser('-')),
{
{'+', [](int64_t a, int64_t b) { check_plus_overflow(a, b); return a + b; }},
{'-', [](int64_t a, int64_t b) { check_minus_overflow(a, b); return a - b; }}
}
);
}
} // namespace Internal
Parsec::Parser<int64_t> roman_calc() {
return Internal::roman_expr();
}
std::stringstream arabic_numeral_to_roman(int64_t x) {
return Internal::RomanNumerals::print_arabic_numeral_to_roman(x);
}
std::string remove_all_spaces(const std::string& str) {
std::string new_str;
for (char c : str) {
if (c != ' ' && c != '\t') {
new_str.push_back(c);
}
}
return new_str;
}
} // namespace CalcParser