Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Interpreter_Runtime.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
55#ifndef INTERPRETER_RUNTIME_H
56#define INTERPRETER_RUNTIME_H
57
58#include <cctype>
59#include <cerrno>
60#include <cstdint>
61#include <cstdlib>
62#include <limits>
63#include <memory>
64#include <sstream>
65#include <string>
66#include <string_view>
67#include <utility>
68
69#include <Compiler_HIR_Model.H>
70#include <ah-diagnostics.H>
71#include <ah-errors.H>
72#include <tpl_dynArray.H>
73
74namespace Aleph {
79
82{
83 Invalid,
84 Unit,
85 Bool,
86 Integer,
87 String,
89 Tuple,
92};
93
96{
97 None,
98 Return,
99 Break,
100 Continue,
102};
103
105inline const char *interpreter_value_kind_name(const Interpreter_Value_Kind kind) noexcept
106{
107 switch (kind)
108 {
110 return "Invalid";
112 return "Unit";
114 return "Bool";
116 return "Int";
118 return "String";
120 return "Char";
122 return "Tuple";
124 return "Function";
126 return "HostFunction";
127 }
128
129 return "Unknown";
130}
131
134{
135 switch (kind)
136 {
138 return "None";
140 return "Return";
142 return "Break";
144 return "Continue";
146 return "RuntimeError";
147 }
148
149 return "Unknown";
150}
151
159{
160 std::string code;
161 std::string message;
163
166 {
167 return not code.empty() or not message.empty();
168 }
169};
170
180 const DynArray<Interpreter_Value> &arguments,
181 Interpreter_Value &result,
183
191{
193 bool bool_value = false;
194 long long integer_value = 0;
195 char character_value = '\0';
196 std::string string_value;
197 std::unique_ptr<Interpreter_Tuple_Storage> tuple_storage;
200 = nullptr;
202 void *host_user_data = nullptr;
203 std::string callable_name;
204
206 Interpreter_Value() = default;
207
210
213
216
219
222
225 {
226 return {};
227 }
228
236
238 static Interpreter_Value make_bool(const bool b)
239 {
242 value.bool_value = b;
243 return value;
244 }
245
247 static Interpreter_Value make_integer(const long long i)
248 {
251 value.integer_value = i;
252 return value;
253 }
254
256 static Interpreter_Value make_string(std::string text)
257 {
260 value.string_value = std::move(text);
261 return value;
262 }
263
266 {
269 value.character_value = ch;
270 return value;
271 }
272
275
278
281 Interpreter_Environment *env = nullptr)
282 {
285 value.function = fn;
286 value.closure_environment = env;
287 value.callable_name = fn != nullptr ? fn->name : "<null-function>";
288 return value;
289 }
290
292 static Interpreter_Value make_host_function(std::string name,
293 const Interpreter_Host_Function callback,
294 void *user_data = nullptr)
295 {
298 value.host_function = callback;
299 value.host_user_data = user_data;
300 value.callable_name = std::move(name);
301 return value;
302 }
303
306 {
308 }
309
315
322};
323
326{
328
330
331 explicit Interpreter_Tuple_Storage(const DynArray<Interpreter_Value> &values) : elements(values)
332 {}
333
335 : elements(std::move(values))
336 {}
337};
338
349
351 : kind(other.kind), bool_value(other.bool_value), integer_value(other.integer_value),
352 character_value(other.character_value), string_value(std::move(other.string_value)),
353 tuple_storage(std::move(other.tuple_storage)), function(other.function),
354 closure_environment(other.closure_environment), host_function(other.host_function),
355 host_user_data(other.host_user_data), callable_name(std::move(other.callable_name))
356{
358}
359
361
363{
364 if (this == &other)
365 return *this;
366
367 tuple_storage.reset();
368
369 kind = other.kind;
370 bool_value = other.bool_value;
371 integer_value = other.integer_value;
372 character_value = other.character_value;
373 string_value = other.string_value;
374 function = other.function;
375 closure_environment = other.closure_environment;
376 host_function = other.host_function;
377 host_user_data = other.host_user_data;
378 callable_name = other.callable_name;
379
380 if (other.tuple_storage != nullptr)
381 tuple_storage = std::make_unique<Interpreter_Tuple_Storage>(*other.tuple_storage);
382 return *this;
383}
384
386{
387 if (this == &other)
388 return *this;
389
390 kind = other.kind;
391 bool_value = other.bool_value;
392 integer_value = other.integer_value;
393 character_value = other.character_value;
394 string_value = std::move(other.string_value);
395 tuple_storage = std::move(other.tuple_storage);
396 function = other.function;
397 closure_environment = other.closure_environment;
398 host_function = other.host_function;
399 host_user_data = other.host_user_data;
400 callable_name = std::move(other.callable_name);
401
403 return *this;
404}
405
407{
410 value.tuple_storage = std::make_unique<Interpreter_Tuple_Storage>(elements);
411 return value;
412}
413
415{
418 value.tuple_storage = std::make_unique<Interpreter_Tuple_Storage>(std::move(elements));
419 return value;
420}
421
426
437{
438 if (lhs.kind != rhs.kind)
439 return false;
440
441 switch (lhs.kind)
442 {
444 return false;
445
447 return true;
448
450 return lhs.bool_value == rhs.bool_value;
451
453 return lhs.integer_value == rhs.integer_value;
454
456 return lhs.string_value == rhs.string_value;
457
459 return lhs.character_value == rhs.character_value;
460
462 if (lhs.tuple_elements().size() != rhs.tuple_elements().size())
463 return false;
464 for (size_t i = 0; i < lhs.tuple_elements().size(); ++i)
466 rhs.tuple_elements().access(i)))
467 return false;
468 return true;
469
471 return lhs.function == rhs.function and lhs.closure_environment == rhs.closure_environment;
472
474 return lhs.host_function == rhs.host_function and lhs.host_user_data == rhs.host_user_data;
475 }
476
477 return false;
478}
479
486{
487 auto escape_text = [](std::string_view text)
488 {
489 std::string escaped;
490 escaped.reserve(text.size());
491 for (const char ch : text)
492 switch (ch)
493 {
494 case '\\':
495 escaped += "\\\\";
496 break;
497 case '"':
498 escaped += "\\\"";
499 break;
500 case '\'':
501 escaped += "\\'";
502 break;
503 case '\n':
504 escaped += "\\n";
505 break;
506 case '\r':
507 escaped += "\\r";
508 break;
509 case '\t':
510 escaped += "\\t";
511 break;
512 default:
513 escaped.push_back(ch);
514 }
515 return escaped;
516 };
517
518 std::ostringstream out;
519 switch (value.kind)
520 {
522 out << "Invalid";
523 break;
524
526 out << "Unit";
527 break;
528
530 out << "Bool(" << (value.bool_value ? "true" : "false") << ")";
531 break;
532
534 out << "Int(" << value.integer_value << ")";
535 break;
536
538 out << "String(\"" << escape_text(value.string_value) << "\")";
539 break;
540
542 out << "Char('" << escape_text(std::string(1, value.character_value)) << "')";
543 break;
544
546 out << "Tuple(";
547 for (size_t i = 0; i < value.tuple_elements().size(); ++i)
548 {
549 if (i > 0)
550 out << ", ";
551 out << interpreter_value_to_string(value.tuple_elements().access(i));
552 }
553 out << ')';
554 break;
555
557 out << "Function(" << value.callable_name << ")";
558 break;
559
561 out << "HostFunction(" << value.callable_name << ")";
562 break;
563 }
564
565 return out.str();
566}
567
574
577{
580
581 Interpreter_Value *find_local(const std::string &name) noexcept
582 {
583 for (size_t i = bindings_.size(); i > 0; --i)
584 if (bindings_.access(i - 1).name == name)
585 return &bindings_.access(i - 1).value;
586 return nullptr;
587 }
588
589 const Interpreter_Value *find_local(const std::string &name) const noexcept
590 {
591 for (size_t i = bindings_.size(); i > 0; --i)
592 if (bindings_.access(i - 1).name == name)
593 return &bindings_.access(i - 1).value;
594 return nullptr;
595 }
596
597public:
602
605 {
607 }
608
614
617 {
618 bindings_.clear();
619 }
620
623 {
624 return bindings_.is_empty();
625 }
626
629 {
630 return bindings_.size();
631 }
632
642
649 bool define(const std::string &name, const Interpreter_Value &value)
650 {
651 if (find_local(name) != nullptr)
652 return false;
653 bindings_.append({name, value});
654 return true;
655 }
656
662 Interpreter_Value *lookup(const std::string &name) noexcept
663 {
664 if (auto *value = find_local(name); value != nullptr)
665 return value;
666 return parent_env != nullptr ? parent_env->lookup(name) : nullptr;
667 }
668
674 const Interpreter_Value *lookup(const std::string &name) const noexcept
675 {
676 if (const auto *value = find_local(name); value != nullptr)
677 return value;
678 return parent_env != nullptr ? parent_env->lookup(name) : nullptr;
679 }
680
687 bool assign(const std::string &name, const Interpreter_Value &value)
688 {
689 if (auto *slot = find_local(name); slot != nullptr)
690 {
691 *slot = value;
692 return true;
693 }
694 return parent_env != nullptr ? parent_env->assign(name, value) : false;
695 }
696};
697
700{
701 size_t max_call_depth = 256;
702};
703
727
735{
741
747
756
760 {
762 result.flow = flow;
763 result.value = value;
764 return result;
765 }
766
768 const std::string &code,
769 const std::string &message,
770 const std::string &note = "",
771 const std::string &help = "") const
772 {
773 if (diagnostics != nullptr)
774 {
775 auto builder = diagnostics->error(span, message).code(code);
776 if (not note.empty())
777 builder.note(note);
778 if (not help.empty())
779 builder.help(help);
780 builder.emit();
781 }
782
786 result.error = {code, message, span};
787 return result;
788 }
789
790 static bool strip_quotes(std::string_view text, const char quote, std::string_view &body) noexcept
791 {
792 if (text.size() < 2)
793 return false;
794 if (text.front() != quote or text.back() != quote)
795 return false;
796 body = text.substr(1, text.size() - 2);
797 return true;
798 }
799
800 static bool decode_escaped_text(const std::string_view body, std::string &out, std::string &error)
801 {
802 out.clear();
803 out.reserve(body.size());
804
805 for (size_t i = 0; i < body.size(); ++i)
806 {
807 const char ch = body[i];
808 if (ch != '\\')
809 {
810 out.push_back(ch);
811 continue;
812 }
813
814 if (i + 1 >= body.size())
815 {
816 error = "unterminated escape sequence";
817 return false;
818 }
819
820 switch (const char esc = body[++i])
821 {
822 case '\\':
823 out.push_back('\\');
824 break;
825 case '"':
826 out.push_back('"');
827 break;
828 case '\'':
829 out.push_back('\'');
830 break;
831 case 'n':
832 out.push_back('\n');
833 break;
834 case 'r':
835 out.push_back('\r');
836 break;
837 case 't':
838 out.push_back('\t');
839 break;
840 case '0':
841 out.push_back('\0');
842 break;
843 default:
844 error = std::string("unsupported escape sequence \\") + esc;
845 return false;
846 }
847 }
848
849 return true;
850 }
851
853 {
854 std::string digits;
855 digits.reserve(node->text.size());
856 for (const char ch : node->text)
857 if (ch != '_')
858 digits.push_back(ch);
859
860 if (digits.empty())
861 return runtime_error(node->span, "RUN012", "malformed integer literal");
862
863 char *end = nullptr;
864 errno = 0;
865 const long long value = std::strtoll(digits.c_str(), &end, 10);
866 if (errno == ERANGE or end == nullptr or *end != '\0')
867 return runtime_error(node->span,
868 "RUN012",
869 "integer literal is out of range for the interpreter");
870
872 }
873
875 {
876 std::string_view body;
877 if (not strip_quotes(node->text, '"', body))
878 return runtime_error(node->span, "RUN012", "malformed string literal");
879
880 std::string decoded;
881 std::string error_message;
883 return runtime_error(node->span, "RUN012", error_message);
884 return ok(Interpreter_Value::make_string(std::move(decoded)));
885 }
886
888 {
889 std::string_view body;
890 if (not strip_quotes(node->text, '\'', body))
891 return runtime_error(node->span, "RUN012", "malformed character literal");
892
893 std::string decoded;
894 std::string error_message;
896 return runtime_error(node->span, "RUN012", error_message);
897 if (decoded.size() != 1)
898 return runtime_error(node->span,
899 "RUN012",
900 "character literals must decode to exactly one character");
901
903 }
904
906 const Source_Span &span,
907 const std::string &context) const
908 {
910 return ok(value);
911
912 return runtime_error(span,
913 "RUN008",
914 context + " requires Int but received "
915 + std::string(interpreter_value_kind_name(value.kind)));
916 }
917
919 const Source_Span &span,
920 const std::string &context) const
921 {
923 return ok(value);
924
925 return runtime_error(span,
926 "RUN003",
927 context + " requires Bool but received "
928 + std::string(interpreter_value_kind_name(value.kind)));
929 }
930
933 {
934 if (expr == nullptr)
935 return runtime_error({}, "RUN001", "null HIR expression");
936
937 switch (expr->kind)
938 {
940 return runtime_error(expr->span, "RUN001", "invalid HIR expression");
941
943 {
944 const auto *node = static_cast<const Compiler_HIR_Constant_Expr *>(expr);
945 switch (node->constant_kind)
946 {
950 return ok(Interpreter_Value::make_bool(node->bool_value));
952 return parse_integer_literal(node);
954 return parse_string_literal(node);
956 return parse_char_literal(node);
957 }
958 return runtime_error(node->span, "RUN001", "unknown HIR constant kind");
959 }
960
962 {
963 const auto *node = static_cast<const Compiler_HIR_Variable_Expr *>(expr);
964 if (auto *value = env.lookup(node->name); value != nullptr)
965 return ok(*value);
966 return runtime_error(node->span,
967 "RUN002",
968 "unknown runtime identifier '" + node->name + "'");
969 }
970
972 {
973 const auto *node = static_cast<const Compiler_HIR_Unary_Expr *>(expr);
974 auto operand = evaluate_expr(node->operand, env);
975 if (not operand.ok())
976 return operand;
977
978 switch (node->op)
979 {
981 return require_integer(operand.value, node->span, "unary '+'");
982
984 {
985 auto checked = require_integer(operand.value, node->span, "unary '-'");
986 if (not checked.ok())
987 return checked;
988 return ok(Interpreter_Value::make_integer(-checked.value.integer_value));
989 }
990
992 {
993 auto checked = require_bool(operand.value, node->span, "unary '!'");
994 if (not checked.ok())
995 return checked;
996 return ok(Interpreter_Value::make_bool(not checked.value.bool_value));
997 }
998
999 default:
1000 return runtime_error(node->span,
1001 "RUN008",
1002 "unsupported unary operator '"
1003 + std::string(compiler_operator_name(node->op)) + "'");
1004 }
1005 }
1006
1008 {
1009 const auto *node = static_cast<const Compiler_HIR_Binary_Expr *>(expr);
1010
1012 {
1013 auto left = evaluate_expr(node->left, env);
1014 if (not left.ok())
1015 return left;
1016 auto left_bool = require_bool(left.value, node->span, "logical operator");
1017 if (not left_bool.ok())
1018 return left_bool;
1019
1020 if (node->op == Compiler_Operator_Kind::AndAnd and not left_bool.value.bool_value)
1021 return ok(Interpreter_Value::make_bool(false));
1022 if (node->op == Compiler_Operator_Kind::OrOr and left_bool.value.bool_value)
1023 return ok(Interpreter_Value::make_bool(true));
1024
1025 auto right = evaluate_expr(node->right, env);
1026 if (not right.ok())
1027 return right;
1028 auto right_bool = require_bool(right.value, node->span, "logical operator");
1029 if (not right_bool.ok())
1030 return right_bool;
1031 return ok(Interpreter_Value::make_bool(right_bool.value.bool_value));
1032 }
1033
1034 const bool is_compound_assignment
1036
1038 {
1039 if (node->left == nullptr or node->left->kind != Compiler_HIR_Expr_Kind::Variable)
1040 return runtime_error(node->span,
1041 "RUN006",
1042 "assignment requires a variable on the left-hand side");
1043 const auto *lhs = static_cast<const Compiler_HIR_Variable_Expr *>(node->left);
1044
1045 if (node->op == Compiler_Operator_Kind::Assign)
1046 {
1047 auto rhs = evaluate_expr(node->right, env);
1048 if (not rhs.ok())
1049 return rhs;
1050
1051 if (not env.assign(lhs->name, rhs.value))
1052 return runtime_error(lhs->span,
1053 "RUN002",
1054 "unknown runtime identifier '" + lhs->name + "'");
1055 return ok(rhs.value);
1056 }
1057
1058 auto current_ptr = env.lookup(lhs->name);
1059 if (current_ptr == nullptr)
1060 return runtime_error(lhs->span,
1061 "RUN002",
1062 "unknown runtime identifier '" + lhs->name + "'");
1063
1064 auto current = require_integer(*current_ptr, lhs->span, "compound assignment");
1065 if (not current.ok())
1066 return current;
1067
1068 auto rhs = evaluate_expr(node->right, env);
1069 if (not rhs.ok())
1070 return rhs;
1071 auto rhs_int = require_integer(rhs.value, node->right->span, "compound assignment");
1072 if (not rhs_int.ok())
1073 return rhs_int;
1074
1075 long long updated = current.value.integer_value;
1076 switch (node->op)
1077 {
1079 updated += rhs_int.value.integer_value;
1080 break;
1082 updated -= rhs_int.value.integer_value;
1083 break;
1085 updated *= rhs_int.value.integer_value;
1086 break;
1088 if (rhs_int.value.integer_value == 0)
1089 return runtime_error(node->span, "RUN007", "division by zero");
1090 updated /= rhs_int.value.integer_value;
1091 break;
1093 if (rhs_int.value.integer_value == 0)
1094 return runtime_error(node->span, "RUN007", "modulo by zero");
1095 updated %= rhs_int.value.integer_value;
1096 break;
1097 default:
1098 return runtime_error(node->span,
1099 "RUN008",
1100 "unsupported compound assignment operator");
1101 }
1102
1103 const auto result = Interpreter_Value::make_integer(updated);
1104 (void) env.assign(lhs->name, result);
1105 return ok(result);
1106 }
1107
1108 auto left = evaluate_expr(node->left, env);
1109 if (not left.ok())
1110 return left;
1111 auto right = evaluate_expr(node->right, env);
1112 if (not right.ok())
1113 return right;
1114
1115 switch (node->op)
1116 {
1125 {
1126 auto lhs = require_integer(left.value, node->left->span, "binary operator");
1127 if (not lhs.ok())
1128 return lhs;
1129 auto rhs = require_integer(right.value, node->right->span, "binary operator");
1130 if (not rhs.ok())
1131 return rhs;
1132
1133 switch (node->op)
1134 {
1136 return ok(Interpreter_Value::make_integer(lhs.value.integer_value
1137 + rhs.value.integer_value));
1139 return ok(Interpreter_Value::make_integer(lhs.value.integer_value
1140 - rhs.value.integer_value));
1142 return ok(Interpreter_Value::make_integer(lhs.value.integer_value
1143 * rhs.value.integer_value));
1145 if (rhs.value.integer_value == 0)
1146 return runtime_error(node->span, "RUN007", "division by zero");
1147 return ok(Interpreter_Value::make_integer(lhs.value.integer_value
1148 / rhs.value.integer_value));
1150 if (rhs.value.integer_value == 0)
1151 return runtime_error(node->span, "RUN007", "modulo by zero");
1152 return ok(Interpreter_Value::make_integer(lhs.value.integer_value
1153 % rhs.value.integer_value));
1155 return ok(Interpreter_Value::make_integer(lhs.value.integer_value
1156 & rhs.value.integer_value));
1158 return ok(Interpreter_Value::make_integer(lhs.value.integer_value
1159 | rhs.value.integer_value));
1161 return ok(Interpreter_Value::make_integer(lhs.value.integer_value
1162 ^ rhs.value.integer_value));
1163 default:
1164 return runtime_error(node->span,
1165 "RUN008",
1166 "unsupported arithmetic operator '"
1167 + std::string(compiler_operator_name(node->op)) + "'");
1168 }
1169 }
1170
1175 {
1176 auto lhs = require_integer(left.value, node->left->span, "comparison");
1177 if (not lhs.ok())
1178 return lhs;
1179 auto rhs = require_integer(right.value, node->right->span, "comparison");
1180 if (not rhs.ok())
1181 return rhs;
1182
1183 bool result = false;
1184 switch (node->op)
1185 {
1187 result = lhs.value.integer_value < rhs.value.integer_value;
1188 break;
1190 result = lhs.value.integer_value <= rhs.value.integer_value;
1191 break;
1193 result = lhs.value.integer_value > rhs.value.integer_value;
1194 break;
1196 result = lhs.value.integer_value >= rhs.value.integer_value;
1197 break;
1198 default:
1199 break;
1200 }
1201
1202 return ok(Interpreter_Value::make_bool(result));
1203 }
1204
1206 return ok(
1207 Interpreter_Value::make_bool(interpreter_values_equal(left.value, right.value)));
1208
1210 return ok(
1212
1213 default:
1214 return runtime_error(node->span,
1215 "RUN008",
1216 "unsupported binary operator '"
1217 + std::string(compiler_operator_name(node->op)) + "'");
1218 }
1219 }
1220
1222 {
1223 const auto *node = static_cast<const Compiler_HIR_Call_Expr *>(expr);
1224 auto callee = evaluate_expr(node->callee, env);
1225 if (not callee.ok())
1226 return callee;
1227
1229 for (size_t i = 0; i < node->arguments.size(); ++i)
1230 {
1231 auto arg = evaluate_expr(node->arguments.access(i), env);
1232 if (not arg.ok())
1233 return arg;
1234 arguments.append(arg.value);
1235 }
1236
1237 return call_value(callee.value, arguments, node->span);
1238 }
1239 }
1240
1241 return runtime_error(expr->span, "RUN001", "unknown HIR expression kind");
1242 }
1243
1246 {
1247 if (stmt == nullptr)
1248 return runtime_error({}, "RUN001", "null HIR statement");
1249
1250 switch (stmt->kind)
1251 {
1253 return runtime_error(stmt->span, "RUN001", "invalid HIR statement");
1254
1256 {
1257 const auto *node = static_cast<const Compiler_HIR_Eval_Stmt *>(stmt);
1258 auto value = evaluate_expr(node->expr, env);
1259 if (not value.ok())
1260 return value;
1261 return ok(value.value);
1262 }
1263
1265 {
1266 const auto *node = static_cast<const Compiler_HIR_Let_Stmt *>(stmt);
1268 if (node->initializer != nullptr)
1269 {
1270 auto init = evaluate_expr(node->initializer, env);
1271 if (not init.ok())
1272 return init;
1273 value = init.value;
1274 }
1275
1276 if (not env.define(node->name, value))
1277 return runtime_error(node->span,
1278 "RUN011",
1279 "duplicate runtime binding '" + node->name + "'");
1280 return ok(value);
1281 }
1282
1284 {
1285 const auto *node = static_cast<const Compiler_HIR_Return_Stmt *>(stmt);
1286 if (node->value == nullptr)
1288 auto value = evaluate_expr(node->value, env);
1289 if (not value.ok())
1290 return value;
1292 }
1293
1295 {
1296 const auto *node = static_cast<const Compiler_HIR_Block_Stmt *>(stmt);
1298 for (size_t i = 0; i < node->statements.size(); ++i)
1299 {
1300 auto result = evaluate_stmt(node->statements.access(i), block_env);
1301 if (result.flow != Interpreter_Control_Flow_Kind::None)
1302 return result;
1303 }
1304 return ok();
1305 }
1306
1308 {
1309 const auto *node = static_cast<const Compiler_HIR_If_Stmt *>(stmt);
1310 auto cond = evaluate_expr(node->condition, env);
1311 if (not cond.ok())
1312 return cond;
1313 auto checked = require_bool(cond.value, node->condition->span, "if condition");
1314 if (not checked.ok())
1315 return checked;
1316
1317 if (checked.value.bool_value)
1318 return node->then_branch != nullptr ? evaluate_stmt(node->then_branch, env) : ok();
1319
1320 return node->else_branch != nullptr ? evaluate_stmt(node->else_branch, env) : ok();
1321 }
1322
1324 {
1325 const auto *node = static_cast<const Compiler_HIR_While_Stmt *>(stmt);
1326 while (true)
1327 {
1328 auto cond = evaluate_expr(node->condition, env);
1329 if (not cond.ok())
1330 return cond;
1331 auto checked = require_bool(cond.value, node->condition->span, "while condition");
1332 if (not checked.ok())
1333 return checked;
1334 if (not checked.value.bool_value)
1335 return ok();
1336
1337 auto body = node->body != nullptr ? evaluate_stmt(node->body, env) : ok();
1340 return body;
1341 if (body.flow == Interpreter_Control_Flow_Kind::Break)
1342 return ok();
1345 continue;
1346 }
1347 }
1348
1351
1354 }
1355
1356 return runtime_error(stmt->span, "RUN001", "unknown HIR statement kind");
1357 }
1358
1360 const DynArray<Interpreter_Value> &arguments,
1361 const Source_Span &span)
1362 {
1363 const auto *function = callee.function;
1364 if (function == nullptr)
1365 return runtime_error(span, "RUN004", "null HIR function");
1366
1367 if (function->parameters.size() != arguments.size())
1368 return runtime_error(span,
1369 "RUN005",
1370 "function '" + function->name + "' expects "
1371 + std::to_string(function->parameters.size())
1372 + " arguments but received " + std::to_string(arguments.size()));
1373
1374 if (call_stack.size() >= options.max_call_depth)
1375 return runtime_error(span,
1376 "RUN010",
1377 "maximum call depth exceeded while calling '" + function->name + "'");
1378
1380 frame.function = function;
1381 frame.locals.set_parent(callee.closure_environment != nullptr ? callee.closure_environment
1382 : &globals);
1383
1384 for (size_t i = 0; i < function->parameters.size(); ++i)
1385 (void) frame.locals.define(function->parameters.access(i).name, arguments.access(i));
1386
1387 call_stack.append(std::move(frame));
1388 auto result = evaluate_stmt(function->body, call_stack.access(call_stack.size() - 1).locals);
1389 (void) call_stack.pop();
1390
1391 if (result.flow == Interpreter_Control_Flow_Kind::Return)
1392 return ok(result.value);
1393 if (result.flow == Interpreter_Control_Flow_Kind::Break
1395 return runtime_error(function->span,
1396 "RUN009",
1397 "control-flow escaped function '" + function->name + "'");
1398 return result;
1399 }
1400
1402 const DynArray<Interpreter_Value> &arguments,
1403 const Source_Span &span) const
1404 {
1405 if (callee.host_function == nullptr)
1406 return runtime_error(span, "RUN004", "null host function");
1407
1410 if (callee.host_function(callee.host_user_data, arguments, result, error))
1411 return ok(result);
1412
1413 auto code = error.code.empty() ? std::string("RUN004") : error.code;
1414 auto message = error.message.empty() ? "host function '" + callee.callable_name + "' failed"
1415 : error.message;
1416 return runtime_error(error.span.is_valid() ? error.span : span, code, message);
1417 }
1418
1420 const DynArray<Interpreter_Value> &arguments,
1421 const Source_Span &span)
1422 {
1423 switch (callee.kind)
1424 {
1426 return call_hir_function(callee, arguments, span);
1427
1429 return call_host_function(callee, arguments, span);
1430
1431 default:
1432 return runtime_error(span,
1433 "RUN004",
1434 "attempted to call a non-callable value of kind "
1435 + std::string(interpreter_value_kind_name(callee.kind)));
1436 }
1437 }
1438
1440 {
1441 if (function == nullptr)
1442 return false;
1443 return globals.define(function->name, Interpreter_Value::make_function(function, &globals));
1444 }
1445
1446public:
1453 const Interpreter_Runtime_Options &opts = {}) noexcept
1454 : options(opts), diagnostics(dx), prelude(nullptr), globals(&prelude)
1455 {}
1456
1459 {
1460 diagnostics = dx;
1461 }
1462
1468
1474
1477 {
1478 return call_stack.size();
1479 }
1480
1483 {
1484 globals.clear();
1486 call_stack.clear();
1487 }
1488
1496 bool bind_host_function(const std::string &name,
1497 const Interpreter_Host_Function callback,
1498 void *user_data = nullptr)
1499 {
1500 ah_runtime_error_unless(callback != nullptr)
1501 << "Interpreter_Runtime::bind_host_function(): callback is null";
1502 return prelude.define(name, Interpreter_Value::make_host_function(name, callback, user_data));
1503 }
1504
1514 {
1515 if (module == nullptr)
1516 return runtime_error({}, "RUN001", "null HIR module");
1517
1518 reset();
1519
1520 for (size_t i = 0; i < module->functions.size(); ++i)
1521 if (not install_module_function(module->functions.access(i)))
1522 return runtime_error(module->functions.access(i)->span,
1523 "RUN011",
1524 "duplicate global function '" + module->functions.access(i)->name
1525 + "'");
1526
1527 for (size_t i = 0; i < module->statements.size(); ++i)
1528 {
1529 auto result = evaluate_stmt(module->statements.access(i), globals);
1531 return result;
1532 if (result.flow == Interpreter_Control_Flow_Kind::Return
1535 return runtime_error(module->statements.access(i)->span,
1536 "RUN009",
1537 "top-level control-flow statement is not allowed");
1538 }
1539
1540 return ok();
1541 }
1542
1551 Interpreter_Execution_Result call(const std::string &name,
1552 const DynArray<Interpreter_Value> &arguments)
1553 {
1554 if (auto *value = globals.lookup(name); value != nullptr)
1555 return call_value(*value, arguments, {});
1556 if (auto *value = prelude.lookup(name); value != nullptr)
1557 return call_value(*value, arguments, {});
1558 return runtime_error({}, "RUN002", "unknown runtime identifier '" + name + "'");
1559 }
1560};
1561
1568{
1569 std::ostringstream out;
1570 out << "Environment\n";
1571 for (size_t i = 0; i < env.bindings().size(); ++i)
1572 {
1573 const auto &binding = env.bindings().access(i);
1574 out << " " << binding.name << " = " << interpreter_value_to_string(binding.value) << '\n';
1575 }
1576 return out.str();
1577}
1578
1581{
1582 std::ostringstream out;
1583 out << "Globals\n";
1584 const auto &env = runtime.global_environment();
1585 for (size_t i = 0; i < env.bindings().size(); ++i)
1586 {
1587 const auto &binding = env.bindings().access(i);
1588 out << " " << binding.name << " = " << interpreter_value_to_string(binding.value) << '\n';
1589 }
1590 return out.str();
1591}
1592} // namespace Aleph
1593
1594#endif
Reusable typed high-level IR model independent from any concrete frontend.
Plain-text diagnostic engine for compiler-style tooling.
Exception handling system with formatted messages for Aleph-w.
#define ah_runtime_error_unless(C)
Throws std::runtime_error if condition does NOT hold.
Definition ah-errors.H:255
size_t size_t int32_t value
Definition ca-c-api.h:116
size_t size_t int32_t * out
Definition ca-c-api.h:120
Diagnostic_Builder & code(const std::string &value)
Sets the stable diagnostic code.
size_t emit() const noexcept
Finalizes the builder and returns the diagnostic index.
Diagnostic_Builder & help(const std::string &msg)
Appends a help line.
Diagnostic_Builder & note(const std::string &msg)
Appends a note line.
Diagnostic accumulator and renderer.
Diagnostic_Builder error(const Source_Span &span, const std::string &msg)
Starts an error diagnostic.
size_t size() const noexcept
Return the current dimension of array.
T & access(const size_t i) const noexcept
Fast access without checking allocation and bound_min_clock checking.
T & append()
Allocate a new entry to the end of array.
Nested runtime environment with lexical lookup semantics.
void clear() noexcept
Drops all bindings from this environment only.
Interpreter_Environment(Interpreter_Environment *parent=nullptr) noexcept
Builds an environment with optional lexical parent.
const Interpreter_Value * find_local(const std::string &name) const noexcept
size_t size() const noexcept
Returns the number of bindings in this environment.
Interpreter_Value * lookup(const std::string &name) noexcept
Finds a visible binding recursively.
bool define(const std::string &name, const Interpreter_Value &value)
Defines one local binding.
void set_parent(Interpreter_Environment *parent) noexcept
Changes the lexical parent for this environment.
bool assign(const std::string &name, const Interpreter_Value &value)
Assigns an existing visible binding.
DynArray< Interpreter_Binding > bindings_
const Interpreter_Value * lookup(const std::string &name) const noexcept
Finds a visible binding recursively.
Interpreter_Environment * parent() const noexcept
Returns the lexical parent, if any.
const DynArray< Interpreter_Binding > & bindings() const noexcept
Returns read-only access to the local bindings.
Interpreter_Value * find_local(const std::string &name) noexcept
Interpreter_Environment * parent_env
bool is_empty() const noexcept
Returns whether this environment is empty.
Structured runtime for executing typed HIR.
Interpreter_Execution_Result parse_integer_literal(const Compiler_HIR_Constant_Expr *node) const
Interpreter_Environment prelude
static Interpreter_Execution_Result control(const Interpreter_Control_Flow_Kind flow, const Interpreter_Value &value=Interpreter_Value::make_unit())
Interpreter_Environment globals
Interpreter_Execution_Result parse_string_literal(const Compiler_HIR_Constant_Expr *node) const
DynArray< Interpreter_Call_Frame > call_stack
Interpreter_Execution_Result call_hir_function(const Interpreter_Value &callee, const DynArray< Interpreter_Value > &arguments, const Source_Span &span)
Interpreter_Execution_Result require_integer(const Interpreter_Value &value, const Source_Span &span, const std::string &context) const
void set_diagnostics(Diagnostic_Engine *dx) noexcept
Sets the optional diagnostic sink for runtime failures.
Interpreter_Execution_Result call_value(const Interpreter_Value &callee, const DynArray< Interpreter_Value > &arguments, const Source_Span &span)
Interpreter_Runtime(Diagnostic_Engine *dx=nullptr, const Interpreter_Runtime_Options &opts={}) noexcept
Builds a fresh interpreter runtime.
size_t call_depth() const noexcept
Returns the current call depth.
Interpreter_Execution_Result require_bool(const Interpreter_Value &value, const Source_Span &span, const std::string &context) const
const Interpreter_Environment & global_environment() const noexcept
Returns the globals environment for the last evaluated module.
Interpreter_Execution_Result runtime_error(const Source_Span &span, const std::string &code, const std::string &message, const std::string &note="", const std::string &help="") const
bool bind_host_function(const std::string &name, const Interpreter_Host_Function callback, void *user_data=nullptr)
Defines one host function in the persistent prelude.
Interpreter_Execution_Result call(const std::string &name, const DynArray< Interpreter_Value > &arguments)
Calls one visible function by name.
static Interpreter_Execution_Result ok(const Interpreter_Value &value=Interpreter_Value::make_unit())
void reset() noexcept
Clears globals and the call stack while preserving host bindings.
Interpreter_Execution_Result evaluate_expr(const Compiler_HIR_Expr *expr, Interpreter_Environment &env)
bool install_module_function(const Compiler_HIR_Function *function)
static bool strip_quotes(std::string_view text, const char quote, std::string_view &body) noexcept
static bool decode_escaped_text(const std::string_view body, std::string &out, std::string &error)
const Interpreter_Environment & prelude_environment() const noexcept
Returns the persistent prelude environment.
Interpreter_Execution_Result evaluate_stmt(const Compiler_HIR_Stmt *stmt, Interpreter_Environment &env)
Interpreter_Execution_Result evaluate_module(const Compiler_HIR_Module *module)
Evaluates one HIR module and leaves its globals installed.
Interpreter_Runtime_Options options
Interpreter_Execution_Result call_host_function(const Interpreter_Value &callee, const DynArray< Interpreter_Value > &arguments, const Source_Span &span) const
Interpreter_Execution_Result parse_char_literal(const Compiler_HIR_Constant_Expr *node) const
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
@ Runtime_Error
Execution failed with a structured runtime error.
@ Return
Return one register value.
@ String
String literal payload.
@ Character
Character literal payload.
@ Invalid
Uninitialized register/slot or explicit invalid placeholder.
void message(const char *file, int line, const char *format,...)
Print an informational message with file and line info.
Definition ahDefs.C:95
size_t size(Node *root) noexcept
Interpreter_Value_Kind
Runtime categories supported by the interpreter MVP.
const char * interpreter_value_kind_name(const Interpreter_Value_Kind kind) noexcept
Stable debug name for one runtime value kind.
const char * compiler_operator_name(const Compiler_Operator_Kind kind) noexcept
Returns a stable debug name for one operator kind.
@ Continue
Skip to the next iteration of the innermost loop.
@ Break
Immediate exit from the innermost loop.
bool interpreter_values_equal(const Interpreter_Value &lhs, const Interpreter_Value &rhs)
Returns whether two runtime values compare equal.
and
Check uniqueness with explicit hash + equality functors.
std::string code(Node *root)
Compute a string with the Lukasiewicz`s word of a tree.
const char * interpreter_control_flow_name(const Interpreter_Control_Flow_Kind kind) noexcept
Stable debug name for one control-flow outcome.
std::string interpreter_dump_environment(const Interpreter_Environment &env)
Dumps one environment deterministically.
void error(const char *file, int line, const char *format,...)
Print an error message with file and line info.
Definition ahDefs.C:100
Interpreter_Control_Flow_Kind
Structured control-flow outcomes during evaluation.
Field< int > Integer
Definition ahField.H:136
bool compiler_operator_is_compound_assignment(const Compiler_Operator_Kind kind) noexcept
Returns whether kind is one compound-assignment operator.
static std::atomic< bool > init
Definition hash-fct.C:54
bool(*)(void *user_data, const DynArray< Interpreter_Value > &arguments, Interpreter_Value &result, Interpreter_Runtime_Error &error) Interpreter_Host_Function
Host callback signature used by runtime built-ins or embeddings.
std::string interpreter_dump_globals(const Interpreter_Runtime &runtime)
Dumps the globals currently installed in one runtime.
std::string interpreter_value_to_string(const Interpreter_Value &value)
Formats one runtime value deterministically for dumps and tests.
@ Function
Function type such as fn(Int) -> Bool.
@ Tuple
Tuple type such as (Int, Bool).
STL namespace.
Structured block statement.
Typed function call expression.
std::string text
Original or normalized constant spelling.
Expression statement in HIR.
Base class for HIR expressions.
Compiler_HIR_Expr_Kind kind
Runtime node kind.
std::string name
Function name.
DynArray< Compiler_HIR_Param > parameters
Parameters in declaration order.
Compiler_HIR_Block_Stmt * body
Function body.
Structured conditional statement.
Typed lexical binding statement.
DynArray< Compiler_HIR_Function * > functions
Lowered top-level functions.
DynArray< Compiler_HIR_Stmt * > statements
Lowered top-level statements.
Source_Span span
Source region associated with the HIR node.
Base class for HIR statements.
Compiler_HIR_Stmt_Kind kind
Runtime node kind.
Typed variable reference expression.
Structured while-loop statement.
One lexical binding inside an interpreter environment.
std::string name
Binding name.
Interpreter_Value value
Stored runtime value.
Result of evaluating one expression, statement, module, or call.
bool has_control_flow() const noexcept
Returns whether this result carries non-local control flow.
Interpreter_Runtime_Error error
Structured runtime failure when flow == Runtime_Error.
Interpreter_Value value
Produced runtime value when relevant.
Interpreter_Control_Flow_Kind flow
Control-flow outcome.
bool ok() const noexcept
Returns whether this result represents a runtime failure.
Source_Span span
Source region associated with the failure.
std::string code
Stable runtime code such as RUN004.
std::string message
Human-readable runtime message.
bool has_error() const noexcept
Returns whether this error object is populated.
Runtime options for the HIR evaluator.
size_t max_call_depth
Maximum nested HIR calls before RUN010.
Heap-owned tuple payload for recursive runtime values.
DynArray< Interpreter_Value > elements
Tuple members in lexical order.
Interpreter_Tuple_Storage(DynArray< Interpreter_Value > &&values)
Interpreter_Tuple_Storage(const DynArray< Interpreter_Value > &values)
std::string callable_name
Stable debug name for callable values.
void * host_user_data
Embedding-specific payload for host functions.
Interpreter_Environment * closure_environment
Captured lexical parent for function values.
static Interpreter_Value make_tuple(const DynArray< Interpreter_Value > &elements)
Builds a tuple runtime value by copying elements.
bool is_unit() const noexcept
Returns whether this value is the Unit singleton.
Interpreter_Host_Function host_function
Payload for host function values.
std::string string_value
Payload for String values.
char character_value
Payload for Char values.
const Compiler_HIR_Function * function
Payload for HIR function values.
long long integer_value
Payload for Int values.
static Interpreter_Value make_bool(const bool b)
Builds a boolean runtime value.
Interpreter_Value & operator=(const Interpreter_Value &other)
Assigns one runtime value by deep copy.
bool is_callable() const noexcept
Returns whether this value is callable.
static Interpreter_Value make_character(const char ch)
Builds a character runtime value.
static Interpreter_Value make_invalid()
Builds an invalid value placeholder.
std::unique_ptr< Interpreter_Tuple_Storage > tuple_storage
Payload for Tuple values.
~Interpreter_Value() noexcept
Releases tuple storage if present.
static Interpreter_Value make_function(const Compiler_HIR_Function *fn, Interpreter_Environment *env=nullptr)
Builds a callable value backed by HIR function code.
bool bool_value
Payload for Bool values.
Interpreter_Value_Kind kind
Active value kind.
Interpreter_Value()=default
Builds a fresh value with no tuple storage.
static Interpreter_Value make_host_function(std::string name, const Interpreter_Host_Function callback, void *user_data=nullptr)
Builds a callable value backed by host code.
static Interpreter_Value make_integer(const long long i)
Builds an integer runtime value.
const DynArray< Interpreter_Value > & tuple_elements() const noexcept
Returns the tuple payload.
static Interpreter_Value make_unit()
Builds the Unit value.
static Interpreter_Value make_string(std::string text)
Builds a string runtime value.
Represents a missing value.
Half-open byte range inside a source file.
Definition ah-source.H:100
Lazy and scalable dynamic array implementation.