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 HEADER_SEXP_VALUE_HPP
20
21#include <assert.h>
22#include <memory>
23#include <string>
24#include <vector>
25#include <sexp/error.hpp>
26#include <stdint.h>
27
28namespace sexp {
29
30class Value
31{
32public:
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
45private:
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
83public:
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
121private:
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
156public:
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
233struct Value::Cons
234{
235 Value car;
236 Value cdr;
237};
238
239inline
240Value::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
246inline void
247Value::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
270inline
271Value::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
307inline bool
308Value::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
346inline Value const&
347Value::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
359inline Value const&
360Value::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
372inline Value&
373Value::get_car()
374{
375 return const_cast<Value&>(static_cast<Value const&>(*this).get_car());
376}
377
378inline Value&
379Value::get_cdr()
380{
381 return const_cast<Value&>(static_cast<Value const&>(*this).get_cdr());
382}
383
384inline void
385Value::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
397inline void
398Value::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
410inline void
411Value::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
423inline bool
424Value::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
436inline int
437Value::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
450inline float
451Value::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
467inline std::string const&
468Value::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
480inline std::vector<Value> const&
481Value::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