-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathruntime.h
274 lines (203 loc) · 10.4 KB
/
runtime.h
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
#pragma once
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>
#include <iostream>
#include <vector>
#include <cassert>
namespace runtime {
// Контекст исполнения инструкций Mython
class Context {
public:
// Возвращает поток вывода для команд print
virtual std::ostream& GetOutputStream() = 0;
protected:
~Context() = default;
};
// Базовый класс для всех объектов языка Mython
class Object {
public:
virtual ~Object() = default;
// выводит в os своё представление в виде строки
virtual void Print(std::ostream& os, Context& context) = 0;
};
// Специальный класс-обёртка, предназначенный для хранения объекта в Mython-программе
class ObjectHolder {
public:
// Создаёт пустое значение
ObjectHolder() = default;
// Возвращает ObjectHolder, владеющий объектом типа T
// Тип T - конкретный класс-наследник Object.
// object копируется или перемещается в кучу
template <typename T>
[[nodiscard]] static ObjectHolder Own(T&& object) {
return ObjectHolder(std::make_shared<T>(std::forward<T>(object)));
}
// Создаёт ObjectHolder, не владеющий объектом (аналог слабой ссылки)
[[nodiscard]] static ObjectHolder Share(Object& object);
// Создаёт пустой ObjectHolder, соответствующий значению None
[[nodiscard]] static ObjectHolder None();
// Возвращает ссылку на Object внутри ObjectHolder.
// ObjectHolder должен быть непустым
Object& operator*() const;
Object* operator->() const;
[[nodiscard]] Object* Get() const;
// Возвращает указатель на объект типа T либо nullptr, если внутри ObjectHolder не хранится
// объект данного типа
template <typename T>
[[nodiscard]] T* TryAs() const {
return dynamic_cast<T*>(this->Get());
}
// Возвращает true, если ObjectHolder не пуст
explicit operator bool() const;
private:
explicit ObjectHolder(std::shared_ptr<Object> data)
: data_(std::move(data)){
}
void AssertIsValid() const;
std::shared_ptr<Object> data_;
};
// Объект-значение, хранящий значение типа T
template <typename T>
class ValueObject : public Object {
public:
ValueObject(T v) // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
: value_(v) {
}
void Print(std::ostream& os, [[maybe_unused]] Context& context) override {
os << value_;
}
[[nodiscard]] const T& GetValue() const {
return value_;
}
private:
T value_;
};
// Таблица символов, связывающая имя объекта с его значением
using Closure = std::unordered_map<std::string, ObjectHolder>;
// Интерфейс для выполнения действий над объектами Mython
class Executable {
public:
virtual ~Executable() = default;
// Выполняет действие над объектами внутри closure, используя context
// Возвращает результирующее значение либо None
virtual ObjectHolder Execute(Closure& closure, Context& context) = 0;
};
// Строковое значение
using String = ValueObject<std::string>;
// Числовое значение
using Number = ValueObject<int>;
// Логическое значение
class Bool : public ValueObject<bool> {
public:
using ValueObject<bool>::ValueObject;
void Print(std::ostream& os, [[maybe_unused]]Context& context) override;
};
// Метод класса
struct Method {
// Имя метода
std::string name;
// Имена формальных параметров метода
std::vector<std::string> formal_params;
// Тело метода
std::shared_ptr<Executable> body;
};
// Проверяет, содержится ли в object значение, приводимое к True
// Для отличных от нуля чисел, True и непустых строк возвращается true. В остальных случаях - false.
bool IsTrue(const ObjectHolder& object);
// Класс
class Class : public Object {
public:
// Создаёт класс с именем name и набором методов methods, унаследованный от класса parent
// Если parent равен nullptr, то создаётся базовый класс
explicit Class(std::string name, std::vector<Method> methods, const Class* parent);
// Возвращает указатель на метод name или nullptr, если метод с таким именем отсутствует
[[nodiscard]] const Method* GetMethod(const std::string& name, size_t args_count) const;
// Возвращает указатель на метод name или nullptr, если метод с таким именем отсутствует
[[nodiscard]] const Method* GetMethod(const std::string& name) const;
// Возвращает имя класса
[[nodiscard]] const std::string& GetName() const;
// Выводит в os строку "Class <имя класса>", например "Class cat"
void Print(std::ostream& os, [[maybe_unused]]Context& context) override;
// Возвращает true, если объект имеет метод method, принимающий argument_count параметров
[[nodiscard]] bool HasMethod(const std::string& name, size_t argument_count) const;
private:
std::string name_;
std::vector<Method> methods_;
const Class* parent_;
};
// Экземпляр класса
class ClassInstance : public Object {
public:
explicit ClassInstance(const Class& cls);
/*
* Если у объекта есть метод __str__, выводит в os результат, возвращённый этим методом.
* В противном случае в os выводится адрес объекта.
*/
void Print(std::ostream& os, Context& context) override;
/*
* Вызывает у объекта метод method, передавая ему actual_args параметров.
* Параметр context задаёт контекст для выполнения метода.
* Если ни сам класс, ни его родители не содержат метод method, метод выбрасывает исключение
* runtime_error
*/
ObjectHolder Call(const std::string& name, const std::vector<ObjectHolder>& actual_args,
Context& context);
// Возвращает true, если объект имеет метод method, принимающий argument_count параметров
[[nodiscard]] bool HasMethod(const std::string& method, size_t argument_count) const;
// Возвращает ссылку на Closure, содержащий поля объекта
[[nodiscard]] Closure& Fields();
// Возвращает константную ссылку на Closure, содержащую поля объекта
[[nodiscard]] const Closure& Fields() const;
private:
const Class& cls_;
Closure closure_;
};
/*
* Возвращает true, если lhs и rhs содержат одинаковые числа, строки или значения типа Bool.
* Если lhs - объект с методом __eq__, функция возвращает результат вызова lhs.__eq__(rhs),
* приведённый к типу Bool. Если lhs и rhs имеют значение None, функция возвращает true.
* В остальных случаях функция выбрасывает исключение runtime_error.
*
* Параметр context задаёт контекст для выполнения метода __eq__
*/
bool Equal(const ObjectHolder& lhs, const ObjectHolder& rhs, Context& context);
/*
* Если lhs и rhs - числа, строки или значения bool, функция возвращает результат их сравнения
* оператором <.
* Если lhs - объект с методом __lt__, возвращает результат вызова lhs.__lt__(rhs),
* приведённый к типу bool. В остальных случаях функция выбрасывает исключение runtime_error.
*
* Параметр context задаёт контекст для выполнения метода __lt__
*/
bool Less(const ObjectHolder& lhs, const ObjectHolder& rhs, Context& context);
// Возвращает значение, противоположное Equal(lhs, rhs, context)
bool NotEqual(const ObjectHolder& lhs, const ObjectHolder& rhs, Context& context);
// Возвращает значение lhs>rhs, используя функции Equal и Less
bool Greater(const ObjectHolder& lhs, const ObjectHolder& rhs, Context& context);
// Возвращает значение lhs<=rhs, используя функции Equal и Less
bool LessOrEqual(const ObjectHolder& lhs, const ObjectHolder& rhs, Context& context);
// Возвращает значение, противоположное Less(lhs, rhs, context)
bool GreaterOrEqual(const ObjectHolder& lhs, const ObjectHolder& rhs, Context& context);
// Контекст-заглушка, применяется в тестах.
// В этом контексте весь вывод перенаправляется в строковый поток вывода output
struct DummyContext : Context {
std::ostream& GetOutputStream() override {
return output;
}
std::ostringstream output;
};
// Простой контекст, в нём вывод происходит в поток output, переданный в конструктор
class SimpleContext : public runtime::Context {
public:
explicit SimpleContext(std::ostream& output)
: output_(output) {
}
std::ostream& GetOutputStream() override {
return output_;
}
private:
std::ostream& output_;
};
} // namespace runtime