-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathformula.cpp
More file actions
166 lines (149 loc) · 7.27 KB
/
formula.cpp
File metadata and controls
166 lines (149 loc) · 7.27 KB
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include "formula.h"
#include "FormulaAST.h"
#include <algorithm>
#include <cassert>
#include <cctype>
#include <sstream>
#include <set>
using namespace std::literals;
std::ostream& operator<<(std::ostream& output, FormulaError fe)
{
// Выводит "#REF!", "#VALUE!", "#DIV/0!" или ""
return output << fe.ToString();
/* PREVIOUS VERSION
// Пока обрабатываем только ошибку деления на ноль
//return output << "#DIV/0!";
*/
}
/*
* Классы Formula, FormulaInterface и все остальные классы
* более высого уровня передают ссылку на таблицу Sheet& для
* доступа к членам классов.
*
* Классы FormulaAST, Expr и все остальные классы более низкого
* уровня передают функтор std::function<double(Position)>&
* (здесь реализован в виде лямбда-функции), получающий адрес
* ячейки в качестве аргумента и ссылку на таблицу в [], что
* позволяет производить расчеты значений ячеек по формулам.
* Реально расчет при помощи функтора делает только CellExpr,
* т.к. только у него есть поле с указателем на индекс ячейки.
*/
namespace {
class Formula : public FormulaInterface {
public:
// Реализуйте следующие методы:
explicit Formula(std::string expression)
: ast_(ParseFormulaAST(std::move(expression))),
referenced_cells_(ast_.GetCells().begin(), ast_.GetCells().end())
{}
Value Evaluate(const SheetInterface& sheet) const override
{
/*
* FormulaAST ast_ ничего не знает о существовании таблицы.
* Но она хранит наименования позиций - индексы ячеек, которые
* имеют смысл для таблицы. Функтор позволяет производить
* обращение к ячейкам таблицы через их индексы (Position)
*/
try
{
return ast_.Execute(
// Лямбда-функция в качестве функтора.
// У нас CellExpr, в котором есть указатель на индекс ячейки pos.
// Сама ячейка принадлежит листу таблицы sheet.
// Т.к. мы в классе Formula (не Text! не Empty!) и это CellExpr,
// то ни текстом, ни пустой строкой результат вычисления быть не может.
[&sheet](const Position& pos)
{
// 1. Если ячейка не существует, трактуем это как 0
if (sheet.GetCell(pos) == nullptr)
{
return 0.0;
}
auto value = sheet.GetCell(pos)->GetValue();
// 2. Если ячейка содержит число, возвращаем его как есть
if (std::holds_alternative<double>(value))
{
return std::get<double>(value);
}
// 3. Если ячейка содержит текст, пытаемся его интерпретировать
// как число.
else if (std::holds_alternative<std::string>(value))
{
try
{
// string без move(get...), чтобы не разрушить значение из variant
// return std::stod(std::get<std::string>(value)); - без проверки пропускает 3D и прочий текст
std::string tmp = std::get<std::string>(value);
if (std::all_of(tmp.cbegin(), tmp.cend(), [](char ch)
{
return (std::isdigit(ch) || ch == '.');
}))
{
return std::stod(tmp);
}
else
{
// Текст не может быть сконвертирован в число
throw FormulaError(FormulaError::Category::Value);
// Подавляем предупреждение компилятора об отсутствии возвращаемого значения
return 0.0;
}
}
catch (...)
{
// Текст не может быть сконвертирован в число
// (для случая выбрасывания исключения при конвертации)
throw FormulaError(FormulaError::Category::Value);
}
}
// 4. Иначе ячейка содержит CellInterface::FormulaError
else
{
throw std::get<FormulaError>(value);
}
// Заглушка для подавления предупреждений компилятора
// об отсутствии возвращаемых значений для некоторых веток
return 0.0;
}
);
}
catch (FormulaError& ex_fe)
{
return ex_fe;
}
}
// Используем "очищенную" формулу без лишних скобок из
// FormulaAST::PrintFormula(std::ostream& out).
std::string GetExpression() const override
{
std::stringstream ss;
ast_.PrintFormula(ss);
return ss.str();
}
std::vector<Position> GetReferencedCells() const override
{
// Вектор результатов (будет возвращен через NVRO)
std::vector<Position> result;
// Убираем дублирование ячеек, прогоняя их через std::set
std::set<Position> unique_ref_cells(referenced_cells_.begin(), referenced_cells_.end());
for (const auto& cell : unique_ref_cells)
{
result.push_back(cell);
}
return result; //NVRO
}
private:
FormulaAST ast_;
std::vector<Position> referenced_cells_;
};
} // namespace
std::unique_ptr<FormulaInterface> ParseFormula(std::string expression) {
try
{
return std::make_unique<Formula>(std::move(expression));
}
catch (const std::exception&)
{
throw FormulaException("Formula parse error");
}
}