1 | // SExp - A S-Expression Parser for C++ |
2 | // Copyright (C) 2006 Matthias Braun <matze@braunis.de> |
3 | // 2015 Ingo Ruhnke <grumbel@gmail.com> |
4 | // |
5 | // This program is free software: you can redistribute it and/or modify |
6 | // it under the terms of the GNU General Public License as published by |
7 | // the Free Software Foundation, either version 3 of the License, or |
8 | // (at your option) any later version. |
9 | // |
10 | // This program is distributed in the hope that it will be useful, |
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | // GNU General Public License for more details. |
14 | // |
15 | // You should have received a copy of the GNU General Public License |
16 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | |
18 | #ifndef HEADER_SEXP_VALUE_HPP |
19 | #define |
20 | |
21 | #include <assert.h> |
22 | #include <memory> |
23 | #include <string> |
24 | #include <vector> |
25 | #include <sexp/error.hpp> |
26 | #include <stdint.h> |
27 | |
28 | namespace sexp { |
29 | |
30 | class Value |
31 | { |
32 | public: |
33 | enum class Type : unsigned char |
34 | { |
35 | NIL, |
36 | BOOLEAN, |
37 | INTEGER, |
38 | REAL, |
39 | STRING, |
40 | SYMBOL, |
41 | CONS, |
42 | ARRAY |
43 | }; |
44 | |
45 | private: |
46 | struct Cons; |
47 | |
48 | #if INTPTR_MAX == INT32_MAX |
49 | unsigned m_line : 24; |
50 | #else |
51 | int m_line; |
52 | #endif |
53 | |
54 | Value::Type m_type; |
55 | |
56 | union Data |
57 | { |
58 | inline Data() {} |
59 | inline Data(bool v) : m_bool(v) {} |
60 | inline Data(int v) : m_int(v) {} |
61 | inline Data(float v) : m_float(v) {} |
62 | inline Data(std::string* v) : m_string(v) {} |
63 | inline Data(Cons* v) : m_cons(v) {} |
64 | inline Data(std::vector<Value>* v) : m_array(v) {} |
65 | |
66 | bool m_bool; |
67 | int m_int; |
68 | float m_float; |
69 | |
70 | std::string* m_string; |
71 | Cons* m_cons; |
72 | std::vector<Value>* m_array; |
73 | } m_data; |
74 | |
75 | struct BooleanTag {}; |
76 | struct IntegerTag {}; |
77 | struct RealTag {}; |
78 | struct StringTag {}; |
79 | struct SymbolTag {}; |
80 | struct ConsTag {}; |
81 | struct ArrayTag {}; |
82 | |
83 | public: |
84 | /** Returns a reference to a nil value for use in functions that |
85 | return a reference and need to return a nil value */ |
86 | static Value const& nil_ref() { static Value const s_nil; return s_nil; } |
87 | static Value nil() { return Value(); } |
88 | static Value boolean(bool v) { return Value(BooleanTag(), v); } |
89 | static Value integer(int v) { return Value(IntegerTag(), v); } |
90 | static Value real(float v) { return Value(RealTag(), v); } |
91 | static Value string(std::string const& v) { return Value(StringTag(), v); } |
92 | static Value symbol(std::string const& v) { return Value(SymbolTag(), v); } |
93 | static Value cons(Value&& car, Value&& cdr) { return Value(ConsTag(), std::move(car), std::move(cdr)); } |
94 | static Value cons() { return Value(ConsTag(), Value::nil(), Value::nil()); } |
95 | |
96 | static Value array(std::vector<Value> arr) { return Value(ArrayTag(), std::move(arr)); } |
97 | template<typename... Args> |
98 | static Value array(Args&&... args) { return Value(ArrayTag(), std::move(args)...); } |
99 | |
100 | static Value list() |
101 | { |
102 | return Value::nil(); |
103 | } |
104 | |
105 | template<typename... Args> |
106 | static Value list(Value&& head, Args&&... rest) |
107 | { |
108 | return Value::cons(std::move(head), list(std::move(rest)...)); |
109 | } |
110 | |
111 | int get_line() const { return static_cast<int>(m_line); } |
112 | void set_line(int line) |
113 | { |
114 | #if INTPTR_MAX == INT32_MAX |
115 | m_line = static_cast<unsigned int>(line) & 0xffffff; |
116 | #else |
117 | m_line = line; |
118 | #endif |
119 | } |
120 | |
121 | private: |
122 | inline explicit Value(BooleanTag, bool value) : m_line(0), m_type(Type::BOOLEAN), m_data(value) {} |
123 | inline explicit Value(IntegerTag, int value) : m_line(0), m_type(Type::INTEGER), m_data(value) {} |
124 | inline explicit Value(RealTag, float value) : m_line(0), m_type(Type::REAL), m_data(value) {} |
125 | inline Value(StringTag, std::string const& value) : |
126 | m_line(0), |
127 | m_type(Type::STRING), |
128 | m_data(new std::string(value)) |
129 | {} |
130 | inline Value(SymbolTag, std::string const& value) : |
131 | m_line(0), |
132 | m_type(Type::SYMBOL), |
133 | m_data(new std::string(value)) |
134 | {} |
135 | inline Value(ConsTag, Value&& car, Value&& cdr); |
136 | inline Value(ArrayTag, std::vector<Value> arr) : |
137 | m_line(0), |
138 | m_type(Type::ARRAY), |
139 | m_data(new std::vector<Value>(std::move(arr))) |
140 | {} |
141 | template<typename... Args> |
142 | inline Value(ArrayTag, Args&&... args) : |
143 | m_line(0), |
144 | m_type(Type::ARRAY), |
145 | m_data(new std::vector<Value>{std::move(args)...}) |
146 | {} |
147 | |
148 | void destroy(); |
149 | |
150 | [[noreturn]] |
151 | void type_error(const char* msg) const |
152 | { |
153 | throw TypeError(m_line, msg); |
154 | } |
155 | |
156 | public: |
157 | Value(Value const& other); |
158 | |
159 | inline Value(Value&& other) : |
160 | m_line(other.m_line), |
161 | m_type(other.m_type), |
162 | m_data(other.m_data) |
163 | { |
164 | other.m_type = Type::NIL; |
165 | } |
166 | |
167 | inline Value() : |
168 | m_line(0), |
169 | m_type(Type::NIL), |
170 | m_data() |
171 | {} |
172 | |
173 | inline ~Value() |
174 | { |
175 | destroy(); |
176 | } |
177 | |
178 | inline Value& operator=(Value&& other) |
179 | { |
180 | destroy(); |
181 | |
182 | m_line = other.m_line; |
183 | m_type = other.m_type; |
184 | m_data = other.m_data; |
185 | |
186 | other.m_type = Type::NIL; |
187 | |
188 | return *this; |
189 | } |
190 | |
191 | inline Value& operator=(Value const& other) |
192 | { |
193 | destroy(); |
194 | new (this) Value(other); |
195 | return *this; |
196 | } |
197 | |
198 | inline Type get_type() const { return m_type; } |
199 | |
200 | inline explicit operator bool() const { return m_type != Type::NIL; } |
201 | |
202 | inline bool is_nil() const { return m_type == Type::NIL; } |
203 | inline bool is_boolean() const { return m_type == Type::BOOLEAN; } |
204 | inline bool is_integer() const { return m_type == Type::INTEGER; } |
205 | inline bool is_real() const { return (m_type == Type::REAL || m_type == Type::INTEGER); } |
206 | inline bool is_string() const { return m_type == Type::STRING; } |
207 | inline bool is_symbol() const { return m_type == Type::SYMBOL; } |
208 | inline bool is_cons() const { return m_type == Type::CONS; } |
209 | inline bool is_array() const { return m_type == Type::ARRAY; } |
210 | |
211 | Value const& get_car() const; |
212 | Value const& get_cdr() const; |
213 | |
214 | Value& get_car(); |
215 | Value& get_cdr(); |
216 | |
217 | void set_car(Value&& sexpr); |
218 | void set_cdr(Value&& sexpr); |
219 | |
220 | void append(Value&& sexpr); |
221 | |
222 | bool as_bool() const; |
223 | int as_int() const; |
224 | float as_float() const; |
225 | std::string const& as_string() const; |
226 | std::vector<Value> const& as_array() const; |
227 | |
228 | bool operator==(Value const& other) const; |
229 | |
230 | std::string str() const; |
231 | }; |
232 | |
233 | struct Value::Cons |
234 | { |
235 | Value car; |
236 | Value cdr; |
237 | }; |
238 | |
239 | inline |
240 | Value::Value(ConsTag, Value&& car, Value&& cdr) : |
241 | m_line(0), |
242 | m_type(Type::CONS), |
243 | m_data(new Cons{std::move(car), std::move(cdr)}) |
244 | {} |
245 | |
246 | inline void |
247 | Value::destroy() |
248 | { |
249 | switch(m_type) |
250 | { |
251 | case Value::Type::STRING: |
252 | case Value::Type::SYMBOL: |
253 | delete m_data.m_string; |
254 | break; |
255 | |
256 | case Value::Type::CONS: |
257 | delete m_data.m_cons; |
258 | break; |
259 | |
260 | case Value::Type::ARRAY: |
261 | delete m_data.m_array; |
262 | break; |
263 | |
264 | default: |
265 | // atoms don't need deletion |
266 | break; |
267 | } |
268 | } |
269 | |
270 | inline |
271 | Value::Value(Value const& other) : |
272 | m_line(other.m_line), |
273 | m_type(other.m_type) |
274 | { |
275 | switch(m_type) |
276 | { |
277 | case Type::NIL: |
278 | break; |
279 | |
280 | case Type::BOOLEAN: |
281 | m_data.m_bool = other.m_data.m_bool; |
282 | break; |
283 | |
284 | case Type::INTEGER: |
285 | m_data.m_int = other.m_data.m_int; |
286 | break; |
287 | |
288 | case Type::REAL: |
289 | m_data.m_float = other.m_data.m_float; |
290 | break; |
291 | |
292 | case Type::STRING: |
293 | case Type::SYMBOL: |
294 | m_data.m_string = new std::string(*other.m_data.m_string); |
295 | break; |
296 | |
297 | case Type::CONS: |
298 | m_data.m_cons = new Cons(*other.m_data.m_cons); |
299 | break; |
300 | |
301 | case Type::ARRAY: |
302 | m_data.m_array = new std::vector<Value>(*other.m_data.m_array); |
303 | break; |
304 | } |
305 | } |
306 | |
307 | inline bool |
308 | Value::operator==(Value const& rhs) const |
309 | { |
310 | if (m_type == rhs.m_type) |
311 | { |
312 | switch(m_type) |
313 | { |
314 | case Type::NIL: |
315 | return true; |
316 | |
317 | case Value::Type::BOOLEAN: |
318 | return m_data.m_bool == rhs.m_data.m_bool; |
319 | |
320 | case Value::Type::INTEGER: |
321 | return m_data.m_int == rhs.m_data.m_int; |
322 | |
323 | case Value::Type::REAL: |
324 | return m_data.m_float == rhs.m_data.m_float; |
325 | |
326 | case Value::Type::STRING: |
327 | case Value::Type::SYMBOL: |
328 | return *m_data.m_string == *rhs.m_data.m_string; |
329 | |
330 | case Value::Type::CONS: |
331 | return (m_data.m_cons->car == rhs.m_data.m_cons->car && |
332 | m_data.m_cons->cdr == rhs.m_data.m_cons->cdr); |
333 | |
334 | case Value::Type::ARRAY: |
335 | return *m_data.m_array == *rhs.m_data.m_array; |
336 | } |
337 | assert(false && "should never be reached" ); |
338 | return false; |
339 | } |
340 | else |
341 | { |
342 | return false; |
343 | } |
344 | } |
345 | |
346 | inline Value const& |
347 | Value::get_car() const |
348 | { |
349 | if (m_type == Type::CONS) |
350 | { |
351 | return m_data.m_cons->car; |
352 | } |
353 | else |
354 | { |
355 | type_error("sexp::Value::get_car(): wrong type, expected Type::CONS" ); |
356 | } |
357 | } |
358 | |
359 | inline Value const& |
360 | Value::get_cdr() const |
361 | { |
362 | if (m_type == Type::CONS) |
363 | { |
364 | return m_data.m_cons->cdr; |
365 | } |
366 | else |
367 | { |
368 | type_error("sexp::Value::get_cdr(): wrong type, expected Type::CONS" ); |
369 | } |
370 | } |
371 | |
372 | inline Value& |
373 | Value::get_car() |
374 | { |
375 | return const_cast<Value&>(static_cast<Value const&>(*this).get_car()); |
376 | } |
377 | |
378 | inline Value& |
379 | Value::get_cdr() |
380 | { |
381 | return const_cast<Value&>(static_cast<Value const&>(*this).get_cdr()); |
382 | } |
383 | |
384 | inline void |
385 | Value::set_car(Value&& sexpr) |
386 | { |
387 | if (m_type == Type::CONS) |
388 | { |
389 | m_data.m_cons->car = std::move(sexpr); |
390 | } |
391 | else |
392 | { |
393 | type_error("sexp::Value::set_car(): wrong type, expected Type::CONS" ); |
394 | } |
395 | } |
396 | |
397 | inline void |
398 | Value::set_cdr(Value&& sexpr) |
399 | { |
400 | if (m_type == Type::CONS) |
401 | { |
402 | m_data.m_cons->cdr = std::move(sexpr); |
403 | } |
404 | else |
405 | { |
406 | type_error("sexp::Value::set_cdr(): wrong type, expected Type::CONS" ); |
407 | } |
408 | } |
409 | |
410 | inline void |
411 | Value::append(Value&& sexpr) |
412 | { |
413 | if (m_type == Type::ARRAY) |
414 | { |
415 | m_data.m_array->push_back(std::move(sexpr)); |
416 | } |
417 | else |
418 | { |
419 | type_error("sexp::Value::append(): wrong type, expected Type::ARRAY" ); |
420 | } |
421 | } |
422 | |
423 | inline bool |
424 | Value::as_bool() const |
425 | { |
426 | if (m_type == Type::BOOLEAN) |
427 | { |
428 | return m_data.m_bool; |
429 | } |
430 | else |
431 | { |
432 | type_error("sexp::Value::as_bool(): wrong type, expected Type::BOOLEAN" ); |
433 | } |
434 | } |
435 | |
436 | inline int |
437 | Value::as_int() const |
438 | { |
439 | if (m_type == Type::INTEGER) |
440 | { |
441 | return m_data.m_int; |
442 | } |
443 | else |
444 | { |
445 | type_error("sexp::Value::as_int(): wrong type, expected Type::INTEGER" ); |
446 | } |
447 | |
448 | } |
449 | |
450 | inline float |
451 | Value::as_float() const |
452 | { |
453 | if (m_type == Type::REAL) |
454 | { |
455 | return m_data.m_float; |
456 | } |
457 | else if (m_type == Type::INTEGER) |
458 | { |
459 | return static_cast<float>(m_data.m_int); |
460 | } |
461 | else |
462 | { |
463 | type_error("sexp::Value::as_float(): wrong type, expected Type::INTEGER or Type::REAL" ); |
464 | } |
465 | } |
466 | |
467 | inline std::string const& |
468 | Value::as_string() const |
469 | { |
470 | if (m_type == Type::SYMBOL || m_type == Type::STRING) |
471 | { |
472 | return *m_data.m_string; |
473 | } |
474 | else |
475 | { |
476 | type_error("sexp::Value::as_float(): wrong type, expected Type::SYMBOL or Type::STRING" ); |
477 | } |
478 | } |
479 | |
480 | inline std::vector<Value> const& |
481 | Value::as_array() const |
482 | { |
483 | if (m_type == Type::ARRAY) |
484 | { |
485 | return *m_data.m_array; |
486 | } |
487 | else |
488 | { |
489 | type_error("sexp::Value::as_array(): wrong type, expected Type::ARRAY" ); |
490 | } |
491 | } |
492 | |
493 | } // namespace sexp |
494 | |
495 | #endif |
496 | |
497 | /* EOF */ |
498 | |