diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d4472e..6ab37f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(lilfbtf5 VERSION 0.1.29) +project(lilfbtf5 VERSION 0.1.30) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) option(ENABLE_UBSAN "Enable the ub sanitizer" OFF) diff --git a/include/lilfbtf/tree.h b/include/lilfbtf/tree.h index cca1b64..862df00 100644 --- a/include/lilfbtf/tree.h +++ b/include/lilfbtf/tree.h @@ -72,6 +72,18 @@ namespace fb ~func_t() = default; }; + enum class tree_init_t + { + // standard grow method + GROW, + // standard full method + FULL, + // standard ramped half-and-half method + RAMPED_HALF_HALF, + // variant of grow/full method where at each level a choice is made between using only non-terminals or terminals + BRETT_HALF_HALF + }; + namespace detail { class node_t @@ -110,12 +122,19 @@ namespace fb } }; - struct node_helper_t + struct node_construction_info_t { blt::bump_allocator& alloc; random& engine; type_engine_t& types; }; + + struct tree_construction_info_t + { + tree_init_t tree_type; + random& engine; + type_engine_t& types; + }; } class tree_t @@ -128,15 +147,18 @@ namespace fb inline blt::bump_allocator& get_allocator() { return alloc; } - static detail::node_t* allocate_non_terminal(detail::node_helper_t details, type_id type); - static detail::node_t* allocate_non_terminal_restricted(detail::node_helper_t details, type_id type); + static detail::node_t* allocate_non_terminal(detail::node_construction_info_t info, type_id type); + + static detail::node_t* allocate_non_terminal_restricted(detail::node_construction_info_t info, type_id type); + + static detail::node_t* allocate_terminal(detail::node_construction_info_t info, type_id type); + - static detail::node_t* allocate_terminal(detail::node_helper_t details, type_id type); public: explicit tree_t(type_engine_t& types); - static tree_t make_tree(type_engine_t& types, random& engine, blt::size_t min_height, blt::size_t max_height, + static tree_t make_tree(detail::tree_construction_info_t tree_info, blt::size_t min_depth, blt::size_t max_depth, std::optional starting_type = {}); std::pair evaluate(); diff --git a/src/tree.cpp b/src/tree.cpp index 14a7131..b82d667 100644 --- a/src/tree.cpp +++ b/src/tree.cpp @@ -28,7 +28,7 @@ namespace fb {} tree_t tree_t::make_tree(type_engine_t& types, random& engine, - blt::size_t min_height, blt::size_t max_height, std::optional starting_type) + blt::size_t min_depth, blt::size_t max_depth, std::optional starting_type) { using detail::node_t; tree_t tree(types); @@ -70,7 +70,7 @@ namespace fb const auto& terminals = types.get_terminals(type_category); const auto& non_terminals = types.get_non_terminals(type_category); - if (depth < min_height && !has_one_non_terminal) + if (depth < min_depth && !has_one_non_terminal) { // make sure we have at least min height possible by using at least one non terminal function_id selection = non_terminals[engine.random_long(0, non_terminals.size() - 1)]; @@ -79,7 +79,7 @@ namespace fb func_init.value()(func); node->children[i] = tree.alloc.template emplace(func, tree.alloc); has_one_non_terminal = true; - } else if (depth >= max_height) + } else if (depth >= max_depth) { // if we are above the max_height select only terminals function_id selection = terminals[engine.random_long(0, terminals.size() - 1)]; @@ -133,6 +133,7 @@ namespace fb nodes.push(root); + // create the correct ordering for the node evaluation while (!nodes.empty()) { auto* top = nodes.top(); @@ -151,48 +152,49 @@ namespace fb return {root->type.getValue(), root->type.getType()}; } - detail::node_t* tree_t::allocate_non_terminal(detail::node_helper_t details, type_id type) + detail::node_t* tree_t::allocate_non_terminal(detail::node_construction_info_t info, type_id type) { - const auto& non_terminals = details.types.get_non_terminals(type); - function_id selection = non_terminals[details.engine.random_long(0, non_terminals.size() - 1)]; - func_t func(details.types.get_function_argc(selection), details.types.get_function(selection), type, selection); - if (const auto& func_init = details.types.get_function_initializer(selection)) + const auto& non_terminals = info.types.get_non_terminals(type); + function_id selection = non_terminals[info.engine.random_long(0, non_terminals.size() - 1)]; + func_t func(info.types.get_function_argc(selection), info.types.get_function(selection), type, selection); + if (const auto& func_init = info.types.get_function_initializer(selection)) func_init.value()(func); - return details.alloc.template emplace(func, details.alloc); + return info.alloc.template emplace(func, info.alloc); } - detail::node_t* tree_t::allocate_terminal(detail::node_helper_t details, type_id type) + detail::node_t* tree_t::allocate_terminal(detail::node_construction_info_t info, type_id type) { - const auto& terminals = details.types.get_terminals(type); + const auto& terminals = info.types.get_terminals(type); // if we cannot allocate a terminal, we need to allocate a non-terminal in hopes of finding a closing path // for example bools might not have an ending terminal, it doesn't make sense to. if (terminals.empty()) - return allocate_non_terminal_restricted(details, type); + return allocate_non_terminal_restricted(info, type); - function_id selection = terminals[details.engine.random_long(0, terminals.size() - 1)]; - func_t func(details.types.get_function_argc(selection), details.types.get_function(selection), type, selection); - if (const auto& func_init = details.types.get_function_initializer(selection)) + function_id selection = terminals[info.engine.random_long(0, terminals.size() - 1)]; + func_t func(info.types.get_function_argc(selection), info.types.get_function(selection), type, selection); + if (const auto& func_init = info.types.get_function_initializer(selection)) func_init.value()(func); - return details.alloc.template emplace(func, details.alloc); + return info.alloc.template emplace(func, info.alloc); } - detail::node_t* tree_t::allocate_non_terminal_restricted(detail::node_helper_t details, type_id type) + detail::node_t* tree_t::allocate_non_terminal_restricted(detail::node_construction_info_t info, type_id type) { function_id selection = 0; - do { - const auto& non_terminals = details.types.get_non_terminals(type); - selection = details.engine.random_long(0, non_terminals.size() - 1); - auto& sel_v = details.types.get_function_allowed_arguments(selection); - // if it does not accept the type we are + do + { + const auto& non_terminals = info.types.get_non_terminals(type); + selection = info.engine.random_long(0, non_terminals.size() - 1); + auto& sel_v = info.types.get_function_allowed_arguments(selection); + // if it does not accept the type we are, we will accept this as a valid "temp" non-terminal if (std::find(sel_v.begin(), sel_v.end(), type) == sel_v.end()) break; - } while(true); + } while (true); - func_t func(details.types.get_function_argc(selection), details.types.get_function(selection), type, selection); - if (const auto& func_init = details.types.get_function_initializer(selection)) - func_init.value()(func); - return details.alloc.template emplace(func, details.alloc); + func_t func(info.types.get_function_argc(selection), info.types.get_function(selection), type, selection); + if (const auto& func_init = info.types.get_function_initializer(selection)) + (*func_init)(func); + return info.alloc.template emplace(func, info.alloc); }