Phasor 3.1.1
Stack VM based Programming Language
Loading...
Searching...
No Matches
Value.hpp
Go to the documentation of this file.
1#pragma once
2#include <iostream>
3#include <string>
4#include <variant>
5#include <unordered_map>
6#include <memory>
7#include <vector>
8#include <format>
9#include <string>
10
11inline std::string escapeString(const std::string& input) {
12 std::string output;
13 output.reserve(input.size());
14
15 for (char c : input) {
16 switch (c) {
17 case '\n': output += "\\n"; break;
18 case '\t': output += "\\t"; break;
19 case '\r': output += "\\r"; break;
20 case '\0': output += "\\0"; break;
21 case '\\': output += "\\\\"; break;
22 case '\"': output += "\\\""; break;
23 case '\'': output += "\\'"; break;
24 case '\a': output += "\\a"; break;
25 case '\b': output += "\\b"; break;
26 case '\f': output += "\\f"; break;
27 case '\v': output += "\\v"; break;
28 default:
29 if (c < 0x20 || c == 0x7F) {
30 char buf[5];
31 snprintf(buf, sizeof(buf), "\\x%02X", (unsigned char)c);
32 output += buf;
33 } else {
34 output += c;
35 }
36 break;
37 }
38 }
39
40 return output;
41}
42
44namespace Phasor
45{
46
60
66class Value
67{
68 public:
70 {
71 std::string structName;
72 std::unordered_map<std::string, Value> fields;
73 };
74 using ArrayInstance = std::vector<Value>;
75
76 private:
77 using DataType = std::variant<std::monostate, bool, int64_t, double, std::string, std::shared_ptr<StructInstance>,
78 std::shared_ptr<ArrayInstance>>;
79
81
82 public:
84 Value() : data(std::monostate{})
85 {
86 }
87
88 Value(bool b) : data(b)
89 {
90 }
91
92 Value(int64_t i) : data(i)
93 {
94 }
95
96 Value(int i) : data(static_cast<int64_t>(i))
97 {
98 }
99
100 Value(double d) : data(d)
101 {
102 }
103
104 Value(const std::string &s) : data(s)
105 {
106 }
107
108 Value(const char *s) : data(std::string(s))
109 {
110 }
111
112 Value(std::shared_ptr<StructInstance> s) : data(std::move(s))
113 {
114 }
115
116 Value(std::shared_ptr<ArrayInstance> a) : data(std::move(a))
117 {
118 }
119
122 {
123 if (std::holds_alternative<std::monostate>(data))
124 return ValueType::Null;
125 if (std::holds_alternative<bool>(data))
126 return ValueType::Bool;
127 if (std::holds_alternative<int64_t>(data))
128 return ValueType::Int;
129 if (std::holds_alternative<double>(data))
130 return ValueType::Float;
131 if (std::holds_alternative<std::string>(data))
132 return ValueType::String;
133 if (std::holds_alternative<std::shared_ptr<StructInstance>>(data))
134 return ValueType::Struct;
135 if (std::holds_alternative<std::shared_ptr<ArrayInstance>>(data))
136 return ValueType::Array;
137 return ValueType::Null; // Should not be reached if default constructed
138 }
139
140 inline static Value typeToString(const ValueType &type)
141 {
142 switch (type)
143 {
144 case ValueType::Null:
145 return Value("null");
146 case ValueType::Bool:
147 return Value("bool");
148 case ValueType::Int:
149 return Value("int");
150 case ValueType::Float:
151 return Value("float");
153 return Value("string");
155 return Value("struct");
156 case ValueType::Array:
157 return Value("array");
158 default:
159 return Value("unknown");
160 }
161 }
162
164 bool isNull() const
165 {
166 return getType() == ValueType::Null;
167 }
168
169 bool isBool() const
170 {
171 return getType() == ValueType::Bool;
172 }
173
174 bool isInt() const
175 {
176 return getType() == ValueType::Int;
177 }
178
179 bool isFloat() const
180 {
181 return getType() == ValueType::Float;
182 }
183
184 bool isString() const
185 {
186 return getType() == ValueType::String;
187 }
188
189 bool isNumber() const
190 {
191 return isInt() || isFloat();
192 }
193
194 bool isArray() const
195 {
196 return std::holds_alternative<std::shared_ptr<ArrayInstance>>(data);
197 }
198
200 bool asBool() const
201 {
202 return std::get<bool>(data);
203 }
204
205 int64_t asInt() const
206 {
207 if (isInt())
208 return std::get<int64_t>(data);
209 if (isFloat())
210 return static_cast<int64_t>(std::get<double>(data));
211 return 0;
212 }
213
214 double asFloat() const
215 {
216 if (isFloat())
217 return std::get<double>(data);
218 if (isInt())
219 return static_cast<double>(std::get<int64_t>(data));
220 return 0.0;
221 }
222
223 std::string asString() const
224 {
225 if (isString())
226 return std::get<std::string>(data);
227 return toString();
228 }
229
230 std::shared_ptr<ArrayInstance> asArray()
231 {
232 return std::get<std::shared_ptr<ArrayInstance>>(data);
233 }
234
236 const std::shared_ptr<const ArrayInstance> asArray() const
237 {
238 return std::get<std::shared_ptr<ArrayInstance>>(data);
239 }
240
242 Value operator+(const Value &other) const
243 {
244 if (isInt() && other.isInt())
245 return Value(asInt() + other.asInt());
246 if (isNumber() && other.isNumber())
247 return Value(asFloat() + other.asFloat());
248 if (isString() && other.isString())
249 return Value(asString() + other.asString());
250 throw std::runtime_error("Cannot add these value types");
251 }
252
254 Value operator-(const Value &other) const
255 {
256 if (isInt() && other.isInt())
257 return Value(asInt() - other.asInt());
258 if (isNumber() && other.isNumber())
259 return Value(asFloat() - other.asFloat());
260 throw std::runtime_error("Cannot subtract these value types");
261 }
262
264 Value operator*(const Value &other) const
265 {
266 if (isInt() && other.isInt())
267 return Value(asInt() * other.asInt());
268 if (isNumber() && other.isNumber())
269 return Value(asFloat() * other.asFloat());
270 throw std::runtime_error("Cannot multiply these value types");
271 }
272
274 Value operator/(const Value &other) const
275 {
276 if (isInt() && other.isInt())
277 {
278 if (other.asInt() == 0)
279 throw std::runtime_error("Division by zero");
280 return Value(asInt() / other.asInt());
281 }
282 if (isNumber() && other.isNumber())
283 {
284 if (other.asFloat() == 0.0)
285 throw std::runtime_error("Division by zero");
286 return Value(asFloat() / other.asFloat());
287 }
288 throw std::runtime_error("Cannot divide these value types");
289 }
290
292 Value operator%(const Value &other) const
293 {
294 if (isInt() && other.isInt())
295 {
296 if (other.asInt() == 0)
297 throw std::runtime_error("Modulo by zero");
298 return Value(asInt() % other.asInt());
299 }
300 throw std::runtime_error("Modulo requires integer operands");
301 }
302
305 {
306 if (isInt())
307 return Value(-asInt());
308 if (isFloat())
309 return Value(-asFloat());
310 throw std::runtime_error("Cannot negate this value type");
311 }
312
315 {
316 return Value(!isTruthy());
317 }
318
320 Value logicalAnd(const Value &other) const
321 {
322 return Value(isTruthy() && other.isTruthy());
323 }
324
326 Value logicalOr(const Value &other) const
327 {
328 return Value(isTruthy() || other.isTruthy());
329 }
330
332 bool isTruthy() const
333 {
334 if (isNull())
335 return false;
336 if (isBool())
337 return asBool();
338 if (isInt())
339 return asInt() != 0;
340 if (isFloat())
341 return asFloat() != 0.0;
342 if (isString())
343 return !asString().empty();
344 return false;
345 }
346
348 bool operator==(const Value &other) const
349 {
350 if (getType() != other.getType())
351 return false;
352 if (isNull())
353 return true;
354 if (isBool())
355 return asBool() == other.asBool();
356 if (isInt())
357 return asInt() == other.asInt();
358 if (isFloat())
359 return asFloat() == other.asFloat();
360 if (isString())
361 return asString() == other.asString();
362 if (isArray())
363 {
364 if (!other.isArray())
365 return false;
366 const auto &self_arr = *asArray();
367 const auto &other_arr = *other.asArray();
368 return self_arr == other_arr;
369 }
370 return false;
371 }
372
374 bool operator!=(const Value &other) const
375 {
376 return !(*this == other);
377 }
378
380 bool operator<(const Value &other) const
381 {
382 if (isInt() && other.isInt())
383 return asInt() < other.asInt();
384 if (isNumber() && other.isNumber())
385 return asFloat() < other.asFloat();
386 if (isString() && other.isString())
387 return asString() < other.asString();
388 throw std::runtime_error("Cannot compare these value types ");
389 }
390
392 bool operator>(const Value &other) const
393 {
394 if (isInt() && other.isInt())
395 return asInt() > other.asInt();
396 if (isNumber() && other.isNumber())
397 return asFloat() > other.asFloat();
398 if (isString() && other.isString())
399 return asString() > other.asString();
400 throw std::runtime_error("Cannot compare these value types ");
401 }
402
404 bool operator<=(const Value &other) const
405 {
406 return !(*this > other);
407 }
408
409 bool operator>=(const Value &other) const
410 {
411 return !(*this < other);
412 }
413
415 std::string toString() const
416 {
417 if (isNull())
418 return "null";
419 if (isBool())
420 return asBool() ? "true" : "false";
421 if (isInt())
422 return std::to_string(asInt());
423 if (isFloat())
424 return std::to_string(asFloat());
425 if (isString())
426 return asString();
427 if (isArray())
428 {
429 std::string result = "[";
430 const auto &arr = *asArray();
431 for (size_t i = 0; i < arr.size(); ++i)
432 {
433 result += arr[i].toString();
434 if (i < arr.size() - 1)
435 {
436 result += ", ";
437 }
438 }
439 result += "]";
440 return result;
441 }
442 return "unknown";
443 }
444
446 const char *c_str() const
447 {
448 if (!isString())
449 throw std::runtime_error("c_str() can only be called on string values");
450 return std::get<std::string>(data).c_str();
451 }
452
454 friend std::ostream &operator<<(std::ostream &os, const Value &v)
455 {
456 os << v.toString();
457 return os;
458 }
459
460 bool isStruct() const
461 {
462 return std::holds_alternative<std::shared_ptr<StructInstance>>(data);
463 }
464
465 std::shared_ptr<StructInstance> asStruct()
466 {
467 return std::get<std::shared_ptr<StructInstance>>(data);
468 }
469
470 const std::shared_ptr<const StructInstance> asStruct() const
471 {
472 return std::get<std::shared_ptr<StructInstance>>(data);
473 }
474
475 static Value createStruct(const std::string &name)
476 {
477 return Value(std::make_shared<StructInstance>(StructInstance{.structName = name, .fields = {}}));
478 }
479
480 static Value createArray(std::vector<Value> elements = {})
481 {
482 return Value(std::make_shared<ArrayInstance>(std::move(elements)));
483 }
484
485 Value getField(const std::string &name) const
486 {
487 if (!std::holds_alternative<std::shared_ptr<StructInstance>>(data))
488 throw std::runtime_error("getField() called on non-struct value");
489 auto s = std::get<std::shared_ptr<StructInstance>>(data);
490 auto it = s->fields.find(name);
491 if (it == s->fields.end())
492 return Value();
493 return it->second;
494 }
495
496 void setField(const std::string &name, Value value)
497 {
498 if (!std::holds_alternative<std::shared_ptr<StructInstance>>(data))
499 throw std::runtime_error("setField() called on non-struct value");
500 auto s = std::get<std::shared_ptr<StructInstance>>(data);
501 s->fields[name] = std::move(value);
502 }
503
504 bool hasField(const std::string &name) const
505 {
506 if (!std::holds_alternative<std::shared_ptr<StructInstance>>(data))
507 return false;
508 auto s = std::get<std::shared_ptr<StructInstance>>(data);
509 return s->fields.find(name) != s->fields.end();
510 }
511};
512} // namespace Phasor
513
514template <>
515struct std::formatter<Phasor::Value>
516{
517 enum class Style { Value, TypeOnly, TypeValue, Debug, Quoted };
519 std::string_view passthrough;
520
521 constexpr auto parse(std::format_parse_context &ctx)
522 {
523 auto it = ctx.begin();
524 auto end = ctx.end();
525
526 auto close = it;
527 while (close != end && *close != '}') ++close;
528
529 std::string_view full(&*it, static_cast<size_t>(close - it));
530 std::string_view inner = full;
531
532 if (!full.empty())
533 {
534 switch (full.back())
535 {
536 case 't': style = Style::TypeOnly; inner = full.substr(0, full.size() - 1); break;
537 case 'T': style = Style::TypeValue; inner = full.substr(0, full.size() - 1); break;
538 case '?': style = Style::Debug; inner = full.substr(0, full.size() - 1); break;
539 case 'q': style = Style::Quoted; inner = full.substr(0, full.size() - 1); break;
540 default: break;
541 }
542 }
543
544 passthrough = inner;
545 return close;
546 }
547
548 template <typename FormatContext>
549 auto format(const Phasor::Value &v, FormatContext &ctx) const
550 {
551 std::string fmtstr;
552 fmtstr.reserve(passthrough.size() + 3);
553 fmtstr += "{:";
554 fmtstr += passthrough;
555 fmtstr += '}';
556
557 auto fwd = [&]<typename T>(const T &val) {
558 return std::vformat_to(ctx.out(), fmtstr, std::make_format_args(val));
559 };
560
561 using namespace Phasor;
562
563 switch (style)
564 {
565 case Style::TypeOnly:
566 return fwd(Value::typeToString(v.getType()).asString());
567
568 case Style::TypeValue:
569 return fwd(Value::typeToString(v.getType()).asString()
570 + "(" + escapeString(v.toString()) + ")");
571
572 case Style::Debug:
573 return fwd(debug_repr(v));
574
575 case Style::Quoted:
576 if (v.isString())
577 return fwd("\"" + escapeString(v.asString()) + "\"");
578 [[fallthrough]];
579
580 case Style::Value:
581 default:
582 switch (v.getType())
583 {
584 case ValueType::Null: return std::format_to(ctx.out(), "null");
585 case ValueType::Bool: return fwd(v.asBool());
586 case ValueType::Int: return fwd(v.asInt());
587 case ValueType::Float: return fwd(v.asFloat());
588 case ValueType::String: return fwd(debug_repr(escapeString(v.asString())));
589 case ValueType::Array: return fwd(v.toString());
590 case ValueType::Struct: return fwd(v.toString());
591 }
592 }
593 return ctx.out();
594 }
595
596 private:
597 static std::string debug_repr(const Phasor::Value &v)
598 {
599 using Phasor::ValueType;
600 switch (v.getType())
601 {
602 case ValueType::Null: return "null";
603 case ValueType::String: return "\"" + escapeString(v.asString()) + "\"";
604 case ValueType::Array:
605 {
606 const auto &arr = *v.asArray();
607 std::string out = "[";
608 for (std::size_t i = 0; i < arr.size(); ++i)
609 {
610 out += debug_repr(arr[i]);
611 if (i + 1 < arr.size()) out += ", ";
612 }
613 return out + "]";
614 }
616 {
617 const auto &s = *v.asStruct();
618 std::string out = s.structName + " { ";
619 bool first = true;
620 for (const auto &[k, val] : s.fields)
621 {
622 if (!first) out += ", ";
623 out += k + ": " + debug_repr(val);
624 first = false;
625 }
626 return out + " }";
627 }
628 default: return v.toString();
629 }
630 }
631
632 static std::string escape(const std::string &s)
633 {
634 std::string out;
635 out.reserve(s.size());
636 for (char c : s)
637 switch (c)
638 {
639 case '"': out += "\\\""; break;
640 case '\\': out += "\\\\"; break;
641 case '\n': out += "\\n"; break;
642 case '\t': out += "\\t"; break;
643 default: out += c;
644 }
645 return out;
646 }
647};
std::string escapeString(const std::string &input)
Definition Value.hpp:11
A value in the Phasor VM.
Definition Value.hpp:67
bool isTruthy() const
Helper to determine truthiness.
Definition Value.hpp:332
const char * c_str() const
Convert to C Style String.
Definition Value.hpp:446
std::shared_ptr< StructInstance > asStruct()
Definition Value.hpp:465
std::string toString() const
Convert to string for printing.
Definition Value.hpp:415
const std::shared_ptr< const StructInstance > asStruct() const
Definition Value.hpp:470
Value operator-() const
Unary negation.
Definition Value.hpp:304
double asFloat() const
Get the value as a double.
Definition Value.hpp:214
Value(std::shared_ptr< StructInstance > s)
Struct constructor.
Definition Value.hpp:112
static Value typeToString(const ValueType &type)
Definition Value.hpp:140
Value(int i)
Integer constructor.
Definition Value.hpp:96
bool isStruct() const
Definition Value.hpp:460
bool isNumber() const
Check if the value is a number.
Definition Value.hpp:189
std::string asString() const
Get the value as a string.
Definition Value.hpp:223
friend std::ostream & operator<<(std::ostream &os, const Value &v)
Print to output stream.
Definition Value.hpp:454
std::shared_ptr< ArrayInstance > asArray()
Get the value as an array.
Definition Value.hpp:230
Value operator/(const Value &other) const
Divide two values.
Definition Value.hpp:274
bool operator>=(const Value &other) const
Greater than or equal to comparison.
Definition Value.hpp:409
Value operator-(const Value &other) const
Subtract two values.
Definition Value.hpp:254
std::vector< Value > ArrayInstance
Definition Value.hpp:74
bool hasField(const std::string &name) const
Definition Value.hpp:504
Value(int64_t i)
Integer constructor.
Definition Value.hpp:92
void setField(const std::string &name, Value value)
Definition Value.hpp:496
bool operator<=(const Value &other) const
Less than or equal to comparison.
Definition Value.hpp:404
bool isNull() const
Check if the value is null.
Definition Value.hpp:164
int64_t asInt() const
Get the value as an integer.
Definition Value.hpp:205
bool isBool() const
Check if the value is a boolean.
Definition Value.hpp:169
Value(bool b)
Boolean constructor.
Definition Value.hpp:88
DataType data
Definition Value.hpp:80
std::variant< std::monostate, bool, int64_t, double, std::string, std::shared_ptr< StructInstance >, std::shared_ptr< ArrayInstance > > DataType
Definition Value.hpp:77
bool isFloat() const
Check if the value is a double.
Definition Value.hpp:179
static Value createStruct(const std::string &name)
Definition Value.hpp:475
bool isArray() const
Check if the value is an array.
Definition Value.hpp:194
ValueType getType() const
Get the type of the value.
Definition Value.hpp:121
const std::shared_ptr< const ArrayInstance > asArray() const
Get the value as an array (const).
Definition Value.hpp:236
Value logicalAnd(const Value &other) const
Logical AND.
Definition Value.hpp:320
bool asBool() const
Get the value as a boolean.
Definition Value.hpp:200
Value(const std::string &s)
String constructor.
Definition Value.hpp:104
bool operator<(const Value &other) const
Less than comparison.
Definition Value.hpp:380
Value operator*(const Value &other) const
Multiply two values.
Definition Value.hpp:264
bool operator>(const Value &other) const
Greater than comparison.
Definition Value.hpp:392
Value operator!() const
Logical negation.
Definition Value.hpp:314
Value(double d)
Double constructor.
Definition Value.hpp:100
bool operator==(const Value &other) const
Comparison operations.
Definition Value.hpp:348
bool operator!=(const Value &other) const
Inequality comparison.
Definition Value.hpp:374
Value operator%(const Value &other) const
Modulo two values.
Definition Value.hpp:292
static Value createArray(std::vector< Value > elements={})
Definition Value.hpp:480
Value(std::shared_ptr< ArrayInstance > a)
Array constructor.
Definition Value.hpp:116
Value operator+(const Value &other) const
Add two values.
Definition Value.hpp:242
Value()
Default constructor.
Definition Value.hpp:84
Value getField(const std::string &name) const
Definition Value.hpp:485
bool isString() const
Check if the value is a string.
Definition Value.hpp:184
Value(const char *s)
String constructor.
Definition Value.hpp:108
Value logicalOr(const Value &other) const
Logical OR.
Definition Value.hpp:326
bool isInt() const
Check if the value is an integer.
Definition Value.hpp:174
The Phasor Programming Language and Runtime.
Definition AST.hpp:11
ValueType
Runtime value types for the VM.
Definition Value.hpp:51
std::unordered_map< std::string, Value > fields
Definition Value.hpp:72
static std::string debug_repr(const Phasor::Value &v)
Definition Value.hpp:597
constexpr auto parse(std::format_parse_context &ctx)
Definition Value.hpp:521
static std::string escape(const std::string &s)
Definition Value.hpp:632
auto format(const Phasor::Value &v, FormatContext &ctx) const
Definition Value.hpp:549
std::string_view passthrough
Definition Value.hpp:519