Skip to content

Commit 76e637b

Browse files
committed
Language/Homoiconic/VirtualMachine
1 parent 898d86e commit 76e637b

12 files changed

Lines changed: 205 additions & 14 deletions

File tree

modules/Container/Tree.mpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export namespace CppUtils::Container
1212
struct Node final
1313
{
1414
using ValueType = T;
15+
1516
ValueType value;
1617
std::vector<Node<T>> nodes = {};
1718

modules/Container/Vector.mpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,23 @@ export namespace CppUtils::Container::Vector
77
template<class T>
88
inline auto merge(std::vector<T>& dest, const std::vector<T>& src) -> void
99
{
10-
dest.reserve(dest.size() + src.size());
11-
dest.insert(dest.end(), src.begin(), src.end());
10+
dest.reserve(std::size(dest) + std::size(src));
11+
dest.insert(std::cend(dest), std::begin(src), std::end(src));
1212
}
1313

1414
[[nodiscard]] inline auto merge(auto&&... containers) -> decltype(auto)
1515
{
1616
using ValueType = typename std::common_type_t<std::decay_t<decltype(*std::begin(containers))>...>;
1717
auto result = std::vector<ValueType>{};
18-
(result.insert(std::end(result), std::make_move_iterator(std::begin(containers)), std::make_move_iterator(std::end(containers))), ...);
18+
(result.insert(std::cend(result), std::make_move_iterator(std::begin(containers)), std::make_move_iterator(std::end(containers))), ...);
1919
return result;
2020
}
21+
22+
template<class T>
23+
[[nodiscard]] inline auto popBack(std::vector<T>& vector) -> T
24+
{
25+
auto value = std::move(vector.back());
26+
vector.pop_back();
27+
return value;
28+
}
2129
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
export module CppUtils.Language.GenericVirtualMachine;
2+
3+
import std;
4+
import CppUtils.Container.Tree;
5+
import CppUtils.Type;
6+
import CppUtils.String;
7+
8+
export namespace CppUtils::Language
9+
{
10+
using Symbol = std::uint64_t;
11+
using ASTNode = Container::Tree::Node<Symbol>;
12+
13+
template<String::StringView Source>
14+
struct Cursor final
15+
{
16+
Source source;
17+
std::size_t position = 0;
18+
19+
[[nodiscard]] inline auto getCurrent() -> const auto&
20+
{
21+
return source[position];
22+
}
23+
};
24+
25+
template<class Context, auto&& interpreter, auto inspector = nullptr>
26+
struct GenericVirtualMachine final
27+
{
28+
using context_type = Context;
29+
Context context = {};
30+
31+
template<auto&&... functions>
32+
inline constexpr auto execute(Type::Specializes<Cursor> auto&& cursor, const ASTNode& instruction = ASTNode{String::hash("main")}) -> std::expected<void, std::string>
33+
{
34+
if constexpr (not std::same_as<decltype(inspector), std::nullptr_t>)
35+
if (auto result = inspector(cursor, instruction, context); not result)
36+
return result;
37+
if (auto result = interpreter.template operator()<functions...>(cursor, instruction, *this); not result)
38+
return result;
39+
return {};
40+
}
41+
};
42+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
export module CppUtils.Language.HomoiconicVirtualMachine;
2+
3+
import std;
4+
import CppUtils.String;
5+
import CppUtils.Type;
6+
import CppUtils.Language.GenericVirtualMachine;
7+
8+
export namespace CppUtils::Language::Homoiconic
9+
{
10+
inline constexpr auto interpreter = []<auto... functions>(Type::Specializes<Cursor> auto& cursor, const ASTNode& instruction, auto& virtualMachine) constexpr -> std::expected<void, std::string> {
11+
using namespace std::literals;
12+
using namespace String::Literals;
13+
auto& context = virtualMachine.context;
14+
auto& [rootAst, hashTable, scopes] = context;
15+
const auto instructionToken = instruction.value;
16+
auto grammar = [instructionToken, &scopes]() -> std::reference_wrapper<ASTNode> {
17+
const auto range = scopes | std::views::reverse;
18+
auto it = std::ranges::find_if(range, [instructionToken](const auto scope) -> bool { return scope.get().exists(instructionToken); });
19+
return it != std::ranges::end(range) ? *it : scopes.front();
20+
}();
21+
if (grammar.get().exists(instructionToken))
22+
{
23+
auto lexemes = std::ref(grammar.get()[instructionToken].nodes);
24+
for (auto instructionPosition = 0uz; instructionPosition < std::size(lexemes.get()); ++instructionPosition)
25+
{
26+
const auto& lexeme = lexemes.get()[instructionPosition];
27+
if (auto result = virtualMachine.template execute<functions...>(cursor, lexeme); not result) [[unlikely]]
28+
return result;
29+
lexemes = std::ref(grammar.get()[instructionToken].nodes);
30+
}
31+
}
32+
else
33+
switch (instructionToken)
34+
{
35+
case "call"_token:
36+
if constexpr (sizeof...(functions) > 0)
37+
{
38+
if (std::empty(instruction.nodes)) [[unlikely]]
39+
return std::unexpected{R"(Instruction "call": Missing parameter)"};
40+
else if (auto result = Type::Tuple::visitAt(std::make_tuple(functions...), instruction.nodes[0].value, [&cursor, &virtualMachine](auto&& function) -> decltype(auto) { return function(cursor, virtualMachine); }); not result)
41+
return std::unexpected{std::string{result.error()}};
42+
}
43+
break;
44+
default:
45+
return std::unexpected{std::format(R"(Unknown instruction "{}")", String::getNameOrValue(instructionToken, hashTable))};
46+
}
47+
return {};
48+
};
49+
50+
struct Context final
51+
{
52+
// clang-format off
53+
ASTNode rootAst = {0uz, {
54+
ASTNode{String::hash("main"), {
55+
ASTNode{String::hash("executeInstruction")},
56+
ASTNode{String::hash("advanceCursor")}
57+
}},
58+
ASTNode{String::hash("executeInstruction"), {
59+
ASTNode{String::hash("call"), {ASTNode{0}}}
60+
}},
61+
ASTNode{String::hash("advanceCursor"), {
62+
ASTNode{String::hash("call"), {ASTNode{1}}}
63+
}}
64+
}};
65+
// clang-format on
66+
String::HashTable<char> hashTable = String::makeHashTable<char>(
67+
"main",
68+
"executeInstruction",
69+
"advanceCursor",
70+
"call");
71+
std::vector<std::reference_wrapper<ASTNode>> scopes = {std::ref(rootAst)};
72+
};
73+
74+
template<auto inspector = nullptr>
75+
using VirtualMachine = GenericVirtualMachine<Context, interpreter, inspector>;
76+
77+
constexpr auto printInstruction = [](const Type::Specializes<Cursor> auto&, const ASTNode& instruction, Context& context) -> std::expected<void, std::string> {
78+
std::println("{}", String::getNameOrValue(instruction.value, context.hashTable));
79+
return {};
80+
};
81+
82+
constexpr auto executeInstruction = [](Type::Specializes<Cursor> auto& cursor, auto& virtualMachine) -> std::expected<void, std::string> {
83+
virtualMachine.template execute<>(cursor, ASTNode{static_cast<Symbol>(cursor.getCurrent())});
84+
return {};
85+
};
86+
87+
constexpr auto advanceCursor = [](Type::Specializes<Cursor> auto& cursor, [[maybe_unused]] auto& virtualMachine) -> std::expected<void, std::string> {
88+
++cursor.position;
89+
return {};
90+
};
91+
}

modules/Language/Language.mpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ export module CppUtils.Language;
22

33
export import CppUtils.Language.TreeParser;
44
export import CppUtils.Language.VirtualMachine;
5+
export import CppUtils.Language.GenericVirtualMachine;
6+
export import CppUtils.Language.HomoiconicVirtualMachine;

modules/Language/VirtualMachine.mpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,11 @@ export namespace CppUtils::Language::VirtualMachine
436436
scopes.push_back(std::ref(scope.get()[pop(stack).value]));
437437
break;
438438
}
439-
case '}': scopes.pop_back(); break;
439+
case '}':
440+
if (std::empty(scopes)) [[unlikely]]
441+
return std::unexpected{"Scope underflow"sv};
442+
scopes.pop_back();
443+
break;
440444
case '#':
441445
if (std::empty(stack)) [[unlikely]]
442446
return std::unexpected{"Stack underflow"sv};

modules/String/Hash.mpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export namespace CppUtils::String
1313
{
1414
auto result = 0xcb'f2'9c'e4'84'22'23'25u;
1515
for (const auto& c : sv)
16-
result = (static_cast<Hash>(c) ^ result) * 0x1'00'00'00'01'b3u;
16+
if (c)
17+
result = (static_cast<Hash>(c) ^ result) * 0x1'00'00'00'01'b3u;
1718
return result;
1819
}
1920

modules/String/HashTable.mpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export namespace CppUtils::String
1414
template<Char CharT>
1515
[[nodiscard]] inline auto makeHashTable(Text<CharT> auto&&... texts) -> decltype(auto)
1616
{
17-
return HashTable<CharT>{std::make_pair(String::hash(texts), std::forward<decltype(texts)>(texts))...};
17+
return HashTable<CharT>{std::make_pair(hash(texts), std::forward<decltype(texts)>(texts))...};
1818
}
1919

2020
template<Char CharT>
@@ -24,7 +24,7 @@ export namespace CppUtils::String
2424

2525
if (tokenNames.contains(token))
2626
{
27-
if (const auto& name = tokenNames.at(token); !std::empty(name))
27+
if (const auto& name = tokenNames.at(token); not std::empty(name))
2828
return std::format("{}", name);
2929
else
3030
return std::format(R"("")");
@@ -34,8 +34,8 @@ export namespace CppUtils::String
3434
auto c = static_cast<char>(token);
3535
if (std::isprint(c))
3636
return std::format("{} '{}'", token, c);
37-
if (String::isEscapable(c))
38-
return std::format(R"({} \'{}')", token, String::unescape(c));
37+
if (isEscapable(c))
38+
return std::format(R"({} \'{}')", token, unescape(c));
3939
}
4040
return std::format("{}", token);
4141
}

modules/Type/Concept.mpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ export namespace CppUtils::Type
2323
};
2424

2525
template<class T, template<class...> class IncompleteType>
26-
inline constexpr bool isSpecializationOf = false;
26+
inline constexpr auto isSpecializationOf = false;
2727

2828
template<template<class...> class IncompleteType, class... Args>
29-
inline constexpr bool isSpecializationOf<IncompleteType<Args...>, IncompleteType> = true;
29+
inline constexpr auto isSpecializationOf<IncompleteType<Args...>, IncompleteType> = true;
3030

3131
template<class T, template<class...> class IncompleteType>
32-
concept Specializes = isSpecializationOf<T, IncompleteType>;
32+
concept Specializes = isSpecializationOf<std::remove_cvref_t<T>, IncompleteType>;
3333

3434
template<class T>
3535
concept Function = std::is_function_v<T>;
@@ -41,7 +41,7 @@ export namespace CppUtils::Type
4141
static_assert(not HasReturnValue<void()>);
4242

4343
template<class T>
44-
inline constexpr bool isFunctionPointer = std::is_pointer_v<T> and std::is_function_v<std::remove_pointer_t<T>>;
44+
inline constexpr auto isFunctionPointer = std::is_pointer_v<T> and std::is_function_v<std::remove_pointer_t<T>>;
4545

4646
template<class T>
4747
concept FunctionPointer = isFunctionPointer<T>;

modules/Type/Utility.mpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,16 @@ export namespace CppUtils::Type
2121

2222
static_assert(std::is_same_v<LazyEvaluationType<true, std::type_identity, int, char>, std::type_identity<int>>);
2323
static_assert(std::is_same_v<LazyEvaluationType<false, std::type_identity, int, char>, char>);
24+
25+
template<class T>
26+
inline constexpr auto preferPassByRef = sizeof(T) > sizeof(void*) or std::is_class_v<T> or std::is_union_v<T> or std::is_array_v<T> or std::is_function_v<T>;
27+
28+
template<class T>
29+
inline constexpr auto preferPassByValue = not preferPassByRef<T>;
30+
31+
template<class T>
32+
using In = std::conditional_t<preferPassByValue<T>, T, const T&>;
33+
34+
static_assert(std::is_same_v<In<int>, int>);
35+
static_assert(std::is_same_v<In<std::string>, const std::string&>);
2436
}

0 commit comments

Comments
 (0)