Skip to content

Commit

Permalink
API: builder children interface (#102)
Browse files Browse the repository at this point in the history
* WIP: started working on children.

* WIP: added missing files.

* WIP: renamed some parameters.

* WIP: fixed type errors.

* WIP: fixed node tree recursive release.

* WIP: added more children tests.

* WIP: added proper child referencing for children builder.

* WIP: updated readme for CI/CD.

* WIP: fixed test regression improving templates.

* WIP: fixed formatting. Fixed builder children sigfault.

* WIP: fixed memory leak in builder.

* Fixed #101. Added children operator. Added unit tests for node payloads.

* Added a test for nodes with no payload.

* Fixed parent-child linkage in children builder.
  • Loading branch information
mcmikecreations authored Nov 5, 2023
1 parent 4ccdec9 commit f5e34f5
Show file tree
Hide file tree
Showing 24 changed files with 1,114 additions and 333 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
![Tests](https://github.com/Evenfall-Tech/Crosslight/actions/workflows/cmake.yml/badge.svg)

# Crosslight

A language translation / interpretation engine for cross-language compilation / execution. Run Crosslight, select languages for input and output, specify input and output files and see the magic!
Expand Down
24 changes: 22 additions & 2 deletions docs/notes/2023_08_23_style.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,20 @@ add(int a, int b) {
```
```cpp
// No tabs for the primary namespace.
namespace oh {
// Bracket on same line.
struct myobj {
int a;
int b;
};
class mymop {
public:
public: // On same tab as class/struct declaration.
int c;
int myfunc(int d) {
int myfunc(int d) { // For definitions in headers, return type on same line.
return d + 0;
}
Expand All @@ -76,4 +78,22 @@ private:
};
}
// For constructors without a body.
mymop::mymop(
int a) :
_a{a} {}
// For constructors with a body.
mymop::mymop(
int b) :
_a{b} {
_b = b;
}
// For single-statement functions body can be in a single line.
// For .cpp implementations return type on separate line.
int
c(int a, int b)
{ return a + b; }
```
17 changes: 12 additions & 5 deletions libs/c/core/src/node.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
#include "core/nodes/scope.h"
#include "core/nodes/heap_type.h"

size_t
cl_node_term(struct cl_node* root, size_t term_children, void(*term)(void*)) {
static size_t
cl_node_term_internal(struct cl_node* root, size_t term_children, void(*term)(void*), size_t term_root) {
if (term == 0) {
return 0;
}
Expand Down Expand Up @@ -37,18 +37,25 @@ cl_node_term(struct cl_node* root, size_t term_children, void(*term)(void*)) {
size_t child_count = root->child_count;

if (term_children != 0 && child_count > 0 && root->children != 0) {
for (size_t i = 0; i < child_count; ++i) {
if (cl_node_term((struct cl_node*)(root->children + i), term_children, term) == 0) {
for (size_t i = child_count - 1; i != (size_t)-1; --i) {
if (cl_node_term_internal((struct cl_node*)(root->children + i), term_children, term, i == 0) == 0) {
return 0;
}
}
}

term(root);
if (term_root) {
term(root);
}

return 1;
}

size_t
cl_node_term(struct cl_node* root, size_t term_children, void(*term)(void*)) {
return cl_node_term_internal(root, term_children, term, 1);
}

size_t
cl_node_source_root_term(struct cl_node_source_root* payload, void(*term)(void*)) {
if (term == 0) {
Expand Down
3 changes: 3 additions & 0 deletions libs/cpp/lang/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ set(PUBLIC_HEADERS_FILES
"${LIBRARY_BASE_PATH}/include/lang/utils.hpp"
"${LIBRARY_BASE_PATH}/include/lang/builders/allocator.hpp"
"${LIBRARY_BASE_PATH}/include/lang/builders/builder.hpp"
"${LIBRARY_BASE_PATH}/include/lang/builders/builders.hpp"
"${LIBRARY_BASE_PATH}/include/lang/builders/source_root.hpp"
"${LIBRARY_BASE_PATH}/include/lang/builders/scope.hpp"
"${LIBRARY_BASE_PATH}/include/lang/builders/heap_type.hpp"
Expand All @@ -25,7 +26,9 @@ add_library(
"${LIBRARY_BASE_PATH}/src/language.cpp"
"${LIBRARY_BASE_PATH}/src/config.cpp"
"${LIBRARY_BASE_PATH}/src/utils.cpp"
"${LIBRARY_BASE_PATH}/src/builders/allocator.cpp"
"${LIBRARY_BASE_PATH}/src/builders/builder.cpp"
"${LIBRARY_BASE_PATH}/src/builders/builders.cpp"
"${LIBRARY_BASE_PATH}/src/builders/source_root.cpp"
"${LIBRARY_BASE_PATH}/src/builders/scope.cpp"
"${LIBRARY_BASE_PATH}/src/builders/heap_type.cpp"
Expand Down
3 changes: 3 additions & 0 deletions libs/cpp/lang/include/lang/builders/allocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ class allocator {
AcquireT acquire;
ReleaseT release;

[[nodiscard]] builder none() const;
builder source_root(const char* filename) const;
builder scope(const char* identifier) const;
builder heap_type(const char* identifier) const;

static bool equal(const allocator& left, const allocator& right);
};

}
32 changes: 16 additions & 16 deletions libs/cpp/lang/include/lang/builders/builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@

#include <cstddef>
#include <type_traits>
#include "lang/language.hpp"
#include "lang/builders/allocator.hpp"

struct cl_node;

namespace cl::lang::builders {

class allocator;
class builder;
class builders;

template <typename BuilderT, typename BuilderU>
using e = std::enable_if_t<
std::is_same_v<std::remove_cv_t<std::remove_reference_t<BuilderT>>, builder> &&
std::is_same_v<std::remove_cv_t<std::remove_reference_t<BuilderU>>, builder>,
builder>;

/**
* @brief Add the child tree to the current tree.
* @brief Add the child tree to the current tree, setting tail to child.
*
* @param[in] current Current node tree to add child to.
* @param[in] child Child node tree to add to the current tree.
Expand All @@ -22,18 +28,12 @@ class builder;
* @warning @p current and @p child builders are invalidated and shouldn't be used after this call.
*/
template <typename BuilderT, typename BuilderU>
std::enable_if_t<
std::is_same_v<std::remove_cv_t<std::remove_reference_t<BuilderT>>, builder> &&
std::is_same_v<std::remove_cv_t<std::remove_reference_t<BuilderU>>, builder>,
builder>
e<BuilderT, BuilderU>
operator <<(BuilderT&& current, BuilderU&& child);

class builder {
public:
class impl; // Use PImpl concept to hide raw alloc and free.

builder(const allocator& m, struct cl_node* root, struct cl_node* parent);
builder(const impl* m, struct cl_node* root, struct cl_node* parent);
static builder from_payload(const allocator& m, void* payload, size_t payload_type);
virtual ~builder();

Expand All @@ -42,16 +42,16 @@ class builder {
builder& operator =(const builder& other) = delete; // Copy assignment
builder(builder& other) = delete; // Copy constructor

struct cl_node* root_get();
struct cl_node* parent_get();
const impl* impl_get();
static bool impl_equal(const impl* left, const impl* right);
void root_clear();
[[nodiscard]] struct cl_node* root_get() const;
[[nodiscard]] struct cl_node* parent_get() const;
[[nodiscard]] const allocator& allocator_get() const;
void prevent_term();

private:
struct cl_node* _root;
struct cl_node* _parent;
impl* _impl;
bool _should_destroy;
allocator _allocator;
};

}
201 changes: 201 additions & 0 deletions libs/cpp/lang/include/lang/builders/builders.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
#pragma once

#include <vector>
#include <cstddef>
#include <tuple>
#include <typeinfo>
#include <type_traits>
#include <utility>
#include "core/node.h"
#include "lang/language.hpp"
#include "lang/builders/builder.hpp"

namespace cl::lang::builders {

namespace {

template <std::size_t Index, typename T>
struct children_node {
inline
constexpr auto & operator()(std::integral_constant<std::size_t, Index>) noexcept
{ return _data; }

inline
constexpr auto & operator()(std::integral_constant<std::size_t, Index>) const noexcept
{ return _data; }

inline constexpr T get(std::integral_constant<std::size_t, Index>) noexcept
{ return std::forward<T>(_data); }

std::conditional_t<
std::is_lvalue_reference_v<T>,
std::remove_reference_t<T> &,
std::remove_reference_t<T>
> _data;
};

template <typename ... Ts>
struct children_base : Ts... {
using Ts::operator()...;
using Ts::get...;
};

template <typename BuilderT>
using child_type = std::conditional_t<
std::is_lvalue_reference_v<BuilderT>,
builder &,
builder &&
>;

template <typename ... Ts, std::size_t ... Indexes>
constexpr children_base<children_node<Indexes, child_type<Ts>>...>
get_children_base(
std::index_sequence<Indexes...> = std::make_index_sequence<sizeof...(Ts)>{}) noexcept;

template <typename ... Ts>
using children_base_t = decltype(get_children_base<Ts...>(std::make_index_sequence<sizeof...(Ts)>{}));

template <typename ... Ts>
struct children_type : children_base_t<Ts...> {
using base = children_base_t<Ts...>;
using base::operator();

constexpr explicit children_type(Ts && ... data) :
base{std::forward<Ts>(data)...} {}

template <std::size_t Index>
constexpr auto & at() const noexcept
{ return base::operator()(std::integral_constant<std::size_t, Index>{}); }

template <std::size_t Index>
constexpr auto && get() noexcept
{ return base::get(std::integral_constant<std::size_t, Index>{}); }
};

template <typename ... Ts>
children_type(Ts && ...) -> children_type<Ts&&...>;

template <typename MaybeBuilderT>
inline constexpr bool is_builder_v = &typeid(MaybeBuilderT) == &typeid(builder);

template <bool... BooleanTs>
inline constexpr bool is_all_v = (0 + ... + BooleanTs) == sizeof...(BooleanTs);
}

template <typename... BuilderTs>
children_type<BuilderTs...> children(BuilderTs&&... children) {
(..., children.prevent_term());
return children_type<BuilderTs...>{std::forward<BuilderTs>(children)...};
}

namespace {
template <std::size_t Index, typename ChildrenT>
constexpr auto && child_get(ChildrenT && children) noexcept {
return children.template get<Index>();
}

template<typename BuilderT, typename ChildrenT, std::size_t... Index>
bool check_children_at(BuilderT&& current, ChildrenT&& children, std::index_sequence<Index...> const&) {
auto child_valid = [&current](auto&& child) {
return child.root_get() == nullptr || current.root_get() == nullptr ||
!allocator::equal(current.allocator_get(), child.allocator_get());
};

return (0 + ... + child_valid(child_get<Index>(children)));
}

template <typename T>
struct is_children : std::false_type {};

template <typename ... BuilderTs>
struct is_children<std::tuple<BuilderTs...>> :
std::bool_constant<is_all_v<is_builder_v<BuilderTs>...>> {};

template <typename ... BuilderTs>
struct is_children<std::tuple<BuilderTs...>&> :
std::bool_constant<is_all_v<is_builder_v<BuilderTs>...>> {};

template <typename ... BuilderTs>
struct is_children<children_type<BuilderTs...>> :
std::bool_constant<is_all_v<is_builder_v<BuilderTs>...>> {};

template <typename ... BuilderTs>
struct is_children<children_type<BuilderTs...> &> :
std::bool_constant<is_all_v<is_builder_v<BuilderTs>...>> {};

template <typename T>
struct children_count {};

template <typename ... Ts, template <typename ...> typename HolderT>
struct children_count<HolderT<Ts...>> :
std::integral_constant<std::size_t, sizeof...(Ts)> {};

template <typename ... Ts, template <typename ...> typename HolderT>
struct children_count<HolderT<Ts...>&> :
std::integral_constant<std::size_t, sizeof...(Ts)> {};

template <typename T>
inline constexpr bool is_children_v = is_children<T>::value;

template <typename T>
inline constexpr std::size_t children_count_v = children_count<T>::value;

template <typename ChildrenT, std::size_t Index>
void attach_child_at(const struct cl_node* parent, struct cl_node* nodes, ChildrenT&& children) {
auto&& child = child_get<Index>(children);
auto* child_node = child.root_get();
child_node->parent = parent;
auto child_count = child_node->child_count;

// Reattach children of children to the newly acquired array memory.
auto* child_children = (struct cl_node*)child_node->children;

if (child_children != nullptr && child_count > 0) {
for (size_t i = 0; i < child_count; ++i) {
(child_children + i)->parent = nodes + Index;
}
}

nodes[Index] = *child_node;
child.prevent_term();
child.allocator_get().release(child_node);
}

template<typename ChildrenT, std::size_t... Index>
void attach_children_at(
struct cl_node* parent,
struct cl_node* nodes,
ChildrenT&& children,
std::index_sequence<Index...> const&) {
(..., attach_child_at<ChildrenT, Index>(parent, nodes, children));
}
}

template <typename BuilderT, typename ChildrenT, typename = std::enable_if_t<is_children_v<ChildrenT>>>
builder operator <<(BuilderT&& current, ChildrenT&& children) {
constexpr std::size_t child_count = children_count_v<ChildrenT>;
constexpr auto indices = std::make_index_sequence<children_count_v<ChildrenT>>();

if (check_children_at(current, children, indices)) {
return { current.allocator_get(), nullptr, nullptr };
}

auto* nodes = static_cast<struct cl_node*>(current.allocator_get().acquire(child_count * sizeof(struct cl_node)));

if (nodes == nullptr) {
return { current.allocator_get(), nullptr, nullptr };
}

auto* parent = current.parent_get();
auto* root = current.root_get();
current.prevent_term();

attach_children_at(parent, nodes, children, indices);

parent->child_count = child_count;
parent->children = nodes;

return { current.allocator_get(), root, nodes + child_count - 1 };
}

}
Loading

0 comments on commit f5e34f5

Please sign in to comment.