From 7cc9facd7bbc97b6e8c16ce9260f0af808c99d49 Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Thu, 23 Jul 2020 12:30:39 -0700 Subject: [PATCH 01/43] Update README.md --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a928b77..c44bccc 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,14 @@ TinyProg is minimalist shader-like scripting language, based on a C++ refactor o A simple program: ``` -x: sqrt(y * y + z * z); -jump: is_negative ? x < 0; -return: x; -label: is_negative; -return: -1 * x; +x: random(-1, 1); +jump: is_negative ? x < 0; +jump: is_positive ? x > 0; +return: 0; +label: is_negative; +return: -1 * x; +label: is_positive; +return: x; ``` The variables for this program are bound thusly: From b4f1151ed22097082090126fba9d090a54d96de4 Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Thu, 23 Jul 2020 13:18:07 -0700 Subject: [PATCH 02/43] Use the gitlab repo --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e62b780..7b17055 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,7 @@ if(VCPKG_DEVELOP_EZ_DIR) vcpkg_setup_ez(DIR ${VCPKG_DEVELOP_EZ_DIR}) else() vcpkg_setup_ez( - REPO https://github.com/loopunit/vcpkg_ez.git + REPO https://gitlab.ct.activision.com/shg_audio/vcpkg_ez.git TAG HEAD DIR ${VCPKG_DEVELOP_ROOT_DIR}/vcpkg_ez ) From a514c9d25f3d26c121877490a673e2187eee1577 Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Thu, 23 Jul 2020 13:31:02 -0700 Subject: [PATCH 03/43] clang-format cleanup --- .clang-format | 8 +-- include/tinyprog.h | 158 ++++++++++++++++++++++++++------------------- 2 files changed, 96 insertions(+), 70 deletions(-) diff --git a/.clang-format b/.clang-format index 32b0fc0..8c1f86c 100644 --- a/.clang-format +++ b/.clang-format @@ -1,7 +1,7 @@ --- BasedOnStyle: LLVM AccessModifierOffset: '-4' -AlignAfterOpenBracket: DontAlign +AlignAfterOpenBracket: AlwaysBreak AlignConsecutiveMacros: 'true' AlignConsecutiveAssignments: 'true' AlignConsecutiveDeclarations: 'true' @@ -36,7 +36,7 @@ IncludeBlocks: Preserve IndentCaseLabels: 'false' IndentPPDirectives: None IndentWidth: '4' -IndentWrappedFunctionNames: 'true' +IndentWrappedFunctionNames: 'false' KeepEmptyLinesAtTheStartOfBlocks: 'false' NamespaceIndentation: All PointerAlignment: Left @@ -47,7 +47,7 @@ SpaceAfterCStyleCast: 'false' SpaceAfterLogicalNot: 'false' SpaceAfterTemplateKeyword: 'false' SpaceBeforeAssignmentOperators: 'true' -SpaceBeforeCpp11BracedList: 'true' +SpaceBeforeCpp11BracedList: 'false' SpaceBeforeCtorInitializerColon: 'true' SpaceBeforeInheritanceColon: 'true' SpaceBeforeParens: ControlStatements @@ -59,8 +59,8 @@ SpacesInCStyleCastParentheses: 'false' SpacesInContainerLiterals: 'false' SpacesInParentheses: 'false' SpacesInSquareBrackets: 'false' +Standard: Cpp11 TabWidth: '4' UseTab: Always -BreakBeforeBinaryOperators: 'All' ... diff --git a/include/tinyprog.h b/include/tinyprog.h index 6d4e350..5fdd346 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -593,32 +593,58 @@ namespace te using t_impl = native_builtins_impl; static inline constexpr variable functions[] = {/* must be in alphabetical order */ - {"abs", t_impl::fabs, TE_FUNCTION1 | TE_FLAG_PURE, 0}, {"acos", t_impl::acos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, {"asin", t_impl::asin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"atan", t_impl::atan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, {"atan2", t_impl::atan2, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"ceil", t_impl::ceil, TE_FUNCTION1 | TE_FLAG_PURE, 0}, {"cos", t_impl::cos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, {"cosh", t_impl::cosh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"e", t_impl::e, TE_FUNCTION0 | TE_FLAG_PURE, 0}, {"exp", t_impl::exp, TE_FUNCTION1 | TE_FLAG_PURE, 0}, {"fac", t_impl::fac, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"floor", t_impl::floor, TE_FUNCTION1 | TE_FLAG_PURE, 0}, {"ln", t_impl::log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"abs", t_impl::fabs, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"acos", t_impl::acos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"asin", t_impl::asin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"atan", t_impl::atan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"atan2", t_impl::atan2, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"ceil", t_impl::ceil, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"cos", t_impl::cos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"cosh", t_impl::cosh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"e", t_impl::e, TE_FUNCTION0 | TE_FLAG_PURE, 0}, + {"exp", t_impl::exp, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"fac", t_impl::fac, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"floor", t_impl::floor, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"ln", t_impl::log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, #ifdef TE_NAT_LOG - {"log", t_impl::log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"log", t_impl::log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, #else - {"log", t_impl::log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"log", t_impl::log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, #endif - {"log10", t_impl::log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, {"ncr", t_impl::ncr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, {"npr", t_impl::npr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"pi", t_impl::pi, TE_FUNCTION0 | TE_FLAG_PURE, 0}, {"pow", t_impl::pow, TE_FUNCTION2 | TE_FLAG_PURE, 0}, {"sin", t_impl::sin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"sinh", t_impl::sinh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, {"sqrt", t_impl::sqrt, TE_FUNCTION1 | TE_FLAG_PURE, 0}, {"tan", t_impl::tan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"tanh", t_impl::tanh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, {0, 0, 0, 0}}; + {"log10", t_impl::log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"ncr", t_impl::ncr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"npr", t_impl::npr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"pi", t_impl::pi, TE_FUNCTION0 | TE_FLAG_PURE, 0}, + {"pow", t_impl::pow, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"sin", t_impl::sin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"sinh", t_impl::sinh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"sqrt", t_impl::sqrt, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"tan", t_impl::tan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"tanh", t_impl::tanh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {0, 0, 0, 0}}; static inline constexpr variable operators[] = {/* must be in alphabetical order */ - {"add", t_impl::add, TE_FUNCTION2 | TE_FLAG_PURE, 0}, {"comma", t_impl::comma, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"divide", t_impl::divide, TE_FUNCTION2 | TE_FLAG_PURE, 0}, {"equal", t_impl::equal, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"fmod", t_impl::fmod, TE_FUNCTION2 | TE_FLAG_PURE, 0}, {"greater", t_impl::greater, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"greater_eq", t_impl::greater_eq, TE_FUNCTION2 | TE_FLAG_PURE, 0}, {"logical_and", t_impl::logical_and, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"logical_not", t_impl::logical_not, TE_FUNCTION1 | TE_FLAG_PURE, 0}, {"logical_notnot", t_impl::logical_notnot, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"logical_or", t_impl::logical_or, TE_FUNCTION2 | TE_FLAG_PURE, 0}, {"lower", t_impl::lower, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"lower_eq", t_impl::lower_eq, TE_FUNCTION2 | TE_FLAG_PURE, 0}, {"mul", t_impl::mul, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"negate", t_impl::negate, TE_FUNCTION1 | TE_FLAG_PURE, 0}, {"negate_logical_not", t_impl::negate_logical_not, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"negate_logical_notnot", t_impl::negate_logical_notnot, TE_FUNCTION1 | TE_FLAG_PURE, 0}, {"not_equal", t_impl::not_equal, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"pow", t_impl::pow, TE_FUNCTION2 | TE_FLAG_PURE, 0}, {"sub", t_impl::sub, TE_FUNCTION2 | TE_FLAG_PURE, 0}, {0, 0, 0, 0}}; + {"add", t_impl::add, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"comma", t_impl::comma, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"divide", t_impl::divide, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"equal", t_impl::equal, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"fmod", t_impl::fmod, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"greater", t_impl::greater, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"greater_eq", t_impl::greater_eq, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"logical_and", t_impl::logical_and, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"logical_not", t_impl::logical_not, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"logical_notnot", t_impl::logical_notnot, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"logical_or", t_impl::logical_or, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"lower", t_impl::lower, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"lower_eq", t_impl::lower_eq, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"mul", t_impl::mul, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"negate", t_impl::negate, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"negate_logical_not", t_impl::negate_logical_not, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"negate_logical_notnot", t_impl::negate_logical_notnot, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"not_equal", t_impl::not_equal, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"pow", t_impl::pow, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"sub", t_impl::sub, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {0, 0, 0, 0}}; static inline int name_compare(const char* a, const char* b, size_t n) { @@ -753,8 +779,9 @@ namespace te } template - static inline auto eval_generic(int type, T_HANDLE_CONSTANT handle_constant, T_HANDLE_VARIABLE handle_variable, T_HANDLE_FUNCTION handle_function, - T_HANDLE_CLOSURE handle_closure, T_HANDLE_ERROR handle_error) + static inline auto eval_generic( + int type, T_HANDLE_CONSTANT handle_constant, T_HANDLE_VARIABLE handle_variable, T_HANDLE_FUNCTION handle_function, T_HANDLE_CLOSURE handle_closure, + T_HANDLE_ERROR handle_error) { const auto t = type_mask(type); if (t == TE_CONSTANT) @@ -928,28 +955,28 @@ namespace te case statement_type::jump: statement_index = statement.arg_a; break; - + case statement_type::jump_if: if (0.0f != eval(((const char*)expr_buffer) + statement.arg_b, expr_context)) // TODO: traits function like nan for zero, or compare function? { statement_index = statement.arg_a; } break; - + case statement_type::return_value: return eval(((const char*)expr_buffer) + statement.arg_a, expr_context); - + case statement_type::assign: - { - auto dest = (env_traits::t_vector*)expr_context[statement.arg_a]; - *dest = eval(((const char*)expr_buffer) + statement.arg_b, expr_context); - } - break; - + { + auto dest = (env_traits::t_vector*)expr_context[statement.arg_a]; + *dest = eval(((const char*)expr_buffer) + statement.arg_b, expr_context); + } + break; + case statement_type::call: eval(((const char*)expr_buffer) + statement.arg_a, expr_context); break; - + default: // fatal error return env_traits::t_vector_builtins::nan(); @@ -1504,9 +1531,8 @@ namespace te } int logical = 0; - while (s->type == TOK_INFIX - && (s->function == t_builtins::find_builtin_address("add") || s->function == t_builtins::find_builtin_address("sub") - || s->function == t_builtins::find_builtin_address("logical_not"))) + while (s->type == TOK_INFIX && (s->function == t_builtins::find_builtin_address("add") || s->function == t_builtins::find_builtin_address("sub") || + s->function == t_builtins::find_builtin_address("logical_not"))) { if (s->function == t_builtins::find_builtin_address("logical_not")) { @@ -1572,10 +1598,10 @@ namespace te const void* left_function = NULL; expr_native* insertion = 0; - if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) - && (ret->function == t_builtins::find_builtin_address("negate") || ret->function == t_builtins::find_builtin_address("logical_not") - || ret->function == t_builtins::find_builtin_address("logical_notnot") || ret->function == t_builtins::find_builtin_address("negate_logical_not") - || ret->function == t_builtins::find_builtin_address("negate_logical_notnot"))) + if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && + (ret->function == t_builtins::find_builtin_address("negate") || ret->function == t_builtins::find_builtin_address("logical_not") || + ret->function == t_builtins::find_builtin_address("logical_notnot") || ret->function == t_builtins::find_builtin_address("negate_logical_not") || + ret->function == t_builtins::find_builtin_address("negate_logical_notnot"))) { left_function = ret->function; expr_native* se = ret->parameters[0]; @@ -1635,9 +1661,8 @@ namespace te /* = {("*" | "/" | "%") } */ expr_native* ret = factor(s); - while (s->type == TOK_INFIX - && (s->function == t_builtins::find_builtin_address("mul") || s->function == t_builtins::find_builtin_address("divide") - || s->function == t_builtins::find_builtin_address("fmod"))) + while (s->type == TOK_INFIX && (s->function == t_builtins::find_builtin_address("mul") || s->function == t_builtins::find_builtin_address("divide") || + s->function == t_builtins::find_builtin_address("fmod"))) { te_fun2 t = (te_fun2)s->function; next_token(s); @@ -1669,10 +1694,9 @@ namespace te /* = {(">" | ">=" | "<" | "<=" | "==" | "!=") } */ expr_native* ret = sum_expr(s); - while (s->type == TOK_INFIX - && (s->function == t_builtins::find_builtin_address("greater") || s->function == t_builtins::find_builtin_address("greater_eq") - || s->function == t_builtins::find_builtin_address("lower") || s->function == t_builtins::find_builtin_address("lower_eq") - || s->function == t_builtins::find_builtin_address("equal") || s->function == t_builtins::find_builtin_address("not_equal"))) + while (s->type == TOK_INFIX && (s->function == t_builtins::find_builtin_address("greater") || s->function == t_builtins::find_builtin_address("greater_eq") || + s->function == t_builtins::find_builtin_address("lower") || s->function == t_builtins::find_builtin_address("lower_eq") || + s->function == t_builtins::find_builtin_address("equal") || s->function == t_builtins::find_builtin_address("not_equal"))) { te_fun2 t = (te_fun2)s->function; next_token(s); @@ -1953,8 +1977,8 @@ namespace te size_t m_build_buffer_size; }; - static size_t export_estimate( - const expr_native* n, size_t& export_size, const variable* lookup, int lookup_len, name_map& name_map, index_map& index_map, int& index_counter) + static size_t + export_estimate(const expr_native* n, size_t& export_size, const variable* lookup, int lookup_len, name_map& name_map, index_map& index_map, int& index_counter) { if (!n) return export_size; @@ -2023,8 +2047,8 @@ namespace te } template - static size_t export_write( - const expr_native* n, size_t& export_size, const variable* lookup, int lookup_len, const unsigned char* out_buffer, T_REGISTER_FUNC register_func) + static size_t + export_write(const expr_native* n, size_t& export_size, const variable* lookup, int lookup_len, const unsigned char* out_buffer, T_REGISTER_FUNC register_func) { if (!n) return export_size; @@ -2153,7 +2177,8 @@ namespace te expr->m_build_buffer_size = export_size; size_t actual_export_size = 0; - portable::export_write(native_expr, actual_export_size, variables, var_count, expr->m_build_buffer.get(), + portable::export_write( + native_expr, actual_export_size, variables, var_count, expr->m_build_buffer.get(), [&](const void* addr, expr_portable* out, const variable* v) -> void { assert(v != nullptr); auto itor = indexer.index_map.find(addr); @@ -2289,9 +2314,9 @@ namespace te { if (index < s.length()) { - return {trim_all_space(std::string_view {&s[0], index}), trim_all_space(std::string_view {&s[index], s.length() - index})}; + return {trim_all_space(std::string_view{&s[0], index}), trim_all_space(std::string_view{&s[index], s.length() - index})}; } - return {s, std::string_view {}}; + return {s, std::string_view{}}; } static inline std::tuple split_at_index_excl(std::string_view s, size_t index) @@ -2299,9 +2324,9 @@ namespace te auto [l, r] = split_at_index(s, index); if (r.length() > 1) { - return {trim_all_space(l), trim_all_space(std::string_view {&r[1], r.length() - 1})}; + return {trim_all_space(l), trim_all_space(std::string_view{&r[1], r.length() - 1})}; } - return {l, std::string_view {}}; + return {l, std::string_view{}}; } static inline std::tuple split_at_char(std::string_view program, char c) @@ -2313,7 +2338,7 @@ namespace te return split_at_index(program, i); } } - return {program, std::string_view {}}; + return {program, std::string_view{}}; } static inline std::tuple split_at_char_excl(std::string_view program, char c) @@ -2325,7 +2350,7 @@ namespace te return split_at_index_excl(program, i); } } - return {program, std::string_view {}}; + return {program, std::string_view{}}; } //// @@ -2335,8 +2360,9 @@ namespace te static inline const auto keyword_label = std::string_view("label"); template - static inline void parse_statement(std::string_view statement, T_ADD_LABEL add_label, T_ADD_JUMP add_jump, T_ADD_JUMP_IF add_jump_if, - T_ADD_RETURN_VALUE add_return_value, T_ADD_ASSIGN add_assign, T_ADD_CALL add_call) + static inline void parse_statement( + std::string_view statement, T_ADD_LABEL add_label, T_ADD_JUMP add_jump, T_ADD_JUMP_IF add_jump_if, T_ADD_RETURN_VALUE add_return_value, T_ADD_ASSIGN add_assign, + T_ADD_CALL add_call) { auto [operation, expression] = split_at_char_excl(statement, ':'); @@ -2522,7 +2548,7 @@ namespace te template compiled_program* compile(const char* text, const variable* variables, int var_count, int* error) { - auto program_src = parser::trim_all_space(std::string_view {text, strlen(text)}); + auto program_src = parser::trim_all_space(std::string_view{text, strlen(text)}); auto program_remaining = program_src; std::vector program_statements; @@ -2544,31 +2570,31 @@ namespace te // jump [&](std::string_view destination_label) { - any_statement s = jump_statement {lm.find_label(destination_label)}; + any_statement s = jump_statement{lm.find_label(destination_label)}; program_statements.push_back(s); }, // jump_if [&](std::string_view destination_label, std::string_view condition) { - any_statement s = jump_if_statement {lm.find_label(destination_label), em.add_expression(condition)}; + any_statement s = jump_if_statement{lm.find_label(destination_label), em.add_expression(condition)}; program_statements.push_back(s); }, // return_value [&](std::string_view expression) { - any_statement s = return_value_statement {em.add_expression(expression)}; + any_statement s = return_value_statement{em.add_expression(expression)}; program_statements.push_back(s); }, // assign [&](std::string_view destination, std::string_view expression) { - any_statement s = assign_statement {vm.find_label(destination), em.add_expression(expression)}; + any_statement s = assign_statement{vm.find_label(destination), em.add_expression(expression)}; program_statements.push_back(s); }, // call [&](std::string_view expression) { - any_statement s = call_statement {em.add_expression(expression)}; + any_statement s = call_statement{em.add_expression(expression)}; program_statements.push_back(s); }); From e10506de15fc746a5bb50ad8719abd12c3072c3a Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Thu, 23 Jul 2020 15:22:44 -0700 Subject: [PATCH 04/43] jump_if is redundant --- include/tinyprog.h | 55 +++++++--------------------------------------- 1 file changed, 8 insertions(+), 47 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 5fdd346..5517c70 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -932,7 +932,6 @@ namespace te enum class statement_type : int { jump, - jump_if, return_value, assign, call, @@ -953,11 +952,7 @@ namespace te switch (statement.type) { case statement_type::jump: - statement_index = statement.arg_a; - break; - - case statement_type::jump_if: - if (0.0f != eval(((const char*)expr_buffer) + statement.arg_b, expr_context)) // TODO: traits function like nan for zero, or compare function? + if (statement.arg_b == -1 || (0.0f != eval(((const char*)expr_buffer) + statement.arg_b, expr_context))) // TODO: traits function like nan for zero, or compare function? { statement_index = statement.arg_a; } @@ -2399,23 +2394,6 @@ namespace te } } } - - // jump | jump_if | return_value | assign | call - // label index | label index | expression | dest variable index | expression - // empty | expression | empty | expression | empty - // struct statement - //{ - // //enum class instruction - // //{ - // // jump, // "jump: example_label;" - // // jump_if, // "jump: example_label ? x * 0.5 > 1;" - // // return_value, // "return: x * 0.5;" - // // assign, // "x: y * 0.1;" - // // call, // "func(x * 0.1);" - // //}; - // // - // //instruction m_instruction; - //}; }; struct label_manager @@ -2506,13 +2484,6 @@ namespace te }; struct jump_statement - { - label_manager::handle m_target_handle; // Pass 1: index isn't known isn't known until whole program is parsed - - int m_target_index; // Pass 2: set from handle to index (of statement immediately following the label) - }; - - struct jump_if_statement { label_manager::handle m_target_handle; // Pass 1: index isn't known isn't known until whole program is parsed int m_expression_index; @@ -2543,7 +2514,7 @@ namespace te int m_expression_offset; }; - using any_statement = std::variant; + using any_statement = std::variant; template compiled_program* compile(const char* text, const variable* variables, int var_count, int* error) @@ -2570,13 +2541,13 @@ namespace te // jump [&](std::string_view destination_label) { - any_statement s = jump_statement{lm.find_label(destination_label)}; + any_statement s = jump_statement{lm.find_label(destination_label), -1}; program_statements.push_back(s); }, // jump_if [&](std::string_view destination_label, std::string_view condition) { - any_statement s = jump_if_statement{lm.find_label(destination_label), em.add_expression(condition)}; + any_statement s = jump_statement{lm.find_label(destination_label), em.add_expression(condition)}; program_statements.push_back(s); }, @@ -2608,10 +2579,6 @@ namespace te { std::get(s).m_target_index = lm.get_label_statement_index(std::get(s).m_target_handle); } - else if (std::holds_alternative(s)) - { - std::get(s).m_target_index = lm.get_label_statement_index(std::get(s).m_target_handle); - } } // Add referenced variables to the lookup dict @@ -2700,11 +2667,11 @@ namespace te } } - if (std::holds_alternative(s)) + if (std::holds_alternative(s)) { - if (std::get(s).m_expression_index == expr_idx) + if (std::get(s).m_expression_index == expr_idx) { - std::get(s).m_expression_offset = current_expr_offset; + std::get(s).m_expression_offset = current_expr_offset; } } } @@ -2738,17 +2705,11 @@ namespace te s_out.arg_a = std::get(s_in).m_expression_offset; s_out.arg_b = -1; } - else if (std::holds_alternative(s_in)) - { - s_out.type = statement_type::jump_if; - s_out.arg_a = std::get(s_in).m_target_index; - s_out.arg_b = std::get(s_in).m_expression_offset; - } else if (std::holds_alternative(s_in)) { s_out.type = statement_type::jump; s_out.arg_a = std::get(s_in).m_target_index; - s_out.arg_b = -1; + s_out.arg_b = std::get(s_in).m_expression_offset; } program->program_statements.push_back(s_out); From 000ad59a2001e4ffbaa13fc3d86f6367ef31b653 Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Thu, 23 Jul 2020 18:46:04 -0700 Subject: [PATCH 05/43] Simplify program eval --- include/tinyprog.h | 18 ++++++++++++++++++ test/example_program.cpp | 12 +----------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 5517c70..b025453 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -902,6 +902,11 @@ namespace te return a; } + static inline t_vector as_truth(t_vector a) noexcept + { + return (a != 0.0f) ? 1.0f : 0.0f; + } + static inline t_vector explicit_load_atom(double a) noexcept { return (t_vector)a; @@ -1029,6 +1034,19 @@ namespace te }; compiled_program* compile_program(const char* program, const variable* variables, int var_count, int* error); + + inline env_traits::t_vector eval_program(compiled_program* prog) + { + auto array_size = prog->get_binding_array_size(); + auto binding_addrs = prog->get_binding_addresses(); + auto binding_names = prog->get_binding_names(); + auto data_size = prog->get_data_size(); + auto data = prog->get_data(); + auto num_statements = prog->get_statement_array_size(); + auto statements = prog->get_statements(); + + return eval_program(statements, (int)num_statements, data, binding_addrs); + } #endif // #if (TE_COMPILER_ENABLED) } // namespace te diff --git a/test/example_program.cpp b/test/example_program.cpp index af4ae56..fb41c32 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -18,17 +18,7 @@ int main(int argc, char* argv[]) int err = 0; auto prog = compile_program(p, vars, 2, &err); - - auto array_size = prog->get_binding_array_size(); - auto binding_addrs = prog->get_binding_addresses(); - auto binding_names = prog->get_binding_names(); - auto data_size = prog->get_data_size(); - auto data = prog->get_data(); - auto num_statements = prog->get_statement_array_size(); - auto statements = prog->get_statements(); - - auto result = eval_program(statements, (int)num_statements, data, binding_addrs); - + auto result = eval_program(prog); delete prog; return 0; } From d4df089168a84e173a8cff5d24beaeedb75d12be Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Thu, 23 Jul 2020 23:09:02 -0700 Subject: [PATCH 06/43] Big refactor, finally encapsulated the program/expression into a trait driven template. This adds a lot of header overhead, but only if compilation is needed, so TP_COMPILER_ENABLED can be used to disable it. --- include/tinyprog.h | 935 ++++++++++++++++++--------------------- test/benchmark.cpp | 20 +- test/example.cpp | 6 +- test/example2.cpp | 12 +- test/example3.cpp | 10 +- test/example_program.cpp | 12 +- test/minctest.h | 10 +- test/test.cpp | 438 +++++++++--------- 8 files changed, 674 insertions(+), 769 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index b025453..f49f433 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -33,43 +33,39 @@ #include #include -#ifndef TE_COMPILER_ENABLED -#define TE_COMPILER_ENABLED 1 -#endif // TE_COMPILER_ENABLED +#ifndef TP_COMPILER_ENABLED +#define TP_COMPILER_ENABLED 0 +#endif // TP_COMPILER_ENABLED -#ifndef TE_IMPLEMENT -#define TE_IMPLEMENT 0 -#endif // TE_COMPILER_ENABLED - -namespace te +namespace tp { enum { - TE_VARIABLE = 0, - - TE_CONSTANT = 1, - - TE_FUNCTION0 = 8, - TE_FUNCTION1, - TE_FUNCTION2, - TE_FUNCTION3, - TE_FUNCTION4, - TE_FUNCTION5, - TE_FUNCTION6, - TE_FUNCTION7, - TE_FUNCTION_MAX, - - TE_CLOSURE0 = 16, - TE_CLOSURE1, - TE_CLOSURE2, - TE_CLOSURE3, - TE_CLOSURE4, - TE_CLOSURE5, - TE_CLOSURE6, - TE_CLOSURE7, - TE_CLOSURE_MAX, - - TE_FLAG_PURE = 32 + VARIABLE = 0, + + CONSTANT = 1, + + FUNCTION0 = 8, + FUNCTION1, + FUNCTION2, + FUNCTION3, + FUNCTION4, + FUNCTION5, + FUNCTION6, + FUNCTION7, + FUNCTION_MAX, + + CLOSURE0 = 16, + CLOSURE1, + CLOSURE2, + CLOSURE3, + CLOSURE4, + CLOSURE5, + CLOSURE6, + CLOSURE7, + CLOSURE_MAX, + + FLAG_PURE = 32 }; struct variable @@ -329,9 +325,9 @@ namespace te return 0.0f; } - static float nan() + static double nan() { - return std::numeric_limits::quiet_NaN(); + return std::numeric_limits::quiet_NaN(); } }; @@ -593,57 +589,57 @@ namespace te using t_impl = native_builtins_impl; static inline constexpr variable functions[] = {/* must be in alphabetical order */ - {"abs", t_impl::fabs, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"acos", t_impl::acos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"asin", t_impl::asin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"atan", t_impl::atan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"atan2", t_impl::atan2, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"ceil", t_impl::ceil, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"cos", t_impl::cos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"cosh", t_impl::cosh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"e", t_impl::e, TE_FUNCTION0 | TE_FLAG_PURE, 0}, - {"exp", t_impl::exp, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"fac", t_impl::fac, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"floor", t_impl::floor, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"ln", t_impl::log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, -#ifdef TE_NAT_LOG - {"log", t_impl::log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"abs", t_impl::fabs, FUNCTION1 | FLAG_PURE, 0}, + {"acos", t_impl::acos, FUNCTION1 | FLAG_PURE, 0}, + {"asin", t_impl::asin, FUNCTION1 | FLAG_PURE, 0}, + {"atan", t_impl::atan, FUNCTION1 | FLAG_PURE, 0}, + {"atan2", t_impl::atan2, FUNCTION2 | FLAG_PURE, 0}, + {"ceil", t_impl::ceil, FUNCTION1 | FLAG_PURE, 0}, + {"cos", t_impl::cos, FUNCTION1 | FLAG_PURE, 0}, + {"cosh", t_impl::cosh, FUNCTION1 | FLAG_PURE, 0}, + {"e", t_impl::e, FUNCTION0 | FLAG_PURE, 0}, + {"exp", t_impl::exp, FUNCTION1 | FLAG_PURE, 0}, + {"fac", t_impl::fac, FUNCTION1 | FLAG_PURE, 0}, + {"floor", t_impl::floor, FUNCTION1 | FLAG_PURE, 0}, + {"ln", t_impl::log, FUNCTION1 | FLAG_PURE, 0}, +#ifdef TP_NAT_LOG + {"log", t_impl::log, FUNCTION1 | FLAG_PURE, 0}, #else - {"log", t_impl::log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"log", t_impl::log10, FUNCTION1 | FLAG_PURE, 0}, #endif - {"log10", t_impl::log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"ncr", t_impl::ncr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"npr", t_impl::npr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"pi", t_impl::pi, TE_FUNCTION0 | TE_FLAG_PURE, 0}, - {"pow", t_impl::pow, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"sin", t_impl::sin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"sinh", t_impl::sinh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"sqrt", t_impl::sqrt, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"tan", t_impl::tan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"tanh", t_impl::tanh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"log10", t_impl::log10, FUNCTION1 | FLAG_PURE, 0}, + {"ncr", t_impl::ncr, FUNCTION2 | FLAG_PURE, 0}, + {"npr", t_impl::npr, FUNCTION2 | FLAG_PURE, 0}, + {"pi", t_impl::pi, FUNCTION0 | FLAG_PURE, 0}, + {"pow", t_impl::pow, FUNCTION2 | FLAG_PURE, 0}, + {"sin", t_impl::sin, FUNCTION1 | FLAG_PURE, 0}, + {"sinh", t_impl::sinh, FUNCTION1 | FLAG_PURE, 0}, + {"sqrt", t_impl::sqrt, FUNCTION1 | FLAG_PURE, 0}, + {"tan", t_impl::tan, FUNCTION1 | FLAG_PURE, 0}, + {"tanh", t_impl::tanh, FUNCTION1 | FLAG_PURE, 0}, {0, 0, 0, 0}}; static inline constexpr variable operators[] = {/* must be in alphabetical order */ - {"add", t_impl::add, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"comma", t_impl::comma, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"divide", t_impl::divide, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"equal", t_impl::equal, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"fmod", t_impl::fmod, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"greater", t_impl::greater, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"greater_eq", t_impl::greater_eq, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"logical_and", t_impl::logical_and, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"logical_not", t_impl::logical_not, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"logical_notnot", t_impl::logical_notnot, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"logical_or", t_impl::logical_or, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"lower", t_impl::lower, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"lower_eq", t_impl::lower_eq, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"mul", t_impl::mul, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"negate", t_impl::negate, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"negate_logical_not", t_impl::negate_logical_not, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"negate_logical_notnot", t_impl::negate_logical_notnot, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"not_equal", t_impl::not_equal, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"pow", t_impl::pow, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"sub", t_impl::sub, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"add", t_impl::add, FUNCTION2 | FLAG_PURE, 0}, + {"comma", t_impl::comma, FUNCTION2 | FLAG_PURE, 0}, + {"divide", t_impl::divide, FUNCTION2 | FLAG_PURE, 0}, + {"equal", t_impl::equal, FUNCTION2 | FLAG_PURE, 0}, + {"fmod", t_impl::fmod, FUNCTION2 | FLAG_PURE, 0}, + {"greater", t_impl::greater, FUNCTION2 | FLAG_PURE, 0}, + {"greater_eq", t_impl::greater_eq, FUNCTION2 | FLAG_PURE, 0}, + {"logical_and", t_impl::logical_and, FUNCTION2 | FLAG_PURE, 0}, + {"logical_not", t_impl::logical_not, FUNCTION1 | FLAG_PURE, 0}, + {"logical_notnot", t_impl::logical_notnot, FUNCTION1 | FLAG_PURE, 0}, + {"logical_or", t_impl::logical_or, FUNCTION2 | FLAG_PURE, 0}, + {"lower", t_impl::lower, FUNCTION2 | FLAG_PURE, 0}, + {"lower_eq", t_impl::lower_eq, FUNCTION2 | FLAG_PURE, 0}, + {"mul", t_impl::mul, FUNCTION2 | FLAG_PURE, 0}, + {"negate", t_impl::negate, FUNCTION1 | FLAG_PURE, 0}, + {"negate_logical_not", t_impl::negate_logical_not, FUNCTION1 | FLAG_PURE, 0}, + {"negate_logical_notnot", t_impl::negate_logical_notnot, FUNCTION1 | FLAG_PURE, 0}, + {"not_equal", t_impl::not_equal, FUNCTION2 | FLAG_PURE, 0}, + {"pow", t_impl::pow, FUNCTION2 | FLAG_PURE, 0}, + {"sub", t_impl::sub, FUNCTION2 | FLAG_PURE, 0}, {0, 0, 0, 0}}; static inline int name_compare(const char* a, const char* b, size_t n) @@ -775,7 +771,7 @@ namespace te template inline T arity(const T t) noexcept { - return (((t) & (TE_FUNCTION0 | TE_CLOSURE0)) ? ((t)&0x00000007) : 0); + return (((t) & (FUNCTION0 | CLOSURE0)) ? ((t)&0x00000007) : 0); } template @@ -784,25 +780,25 @@ namespace te T_HANDLE_ERROR handle_error) { const auto t = type_mask(type); - if (t == TE_CONSTANT) + if (t == CONSTANT) { return handle_constant(); } - else if (t == TE_VARIABLE) + else if (t == VARIABLE) { return handle_variable(); } - else if (t >= TE_FUNCTION0) + else if (t >= FUNCTION0) { - if (t < TE_FUNCTION_MAX) + if (t < FUNCTION_MAX) { - return handle_function(t - TE_FUNCTION0); + return handle_function(t - FUNCTION0); } - if (t >= TE_CLOSURE0) + if (t >= CLOSURE0) { - if (t < TE_CLOSURE_MAX) + if (t < CLOSURE_MAX) { - return handle_closure(t - TE_CLOSURE0); + return handle_closure(t - CLOSURE0); } } } @@ -813,57 +809,57 @@ namespace te template auto eval_function(int a, const void* fn, T_RET error_val, T_EVAL_ARG eval_arg) -> T_RET { -#define TE_FUN(...) ((T_RET(*)(__VA_ARGS__))fn) +#define FUN(...) ((T_RET(*)(__VA_ARGS__))fn) switch (a) { case 0: - return TE_FUN(void)(); + return FUN(void)(); case 1: - return TE_FUN(T_VECTOR)(eval_arg(0)); + return FUN(T_VECTOR)(eval_arg(0)); case 2: - return TE_FUN(T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1)); + return FUN(T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1)); case 3: - return TE_FUN(T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2)); + return FUN(T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2)); case 4: - return TE_FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3)); + return FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3)); case 5: - return TE_FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4)); + return FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4)); case 6: - return TE_FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4), eval_arg(5)); + return FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4), eval_arg(5)); case 7: - return TE_FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)( + return FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)( eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4), eval_arg(5), eval_arg(6)); } -#undef TE_FUN +#undef FUN return error_val; } template auto eval_closure(int a, const void* fn, const void* arity_params, T_RET error_val, T_EVAL_ARG eval_arg) -> T_RET { -#define TE_FUN(...) ((T_RET(*)(__VA_ARGS__))fn) +#define FUN(...) ((T_RET(*)(__VA_ARGS__))fn) switch (a) { case 0: - return TE_FUN(const void*)(arity_params); + return FUN(const void*)(arity_params); case 1: - return TE_FUN(const void*, T_VECTOR)(arity_params, eval_arg(0)); + return FUN(const void*, T_VECTOR)(arity_params, eval_arg(0)); case 2: - return TE_FUN(const void*, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1)); + return FUN(const void*, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1)); case 3: - return TE_FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1), eval_arg(2)); + return FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1), eval_arg(2)); case 4: - return TE_FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3)); + return FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3)); case 5: - return TE_FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4)); + return FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4)); case 6: - return TE_FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)( + return FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)( arity_params, eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4), eval_arg(5)); case 7: - return TE_FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)( + return FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)( arity_params, eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4), eval_arg(5), eval_arg(6)); } -#undef TE_FUN +#undef FUN return error_val; } @@ -889,51 +885,6 @@ namespace te } } // namespace eval_details - struct env_traits - { - using t_atom = float; - using t_vector = float; - using t_vector_int = int; - using t_atom_builtins = native_builtins; - using t_vector_builtins = native_builtins; - - static inline t_vector load_atom(t_atom a) noexcept - { - return a; - } - - static inline t_vector as_truth(t_vector a) noexcept - { - return (a != 0.0f) ? 1.0f : 0.0f; - } - - static inline t_vector explicit_load_atom(double a) noexcept - { - return (t_vector)a; - } - - static inline t_vector explicit_load_atom(int a) noexcept - { - return (t_vector)a; - } - - static inline double explicit_store_double(t_vector a) - { - return (double)a; - } - - static inline int explicit_store_int(t_vector a) - { - return (int)a; - } - }; - - inline env_traits::t_vector eval(const void* expr_buffer, const void* const expr_context[]) noexcept - { - return eval_details::eval_portable_impl( - (const expr_portable*)expr_buffer, (const unsigned char*)expr_buffer, expr_context); - } - enum class statement_type : int { jump, @@ -949,117 +900,41 @@ namespace te int arg_b; }; - inline env_traits::t_vector eval_program(const statement* statement_array, int statement_array_size, const void* expr_buffer, const void* const expr_context[]) - { - for (int statement_index = 0; statement_index < statement_array_size; ++statement_index) - { - auto& statement = statement_array[statement_index]; - switch (statement.type) - { - case statement_type::jump: - if (statement.arg_b == -1 || (0.0f != eval(((const char*)expr_buffer) + statement.arg_b, expr_context))) // TODO: traits function like nan for zero, or compare function? - { - statement_index = statement.arg_a; - } - break; - - case statement_type::return_value: - return eval(((const char*)expr_buffer) + statement.arg_a, expr_context); - - case statement_type::assign: - { - auto dest = (env_traits::t_vector*)expr_context[statement.arg_a]; - *dest = eval(((const char*)expr_buffer) + statement.arg_b, expr_context); - } - break; - - case statement_type::call: - eval(((const char*)expr_buffer) + statement.arg_a, expr_context); - break; - - default: - // fatal error - return env_traits::t_vector_builtins::nan(); - } - } - return env_traits::t_vector_builtins::nan(); - } - -#if (TE_COMPILER_ENABLED) +#if (TP_COMPILER_ENABLED) struct compiled_expr { virtual ~compiled_expr() = default; - size_t get_binding_array_size() const; - const void* const* get_binding_addresses() const; - const char* const* get_binding_names() const; - size_t get_data_size() const; - const unsigned char* get_data() const; + virtual size_t get_binding_array_size() const = 0; + virtual const void* const* get_binding_addresses() const = 0; + virtual const char* const* get_binding_names() const = 0; + virtual size_t get_data_size() const = 0; + virtual const unsigned char* get_data() const = 0; }; - compiled_expr* compile(const char* expression, const variable* variables, int var_count, int* error); - - inline env_traits::t_vector eval(const compiled_expr* n) - { - return eval(n->get_data(), n->get_binding_addresses()); - } - - inline env_traits::t_vector interp(const char* expression, int* error) - { - compiled_expr* n = compile(expression, 0, 0, error); - env_traits::t_vector ret; - if (n) - { - ret = eval(n); - delete n; - } - else - { - ret = env_traits::t_vector_builtins::nan(); - } - return ret; - } - struct compiled_program { virtual ~compiled_program() = default; - size_t get_binding_array_size() const; - const void* const* get_binding_addresses() const; - const char* const* get_binding_names() const; - size_t get_data_size() const; - const unsigned char* get_data() const; - size_t get_statement_array_size() const; - const statement* get_statements() const; + virtual size_t get_binding_array_size() const = 0; + virtual const void* const* get_binding_addresses() const = 0; + virtual const char* const* get_binding_names() const = 0; + virtual size_t get_data_size() const = 0; + virtual const unsigned char* get_data() const = 0; + virtual size_t get_statement_array_size() const = 0; + virtual const statement* get_statements() const = 0; }; +#endif // #if (TP_COMPILER_ENABLED) +} // namespace tp - compiled_program* compile_program(const char* program, const variable* variables, int var_count, int* error); - - inline env_traits::t_vector eval_program(compiled_program* prog) - { - auto array_size = prog->get_binding_array_size(); - auto binding_addrs = prog->get_binding_addresses(); - auto binding_names = prog->get_binding_names(); - auto data_size = prog->get_data_size(); - auto data = prog->get_data(); - auto num_statements = prog->get_statement_array_size(); - auto statements = prog->get_statements(); - - return eval_program(statements, (int)num_statements, data, binding_addrs); - } -#endif // #if (TE_COMPILER_ENABLED) -} // namespace te - -#if TE_IMPLEMENT +#if (TP_COMPILER_ENABLED) #include #include #include #include #include -#if (TE_COMPILER_ENABLED) - -namespace te +namespace tp { template struct compiler_builtins : native_builtins @@ -1127,17 +1002,17 @@ namespace te typedef t_vector (*te_fun2)(t_vector, t_vector); - enum - { - TOK_NULL = TE_CLOSURE_MAX, - TOK_ERROR, - TOK_END, - TOK_SEP, - TOK_OPEN, - TOK_CLOSE, - TOK_NUMBER, - TOK_VARIABLE, - TOK_INFIX + enum class TOK : int + { + NUL = CLOSURE_MAX, + ERROR, + END, + SEP, + OPEN, + CLOSE, + NUMBER, + VARIABLE, + INFIX }; struct state @@ -1159,17 +1034,17 @@ namespace te static inline bool is_pure(int t) noexcept { - return (((t)&TE_FLAG_PURE) != 0); + return (((t)&FLAG_PURE) != 0); } static inline bool is_function(int t) noexcept { - return (((t)&TE_FUNCTION0) != 0); + return (((t)&FUNCTION0) != 0); } static inline bool is_closure(int t) noexcept { - return (((t)&TE_CLOSURE0) != 0); + return (((t)&CLOSURE0) != 0); } #define NEW_EXPR(type, ...) \ @@ -1237,13 +1112,13 @@ namespace te static void next_token(state* s) { - s->type = TOK_NULL; + s->type = (int)TOK::NUL; do { if (!*s->next || *s->next == ';') { - s->type = TOK_END; + s->type = (int)TOK::END; return; } @@ -1251,7 +1126,7 @@ namespace te if ((s->next[0] >= '0' && s->next[0] <= '9') || s->next[0] == '.') { s->value = (t_atom)strtod(s->next, (char**)&s->next); - s->type = TOK_NUMBER; + s->type = (int)TOK::NUMBER; } else { @@ -1269,26 +1144,26 @@ namespace te if (!var) { - s->type = TOK_ERROR; + s->type = (int)TOK::ERROR; } else { const auto t = eval_details::type_mask(var->type); - if (t == TE_VARIABLE) + if (t == VARIABLE) { - s->type = TOK_VARIABLE; + s->type = (int)TOK::VARIABLE; s->bound = (const t_atom*)var->address; } - else if (t >= TE_FUNCTION0) + else if (t >= FUNCTION0) { - if (t < TE_FUNCTION_MAX) + if (t < FUNCTION_MAX) { s->type = var->type; s->function = var->address; } - else if (t >= TE_CLOSURE0) + else if (t >= CLOSURE0) { - if (t < TE_CLOSURE_MAX) + if (t < CLOSURE_MAX) { s->context = var->context; s->type = var->type; @@ -1304,109 +1179,109 @@ namespace te switch (s->next++[0]) { case '+': - s->type = TOK_INFIX; + s->type = (int)TOK::INFIX; s->function = t_builtins::find_builtin_address("add"); break; case '-': - s->type = TOK_INFIX; + s->type = (int)TOK::INFIX; s->function = t_builtins::find_builtin_address("sub"); break; case '*': - s->type = TOK_INFIX; + s->type = (int)TOK::INFIX; s->function = t_builtins::find_builtin_address("mul"); break; case '/': - s->type = TOK_INFIX; + s->type = (int)TOK::INFIX; s->function = t_builtins::find_builtin_address("divide"); break; case '^': - s->type = TOK_INFIX; + s->type = (int)TOK::INFIX; s->function = t_builtins::find_builtin_address("pow"); break; case '%': - s->type = TOK_INFIX; + s->type = (int)TOK::INFIX; s->function = t_builtins::find_builtin_address("fmod"); break; case '!': if (s->next++[0] == '=') { - s->type = TOK_INFIX; + s->type = (int)TOK::INFIX; s->function = t_builtins::find_builtin_address("not_equal"); } else { s->next--; - s->type = TOK_INFIX; + s->type = (int)TOK::INFIX; s->function = t_builtins::find_builtin_address("logical_not"); } break; case '=': if (s->next++[0] == '=') { - s->type = TOK_INFIX; + s->type = (int)TOK::INFIX; s->function = t_builtins::find_builtin_address("equal"); } else { - s->type = TOK_ERROR; + s->type = (int)TOK::ERROR; } break; case '<': if (s->next++[0] == '=') { - s->type = TOK_INFIX; + s->type = (int)TOK::INFIX; s->function = t_builtins::find_builtin_address("lower_eq"); } else { s->next--; - s->type = TOK_INFIX; + s->type = (int)TOK::INFIX; s->function = t_builtins::find_builtin_address("lower"); } break; case '>': if (s->next++[0] == '=') { - s->type = TOK_INFIX; + s->type = (int)TOK::INFIX; s->function = t_builtins::find_builtin_address("greater_eq"); } else { s->next--; - s->type = TOK_INFIX; + s->type = (int)TOK::INFIX; s->function = t_builtins::find_builtin_address("greater"); } break; case '&': if (s->next++[0] == '&') { - s->type = TOK_INFIX; + s->type = (int)TOK::INFIX; s->function = t_builtins::find_builtin_address("logical_and"); } else { - s->type = TOK_ERROR; + s->type = (int)TOK::ERROR; } break; case '|': if (s->next++[0] == '|') { - s->type = TOK_INFIX; + s->type = (int)TOK::INFIX; s->function = t_builtins::find_builtin_address("logical_or"); } else { - s->type = TOK_ERROR; + s->type = (int)TOK::ERROR; } break; case '(': - s->type = TOK_OPEN; + s->type = (int)TOK::OPEN; break; case ')': - s->type = TOK_CLOSE; + s->type = (int)TOK::CLOSE; break; case ',': - s->type = TOK_SEP; + s->type = (int)TOK::SEP; break; case ' ': case '\t': @@ -1414,12 +1289,12 @@ namespace te case '\r': break; default: - s->type = TOK_ERROR; + s->type = (int)TOK::ERROR; break; } } } - } while (s->type == TOK_NULL); + } while (s->type == (int)TOK::NUL); } static expr_native* base(state* s) @@ -1430,19 +1305,19 @@ namespace te const auto t = eval_details::type_mask(s->type); - if (t == TOK_NUMBER) + if (t == (int)TOK::NUMBER) { - ret = new_expr(TE_CONSTANT, 0); + ret = new_expr(CONSTANT, 0); ret->value = s->value; next_token(s); } - else if (t == TOK_VARIABLE) + else if (t == (int)TOK::VARIABLE) { - ret = new_expr(TE_VARIABLE, 0); + ret = new_expr(VARIABLE, 0); ret->bound = s->bound; next_token(s); } - else if ((t >= TE_FUNCTION0 && t < TE_FUNCTION_MAX) || (t >= TE_CLOSURE0 && t < TE_CLOSURE_MAX)) + else if ((t >= FUNCTION0 && t < FUNCTION_MAX) || (t >= CLOSURE0 && t < CLOSURE_MAX)) { const auto arity = eval_details::arity(s->type); if (arity == 0) @@ -1452,12 +1327,12 @@ namespace te if (is_closure(s->type)) ret->parameters[0] = s->context; next_token(s); - if (s->type == TOK_OPEN) + if (s->type == (int)TOK::OPEN) { next_token(s); - if (s->type != TOK_CLOSE) + if (s->type != (int)TOK::CLOSE) { - s->type = TOK_ERROR; + s->type = (int)TOK::ERROR; } else { @@ -1482,9 +1357,9 @@ namespace te ret->parameters[arity] = s->context; next_token(s); - if (s->type != TOK_OPEN) + if (s->type != (int)TOK::OPEN) { - s->type = TOK_ERROR; + s->type = (int)TOK::ERROR; } else { @@ -1493,14 +1368,14 @@ namespace te { next_token(s); ret->parameters[i] = expr(s); - if (s->type != TOK_SEP) + if (s->type != (int)TOK::SEP) { break; } } - if (s->type != TOK_CLOSE || i != arity - 1) + if (s->type != (int)TOK::CLOSE || i != arity - 1) { - s->type = TOK_ERROR; + s->type = (int)TOK::ERROR; } else { @@ -1509,13 +1384,13 @@ namespace te } } } - else if (t == TOK_OPEN) + else if (t == (int)TOK::OPEN) { next_token(s); ret = list(s); - if (s->type != TOK_CLOSE) + if (s->type != (int)TOK::CLOSE) { - s->type = TOK_ERROR; + s->type = (int)TOK::ERROR; } else { @@ -1525,7 +1400,7 @@ namespace te else { ret = new_expr(0, 0); - s->type = TOK_ERROR; + s->type = (int)TOK::ERROR; ret->value = t_builtins::nan(); } @@ -1536,7 +1411,7 @@ namespace te { /* = {("-" | "+" | "!")} */ int sign = 1; - while (s->type == TOK_INFIX && (s->function == t_builtins::find_builtin_address("add") || s->function == t_builtins::find_builtin_address("sub"))) + while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("add") || s->function == t_builtins::find_builtin_address("sub"))) { if (s->function == t_builtins::find_builtin_address("sub")) sign = -sign; @@ -1544,8 +1419,8 @@ namespace te } int logical = 0; - while (s->type == TOK_INFIX && (s->function == t_builtins::find_builtin_address("add") || s->function == t_builtins::find_builtin_address("sub") || - s->function == t_builtins::find_builtin_address("logical_not"))) + while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("add") || s->function == t_builtins::find_builtin_address("sub") || + s->function == t_builtins::find_builtin_address("logical_not"))) { if (s->function == t_builtins::find_builtin_address("logical_not")) { @@ -1571,12 +1446,12 @@ namespace te } else if (logical == -1) { - ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); + ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); ret->function = t_builtins::find_builtin_address("logical_not"); } else { - ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); + ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); ret->function = t_builtins::find_builtin_address("logical_notnot"); } } @@ -1584,17 +1459,17 @@ namespace te { if (logical == 0) { - ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); + ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); ret->function = t_builtins::find_builtin_address("negate"); } else if (logical == -1) { - ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); + ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); ret->function = t_builtins::find_builtin_address("negate_logical_not"); } else { - ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); + ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); ret->function = t_builtins::find_builtin_address("negate_logical_notnot"); } } @@ -1602,16 +1477,16 @@ namespace te return ret; } -#ifdef TE_POW_FROM_RIGHT +#ifdef TP_POW_FROM_RIGHT static expr_native* factor(state* s) { /* = {"^" } */ expr_native* ret = power(s); - const void* left_function = NULL; + const void* left_function = NUL; expr_native* insertion = 0; - if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && + if (ret->type == (FUNCTION1 | FLAG_PURE) && (ret->function == t_builtins::find_builtin_address("negate") || ret->function == t_builtins::find_builtin_address("logical_not") || ret->function == t_builtins::find_builtin_address("logical_notnot") || ret->function == t_builtins::find_builtin_address("negate_logical_not") || ret->function == t_builtins::find_builtin_address("negate_logical_notnot"))) @@ -1622,7 +1497,7 @@ namespace te ret = se; } - while (s->type == TOK_INFIX && (s->function == t_builtins::find_builtin_address("pow"))) + while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("pow"))) { te_fun2 t = s->function; next_token(s); @@ -1630,14 +1505,14 @@ namespace te if (insertion) { /* Make exponentiation go right-to-left. */ - expr_native* insert = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, insertion->parameters[1], power(s)); + expr_native* insert = NEW_EXPR(FUNCTION2 | FLAG_PURE, insertion->parameters[1], power(s)); insert->function = t; insertion->parameters[1] = insert; insertion = insert; } else { - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s)); + ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, power(s)); ret->function = t; insertion = ret; } @@ -1645,7 +1520,7 @@ namespace te if (left_function) { - ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, ret); + ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, ret); ret->function = left_function; } @@ -1657,11 +1532,11 @@ namespace te /* = {"^" } */ expr_native* ret = power(s); - while (s->type == TOK_INFIX && (s->function == t_builtins::find_builtin_address("pow"))) + while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("pow"))) { te_fun2 t = (te_fun2)s->function; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s)); + ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, power(s)); ret->function = t; } @@ -1674,12 +1549,12 @@ namespace te /* = {("*" | "/" | "%") } */ expr_native* ret = factor(s); - while (s->type == TOK_INFIX && (s->function == t_builtins::find_builtin_address("mul") || s->function == t_builtins::find_builtin_address("divide") || - s->function == t_builtins::find_builtin_address("fmod"))) + while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("mul") || s->function == t_builtins::find_builtin_address("divide") || + s->function == t_builtins::find_builtin_address("fmod"))) { te_fun2 t = (te_fun2)s->function; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, factor(s)); + ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, factor(s)); ret->function = t; } @@ -1691,11 +1566,11 @@ namespace te /* = {("+" | "-") } */ expr_native* ret = term(s); - while (s->type == TOK_INFIX && (s->function == t_builtins::find_builtin_address("add") || s->function == t_builtins::find_builtin_address("sub"))) + while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("add") || s->function == t_builtins::find_builtin_address("sub"))) { te_fun2 t = (te_fun2)s->function; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, term(s)); + ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, term(s)); ret->function = t; } @@ -1707,13 +1582,13 @@ namespace te /* = {(">" | ">=" | "<" | "<=" | "==" | "!=") } */ expr_native* ret = sum_expr(s); - while (s->type == TOK_INFIX && (s->function == t_builtins::find_builtin_address("greater") || s->function == t_builtins::find_builtin_address("greater_eq") || - s->function == t_builtins::find_builtin_address("lower") || s->function == t_builtins::find_builtin_address("lower_eq") || - s->function == t_builtins::find_builtin_address("equal") || s->function == t_builtins::find_builtin_address("not_equal"))) + while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("greater") || s->function == t_builtins::find_builtin_address("greater_eq") || + s->function == t_builtins::find_builtin_address("lower") || s->function == t_builtins::find_builtin_address("lower_eq") || + s->function == t_builtins::find_builtin_address("equal") || s->function == t_builtins::find_builtin_address("not_equal"))) { te_fun2 t = (te_fun2)s->function; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, sum_expr(s)); + ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, sum_expr(s)); ret->function = t; } @@ -1725,11 +1600,11 @@ namespace te /* = {("&&" | "||") } */ expr_native* ret = test_expr(s); - while (s->type == TOK_INFIX && (s->function == t_builtins::find_builtin_address("logical_and") || s->function == t_builtins::find_builtin_address("logical_or"))) + while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("logical_and") || s->function == t_builtins::find_builtin_address("logical_or"))) { te_fun2 t = (te_fun2)s->function; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, test_expr(s)); + ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, test_expr(s)); ret->function = t; } @@ -1741,10 +1616,10 @@ namespace te /* = {"," } */ expr_native* ret = expr(s); - while (s->type == TOK_SEP) + while (s->type == (int)TOK::SEP) { next_token(s); - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, expr(s)); + ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, expr(s)); ret->function = t_builtins::find_builtin_address("comma"); } @@ -1770,9 +1645,9 @@ namespace te static void optimize(expr_native* n) { /* Evaluates as much as possible. */ - if (n->type == TE_CONSTANT) + if (n->type == CONSTANT) return; - if (n->type == TE_VARIABLE) + if (n->type == VARIABLE) return; /* Only optimize out functions flagged as pure. */ @@ -1784,7 +1659,7 @@ namespace te for (i = 0; i < arity; ++i) { optimize((expr_native*)n->parameters[i]); - if (((expr_native*)(n->parameters[i]))->type != TE_CONSTANT) + if (((expr_native*)(n->parameters[i]))->type != CONSTANT) { known = 0; } @@ -1793,7 +1668,7 @@ namespace te { const t_vector value = eval_native(n); free_parameters(n); - n->type = TE_CONSTANT; + n->type = CONSTANT; n->value = value; } } @@ -1809,7 +1684,7 @@ namespace te next_token(&s); expr_native* root = list(&s); - if (s.type != TOK_END) + if (s.type != (int)TOK::END) { free_native(root); if (error) @@ -1853,15 +1728,15 @@ namespace te const auto t = type_mask(n->type); - if (t == TE_CONSTANT) + if (t == CONSTANT) { printf("%f\n", n->value); } - else if (t == TE_VARIABLE) + else if (t == VARIABLE) { printf("bound %p\n", n->bound); } - else if ((t >= TE_FUNCTION0 && t < TE_FUNCTION_MAX) || (t >= TE_CLOSURE0 && t < TE_CLOSURE_MAX)) + else if ((t >= FUNCTION0 && t < FUNCTION_MAX) || (t >= CLOSURE0 && t < CLOSURE_MAX)) { arity = arity(n->type); printf("f%d", arity); @@ -1983,11 +1858,36 @@ namespace te std::vector index_to_name_c_str; }; - struct compiled_expr : ::te::compiled_expr + struct compiled_expr : ::tp::compiled_expr { expr_portable_expression_build_bindings m_bindings; std::unique_ptr m_build_buffer; size_t m_build_buffer_size; + + virtual size_t get_binding_array_size() const + { + return m_bindings.index_to_address.size(); + } + + virtual const void* const* get_binding_addresses() const + { + return (m_bindings.index_to_address.size() > 0) ? &(*m_bindings.index_to_address.cbegin()) : nullptr; + } + + virtual const char* const* get_binding_names() const + { + return &(*m_bindings.index_to_name_c_str.cbegin()); + } + + virtual size_t get_data_size() const + { + return m_build_buffer_size; + } + + virtual const unsigned char* get_data() const + { + return m_build_buffer.get(); + } }; static size_t @@ -2012,7 +1912,7 @@ namespace te index_map.insert(std::make_pair(var->address, index_counter++)); } - if (var->type >= TE_CLOSURE0 && var->type < TE_CLOSURE_MAX) + if (var->type >= CLOSURE0 && var->type < CLOSURE_MAX) { auto itor = name_map.find(var->context); if (itor == name_map.end()) @@ -2143,7 +2043,7 @@ namespace te [&]() { return t_traits::nan(); }); } - struct compiled_program : ::te::compiled_program + struct portable_compiled_program : compiled_program { std::vector program_statements; std::vector binding_table; @@ -2151,6 +2051,41 @@ namespace te std::vector address_table; std::unique_ptr program_expression_buffer; size_t program_expression_buffer_size = 0; + + virtual size_t get_binding_array_size() const + { + return address_table.size(); + } + + virtual const void* const* get_binding_addresses() const + { + return &address_table[0]; + } + + virtual const char* const* get_binding_names() const + { + return &binding_table_cstr[0]; + } + + virtual size_t get_data_size() const + { + return program_expression_buffer_size; + } + + virtual const unsigned char* get_data() const + { + return program_expression_buffer.get(); + } + + virtual size_t get_statement_array_size() const + { + return (int)program_statements.size(); + } + + virtual const statement* get_statements() const + { + return &program_statements[0]; + } }; }; @@ -2198,7 +2133,7 @@ namespace te assert(itor != indexer.index_map.end()); out->function = itor->second; - if (v->type >= TE_CLOSURE0 && v->type < TE_CLOSURE_MAX) + if (v->type >= CLOSURE0 && v->type < CLOSURE_MAX) { auto itor2 = indexer.index_map.find(v->context); assert(itor2 != indexer.index_map.end()); @@ -2219,60 +2154,6 @@ namespace te return compile_using_indexer(indexer, expression, variables, var_count, error); } - template - size_t get_binding_array_size(const compiled_expr* n) - { - auto n_impl = (const portable::compiled_expr*)n; - if (n_impl) - { - return n_impl->m_bindings.index_to_address.size(); - } - return 0; - } - - template - const void* const* get_binding_addresses(const compiled_expr* n) - { - auto n_impl = (const portable::compiled_expr*)n; - if (n_impl && (n_impl->m_bindings.index_to_address.size() > 0)) - { - return &(*n_impl->m_bindings.index_to_address.cbegin()); - } - return nullptr; - } - - template - const char* const* get_binding_names(const compiled_expr* n) - { - auto n_impl = (const portable::compiled_expr*)n; - if (n_impl) - { - return &(*n_impl->m_bindings.index_to_name_c_str.cbegin()); - } - return nullptr; - } - - template - size_t get_data_size(const compiled_expr* n) - { - auto n_impl = (const portable::compiled_expr*)n; - if (n_impl) - { - return n_impl->m_build_buffer_size; - } - return 0; - } - - template - const unsigned char* get_data(const compiled_expr* n) - { - auto n_impl = (const portable::compiled_expr*)n; - if (n_impl) - { - return n_impl->m_build_buffer.get(); - } - return nullptr; - } } // namespace expr_details namespace program_details @@ -2535,13 +2416,13 @@ namespace te using any_statement = std::variant; template - compiled_program* compile(const char* text, const variable* variables, int var_count, int* error) + auto compile(const char* text, const variable* variables, int var_count, int* error) -> typename portable::portable_compiled_program* { auto program_src = parser::trim_all_space(std::string_view{text, strlen(text)}); auto program_remaining = program_src; std::vector program_statements; - using program_impl = typename portable::compiled_program; + using program_impl = typename portable::portable_compiled_program; auto program = new program_impl(); label_manager lm; variable_manager vm; @@ -2743,130 +2624,188 @@ namespace te return program; } + } // namespace program_details +} // namespace tp +#endif // #if (TP_COMPILER_ENABLED) - template - size_t get_binding_array_size(const compiled_program* p) +namespace tp +{ + struct env_traits_f32 + { + using t_atom = float; + using t_vector = float; + using t_vector_int = int; + using t_atom_builtins = native_builtins; + using t_vector_builtins = native_builtins; + + static inline t_vector load_atom(t_atom a) noexcept { - auto p_impl = (const portable::compiled_program*)p; - return p_impl->address_table.size(); + return a; } - template - const void* const* get_binding_addresses(const compiled_program* p) + static inline t_vector as_truth(t_vector a) noexcept { - auto p_impl = (const portable::compiled_program*)p; - return &p_impl->address_table[0]; + return (a != 0.0f) ? 1.0f : 0.0f; } - template - const char* const* get_binding_names(const compiled_program* p) + static inline t_vector explicit_load_atom(double a) noexcept { - auto p_impl = (const portable::compiled_program*)p; - return &p_impl->binding_table_cstr[0]; + return (t_vector)a; } - template - size_t get_data_size(const compiled_program* p) + static inline t_vector explicit_load_atom(int a) noexcept { - auto p_impl = (const portable::compiled_program*)p; - return p_impl->program_expression_buffer_size; + return (t_vector)a; } - template - const unsigned char* get_data(const compiled_program* p) + static inline double explicit_store_double(t_vector a) { - auto p_impl = (const portable::compiled_program*)p; - return p_impl->program_expression_buffer.get(); + return (double)a; } - template - size_t get_statement_array_size(const compiled_program* p) + static inline int explicit_store_int(t_vector a) { - auto p_impl = (const portable::compiled_program*)p; - return (int)p_impl->program_statements.size(); + return (int)a; } + }; - template - const statement* get_statements(const compiled_program* p) + struct env_traits_d64 + { + using t_atom = double; + using t_vector = double; + using t_vector_int = int; + using t_atom_builtins = native_builtins; + using t_vector_builtins = native_builtins; + + static inline t_vector load_atom(t_atom a) noexcept { - auto p_impl = (const portable::compiled_program*)p; - return &p_impl->program_statements[0]; + return a; } - } // namespace program_details - compiled_expr* compile(const char* expression, const variable* variables, int var_count, int* error) - { - return expr_details::compile(expression, variables, var_count, error); - } + static inline t_vector as_truth(t_vector a) noexcept + { + return (a != 0.0f) ? 1.0f : 0.0f; + } - size_t compiled_expr::get_binding_array_size() const - { - return expr_details::get_binding_array_size(this); - } + static inline t_vector explicit_load_atom(double a) noexcept + { + return (t_vector)a; + } - const void* const* compiled_expr::get_binding_addresses() const - { - return expr_details::get_binding_addresses(this); - } + static inline t_vector explicit_load_atom(int a) noexcept + { + return (t_vector)a; + } - const char* const* compiled_expr::get_binding_names() const - { - return expr_details::get_binding_names(this); - } + static inline double explicit_store_double(t_vector a) + { + return (double)a; + } - size_t compiled_expr::get_data_size() const - { - return expr_details::get_data_size(this); - } + static inline int explicit_store_int(t_vector a) + { + return (int)a; + } + }; - const unsigned char* compiled_expr::get_data() const + template + struct impl { - return expr_details::get_data(this); - } + using env_traits = T_TRAITS; + using variable = ::tp::variable; + using t_atom = typename env_traits::t_atom; + using t_vector = typename env_traits::t_vector; - //// + static inline t_vector eval(const void* expr_buffer, const void* const expr_context[]) noexcept + { + return eval_details::eval_portable_impl((const expr_portable*)expr_buffer, (const unsigned char*)expr_buffer, expr_context); + } - compiled_program* compile_program(const char* program, const variable* variables, int var_count, int* error) - { - return program_details::compile(program, variables, var_count, error); - } + static inline t_vector eval_program(const statement* statement_array, int statement_array_size, const void* expr_buffer, const void* const expr_context[]) + { + for (int statement_index = 0; statement_index < statement_array_size; ++statement_index) + { + auto& statement = statement_array[statement_index]; + switch (statement.type) + { + case statement_type::jump: + if (statement.arg_b == -1 || + (0.0f != eval(((const char*)expr_buffer) + statement.arg_b, expr_context))) // TODO: traits function like nan for zero, or compare function? + { + statement_index = statement.arg_a; + } + break; - size_t compiled_program::get_binding_array_size() const - { - return program_details::get_binding_array_size(this); - } + case statement_type::return_value: + return eval(((const char*)expr_buffer) + statement.arg_a, expr_context); - const void* const* compiled_program::get_binding_addresses() const - { - return program_details::get_binding_addresses(this); - } + case statement_type::assign: + { + auto dest = (t_vector*)expr_context[statement.arg_a]; + *dest = eval(((const char*)expr_buffer) + statement.arg_b, expr_context); + } + break; - const char* const* compiled_program::get_binding_names() const - { - return program_details::get_binding_names(this); - } + case statement_type::call: + eval(((const char*)expr_buffer) + statement.arg_a, expr_context); + break; - size_t compiled_program::get_data_size() const - { - return program_details::get_data_size(this); - } + default: + // fatal error + return env_traits::t_vector_builtins::nan(); + } + } + return env_traits::t_vector_builtins::nan(); + } - const unsigned char* compiled_program::get_data() const - { - return program_details::get_data(this); - } +#if (TP_COMPILER_ENABLED) + static compiled_expr* compile(const char* expression, const variable* variables, int var_count, int* error) + { + return expr_details::compile(expression, variables, var_count, error); + } - size_t compiled_program::get_statement_array_size() const - { - return program_details::get_statement_array_size(this); - } + static compiled_program* compile_program(const char* program, const variable* variables, int var_count, int* error) + { + return (compiled_program*)program_details::compile(program, variables, var_count, error); + } - const statement* compiled_program::get_statements() const - { - return program_details::get_statements(this); - } -} // namespace te -#endif // #if (TE_COMPILER_ENABLED) -#endif // #if TE_IMPLEMENT + static inline t_vector eval(const compiled_expr* n) + { + return eval(n->get_data(), n->get_binding_addresses()); + } + + static inline t_vector interp(const char* expression, int* error) + { + compiled_expr* n = compile(expression, 0, 0, error); + t_vector ret; + if (n) + { + ret = eval(n); + delete n; + } + else + { + ret = env_traits::t_vector_builtins::nan(); + } + return ret; + } + + static inline t_vector eval_program(compiled_program* prog) + { + auto array_size = prog->get_binding_array_size(); + auto binding_addrs = prog->get_binding_addresses(); + auto binding_names = prog->get_binding_names(); + auto data_size = prog->get_data_size(); + auto data = prog->get_data(); + auto num_statements = prog->get_statement_array_size(); + auto statements = prog->get_statements(); + + return eval_program(statements, (int)num_statements, data, binding_addrs); + } +#endif // #if (TP_COMPILER_ENABLED) + }; +} // namespace tp + +using te = tp::impl; #endif /*__TINYPROG_H__*/ diff --git a/test/benchmark.cpp b/test/benchmark.cpp index 6946c23..79dfc3e 100644 --- a/test/benchmark.cpp +++ b/test/benchmark.cpp @@ -31,7 +31,7 @@ #include #include -#define TE_IMPLEMENT 1 +#define TP_COMPILER_ENABLED 1 #include "tinyprog.h" #define loops 10000 @@ -40,14 +40,12 @@ typedef te::env_traits::t_atom (*function1)(te::env_traits::t_atom); void bench(const char* expr, function1 func) { - using namespace te; - int i, j; - volatile env_traits::t_atom d; - env_traits::t_atom tmp; + volatile te::env_traits::t_atom d; + te::env_traits::t_atom tmp; clock_t start; - variable lk = {"a", &tmp}; + te::variable lk = {"a", &tmp}; printf("Expression: %s\n", expr); @@ -57,7 +55,7 @@ void bench(const char* expr, function1 func) for (j = 0; j < loops; ++j) for (i = 0; i < loops; ++i) { - tmp = (env_traits::t_atom)i; + tmp = (te::env_traits::t_atom)i; d += func(tmp); } const int nelapsed = (clock() - start) * 1000 / CLOCKS_PER_SEC; @@ -70,14 +68,14 @@ void bench(const char* expr, function1 func) printf("\tinf\n"); printf("interp "); - auto n = compile(expr, &lk, 1, 0); + auto n = te::compile(expr, &lk, 1, 0); start = clock(); d = 0; for (j = 0; j < loops; ++j) for (i = 0; i < loops; ++i) { - tmp = (env_traits::t_atom)i; - d += eval(n); + tmp = (te::env_traits::t_atom)i; + d += te::eval(n); } const int eelapsed = (clock() - start) * 1000 / CLOCKS_PER_SEC; delete n; @@ -89,7 +87,7 @@ void bench(const char* expr, function1 func) else printf("\tinf\n"); - printf("%.2f%% longer\n", (((env_traits::t_atom)eelapsed / nelapsed) - 1.0) * 100.0); + printf("%.2f%% longer\n", (((te::env_traits::t_atom)eelapsed / nelapsed) - 1.0) * 100.0); printf("\n"); } diff --git a/test/example.cpp b/test/example.cpp index e699d62..93b0225 100644 --- a/test/example.cpp +++ b/test/example.cpp @@ -1,13 +1,11 @@ -#define TE_IMPLEMENT 1 +#define TP_COMPILER_ENABLED 1 #include "tinyprog.h" #include int main(int argc, char *argv[]) { - using namespace te; - const char *c = "sqrt(5^2+7^2+11^2+(8-2)^2)"; - env_traits::t_atom r = interp(c, 0); + te::env_traits::t_atom r = te::interp(c, 0); printf("The expression:\n\t%s\nevaluates to:\n\t%f\n", c, r); return 0; } diff --git a/test/example2.cpp b/test/example2.cpp index 219068f..3c5f4a5 100644 --- a/test/example2.cpp +++ b/test/example2.cpp @@ -1,11 +1,9 @@ -#define TE_IMPLEMENT 1 +#define TP_COMPILER_ENABLED 1 #include "tinyprog.h" #include int main(int argc, char* argv[]) { - using namespace te; - if (argc < 2) { printf("Usage: example2 \"expression\"\n"); @@ -17,12 +15,12 @@ int main(int argc, char* argv[]) /* This shows an example where the variables * x and y are bound at eval-time. */ - env_traits::t_atom x, y; - variable vars[] = {{"x", &x}, {"y", &y}}; + te::env_traits::t_atom x, y; + te::variable vars[] = {{"x", &x}, {"y", &y}}; /* This will compile the expression and check for errors. */ int err; - auto n = compile(expression, vars, 2, &err); + auto n = te::compile(expression, vars, 2, &err); if (n) { @@ -31,7 +29,7 @@ int main(int argc, char* argv[]) * already been done. */ x = 3; y = 4; - const env_traits::t_atom r = eval(n); + const te::env_traits::t_atom r = te::eval(n); printf("Result:\n\t%f\n", r); delete n; diff --git a/test/example3.cpp b/test/example3.cpp index d9f8546..86b9080 100644 --- a/test/example3.cpp +++ b/test/example3.cpp @@ -1,4 +1,4 @@ -#define TE_IMPLEMENT 1 +#define TP_COMPILER_ENABLED 1 #include "tinyprog.h" #include @@ -11,19 +11,17 @@ te::env_traits::t_atom my_sum(te::env_traits::t_atom a, te::env_traits::t_atom b int main(int argc, char* argv[]) { - using namespace te; - - variable vars[] = {{"mysum", my_sum, TE_FUNCTION2}}; + te::variable vars[] = {{"mysum", my_sum, tp::FUNCTION2}}; const char* expression = "mysum(5, 6)"; printf("Evaluating:\n\t%s\n", expression); int err; - auto n = compile(expression, vars, 1, &err); + auto n = te::compile(expression, vars, 1, &err); if (n) { - const env_traits::t_atom r = eval(n); + const te::env_traits::t_atom r = te::eval(n); printf("Result:\n\t%f\n", r); delete n; } diff --git a/test/example_program.cpp b/test/example_program.cpp index fb41c32..f8f685e 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -1,11 +1,9 @@ -#define TE_IMPLEMENT 1 +#define TP_COMPILER_ENABLED 1 #include "tinyprog.h" #include int main(int argc, char* argv[]) { - using namespace te; - const char* p = "x: sqrt(5^2+7^2+11^2+(8-2)^2);" "jump: is_negative ? x < 0;" @@ -13,12 +11,12 @@ int main(int argc, char* argv[]) "label: is_negative;" "return: -1 * x;"; - env_traits::t_atom x = 0.0f, y = 0.0f; - variable vars[] = {{"x", &x}, {"y", &y}}; + te::env_traits::t_atom x = 0.0f, y = 0.0f; + te::variable vars[] = {{"x", &x}, {"y", &y}}; int err = 0; - auto prog = compile_program(p, vars, 2, &err); - auto result = eval_program(prog); + auto prog = te::compile_program(p, vars, 2, &err); + auto result = te::eval_program(prog); delete prog; return 0; } diff --git a/test/minctest.h b/test/minctest.h index 77cf12d..eec6537 100644 --- a/test/minctest.h +++ b/test/minctest.h @@ -109,21 +109,21 @@ static int lfails = 0; /* Assert two integers are equal. */ #define lequal(a, b) do {\ ++ltests;\ - if ((a) != env_traits::explicit_store_int(b)) \ + if ((a) != te::env_traits::explicit_store_int(b)) \ {\ ++lfails;\ - printf("%s:%d (%d != %d)\n", __FILE__, __LINE__, (a), env_traits::explicit_store_int(b));\ + printf("%s:%d (%d != %d)\n", __FILE__, __LINE__, (a), te::env_traits::explicit_store_int(b));\ }} while (0) /* Assert two floats are equal (Within LTEST_FLOAT_TOLERANCE). */ #define lfequal(a, b) do {\ ++ltests;\ - const env_traits::t_atom __LF_COMPARE = \ - (env_traits::t_atom)fabs((env_traits::t_atom)(a) - (env_traits::t_atom)(b));\ + const te::env_traits::t_atom __LF_COMPARE = \ + (te::env_traits::t_atom)fabs((te::env_traits::t_atom)(a) - (te::env_traits::t_atom)(b));\ if (__LF_COMPARE > LTEST_FLOAT_TOLERANCE || (__LF_COMPARE != __LF_COMPARE)) {\ ++lfails;\ - printf("%s:%d (%f != %f)\n", __FILE__, __LINE__, (env_traits::t_atom)(a), (env_traits::t_atom)(b));\ + printf("%s:%d (%f != %f)\n", __FILE__, __LINE__, (te::env_traits::t_atom)(a), (te::env_traits::t_atom)(b));\ }} while (0) diff --git a/test/test.cpp b/test/test.cpp index 7308a39..3268d8a 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -27,7 +27,7 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#define TE_IMPLEMENT 1 +#define TP_COMPILER_ENABLED 1 #include "tinyprog.h" #include @@ -47,111 +47,109 @@ typedef struct void test_results() { - using namespace te; - test_case cases[] = { - {"1", env_traits::explicit_load_atom(1)}, - {"1 ", env_traits::explicit_load_atom(1)}, - {"(1)", env_traits::explicit_load_atom(1)}, - - {"pi", env_traits::explicit_load_atom(3.14159)}, - {"atan(1)*4 - pi", env_traits::explicit_load_atom(0)}, - {"e", env_traits::explicit_load_atom(2.71828)}, - - {"2+1", env_traits::explicit_load_atom(2 + 1)}, - {"(((2+(1))))", env_traits::explicit_load_atom(2 + 1)}, - {"3+2", env_traits::explicit_load_atom(3 + 2)}, - - {"3+2+4", env_traits::explicit_load_atom(3 + 2 + 4)}, - {"(3+2)+4", env_traits::explicit_load_atom(3 + 2 + 4)}, - {"3+(2+4)", env_traits::explicit_load_atom(3 + 2 + 4)}, - {"(3+2+4)", env_traits::explicit_load_atom(3 + 2 + 4)}, - - {"3*2*4", env_traits::explicit_load_atom(3 * 2 * 4)}, - {"(3*2)*4", env_traits::explicit_load_atom(3 * 2 * 4)}, - {"3*(2*4)", env_traits::explicit_load_atom(3 * 2 * 4)}, - {"(3*2*4)", env_traits::explicit_load_atom(3 * 2 * 4)}, - - {"3-2-4", env_traits::explicit_load_atom(3 - 2 - 4)}, - {"(3-2)-4", env_traits::explicit_load_atom((3 - 2) - 4)}, - {"3-(2-4)", env_traits::explicit_load_atom(3 - (2 - 4))}, - {"(3-2-4)", env_traits::explicit_load_atom(3 - 2 - 4)}, - - {"3/2/4", env_traits::explicit_load_atom(3.0 / 2.0 / 4.0)}, - {"(3/2)/4", env_traits::explicit_load_atom((3.0 / 2.0) / 4.0)}, - {"3/(2/4)", env_traits::explicit_load_atom(3.0 / (2.0 / 4.0))}, - {"(3/2/4)", env_traits::explicit_load_atom(3.0 / 2.0 / 4.0)}, - - {"(3*2/4)", env_traits::explicit_load_atom(3.0 * 2.0 / 4.0)}, - {"(3/2*4)", env_traits::explicit_load_atom(3.0 / 2.0 * 4.0)}, - {"3*(2/4)", env_traits::explicit_load_atom(3.0 * (2.0 / 4.0))}, - - {"asin sin .5", env_traits::explicit_load_atom(0.5)}, - {"sin asin .5", env_traits::explicit_load_atom(0.5)}, - {"ln exp .5", env_traits::explicit_load_atom(0.5)}, - {"exp ln .5", env_traits::explicit_load_atom(0.5)}, - - {"asin sin-.5", env_traits::explicit_load_atom(-0.5)}, - {"asin sin-0.5", env_traits::explicit_load_atom(-0.5)}, - {"asin sin -0.5", env_traits::explicit_load_atom(-0.5)}, - {"asin (sin -0.5)", env_traits::explicit_load_atom(-0.5)}, - {"asin (sin (-0.5))", env_traits::explicit_load_atom(-0.5)}, - {"asin sin (-0.5)", env_traits::explicit_load_atom(-0.5)}, - {"(asin sin (-0.5))", env_traits::explicit_load_atom(-0.5)}, - - {"log10 1000", env_traits::explicit_load_atom(3)}, - {"log10 1e3", env_traits::explicit_load_atom(3)}, - {"log10 1000", env_traits::explicit_load_atom(3)}, - {"log10 1e3", env_traits::explicit_load_atom(3)}, - {"log10(1000)", env_traits::explicit_load_atom(3)}, - {"log10(1e3)", env_traits::explicit_load_atom(3)}, - {"log10 1.0e3", env_traits::explicit_load_atom(3)}, - {"10^5*5e-5", env_traits::explicit_load_atom(5)}, + {"1", te::env_traits::explicit_load_atom(1)}, + {"1 ", te::env_traits::explicit_load_atom(1)}, + {"(1)", te::env_traits::explicit_load_atom(1)}, + + {"pi", te::env_traits::explicit_load_atom(3.14159)}, + {"atan(1)*4 - pi", te::env_traits::explicit_load_atom(0)}, + {"e", te::env_traits::explicit_load_atom(2.71828)}, + + {"2+1", te::env_traits::explicit_load_atom(2 + 1)}, + {"(((2+(1))))", te::env_traits::explicit_load_atom(2 + 1)}, + {"3+2", te::env_traits::explicit_load_atom(3 + 2)}, + + {"3+2+4", te::env_traits::explicit_load_atom(3 + 2 + 4)}, + {"(3+2)+4", te::env_traits::explicit_load_atom(3 + 2 + 4)}, + {"3+(2+4)", te::env_traits::explicit_load_atom(3 + 2 + 4)}, + {"(3+2+4)", te::env_traits::explicit_load_atom(3 + 2 + 4)}, + + {"3*2*4", te::env_traits::explicit_load_atom(3 * 2 * 4)}, + {"(3*2)*4", te::env_traits::explicit_load_atom(3 * 2 * 4)}, + {"3*(2*4)", te::env_traits::explicit_load_atom(3 * 2 * 4)}, + {"(3*2*4)", te::env_traits::explicit_load_atom(3 * 2 * 4)}, + + {"3-2-4", te::env_traits::explicit_load_atom(3 - 2 - 4)}, + {"(3-2)-4", te::env_traits::explicit_load_atom((3 - 2) - 4)}, + {"3-(2-4)", te::env_traits::explicit_load_atom(3 - (2 - 4))}, + {"(3-2-4)", te::env_traits::explicit_load_atom(3 - 2 - 4)}, + + {"3/2/4", te::env_traits::explicit_load_atom(3.0 / 2.0 / 4.0)}, + {"(3/2)/4", te::env_traits::explicit_load_atom((3.0 / 2.0) / 4.0)}, + {"3/(2/4)", te::env_traits::explicit_load_atom(3.0 / (2.0 / 4.0))}, + {"(3/2/4)", te::env_traits::explicit_load_atom(3.0 / 2.0 / 4.0)}, + + {"(3*2/4)", te::env_traits::explicit_load_atom(3.0 * 2.0 / 4.0)}, + {"(3/2*4)", te::env_traits::explicit_load_atom(3.0 / 2.0 * 4.0)}, + {"3*(2/4)", te::env_traits::explicit_load_atom(3.0 * (2.0 / 4.0))}, + + {"asin sin .5", te::env_traits::explicit_load_atom(0.5)}, + {"sin asin .5", te::env_traits::explicit_load_atom(0.5)}, + {"ln exp .5", te::env_traits::explicit_load_atom(0.5)}, + {"exp ln .5", te::env_traits::explicit_load_atom(0.5)}, + + {"asin sin-.5", te::env_traits::explicit_load_atom(-0.5)}, + {"asin sin-0.5", te::env_traits::explicit_load_atom(-0.5)}, + {"asin sin -0.5", te::env_traits::explicit_load_atom(-0.5)}, + {"asin (sin -0.5)", te::env_traits::explicit_load_atom(-0.5)}, + {"asin (sin (-0.5))", te::env_traits::explicit_load_atom(-0.5)}, + {"asin sin (-0.5)", te::env_traits::explicit_load_atom(-0.5)}, + {"(asin sin (-0.5))", te::env_traits::explicit_load_atom(-0.5)}, + + {"log10 1000", te::env_traits::explicit_load_atom(3)}, + {"log10 1e3", te::env_traits::explicit_load_atom(3)}, + {"log10 1000", te::env_traits::explicit_load_atom(3)}, + {"log10 1e3", te::env_traits::explicit_load_atom(3)}, + {"log10(1000)", te::env_traits::explicit_load_atom(3)}, + {"log10(1e3)", te::env_traits::explicit_load_atom(3)}, + {"log10 1.0e3", te::env_traits::explicit_load_atom(3)}, + {"10^5*5e-5", te::env_traits::explicit_load_atom(5)}, #ifdef TE_NAT_LOG - {"log 1000", env_traits::explicit_load_atom(6.9078)}, - {"log e", env_traits::explicit_load_atom(1)}, - {"log (e^10)", env_traits::explicit_load_atom(10)}, + {"log 1000", te::env_traits::explicit_load_atom(6.9078)}, + {"log e", te::env_traits::explicit_load_atom(1)}, + {"log (e^10)", te::env_traits::explicit_load_atom(10)}, #else - {"log 1000", env_traits::explicit_load_atom(3)}, + {"log 1000", te::env_traits::explicit_load_atom(3)}, #endif - {"ln (e^10)", env_traits::explicit_load_atom(10)}, - {"100^.5+1", env_traits::explicit_load_atom(11)}, - {"100 ^.5+1", env_traits::explicit_load_atom(11)}, - {"100^+.5+1", env_traits::explicit_load_atom(11)}, - {"100^--.5+1", env_traits::explicit_load_atom(11)}, - {"100^---+-++---++-+-+-.5+1", env_traits::explicit_load_atom(11)}, - - {"100^-.5+1", env_traits::explicit_load_atom(1.1)}, - {"100^---.5+1", env_traits::explicit_load_atom(1.1)}, - {"100^+---.5+1", env_traits::explicit_load_atom(1.1)}, - {"1e2^+---.5e0+1e0", env_traits::explicit_load_atom(1.1)}, - {"--(1e2^(+(-(-(-.5e0))))+1e0)", env_traits::explicit_load_atom(1.1)}, - - {"sqrt 100 + 7", env_traits::explicit_load_atom(17)}, - {"sqrt 100 * 7", env_traits::explicit_load_atom(70)}, - {"sqrt (100 * 100)", env_traits::explicit_load_atom(100)}, - - {"1,2", env_traits::explicit_load_atom(2)}, - {"1,2+1", env_traits::explicit_load_atom(3)}, - {"1+1,2+2,2+1", env_traits::explicit_load_atom(3)}, - {"1,2,3", env_traits::explicit_load_atom(3)}, - {"(1,2),3", env_traits::explicit_load_atom(3)}, - {"1,(2,3)", env_traits::explicit_load_atom(3)}, - {"-(1,(2,3))", env_traits::explicit_load_atom(-3)}, - - {"2^2", env_traits::explicit_load_atom(4)}, - {"pow(2,2)", env_traits::explicit_load_atom(4)}, - - {"atan2(1,1)", env_traits::explicit_load_atom(0.7854)}, - {"atan2(1,2)", env_traits::explicit_load_atom(0.4636)}, - {"atan2(2,1)", env_traits::explicit_load_atom(1.1071)}, - {"atan2(3,4)", env_traits::explicit_load_atom(0.6435)}, - {"atan2(3+3,4*2)", env_traits::explicit_load_atom(0.6435)}, - {"atan2(3+3,(4*2))", env_traits::explicit_load_atom(0.6435)}, - {"atan2((3+3),4*2)", env_traits::explicit_load_atom(0.6435)}, - {"atan2((3+3),(4*2))", env_traits::explicit_load_atom(0.6435)}, + {"ln (e^10)", te::env_traits::explicit_load_atom(10)}, + {"100^.5+1", te::env_traits::explicit_load_atom(11)}, + {"100 ^.5+1", te::env_traits::explicit_load_atom(11)}, + {"100^+.5+1", te::env_traits::explicit_load_atom(11)}, + {"100^--.5+1", te::env_traits::explicit_load_atom(11)}, + {"100^---+-++---++-+-+-.5+1", te::env_traits::explicit_load_atom(11)}, + + {"100^-.5+1", te::env_traits::explicit_load_atom(1.1)}, + {"100^---.5+1", te::env_traits::explicit_load_atom(1.1)}, + {"100^+---.5+1", te::env_traits::explicit_load_atom(1.1)}, + {"1e2^+---.5e0+1e0", te::env_traits::explicit_load_atom(1.1)}, + {"--(1e2^(+(-(-(-.5e0))))+1e0)", te::env_traits::explicit_load_atom(1.1)}, + + {"sqrt 100 + 7", te::env_traits::explicit_load_atom(17)}, + {"sqrt 100 * 7", te::env_traits::explicit_load_atom(70)}, + {"sqrt (100 * 100)", te::env_traits::explicit_load_atom(100)}, + + {"1,2", te::env_traits::explicit_load_atom(2)}, + {"1,2+1", te::env_traits::explicit_load_atom(3)}, + {"1+1,2+2,2+1", te::env_traits::explicit_load_atom(3)}, + {"1,2,3", te::env_traits::explicit_load_atom(3)}, + {"(1,2),3", te::env_traits::explicit_load_atom(3)}, + {"1,(2,3)", te::env_traits::explicit_load_atom(3)}, + {"-(1,(2,3))", te::env_traits::explicit_load_atom(-3)}, + + {"2^2", te::env_traits::explicit_load_atom(4)}, + {"pow(2,2)", te::env_traits::explicit_load_atom(4)}, + + {"atan2(1,1)", te::env_traits::explicit_load_atom(0.7854)}, + {"atan2(1,2)", te::env_traits::explicit_load_atom(0.4636)}, + {"atan2(2,1)", te::env_traits::explicit_load_atom(1.1071)}, + {"atan2(3,4)", te::env_traits::explicit_load_atom(0.6435)}, + {"atan2(3+3,4*2)", te::env_traits::explicit_load_atom(0.6435)}, + {"atan2(3+3,(4*2))", te::env_traits::explicit_load_atom(0.6435)}, + {"atan2((3+3),4*2)", te::env_traits::explicit_load_atom(0.6435)}, + {"atan2((3+3),(4*2))", te::env_traits::explicit_load_atom(0.6435)}, }; @@ -159,10 +157,10 @@ void test_results() for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { const char* expr = cases[i].expr; - const env_traits::t_vector answer = cases[i].answer; + const te::env_traits::t_vector answer = cases[i].answer; int err; - const env_traits::t_vector ev = interp(expr, &err); + const te::env_traits::t_vector ev = te::interp(expr, &err); lok(!err); lfequal(ev, answer); @@ -175,22 +173,20 @@ void test_results() void test_syntax() { - using namespace te; - test_case errors[] = { - {"", env_traits::explicit_load_atom(1)}, - {"1+", env_traits::explicit_load_atom(2)}, - {"1)", env_traits::explicit_load_atom(2)}, - {"(1", env_traits::explicit_load_atom(2)}, - {"1**1", env_traits::explicit_load_atom(3)}, - {"1*2(+4", env_traits::explicit_load_atom(4)}, - {"1*2(1+4", env_traits::explicit_load_atom(4)}, - {"a+5", env_traits::explicit_load_atom(1)}, - {"A+5", env_traits::explicit_load_atom(1)}, - {"Aa+5", env_traits::explicit_load_atom(1)}, - {"1^^5", env_traits::explicit_load_atom(3)}, - {"1**5", env_traits::explicit_load_atom(3)}, - {"sin(cos5", env_traits::explicit_load_atom(8)}, + {"", te::env_traits::explicit_load_atom(1)}, + {"1+", te::env_traits::explicit_load_atom(2)}, + {"1)", te::env_traits::explicit_load_atom(2)}, + {"(1", te::env_traits::explicit_load_atom(2)}, + {"1**1", te::env_traits::explicit_load_atom(3)}, + {"1*2(+4", te::env_traits::explicit_load_atom(4)}, + {"1*2(1+4", te::env_traits::explicit_load_atom(4)}, + {"a+5", te::env_traits::explicit_load_atom(1)}, + {"A+5", te::env_traits::explicit_load_atom(1)}, + {"Aa+5", te::env_traits::explicit_load_atom(1)}, + {"1^^5", te::env_traits::explicit_load_atom(3)}, + {"1**5", te::env_traits::explicit_load_atom(3)}, + {"sin(cos5", te::env_traits::explicit_load_atom(8)}, }; int i; @@ -200,11 +196,11 @@ void test_syntax() const auto e = errors[i].answer; int err; - const env_traits::t_vector r = interp(expr, &err); + const te::env_traits::t_vector r = te::interp(expr, &err); lequal(err, e); lok(r != r); - auto n = compile(expr, 0, 0, &err); + auto n = te::compile(expr, 0, 0, &err); lequal(err, e); lok(!n); @@ -213,15 +209,13 @@ void test_syntax() printf("FAILED: %s\n", expr); } - const env_traits::t_vector k = interp(expr, 0); + const te::env_traits::t_vector k = te::interp(expr, 0); lok(k != k); } } void test_nans() { - using namespace te; - const char* nans[] = { "0/0", "1%0", @@ -242,14 +236,14 @@ void test_nans() const char* expr = nans[i]; int err; - const env_traits::t_vector r = interp(expr, &err); + const te::env_traits::t_vector r = te::interp(expr, &err); lequal(err, 0); lok(r != r); - auto n = compile(expr, 0, 0, &err); + auto n = te::compile(expr, 0, 0, &err); lok(n); lequal(err, 0); - const env_traits::t_vector c = eval(n); + const te::env_traits::t_vector c = te::eval(n); lok(c != c); delete n; } @@ -257,8 +251,6 @@ void test_nans() void test_infs() { - using namespace te; - const char* infs[] = { "1/0", "log(0)", @@ -278,14 +270,14 @@ void test_infs() const char* expr = infs[i]; int err; - const env_traits::t_vector r = interp(expr, &err); + const te::env_traits::t_vector r = te::interp(expr, &err); lequal(err, 0); lok(r == r + 1); - auto n = compile(expr, 0, 0, &err); + auto n = te::compile(expr, 0, 0, &err); lok(n); lequal(err, 0); - const env_traits::t_vector c = eval(n); + const te::env_traits::t_vector c = te::eval(n); lok(c == c + 1); delete n; } @@ -293,26 +285,24 @@ void test_infs() void test_variables() { - using namespace te; - - env_traits::t_vector x, y, test; - variable lookup[] = {{"x", &x}, {"y", &y}, {"te_st", &test}}; + te::env_traits::t_vector x, y, test; + te::variable lookup[] = {{"x", &x}, {"y", &y}, {"te_st", &test}}; int err; - auto expr1 = compile("cos x + sin y", lookup, 2, &err); + auto expr1 = te::compile("cos x + sin y", lookup, 2, &err); lok(expr1); lok(!err); - auto expr2 = compile("x+x+x-y", lookup, 2, &err); + auto expr2 = te::compile("x+x+x-y", lookup, 2, &err); lok(expr2); lok(!err); - auto expr3 = compile("x*y^3", lookup, 2, &err); + auto expr3 = te::compile("x*y^3", lookup, 2, &err); lok(expr3); lok(!err); - auto expr4 = compile("te_st+5", lookup, 3, &err); + auto expr4 = te::compile("te_st+5", lookup, 3, &err); lok(expr4); lok(!err); @@ -320,19 +310,19 @@ void test_variables() { for (x = 0; x < 5; ++x) { - env_traits::t_vector ev; + te::env_traits::t_vector ev; - ev = eval(expr1); + ev = te::eval(expr1); lfequal(ev, cos(x) + sin(y)); - ev = eval(expr2); + ev = te::eval(expr2); lfequal(ev, x + x + x - y); - ev = eval(expr3); + ev = te::eval(expr3); lfequal(ev, x * y * y * y); test = x; - ev = eval(expr4); + ev = te::eval(expr4); lfequal(ev, x + 5); } } @@ -342,19 +332,19 @@ void test_variables() delete expr3; delete expr4; - auto expr5 = compile("xx*y^3", lookup, 2, &err); + auto expr5 = te::compile("xx*y^3", lookup, 2, &err); lok(!expr5); lok(err); - auto expr6 = compile("tes", lookup, 3, &err); + auto expr6 = te::compile("tes", lookup, 3, &err); lok(!expr6); lok(err); - auto expr7 = compile("sinn x", lookup, 2, &err); + auto expr7 = te::compile("sinn x", lookup, 2, &err); lok(!expr7); lok(err); - auto expr8 = compile("si x", lookup, 2, &err); + auto expr8 = te::compile("si x", lookup, 2, &err); lok(!expr8); lok(err); } @@ -364,46 +354,44 @@ void test_variables() { \ if ((b) != (b)) \ break; \ - auto expr = compile((a), lookup, 2, &err); \ - lfequal(eval(expr), (b)); \ + auto expr = te::compile((a), lookup, 2, &err); \ + lfequal(te::eval(expr), (b)); \ lok(!err); \ delete expr; \ } while (0) void test_functions() { - using namespace te; - - env_traits::t_atom x, y; - variable lookup[] = {{"x", &x}, {"y", &y}}; + te::env_traits::t_atom x, y; + te::variable lookup[] = {{"x", &x}, {"y", &y}}; int err; - for (x = env_traits::t_atom(-5); x < env_traits::t_atom(5); x += env_traits::t_atom(.2)) + for (x = te::env_traits::t_atom(-5); x < te::env_traits::t_atom(5); x += te::env_traits::t_atom(.2)) { - cross_check("abs x", env_traits::explicit_load_atom(fabs(x))); - cross_check("acos x", env_traits::explicit_load_atom(acos(x))); - cross_check("asin x", env_traits::explicit_load_atom(asin(x))); - cross_check("atan x", env_traits::explicit_load_atom(atan(x))); - cross_check("ceil x", env_traits::explicit_load_atom(ceil(x))); - cross_check("cos x", env_traits::explicit_load_atom(cos(x))); - cross_check("cosh x", env_traits::explicit_load_atom(cosh(x))); - cross_check("exp x", env_traits::explicit_load_atom(exp(x))); - cross_check("floor x", env_traits::explicit_load_atom(floor(x))); - cross_check("ln x", env_traits::explicit_load_atom(log(x))); - cross_check("log10 x", env_traits::explicit_load_atom(log10(x))); - cross_check("sin x", env_traits::explicit_load_atom(sin(x))); - cross_check("sinh x", env_traits::explicit_load_atom(sinh(x))); - cross_check("sqrt x", env_traits::explicit_load_atom(sqrt(x))); - cross_check("tan x", env_traits::explicit_load_atom(tan(x))); - cross_check("tanh x", env_traits::explicit_load_atom(tanh(x))); - - for (y = env_traits::t_atom(-2); y < env_traits::t_atom(2); y += env_traits::t_atom(.2)) + cross_check("abs x", te::env_traits::explicit_load_atom(fabs(x))); + cross_check("acos x", te::env_traits::explicit_load_atom(acos(x))); + cross_check("asin x", te::env_traits::explicit_load_atom(asin(x))); + cross_check("atan x", te::env_traits::explicit_load_atom(atan(x))); + cross_check("ceil x", te::env_traits::explicit_load_atom(ceil(x))); + cross_check("cos x", te::env_traits::explicit_load_atom(cos(x))); + cross_check("cosh x", te::env_traits::explicit_load_atom(cosh(x))); + cross_check("exp x", te::env_traits::explicit_load_atom(exp(x))); + cross_check("floor x", te::env_traits::explicit_load_atom(floor(x))); + cross_check("ln x", te::env_traits::explicit_load_atom(log(x))); + cross_check("log10 x", te::env_traits::explicit_load_atom(log10(x))); + cross_check("sin x", te::env_traits::explicit_load_atom(sin(x))); + cross_check("sinh x", te::env_traits::explicit_load_atom(sinh(x))); + cross_check("sqrt x", te::env_traits::explicit_load_atom(sqrt(x))); + cross_check("tan x", te::env_traits::explicit_load_atom(tan(x))); + cross_check("tanh x", te::env_traits::explicit_load_atom(tanh(x))); + + for (y = te::env_traits::t_atom(-2); y < te::env_traits::t_atom(2); y += te::env_traits::t_atom(.2)) { if (fabs(x) < 0.01) break; - cross_check("atan2(x,y)", env_traits::explicit_load_atom(atan2(x, y))); - cross_check("pow(x,y)", env_traits::explicit_load_atom(pow(x, y))); + cross_check("atan2(x,y)", te::env_traits::explicit_load_atom(atan2(x, y))); + cross_check("pow(x,y)", te::env_traits::explicit_load_atom(pow(x, y))); } } } @@ -459,20 +447,18 @@ te::env_traits::t_vector sum7(te::env_traits::t_vector a, void test_dynamic() { - using namespace te; - - env_traits::t_vector x, f; - variable lookup[] = { + te::env_traits::t_vector x, f; + te::variable lookup[] = { {"x", &x}, {"f", &f}, - {"sum0", sum0, TE_FUNCTION0}, - {"sum1", sum1, TE_FUNCTION1}, - {"sum2", sum2, TE_FUNCTION2}, - {"sum3", sum3, TE_FUNCTION3}, - {"sum4", sum4, TE_FUNCTION4}, - {"sum5", sum5, TE_FUNCTION5}, - {"sum6", sum6, TE_FUNCTION6}, - {"sum7", sum7, TE_FUNCTION7}, + {"sum0", sum0, tp::FUNCTION0}, + {"sum1", sum1, tp::FUNCTION1}, + {"sum2", sum2, tp::FUNCTION2}, + {"sum3", sum3, tp::FUNCTION3}, + {"sum4", sum4, tp::FUNCTION4}, + {"sum5", sum5, tp::FUNCTION5}, + {"sum6", sum6, tp::FUNCTION6}, + {"sum7", sum7, tp::FUNCTION7}, }; test_case cases[] = { @@ -507,12 +493,12 @@ void test_dynamic() for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { const char* expr = cases[i].expr; - const env_traits::t_vector answer = cases[i].answer; + const te::env_traits::t_vector answer = cases[i].answer; int err; - auto ex = compile(expr, lookup, sizeof(lookup) / sizeof(variable), &err); + auto ex = te::compile(expr, lookup, sizeof(lookup) / sizeof(te::variable), &err); lok(ex); - lfequal(eval(ex), answer); + lfequal(te::eval(ex), answer); delete ex; } } @@ -544,16 +530,14 @@ te::env_traits::t_vector cell(void* context, te::env_traits::t_vector a) void test_closure() { - using namespace te; - - env_traits::t_vector extra; - env_traits::t_vector c[] = {5, 6, 7, 8, 9}; - - variable lookup[] = { - {"c0", clo0, TE_CLOSURE0, &extra}, - {"c1", clo1, TE_CLOSURE1, &extra}, - {"c2", clo2, TE_CLOSURE2, &extra}, - {"cell", cell, TE_CLOSURE1, c}, + te::env_traits::t_vector extra; + te::env_traits::t_vector c[] = {5, 6, 7, 8, 9}; + + te::variable lookup[] = { + {"c0", clo0, tp::CLOSURE0, &extra}, + {"c1", clo1, tp::CLOSURE1, &extra}, + {"c2", clo2, tp::CLOSURE2, &extra}, + {"cell", cell, tp::CLOSURE1, c}, }; test_case cases[] = { @@ -566,17 +550,17 @@ void test_closure() for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { const char* expr = cases[i].expr; - const env_traits::t_vector answer = cases[i].answer; + const te::env_traits::t_vector answer = cases[i].answer; int err; - auto ex = compile(expr, lookup, sizeof(lookup) / sizeof(variable), &err); + auto ex = te::compile(expr, lookup, sizeof(lookup) / sizeof(te::variable), &err); lok(ex); extra = 0; - lfequal(eval(ex), answer + extra); + lfequal(te::eval(ex), answer + extra); extra = 10; - lfequal(eval(ex), answer + extra); + lfequal(te::eval(ex), answer + extra); delete ex; } @@ -591,41 +575,39 @@ void test_closure() for (i = 0; i < sizeof(cases2) / sizeof(test_case); ++i) { const char* expr = cases2[i].expr; - const env_traits::t_vector answer = cases2[i].answer; + const te::env_traits::t_vector answer = cases2[i].answer; int err; - auto ex = compile(expr, lookup, sizeof(lookup) / sizeof(variable), &err); + auto ex = te::compile(expr, lookup, sizeof(lookup) / sizeof(te::variable), &err); lok(ex); - lfequal(eval(ex), answer); + lfequal(te::eval(ex), answer); delete ex; } } void test_optimize() { - using namespace te; - test_case cases[] = { - {"5+5", env_traits::explicit_load_atom(10)}, - {"pow(2,2)", env_traits::explicit_load_atom(4)}, - {"sqrt 100", env_traits::explicit_load_atom(10)}, - {"pi * 2", env_traits::explicit_load_atom(6.2832)}, + {"5+5", te::env_traits::explicit_load_atom(10)}, + {"pow(2,2)", te::env_traits::explicit_load_atom(4)}, + {"sqrt 100", te::env_traits::explicit_load_atom(10)}, + {"pi * 2", te::env_traits::explicit_load_atom(6.2832)}, }; int i; for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { const char* expr = cases[i].expr; - const env_traits::t_vector answer = cases[i].answer; + const te::env_traits::t_vector answer = cases[i].answer; int err; - auto ex = compile(expr, 0, 0, &err); + auto ex = te::compile(expr, 0, 0, &err); lok(ex); /* The answer should be know without * even running eval. */ // lfequal(ex->value, answer); // TODO - lfequal(eval(ex), answer); + lfequal(te::eval(ex), answer); delete ex; } @@ -633,8 +615,6 @@ void test_optimize() void test_pow() { - using namespace te; - #ifdef TE_POW_FROM_RIGHT test_equ cases[] = {{"2^3^4", "2^(3^4)"}, {"-2^2", "-(2^2)"}, @@ -657,9 +637,9 @@ void test_pow() {"-a^-b", "(-a)^(-b)"}}; #endif - env_traits::t_vector a = 2, b = 3; + te::env_traits::t_vector a = 2, b = 3; - variable lookup[] = {{"a", &a}, {"b", &b}}; + te::variable lookup[] = {{"a", &a}, {"b", &b}}; int i; for (i = 0; i < sizeof(cases) / sizeof(test_equ); ++i) @@ -667,14 +647,14 @@ void test_pow() const char* expr1 = cases[i].expr1; const char* expr2 = cases[i].expr2; - auto ex1 = compile(expr1, lookup, sizeof(lookup) / sizeof(variable), 0); - auto ex2 = compile(expr2, lookup, sizeof(lookup) / sizeof(variable), 0); + auto ex1 = te::compile(expr1, lookup, sizeof(lookup) / sizeof(te::variable), 0); + auto ex2 = te::compile(expr2, lookup, sizeof(lookup) / sizeof(te::variable), 0); lok(ex1); lok(ex2); - env_traits::t_vector r1 = eval(ex1); - env_traits::t_vector r2 = eval(ex2); + te::env_traits::t_vector r1 = te::eval(ex1); + te::env_traits::t_vector r2 = te::eval(ex2); fflush(stdout); lfequal(r1, r2); @@ -686,8 +666,6 @@ void test_pow() void test_combinatorics() { - using namespace te; - test_case cases[] = { {"fac(0)", 1}, {"fac(0.2)", 1}, @@ -717,10 +695,10 @@ void test_combinatorics() for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { const char* expr = cases[i].expr; - const env_traits::t_vector answer = cases[i].answer; + const te::env_traits::t_vector answer = cases[i].answer; int err; - const env_traits::t_vector ev = interp(expr, &err); + const te::env_traits::t_vector ev = te::interp(expr, &err); lok(!err); lfequal(ev, answer); @@ -733,8 +711,6 @@ void test_combinatorics() void test_logic() { - using namespace te; - test_case cases[] = { {"1 && 1", 1}, {"1 && 0", 0}, @@ -814,10 +790,10 @@ void test_logic() for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { const char* expr = cases[i].expr; - const env_traits::t_vector answer = cases[i].answer; + const te::env_traits::t_vector answer = cases[i].answer; int err; - const env_traits::t_vector ev = interp(expr, &err); + const te::env_traits::t_vector ev = te::interp(expr, &err); lok(!err); lfequal(ev, answer); From f88fbf47d7f6f63460d2847047c8e9d22588cfd1 Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Fri, 24 Jul 2020 13:34:18 -0700 Subject: [PATCH 07/43] Extract the standard library functionality, move it to its own namespace, wrap it in a define, make it configurable on the user end. Test configuration is getting more complex, so move it to its own define. --- include/tinyprog.h | 4264 +++++++++++++++++++------------------- test/benchmark.cpp | 2 +- test/example.cpp | 2 +- test/example2.cpp | 2 +- test/example3.cpp | 2 +- test/example_program.cpp | 2 +- test/test.cpp | 2 +- 7 files changed, 2156 insertions(+), 2120 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index f49f433..b68494d 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -33,9 +33,22 @@ #include #include +#ifndef TP_TESTING +#define TP_TESTING 0 +#endif // #ifndef TP_TESTING + +#if TP_TESTING +#define TP_COMPILER_ENABLED 1 +#define TP_STANDARD_LIBRARY 1 +#endif // #if TP_TESTING + #ifndef TP_COMPILER_ENABLED #define TP_COMPILER_ENABLED 0 -#endif // TP_COMPILER_ENABLED +#endif // #ifndef TP_COMPILER_ENABLED + +#ifndef TP_STANDARD_LIBRARY +#define TP_STANDARD_LIBRARY 0 +#endif // #ifndef TP_STANDARD_LIBRARY namespace tp { @@ -76,2638 +89,2571 @@ namespace tp void* context; }; - template - struct native_builtins_impl; + template + struct expr_portable + { + using t_traits = T_TRAITS; + using t_atom = typename T_TRAITS::t_atom; - template<> - struct native_builtins_impl + int type; + union + { + t_atom value; + size_t bound; + size_t function; + }; + size_t parameters[1]; + }; + + namespace eval_details { - static double pi(void) + template + inline T type_mask(const T t) noexcept { - return 3.14159265358979323846; + return ((t)&0x0000001F); } - static double e(void) + template + inline T arity(const T t) noexcept { - return 2.71828182845904523536; + return (((t) & (FUNCTION0 | CLOSURE0)) ? ((t)&0x00000007) : 0); } - static double fac(double a) - { /* simplest version of fac */ - if (a < 0.0) - return nan(); - if (a > UINT_MAX) - return INFINITY; - unsigned int ua = (unsigned int)(a); - unsigned long int result = 1, i; - for (i = 1; i <= ua; i++) + template + static inline auto eval_generic( + int type, T_HANDLE_CONSTANT handle_constant, T_HANDLE_VARIABLE handle_variable, T_HANDLE_FUNCTION handle_function, T_HANDLE_CLOSURE handle_closure, + T_HANDLE_ERROR handle_error) + { + const auto t = type_mask(type); + if (t == CONSTANT) { - if (i > ULONG_MAX / result) - return INFINITY; - result *= i; + return handle_constant(); } - return (double)result; + else if (t == VARIABLE) + { + return handle_variable(); + } + else if (t >= FUNCTION0) + { + if (t < FUNCTION_MAX) + { + return handle_function(t - FUNCTION0); + } + if (t >= CLOSURE0) + { + if (t < CLOSURE_MAX) + { + return handle_closure(t - CLOSURE0); + } + } + } + + return handle_error(); } - static double ncr(double n, double r) + template + auto eval_function(int a, const void* fn, T_RET error_val, T_EVAL_ARG eval_arg) -> T_RET { - if (n < 0.0 || r < 0.0 || n < r) - return nan(); - if (n > UINT_MAX || r > UINT_MAX) - return INFINITY; - unsigned long int un = (unsigned int)(n), ur = (unsigned int)(r), i; - unsigned long int result = 1; - if (ur > un / 2) - ur = un - ur; - for (i = 1; i <= ur; i++) +#define FUN(...) ((T_RET(*)(__VA_ARGS__))fn) + switch (a) { - if (result > ULONG_MAX / (un - ur + i)) - return INFINITY; - result *= un - ur + i; - result /= i; + case 0: + return FUN(void)(); + case 1: + return FUN(T_VECTOR)(eval_arg(0)); + case 2: + return FUN(T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1)); + case 3: + return FUN(T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2)); + case 4: + return FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3)); + case 5: + return FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4)); + case 6: + return FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4), eval_arg(5)); + case 7: + return FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)( + eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4), eval_arg(5), eval_arg(6)); } - return result; +#undef FUN + return error_val; } - static double npr(double n, double r) + template + auto eval_closure(int a, const void* fn, const void* arity_params, T_RET error_val, T_EVAL_ARG eval_arg) -> T_RET { - return ncr(n, r) * fac(r); +#define FUN(...) ((T_RET(*)(__VA_ARGS__))fn) + switch (a) + { + case 0: + return FUN(const void*)(arity_params); + case 1: + return FUN(const void*, T_VECTOR)(arity_params, eval_arg(0)); + case 2: + return FUN(const void*, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1)); + case 3: + return FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1), eval_arg(2)); + case 4: + return FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3)); + case 5: + return FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4)); + case 6: + return FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)( + arity_params, eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4), eval_arg(5)); + case 7: + return FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)( + arity_params, eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4), eval_arg(5), eval_arg(6)); + } +#undef FUN + return error_val; } - static double fabs(double n) + template + static inline auto eval_portable_impl(const expr_portable* n_portable, const unsigned char* expr_buffer, const void* const expr_context[]) noexcept -> + typename T_VECTOR { - return ::fabs(n); - } + using t_atom = T_ATOM; + using t_vector = T_VECTOR; + using t_traits = T_TRAITS; + using t_vector_builtins = typename T_TRAITS::t_vector_builtins; - static double acos(double n) - { - return ::acos(n); - } + auto eval_arg = [&](int e) { + return eval_portable_impl((const expr_portable*)&expr_buffer[n_portable->parameters[e]], expr_buffer, expr_context); + }; - static double cosh(double n) - { - return ::cosh(n); + return eval_generic( + n_portable->type, [&]() { return t_traits::load_atom(n_portable->value); }, + [&]() { return t_traits::load_atom((expr_context != nullptr) ? *((const t_vector*)(expr_context[n_portable->bound])) : t_vector_builtins::nan()); }, + [&](int a) { return eval_function(a, expr_context[n_portable->function], t_vector_builtins::nan(), eval_arg); }, + [&](int a) { + return eval_closure(a, expr_context[n_portable->function], (void*)expr_context[n_portable->parameters[a]], t_vector_builtins::nan(), eval_arg); + }, + [&]() { return t_vector_builtins::nan(); }); } + } // namespace eval_details - static double cos(double n) - { - return ::cos(n); - } + enum class statement_type : int + { + jump, + return_value, + assign, + call, + }; - static double exp(double n) - { - return ::exp(n); - } + struct statement + { + statement_type type; + int arg_a; + int arg_b; + }; - static double asin(double n) - { - return ::asin(n); - } +#if (TP_COMPILER_ENABLED) + struct compiled_expr + { + virtual ~compiled_expr() = default; - static double sinh(double n) - { - return ::sinh(n); - } + virtual size_t get_binding_array_size() const = 0; + virtual const void* const* get_binding_addresses() const = 0; + virtual const char* const* get_binding_names() const = 0; + virtual size_t get_data_size() const = 0; + virtual const unsigned char* get_data() const = 0; + }; - static double sin(double n) - { - return ::sin(n); - } + struct compiled_program + { + virtual ~compiled_program() = default; - static double sqrt(double n) - { - return ::sqrt(n); - } + virtual size_t get_binding_array_size() const = 0; + virtual const void* const* get_binding_addresses() const = 0; + virtual const char* const* get_binding_names() const = 0; + virtual size_t get_data_size() const = 0; + virtual const unsigned char* get_data() const = 0; + virtual size_t get_statement_array_size() const = 0; + virtual const statement* get_statements() const = 0; + }; +#endif // #if (TP_COMPILER_ENABLED) +} // namespace tp - static double log(double n) - { - return ::log(n); - } +#if (TP_COMPILER_ENABLED) +#include +#include +#include +#include +#include - static double log10(double n) - { - return ::log10(n); - } +namespace tp +{ + template + struct compiler_builtins : T_NATIVE + { + using t_base = T_NATIVE; - static double atan(double n) + static inline int name_compare(const char* a, const char* b, size_t n) { - return ::atan(n); + while (n && *a && (*a == *b)) + { + ++a; + ++b; + --n; + } + if (n == 0) + { + return 0; + } + else + { + return (*(unsigned char*)a - *(unsigned char*)b); + } } - static double tanh(double n) + static inline const variable* find_builtin_function(const char* name, int len) { - return ::tanh(n); - } + int imin = 0; + int imax = sizeof(t_base::functions) / sizeof(variable) - 2; - static double fmod(double n, double m) - { - return ::fmod(n, m); + /*Binary search.*/ + while (imax >= imin) + { + const int i = (imin + ((imax - imin) / 2)); + int c = name_compare(name, t_base::functions[i].name, len); + if (!c) + c = '\0' - t_base::functions[i].name[len]; + if (c == 0) + { + return t_base::functions + i; + } + else if (c > 0) + { + imin = i + 1; + } + else + { + imax = i - 1; + } + } + return nullptr; } - static double tan(double n) + static inline const variable* find_builtin_operator(const char* name, int len) { - return ::tan(n); - } + int imin = 0; + int imax = sizeof(t_base::operators) / sizeof(variable) - 2; - static double atan2(double n, double m) - { - return ::atan2(n, m); - } + /*Binary search.*/ + while (imax >= imin) + { + const int i = (imin + ((imax - imin) / 2)); + int c = name_compare(name, t_base::operators[i].name, len); - static double pow(double n, double m) - { - return ::pow(n, m); - } + if (!c) + c = '\0' - t_base::operators[i].name[len]; - static double floor(double d) - { - return ::floor(d); + if (c == 0) + { + return t_base::operators + i; + } + else if (c > 0) + { + imin = i + 1; + } + else + { + imax = i - 1; + } + } + return nullptr; } - static double ceil(double d) + static inline const variable* find_builtin(const char* name, int len) { - return ::ceil(d); + auto res = find_builtin_function(name, len); + if (!res) + { + res = find_builtin_operator(name, len); + } + return res; } - static double add(double a, double b) + static inline const variable* find_builtin(const char* name) { - return a + b; + return find_builtin(name, static_cast(::strlen(name))); } - static double sub(double a, double b) + static inline const void* find_builtin_address(const char* name) { - return a - b; + auto b = find_builtin(name, static_cast(::strlen(name))); + if (b) + { + return b->address; + } + return nullptr; } - static double mul(double a, double b) + static inline const variable* find_function_by_addr(const void* addr) { - return a * b; + for (auto var = &t_base::functions[0]; var->name != 0; ++var) + { + if (var->address == addr) + { + return var; + } + } + return nullptr; } - static double divide(double a, double b) + static inline const variable* find_operator_by_addr(const void* addr) { - return a / b; + for (auto var = &t_base::operators[0]; var->name != 0; ++var) + { + if (var->address == addr) + { + return var; + } + } + return nullptr; } - static double negate(double a) + static inline const variable* find_any_by_addr(const void* addr) { - return -a; + const variable* var = find_function_by_addr(addr); + if (!var) + { + var = find_operator_by_addr(addr); + if (!var) + { + return find_builtin("nul"); + } + } + return var; } + }; - static double comma(double a, double b) - { - (void)a; - return b; - } + template + struct native + { + using t_traits = T_TRAITS; + using t_atom = typename T_TRAITS::t_atom; + using t_vector = typename T_TRAITS::t_vector; + using t_atom_builtins = compiler_builtins; + using t_vector_builtins = compiler_builtins; - static double greater(double a, double b) + struct expr_native { - return a > b; - } + int type; + union + { + t_atom value; + const t_atom* bound; + const void* function; + }; + void* parameters[1]; + }; - static double greater_eq(double a, double b) - { - return a >= b; - } + typedef t_vector (*te_fun2)(t_vector, t_vector); - static double lower(double a, double b) + enum class TOK : int { - return a < b; - } + NUL = CLOSURE_MAX, + ERROR, + END, + SEP, + OPEN, + CLOSE, + NUMBER, + VARIABLE, + INFIX + }; - static double lower_eq(double a, double b) + struct state { - return a <= b; - } + const char* start; + const char* next; + int type; + union + { + t_atom value; + const t_atom* bound; + const void* function; + }; + void* context; - static double equal(double a, double b) - { - return a == b; - } + const variable* lookup; + int lookup_len; + }; - static double not_equal(double a, double b) + static inline bool is_pure(int t) noexcept { - return a != b; + return (((t)&FLAG_PURE) != 0); } - static double logical_and(double a, double b) + static inline bool is_function(int t) noexcept { - return a != 0.0 && b != 0.0; + return (((t)&FUNCTION0) != 0); } - static double logical_or(double a, double b) + static inline bool is_closure(int t) noexcept { - return a != 0.0 || b != 0.0; + return (((t)&CLOSURE0) != 0); } - static double logical_not(double a) - { - return a == 0.0; - } +#define NEW_EXPR(type, ...) \ + [&]() { \ + const expr_native* _args[] = {__VA_ARGS__}; \ + return new_expr((type), _args); \ + }() - static double logical_notnot(double a) + static expr_native* new_expr(const int type, const expr_native* parameters[]) { - return a != 0.0; + const int arity = eval_details::arity(type); + const int psize = sizeof(void*) * arity; + const int size = (sizeof(expr_native) - sizeof(void*)) + psize + (is_closure(type) ? sizeof(void*) : 0); + expr_native* ret = (expr_native*)malloc(size); + memset(ret, 0, size); + if (arity && parameters) + { + memcpy(ret->parameters, parameters, psize); + } + ret->type = type; + ret->bound = 0; + return ret; } - static double negate_logical_not(double a) + static void free_parameters(expr_native* n) { - return -(a == 0.0); - } + if (!n) + return; - static double negate_logical_notnot(double a) - { - return -(a != 0.0); + for (int arity = eval_details::arity(n->type); arity > 0; --arity) + { + free_native((expr_native*)n->parameters[arity - 1]); + } } - static double nul() + static void free_native(expr_native* n) { - return 0.0f; + if (!n) + return; + free_parameters(n); + free(n); } - static double nan() + static const variable* find_lookup(const variable* lookup, int lookup_len, const char* name, int len) { - return std::numeric_limits::quiet_NaN(); + if (!lookup) + return 0; + + const variable* var = lookup; + int iters = lookup_len; + for (; iters; ++var, --iters) + { + if (strncmp(name, var->name, len) == 0 && var->name[len] == '\0') + { + return var; + } + } + return 0; } - }; - template<> - struct native_builtins_impl - { - static float pi(void) + static const variable* find_lookup(const state* s, const char* name, int len) { - return 3.14159265358979323846f; + return find_lookup(s->lookup, s->lookup_len, name, len); } - static float e(void) + static void next_token(state* s) { - return 2.71828182845904523536f; - } - - static float fac(float a) - { /* simplest version of fac */ - if (a < 0.0f) - return nan(); - if (a > UINT_MAX) - return INFINITY; - unsigned int ua = (unsigned int)(a); - unsigned long int result = 1, i; - for (i = 1; i <= ua; i++) - { - if (i > ULONG_MAX / result) - return INFINITY; - result *= i; - } - return (float)result; - } + s->type = (int)TOK::NUL; - static float ncr(float n, float r) - { - if (n < 0.0f || r < 0.0f || n < r) - return nan(); - if (n > UINT_MAX || r > UINT_MAX) - return INFINITY; - unsigned long int un = (unsigned int)(n), ur = (unsigned int)(r), i; - unsigned long int result = 1; - if (ur > un / 2) - ur = un - ur; - for (i = 1; i <= ur; i++) + do { - if (result > ULONG_MAX / (un - ur + i)) - return INFINITY; - result *= un - ur + i; - result /= i; - } - return (float)result; - } - - static float npr(float n, float r) - { - return ncr(n, r) * fac(r); - } - - static float fabs(float n) - { - return ::fabsf(n); - } - - static float acos(float n) - { - return ::acosf(n); - } - - static float cosh(float n) - { - return ::coshf(n); - } - - static float cos(float n) - { - return ::cosf(n); - } - - static float exp(float n) - { - return ::expf(n); - } - - static float asin(float n) - { - return ::asinf(n); - } - - static float sinh(float n) - { - return ::sinhf(n); - } - - static float sin(float n) - { - return ::sinf(n); - } - - static float sqrt(float n) - { - return ::sqrtf(n); - } - - static float log(float n) - { - return ::logf(n); - } - - static float log10(float n) - { - return ::log10f(n); - } - - static float atan(float n) - { - return ::atanf(n); - } - - static float tanh(float n) - { - return ::tanhf(n); - } - - static float fmod(float n, float m) - { - return ::fmodf(n, m); - } - - static float tan(float n) - { - return ::tanf(n); - } - - static float atan2(float n, float m) - { - return ::atan2f(n, m); - } - - static float pow(float n, float m) - { - return ::powf(n, m); - } - - static float floor(float d) - { - return ::floorf(d); - } - - static float ceil(float d) - { - return ::ceilf(d); - } - - static float add(float a, float b) - { - return a + b; - } - - static float sub(float a, float b) - { - return a - b; - } - - static float mul(float a, float b) - { - return a * b; - } - - static float divide(float a, float b) - { - return a / b; - } - - static float negate(float a) - { - return -a; - } - - static float comma(float a, float b) - { - (void)a; - return b; - } - - static float greater(float a, float b) - { - return a > b; - } - - static float greater_eq(float a, float b) - { - return a >= b; - } - - static float lower(float a, float b) - { - return a < b; - } - - static float lower_eq(float a, float b) - { - return a <= b; - } - - static float equal(float a, float b) - { - return a == b; - } - - static float not_equal(float a, float b) - { - return a != b; - } - - static float logical_and(float a, float b) - { - return a != 0.0f && b != 0.0f; - } - - static float logical_or(float a, float b) - { - return a != 0.0f || b != 0.0f; - } - - static float logical_not(float a) - { - return a == 0.0f; - } - - static float logical_notnot(float a) - { - return a != 0.0f; - } - - static float negate_logical_not(float a) - { - return (float)-(a == 0.0f); - } + if (!*s->next || *s->next == ';') + { + s->type = (int)TOK::END; + return; + } - static float negate_logical_notnot(float a) - { - return (float)-(a != 0.0f); - } + /* Try reading a number. */ + if ((s->next[0] >= '0' && s->next[0] <= '9') || s->next[0] == '.') + { + s->value = (t_atom)strtod(s->next, (char**)&s->next); + s->type = (int)TOK::NUMBER; + } + else + { + /* Look for a variable or builtin function call. */ + if (s->next[0] >= 'a' && s->next[0] <= 'z') + { + const char* start; + start = s->next; + while ((s->next[0] >= 'a' && s->next[0] <= 'z') || (s->next[0] >= '0' && s->next[0] <= '9') || (s->next[0] == '_')) + s->next++; - static float nul() - { - return 0.0f; - } + const variable* var = find_lookup(s, start, static_cast(s->next - start)); + if (!var) + var = t_vector_builtins::find_builtin(start, static_cast(s->next - start)); - static float nan() - { - return std::numeric_limits::quiet_NaN(); + if (!var) + { + s->type = (int)TOK::ERROR; + } + else + { + const auto t = eval_details::type_mask(var->type); + if (t == VARIABLE) + { + s->type = (int)TOK::VARIABLE; + s->bound = (const t_atom*)var->address; + } + else if (t >= FUNCTION0) + { + if (t < FUNCTION_MAX) + { + s->type = var->type; + s->function = var->address; + } + else if (t >= CLOSURE0) + { + if (t < CLOSURE_MAX) + { + s->context = var->context; + s->type = var->type; + s->function = var->address; + } + } + } + } + } + else + { + /* Look for an operator or special character. */ + switch (s->next++[0]) + { + case '+': + s->type = (int)TOK::INFIX; + s->function = t_vector_builtins::find_builtin_address("add"); + break; + case '-': + s->type = (int)TOK::INFIX; + s->function = t_vector_builtins::find_builtin_address("sub"); + break; + case '*': + s->type = (int)TOK::INFIX; + s->function = t_vector_builtins::find_builtin_address("mul"); + break; + case '/': + s->type = (int)TOK::INFIX; + s->function = t_vector_builtins::find_builtin_address("divide"); + break; + case '^': + s->type = (int)TOK::INFIX; + s->function = t_vector_builtins::find_builtin_address("pow"); + break; + case '%': + s->type = (int)TOK::INFIX; + s->function = t_vector_builtins::find_builtin_address("fmod"); + break; + case '!': + if (s->next++[0] == '=') + { + s->type = (int)TOK::INFIX; + s->function = t_vector_builtins::find_builtin_address("not_equal"); + } + else + { + s->next--; + s->type = (int)TOK::INFIX; + s->function = t_vector_builtins::find_builtin_address("logical_not"); + } + break; + case '=': + if (s->next++[0] == '=') + { + s->type = (int)TOK::INFIX; + s->function = t_vector_builtins::find_builtin_address("equal"); + } + else + { + s->type = (int)TOK::ERROR; + } + break; + case '<': + if (s->next++[0] == '=') + { + s->type = (int)TOK::INFIX; + s->function = t_vector_builtins::find_builtin_address("lower_eq"); + } + else + { + s->next--; + s->type = (int)TOK::INFIX; + s->function = t_vector_builtins::find_builtin_address("lower"); + } + break; + case '>': + if (s->next++[0] == '=') + { + s->type = (int)TOK::INFIX; + s->function = t_vector_builtins::find_builtin_address("greater_eq"); + } + else + { + s->next--; + s->type = (int)TOK::INFIX; + s->function = t_vector_builtins::find_builtin_address("greater"); + } + break; + case '&': + if (s->next++[0] == '&') + { + s->type = (int)TOK::INFIX; + s->function = t_vector_builtins::find_builtin_address("logical_and"); + } + else + { + s->type = (int)TOK::ERROR; + } + break; + case '|': + if (s->next++[0] == '|') + { + s->type = (int)TOK::INFIX; + s->function = t_vector_builtins::find_builtin_address("logical_or"); + } + else + { + s->type = (int)TOK::ERROR; + } + break; + case '(': + s->type = (int)TOK::OPEN; + break; + case ')': + s->type = (int)TOK::CLOSE; + break; + case ',': + s->type = (int)TOK::SEP; + break; + case ' ': + case '\t': + case '\n': + case '\r': + break; + default: + s->type = (int)TOK::ERROR; + break; + } + } + } + } while (s->type == (int)TOK::NUL); } - }; - template - struct native_builtins : native_builtins_impl - { - using t_impl = native_builtins_impl; + static expr_native* base(state* s) + { + /* = | | {"(" ")"} | | + * "(" {"," } ")" | "(" ")" */ + expr_native* ret; - static inline constexpr variable functions[] = {/* must be in alphabetical order */ - {"abs", t_impl::fabs, FUNCTION1 | FLAG_PURE, 0}, - {"acos", t_impl::acos, FUNCTION1 | FLAG_PURE, 0}, - {"asin", t_impl::asin, FUNCTION1 | FLAG_PURE, 0}, - {"atan", t_impl::atan, FUNCTION1 | FLAG_PURE, 0}, - {"atan2", t_impl::atan2, FUNCTION2 | FLAG_PURE, 0}, - {"ceil", t_impl::ceil, FUNCTION1 | FLAG_PURE, 0}, - {"cos", t_impl::cos, FUNCTION1 | FLAG_PURE, 0}, - {"cosh", t_impl::cosh, FUNCTION1 | FLAG_PURE, 0}, - {"e", t_impl::e, FUNCTION0 | FLAG_PURE, 0}, - {"exp", t_impl::exp, FUNCTION1 | FLAG_PURE, 0}, - {"fac", t_impl::fac, FUNCTION1 | FLAG_PURE, 0}, - {"floor", t_impl::floor, FUNCTION1 | FLAG_PURE, 0}, - {"ln", t_impl::log, FUNCTION1 | FLAG_PURE, 0}, -#ifdef TP_NAT_LOG - {"log", t_impl::log, FUNCTION1 | FLAG_PURE, 0}, -#else - {"log", t_impl::log10, FUNCTION1 | FLAG_PURE, 0}, -#endif - {"log10", t_impl::log10, FUNCTION1 | FLAG_PURE, 0}, - {"ncr", t_impl::ncr, FUNCTION2 | FLAG_PURE, 0}, - {"npr", t_impl::npr, FUNCTION2 | FLAG_PURE, 0}, - {"pi", t_impl::pi, FUNCTION0 | FLAG_PURE, 0}, - {"pow", t_impl::pow, FUNCTION2 | FLAG_PURE, 0}, - {"sin", t_impl::sin, FUNCTION1 | FLAG_PURE, 0}, - {"sinh", t_impl::sinh, FUNCTION1 | FLAG_PURE, 0}, - {"sqrt", t_impl::sqrt, FUNCTION1 | FLAG_PURE, 0}, - {"tan", t_impl::tan, FUNCTION1 | FLAG_PURE, 0}, - {"tanh", t_impl::tanh, FUNCTION1 | FLAG_PURE, 0}, - {0, 0, 0, 0}}; - - static inline constexpr variable operators[] = {/* must be in alphabetical order */ - {"add", t_impl::add, FUNCTION2 | FLAG_PURE, 0}, - {"comma", t_impl::comma, FUNCTION2 | FLAG_PURE, 0}, - {"divide", t_impl::divide, FUNCTION2 | FLAG_PURE, 0}, - {"equal", t_impl::equal, FUNCTION2 | FLAG_PURE, 0}, - {"fmod", t_impl::fmod, FUNCTION2 | FLAG_PURE, 0}, - {"greater", t_impl::greater, FUNCTION2 | FLAG_PURE, 0}, - {"greater_eq", t_impl::greater_eq, FUNCTION2 | FLAG_PURE, 0}, - {"logical_and", t_impl::logical_and, FUNCTION2 | FLAG_PURE, 0}, - {"logical_not", t_impl::logical_not, FUNCTION1 | FLAG_PURE, 0}, - {"logical_notnot", t_impl::logical_notnot, FUNCTION1 | FLAG_PURE, 0}, - {"logical_or", t_impl::logical_or, FUNCTION2 | FLAG_PURE, 0}, - {"lower", t_impl::lower, FUNCTION2 | FLAG_PURE, 0}, - {"lower_eq", t_impl::lower_eq, FUNCTION2 | FLAG_PURE, 0}, - {"mul", t_impl::mul, FUNCTION2 | FLAG_PURE, 0}, - {"negate", t_impl::negate, FUNCTION1 | FLAG_PURE, 0}, - {"negate_logical_not", t_impl::negate_logical_not, FUNCTION1 | FLAG_PURE, 0}, - {"negate_logical_notnot", t_impl::negate_logical_notnot, FUNCTION1 | FLAG_PURE, 0}, - {"not_equal", t_impl::not_equal, FUNCTION2 | FLAG_PURE, 0}, - {"pow", t_impl::pow, FUNCTION2 | FLAG_PURE, 0}, - {"sub", t_impl::sub, FUNCTION2 | FLAG_PURE, 0}, - {0, 0, 0, 0}}; + const auto t = eval_details::type_mask(s->type); - static inline int name_compare(const char* a, const char* b, size_t n) - { - while (n && *a && (*a == *b)) - { - ++a; - ++b; - --n; - } - if (n == 0) + if (t == (int)TOK::NUMBER) { - return 0; + ret = new_expr(CONSTANT, 0); + ret->value = s->value; + next_token(s); } - else + else if (t == (int)TOK::VARIABLE) { - return (*(unsigned char*)a - *(unsigned char*)b); + ret = new_expr(VARIABLE, 0); + ret->bound = s->bound; + next_token(s); } - } - - static inline const variable* find_builtin_function(const char* name, int len) - { - int imin = 0; - int imax = sizeof(functions) / sizeof(variable) - 2; - - /*Binary search.*/ - while (imax >= imin) + else if ((t >= FUNCTION0 && t < FUNCTION_MAX) || (t >= CLOSURE0 && t < CLOSURE_MAX)) { - const int i = (imin + ((imax - imin) / 2)); - int c = name_compare(name, functions[i].name, len); - if (!c) - c = '\0' - functions[i].name[len]; - if (c == 0) + const auto arity = eval_details::arity(s->type); + if (arity == 0) { - return functions + i; + ret = new_expr(s->type, 0); + ret->function = s->function; + if (is_closure(s->type)) + ret->parameters[0] = s->context; + next_token(s); + if (s->type == (int)TOK::OPEN) + { + next_token(s); + if (s->type != (int)TOK::CLOSE) + { + s->type = (int)TOK::ERROR; + } + else + { + next_token(s); + } + } } - else if (c > 0) + else if (arity == 1) { - imin = i + 1; + ret = new_expr(s->type, 0); + ret->function = s->function; + if (is_closure(s->type)) + ret->parameters[1] = s->context; + next_token(s); + ret->parameters[0] = power(s); } else { - imax = i - 1; - } - } - return nullptr; - } - - static inline const variable* find_builtin_operator(const char* name, int len) - { - int imin = 0; - int imax = sizeof(operators) / sizeof(variable) - 2; - - /*Binary search.*/ - while (imax >= imin) - { - const int i = (imin + ((imax - imin) / 2)); - int c = name_compare(name, operators[i].name, len); - - if (!c) - c = '\0' - operators[i].name[len]; + ret = new_expr(s->type, 0); + ret->function = s->function; + if (is_closure(s->type)) + ret->parameters[arity] = s->context; + next_token(s); - if (c == 0) - { - return operators + i; - } - else if (c > 0) - { - imin = i + 1; - } - else - { - imax = i - 1; + if (s->type != (int)TOK::OPEN) + { + s->type = (int)TOK::ERROR; + } + else + { + int i; + for (i = 0; i < arity; i++) + { + next_token(s); + ret->parameters[i] = expr(s); + if (s->type != (int)TOK::SEP) + { + break; + } + } + if (s->type != (int)TOK::CLOSE || i != arity - 1) + { + s->type = (int)TOK::ERROR; + } + else + { + next_token(s); + } + } } } - return nullptr; - } - - static inline const variable* find_builtin(const char* name, int len) - { - auto res = find_builtin_function(name, len); - if (!res) - { - res = find_builtin_operator(name, len); - } - return res; - } - - static inline const variable* find_builtin(const char* name) - { - return find_builtin(name, static_cast(::strlen(name))); - } - - static inline const void* find_builtin_address(const char* name) - { - auto b = find_builtin(name, static_cast(::strlen(name))); - if (b) - { - return b->address; - } - return nullptr; - } - }; - - template - struct expr_portable - { - using t_traits = T_TRAITS; - using t_atom = typename T_TRAITS::t_atom; - - int type; - union - { - t_atom value; - size_t bound; - size_t function; - }; - size_t parameters[1]; - }; - - namespace eval_details - { - template - inline T type_mask(const T t) noexcept - { - return ((t)&0x0000001F); - } - - template - inline T arity(const T t) noexcept - { - return (((t) & (FUNCTION0 | CLOSURE0)) ? ((t)&0x00000007) : 0); - } - - template - static inline auto eval_generic( - int type, T_HANDLE_CONSTANT handle_constant, T_HANDLE_VARIABLE handle_variable, T_HANDLE_FUNCTION handle_function, T_HANDLE_CLOSURE handle_closure, - T_HANDLE_ERROR handle_error) - { - const auto t = type_mask(type); - if (t == CONSTANT) - { - return handle_constant(); - } - else if (t == VARIABLE) - { - return handle_variable(); - } - else if (t >= FUNCTION0) + else if (t == (int)TOK::OPEN) { - if (t < FUNCTION_MAX) + next_token(s); + ret = list(s); + if (s->type != (int)TOK::CLOSE) { - return handle_function(t - FUNCTION0); + s->type = (int)TOK::ERROR; } - if (t >= CLOSURE0) + else { - if (t < CLOSURE_MAX) - { - return handle_closure(t - CLOSURE0); - } - } - } - - return handle_error(); - } - - template - auto eval_function(int a, const void* fn, T_RET error_val, T_EVAL_ARG eval_arg) -> T_RET - { -#define FUN(...) ((T_RET(*)(__VA_ARGS__))fn) - switch (a) - { - case 0: - return FUN(void)(); - case 1: - return FUN(T_VECTOR)(eval_arg(0)); - case 2: - return FUN(T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1)); - case 3: - return FUN(T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2)); - case 4: - return FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3)); - case 5: - return FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4)); - case 6: - return FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4), eval_arg(5)); - case 7: - return FUN(T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)( - eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4), eval_arg(5), eval_arg(6)); - } -#undef FUN - return error_val; - } - - template - auto eval_closure(int a, const void* fn, const void* arity_params, T_RET error_val, T_EVAL_ARG eval_arg) -> T_RET - { -#define FUN(...) ((T_RET(*)(__VA_ARGS__))fn) - switch (a) + next_token(s); + } + } + else { - case 0: - return FUN(const void*)(arity_params); - case 1: - return FUN(const void*, T_VECTOR)(arity_params, eval_arg(0)); - case 2: - return FUN(const void*, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1)); - case 3: - return FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1), eval_arg(2)); - case 4: - return FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3)); - case 5: - return FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)(arity_params, eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4)); - case 6: - return FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)( - arity_params, eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4), eval_arg(5)); - case 7: - return FUN(const void*, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR, T_VECTOR)( - arity_params, eval_arg(0), eval_arg(1), eval_arg(2), eval_arg(3), eval_arg(4), eval_arg(5), eval_arg(6)); + ret = new_expr(0, 0); + s->type = (int)TOK::ERROR; + ret->value = t_vector_builtins::nan(); } -#undef FUN - return error_val; - } - - template - static inline auto eval_portable_impl(const expr_portable* n_portable, const unsigned char* expr_buffer, const void* const expr_context[]) noexcept -> - typename T_VECTOR - { - using t_atom = T_ATOM; - using t_vector = T_VECTOR; - using t_traits = T_TRAITS; - using t_builtins = typename T_TRAITS::t_vector_builtins; - - auto eval_arg = [&](int e) { - return eval_portable_impl((const expr_portable*)&expr_buffer[n_portable->parameters[e]], expr_buffer, expr_context); - }; - return eval_generic( - n_portable->type, [&]() { return t_traits::load_atom(n_portable->value); }, - [&]() { return t_traits::load_atom((expr_context != nullptr) ? *((const t_vector*)(expr_context[n_portable->bound])) : t_builtins::nan()); }, - [&](int a) { return eval_function(a, expr_context[n_portable->function], t_builtins::nan(), eval_arg); }, - [&](int a) { return eval_closure(a, expr_context[n_portable->function], (void*)expr_context[n_portable->parameters[a]], t_builtins::nan(), eval_arg); }, - [&]() { return t_builtins::nan(); }); + return ret; } - } // namespace eval_details - - enum class statement_type : int - { - jump, - return_value, - assign, - call, - }; - - struct statement - { - statement_type type; - int arg_a; - int arg_b; - }; - -#if (TP_COMPILER_ENABLED) - struct compiled_expr - { - virtual ~compiled_expr() = default; - - virtual size_t get_binding_array_size() const = 0; - virtual const void* const* get_binding_addresses() const = 0; - virtual const char* const* get_binding_names() const = 0; - virtual size_t get_data_size() const = 0; - virtual const unsigned char* get_data() const = 0; - }; - - struct compiled_program - { - virtual ~compiled_program() = default; - - virtual size_t get_binding_array_size() const = 0; - virtual const void* const* get_binding_addresses() const = 0; - virtual const char* const* get_binding_names() const = 0; - virtual size_t get_data_size() const = 0; - virtual const unsigned char* get_data() const = 0; - virtual size_t get_statement_array_size() const = 0; - virtual const statement* get_statements() const = 0; - }; -#endif // #if (TP_COMPILER_ENABLED) -} // namespace tp - -#if (TP_COMPILER_ENABLED) -#include -#include -#include -#include -#include -namespace tp -{ - template - struct compiler_builtins : native_builtins - { - using t_impl = native_builtins; - - static inline const variable* find_function_by_addr(const void* addr) + static expr_native* power(state* s) { - for (auto var = &t_impl::functions[0]; var->name != 0; ++var) + /* = {("-" | "+" | "!")} */ + int sign = 1; + while (s->type == (int)TOK::INFIX && (s->function == t_vector_builtins::find_builtin_address("add") || s->function == t_vector_builtins::find_builtin_address("sub"))) { - if (var->address == addr) + if (s->function == t_vector_builtins::find_builtin_address("sub")) + sign = -sign; + next_token(s); + } + + int logical = 0; + while (s->type == (int)TOK::INFIX && (s->function == t_vector_builtins::find_builtin_address("add") || s->function == t_vector_builtins::find_builtin_address("sub") || + s->function == t_vector_builtins::find_builtin_address("logical_not"))) + { + if (s->function == t_vector_builtins::find_builtin_address("logical_not")) { - return var; + if (logical == 0) + { + logical = -1; + } + else + { + logical = -logical; + } } + next_token(s); } - return nullptr; - } - static inline const variable* find_operator_by_addr(const void* addr) - { - for (auto var = &t_impl::operators[0]; var->name != 0; ++var) + expr_native* ret; + + if (sign == 1) { - if (var->address == addr) + if (logical == 0) { - return var; + ret = base(s); + } + else if (logical == -1) + { + ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); + ret->function = t_vector_builtins::find_builtin_address("logical_not"); + } + else + { + ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); + ret->function = t_vector_builtins::find_builtin_address("logical_notnot"); } } - return nullptr; - } - - static inline const variable* find_any_by_addr(const void* addr) - { - const variable* var = find_function_by_addr(addr); - if (!var) + else { - var = find_operator_by_addr(addr); - if (!var) + if (logical == 0) + { + ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); + ret->function = t_vector_builtins::find_builtin_address("negate"); + } + else if (logical == -1) + { + ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); + ret->function = t_vector_builtins::find_builtin_address("negate_logical_not"); + } + else { - return t_impl::find_builtin("nul"); + ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); + ret->function = t_vector_builtins::find_builtin_address("negate_logical_notnot"); } } - return var; - } - }; - template - struct native - { - using t_traits = T_TRAITS; - using t_atom = typename T_TRAITS::t_atom; - using t_vector = typename T_TRAITS::t_vector; - using t_builtins = compiler_builtins; + return ret; + } - struct expr_native +#ifdef TP_POW_FROM_RIGHT + static expr_native* factor(state* s) { - int type; - union - { - t_atom value; - const t_atom* bound; - const void* function; - }; - void* parameters[1]; - }; + /* = {"^" } */ + expr_native* ret = power(s); - typedef t_vector (*te_fun2)(t_vector, t_vector); + const void* left_function = NUL; + expr_native* insertion = 0; - enum class TOK : int - { - NUL = CLOSURE_MAX, - ERROR, - END, - SEP, - OPEN, - CLOSE, - NUMBER, - VARIABLE, - INFIX - }; + if (ret->type == (FUNCTION1 | FLAG_PURE) && + (ret->function == t_vector_builtins::find_builtin_address("negate") || ret->function == t_vector_builtins::find_builtin_address("logical_not") || + ret->function == t_vector_builtins::find_builtin_address("logical_notnot") || ret->function == t_vector_builtins::find_builtin_address("negate_logical_not") || + ret->function == t_vector_builtins::find_builtin_address("negate_logical_notnot"))) + { + left_function = ret->function; + expr_native* se = ret->parameters[0]; + free(ret); + ret = se; + } - struct state - { - const char* start; - const char* next; - int type; - union + while (s->type == (int)TOK::INFIX && (s->function == t_vector_builtins::find_builtin_address("pow"))) { - t_atom value; - const t_atom* bound; - const void* function; - }; - void* context; + te_fun2 t = s->function; + next_token(s); - const variable* lookup; - int lookup_len; - }; + if (insertion) + { + /* Make exponentiation go right-to-left. */ + expr_native* insert = NEW_EXPR(FUNCTION2 | FLAG_PURE, insertion->parameters[1], power(s)); + insert->function = t; + insertion->parameters[1] = insert; + insertion = insert; + } + else + { + ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, power(s)); + ret->function = t; + insertion = ret; + } + } - static inline bool is_pure(int t) noexcept - { - return (((t)&FLAG_PURE) != 0); - } + if (left_function) + { + ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, ret); + ret->function = left_function; + } - static inline bool is_function(int t) noexcept - { - return (((t)&FUNCTION0) != 0); + return ret; } - - static inline bool is_closure(int t) noexcept +#else + static expr_native* factor(state* s) { - return (((t)&CLOSURE0) != 0); + /* = {"^" } */ + expr_native* ret = power(s); + + while (s->type == (int)TOK::INFIX && (s->function == t_vector_builtins::find_builtin_address("pow"))) + { + te_fun2 t = (te_fun2)s->function; + next_token(s); + ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, power(s)); + ret->function = t; + } + + return ret; } +#endif -#define NEW_EXPR(type, ...) \ - [&]() { \ - const expr_native* _args[] = {__VA_ARGS__}; \ - return new_expr((type), _args); \ - }() - - static expr_native* new_expr(const int type, const expr_native* parameters[]) + static expr_native* term(state* s) { - const int arity = eval_details::arity(type); - const int psize = sizeof(void*) * arity; - const int size = (sizeof(expr_native) - sizeof(void*)) + psize + (is_closure(type) ? sizeof(void*) : 0); - expr_native* ret = (expr_native*)malloc(size); - memset(ret, 0, size); - if (arity && parameters) + /* = {("*" | "/" | "%") } */ + expr_native* ret = factor(s); + + while (s->type == (int)TOK::INFIX && + (s->function == t_vector_builtins::find_builtin_address("mul") || s->function == t_vector_builtins::find_builtin_address("divide") || + s->function == t_vector_builtins::find_builtin_address("fmod"))) { - memcpy(ret->parameters, parameters, psize); + te_fun2 t = (te_fun2)s->function; + next_token(s); + ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, factor(s)); + ret->function = t; } - ret->type = type; - ret->bound = 0; + return ret; } - static void free_parameters(expr_native* n) + static expr_native* sum_expr(state* s) { - if (!n) - return; + /* = {("+" | "-") } */ + expr_native* ret = term(s); - for (int arity = eval_details::arity(n->type); arity > 0; --arity) + while (s->type == (int)TOK::INFIX && (s->function == t_vector_builtins::find_builtin_address("add") || s->function == t_vector_builtins::find_builtin_address("sub"))) { - free_native((expr_native*)n->parameters[arity - 1]); + te_fun2 t = (te_fun2)s->function; + next_token(s); + ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, term(s)); + ret->function = t; } - } - static void free_native(expr_native* n) - { - if (!n) - return; - free_parameters(n); - free(n); + return ret; } - static const variable* find_lookup(const variable* lookup, int lookup_len, const char* name, int len) + static expr_native* test_expr(state* s) { - if (!lookup) - return 0; + /* = {(">" | ">=" | "<" | "<=" | "==" | "!=") } */ + expr_native* ret = sum_expr(s); - const variable* var = lookup; - int iters = lookup_len; - for (; iters; ++var, --iters) + while (s->type == (int)TOK::INFIX && + (s->function == t_vector_builtins::find_builtin_address("greater") || s->function == t_vector_builtins::find_builtin_address("greater_eq") || + s->function == t_vector_builtins::find_builtin_address("lower") || s->function == t_vector_builtins::find_builtin_address("lower_eq") || + s->function == t_vector_builtins::find_builtin_address("equal") || s->function == t_vector_builtins::find_builtin_address("not_equal"))) { - if (strncmp(name, var->name, len) == 0 && var->name[len] == '\0') - { - return var; - } + te_fun2 t = (te_fun2)s->function; + next_token(s); + ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, sum_expr(s)); + ret->function = t; } - return 0; + + return ret; } - static const variable* find_lookup(const state* s, const char* name, int len) + static expr_native* expr(state* s) { - return find_lookup(s->lookup, s->lookup_len, name, len); + /* = {("&&" | "||") } */ + expr_native* ret = test_expr(s); + + while (s->type == (int)TOK::INFIX && + (s->function == t_vector_builtins::find_builtin_address("logical_and") || s->function == t_vector_builtins::find_builtin_address("logical_or"))) + { + te_fun2 t = (te_fun2)s->function; + next_token(s); + ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, test_expr(s)); + ret->function = t; + } + + return ret; } - static void next_token(state* s) + static expr_native* list(state* s) { - s->type = (int)TOK::NUL; + /* = {"," } */ + expr_native* ret = expr(s); - do + while (s->type == (int)TOK::SEP) { - if (!*s->next || *s->next == ';') - { - s->type = (int)TOK::END; - return; - } - - /* Try reading a number. */ - if ((s->next[0] >= '0' && s->next[0] <= '9') || s->next[0] == '.') - { - s->value = (t_atom)strtod(s->next, (char**)&s->next); - s->type = (int)TOK::NUMBER; - } - else - { - /* Look for a variable or builtin function call. */ - if (s->next[0] >= 'a' && s->next[0] <= 'z') - { - const char* start; - start = s->next; - while ((s->next[0] >= 'a' && s->next[0] <= 'z') || (s->next[0] >= '0' && s->next[0] <= '9') || (s->next[0] == '_')) - s->next++; + next_token(s); + ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, expr(s)); + ret->function = t_vector_builtins::find_builtin_address("comma"); + } - const variable* var = find_lookup(s, start, static_cast(s->next - start)); - if (!var) - var = t_builtins::find_builtin(start, static_cast(s->next - start)); + return ret; + } - if (!var) - { - s->type = (int)TOK::ERROR; - } - else - { - const auto t = eval_details::type_mask(var->type); - if (t == VARIABLE) - { - s->type = (int)TOK::VARIABLE; - s->bound = (const t_atom*)var->address; - } - else if (t >= FUNCTION0) - { - if (t < FUNCTION_MAX) - { - s->type = var->type; - s->function = var->address; - } - else if (t >= CLOSURE0) - { - if (t < CLOSURE_MAX) - { - s->context = var->context; - s->type = var->type; - s->function = var->address; - } - } - } - } - } - else - { - /* Look for an operator or special character. */ - switch (s->next++[0]) - { - case '+': - s->type = (int)TOK::INFIX; - s->function = t_builtins::find_builtin_address("add"); - break; - case '-': - s->type = (int)TOK::INFIX; - s->function = t_builtins::find_builtin_address("sub"); - break; - case '*': - s->type = (int)TOK::INFIX; - s->function = t_builtins::find_builtin_address("mul"); - break; - case '/': - s->type = (int)TOK::INFIX; - s->function = t_builtins::find_builtin_address("divide"); - break; - case '^': - s->type = (int)TOK::INFIX; - s->function = t_builtins::find_builtin_address("pow"); - break; - case '%': - s->type = (int)TOK::INFIX; - s->function = t_builtins::find_builtin_address("fmod"); - break; - case '!': - if (s->next++[0] == '=') - { - s->type = (int)TOK::INFIX; - s->function = t_builtins::find_builtin_address("not_equal"); - } - else - { - s->next--; - s->type = (int)TOK::INFIX; - s->function = t_builtins::find_builtin_address("logical_not"); - } - break; - case '=': - if (s->next++[0] == '=') - { - s->type = (int)TOK::INFIX; - s->function = t_builtins::find_builtin_address("equal"); - } - else - { - s->type = (int)TOK::ERROR; - } - break; - case '<': - if (s->next++[0] == '=') - { - s->type = (int)TOK::INFIX; - s->function = t_builtins::find_builtin_address("lower_eq"); - } - else - { - s->next--; - s->type = (int)TOK::INFIX; - s->function = t_builtins::find_builtin_address("lower"); - } - break; - case '>': - if (s->next++[0] == '=') - { - s->type = (int)TOK::INFIX; - s->function = t_builtins::find_builtin_address("greater_eq"); - } - else - { - s->next--; - s->type = (int)TOK::INFIX; - s->function = t_builtins::find_builtin_address("greater"); - } - break; - case '&': - if (s->next++[0] == '&') - { - s->type = (int)TOK::INFIX; - s->function = t_builtins::find_builtin_address("logical_and"); - } - else - { - s->type = (int)TOK::ERROR; - } - break; - case '|': - if (s->next++[0] == '|') - { - s->type = (int)TOK::INFIX; - s->function = t_builtins::find_builtin_address("logical_or"); - } - else - { - s->type = (int)TOK::ERROR; - } - break; - case '(': - s->type = (int)TOK::OPEN; - break; - case ')': - s->type = (int)TOK::CLOSE; - break; - case ',': - s->type = (int)TOK::SEP; - break; - case ' ': - case '\t': - case '\n': - case '\r': - break; - default: - s->type = (int)TOK::ERROR; - break; - } - } - } - } while (s->type == (int)TOK::NUL); + static t_vector eval_native(const expr_native* n) + { + if (!n) + return t_vector_builtins::nan(); + + auto eval_arg = [&](int e) { + return eval_native((const expr_native*)n->parameters[e]); + }; + + return eval_details::eval_generic( + n->type, [&]() { return n->value; }, [&]() { return *n->bound; }, + [&](int a) { return eval_details::eval_function(a, n->function, t_vector_builtins::nan(), eval_arg); }, + [&](int a) { return eval_details::eval_closure(a, n->function, (void*)n->parameters[a], t_vector_builtins::nan(), eval_arg); }, + [&]() { return t_vector_builtins::nan(); }); } - static expr_native* base(state* s) + static void optimize(expr_native* n) { - /* = | | {"(" ")"} | | - * "(" {"," } ")" | "(" ")" */ - expr_native* ret; - - const auto t = eval_details::type_mask(s->type); + /* Evaluates as much as possible. */ + if (n->type == CONSTANT) + return; + if (n->type == VARIABLE) + return; - if (t == (int)TOK::NUMBER) - { - ret = new_expr(CONSTANT, 0); - ret->value = s->value; - next_token(s); - } - else if (t == (int)TOK::VARIABLE) - { - ret = new_expr(VARIABLE, 0); - ret->bound = s->bound; - next_token(s); - } - else if ((t >= FUNCTION0 && t < FUNCTION_MAX) || (t >= CLOSURE0 && t < CLOSURE_MAX)) + /* Only optimize out functions flagged as pure. */ + if (is_pure(n->type)) { - const auto arity = eval_details::arity(s->type); - if (arity == 0) + const int arity = eval_details::arity(n->type); + int known = 1; + int i; + for (i = 0; i < arity; ++i) { - ret = new_expr(s->type, 0); - ret->function = s->function; - if (is_closure(s->type)) - ret->parameters[0] = s->context; - next_token(s); - if (s->type == (int)TOK::OPEN) + optimize((expr_native*)n->parameters[i]); + if (((expr_native*)(n->parameters[i]))->type != CONSTANT) { - next_token(s); - if (s->type != (int)TOK::CLOSE) - { - s->type = (int)TOK::ERROR; - } - else - { - next_token(s); - } + known = 0; } } - else if (arity == 1) + if (known) { - ret = new_expr(s->type, 0); - ret->function = s->function; - if (is_closure(s->type)) - ret->parameters[1] = s->context; - next_token(s); - ret->parameters[0] = power(s); + const t_vector value = eval_native(n); + free_parameters(n); + n->type = CONSTANT; + n->value = value; } - else - { - ret = new_expr(s->type, 0); - ret->function = s->function; - if (is_closure(s->type)) - ret->parameters[arity] = s->context; - next_token(s); + } + } - if (s->type != (int)TOK::OPEN) - { - s->type = (int)TOK::ERROR; - } - else - { - int i; - for (i = 0; i < arity; i++) - { - next_token(s); - ret->parameters[i] = expr(s); - if (s->type != (int)TOK::SEP) - { - break; - } - } - if (s->type != (int)TOK::CLOSE || i != arity - 1) - { - s->type = (int)TOK::ERROR; - } - else - { - next_token(s); - } - } + static expr_native* compile_native(const char* expression, const variable* variables, int var_count, int* error) + { + state s; + s.start = s.next = expression; + s.lookup = variables; + s.lookup_len = var_count; + + next_token(&s); + expr_native* root = list(&s); + + if (s.type != (int)TOK::END) + { + free_native(root); + if (error) + { + *error = static_cast(s.next - s.start); + if (*error == 0) + *error = 1; } + return 0; } - else if (t == (int)TOK::OPEN) + else { - next_token(s); - ret = list(s); - if (s->type != (int)TOK::CLOSE) + optimize(root); + if (error) + *error = 0; + + return root; + } + } + + static t_vector interp_native(const char* expression, int* error) + { + expr_native* n = compile_native(expression, 0, 0, error); + t_vector ret; + if (n) + { + ret = eval_native(n); + free_native(n); + } + else + { + ret = t_traits::nan(); + } + return ret; + } + + static void pn(const expr_native* n, int depth) + { + int i, arity; + printf("%*s", depth, ""); + + const auto t = type_mask(n->type); + + if (t == CONSTANT) + { + printf("%f\n", n->value); + } + else if (t == VARIABLE) + { + printf("bound %p\n", n->bound); + } + else if ((t >= FUNCTION0 && t < FUNCTION_MAX) || (t >= CLOSURE0 && t < CLOSURE_MAX)) + { + arity = arity(n->type); + printf("f%d", arity); + for (i = 0; i < arity; i++) { - s->type = (int)TOK::ERROR; + printf(" %p", n->parameters[i]); } - else + printf("\n"); + for (i = 0; i < arity; i++) { - next_token(s); + pn((const expr_native*)n->parameters[i], depth + 1); } } - else + } + + static void print(const expr_native* n) + { + pn(n, 0); + } + + //// + + static const variable* find_bind_by_addr(const void* addr, const variable* lookup, int lookup_len) + { + for (int i = 0; i < lookup_len; ++i) { - ret = new_expr(0, 0); - s->type = (int)TOK::ERROR; - ret->value = t_builtins::nan(); + if (lookup[i].address == addr) + { + return &lookup[i]; + } } - - return ret; + return nullptr; } - static expr_native* power(state* s) + static const variable* find_closure_by_addr(const void* addr, const variable* lookup, int lookup_len) { - /* = {("-" | "+" | "!")} */ - int sign = 1; - while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("add") || s->function == t_builtins::find_builtin_address("sub"))) + for (int i = 0; i < lookup_len; ++i) { - if (s->function == t_builtins::find_builtin_address("sub")) - sign = -sign; - next_token(s); + if (lookup[i].context == addr) + { + return &lookup[i]; + } } + return nullptr; + } - int logical = 0; - while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("add") || s->function == t_builtins::find_builtin_address("sub") || - s->function == t_builtins::find_builtin_address("logical_not"))) + static const variable* find_bind_or_any_by_addr(const void* addr, const variable* lookup, int lookup_len) + { + auto res = t_vector_builtins::find_any_by_addr(addr); + if (!res) { - if (s->function == t_builtins::find_builtin_address("logical_not")) + res = find_bind_by_addr(addr, lookup, lookup_len); + if (!res) { - if (logical == 0) - { - logical = -1; - } - else - { - logical = -logical; - } + // maybe this is a closure? + res = find_closure_by_addr(addr, lookup, lookup_len); } - next_token(s); + } + return res; + } + }; + + template + struct portable + { + using t_traits = T_TRAITS; + using t_atom = typename T_TRAITS::t_atom; + using t_vector = typename T_TRAITS::t_vector; + using expr_native = typename native::expr_native; + + using name_map = std::unordered_map; + + using index_map = std::unordered_map; + + struct expr_portable_expression_build_indexer + { + name_map name_map; + index_map index_map; + int index_counter = 0; + + int add_referenced_variable(const variable* var) + { + int idx = index_counter++; + name_map.insert(std::make_pair(var->address, std::string(var->name))); + index_map.insert(std::make_pair(var->address, idx)); + return idx; } - expr_native* ret; - - if (sign == 1) + std::vector get_binding_table() { - if (logical == 0) - { - ret = base(s); - } - else if (logical == -1) - { - ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); - ret->function = t_builtins::find_builtin_address("logical_not"); - } - else + std::vector t; + t.resize(index_counter); + for (auto index_map_itor : index_map) { - ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); - ret->function = t_builtins::find_builtin_address("logical_notnot"); + auto name_map_itor = name_map.find(index_map_itor.first); + t[index_map_itor.second] = name_map_itor->second; } + + return t; } - else + + std::vector get_address_table() { - if (logical == 0) - { - ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); - ret->function = t_builtins::find_builtin_address("negate"); - } - else if (logical == -1) - { - ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); - ret->function = t_builtins::find_builtin_address("negate_logical_not"); - } - else + std::vector t; + t.resize(index_counter); + for (auto index_map_itor : index_map) { - ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); - ret->function = t_builtins::find_builtin_address("negate_logical_notnot"); + t[index_map_itor.second] = index_map_itor.first; } + + return t; } + }; - return ret; - } + struct expr_portable_expression_build_bindings + { + std::vector index_to_address; // this contains the native function/value address as originally compiled + std::vector index_to_name; + std::vector index_to_name_c_str; + }; -#ifdef TP_POW_FROM_RIGHT - static expr_native* factor(state* s) + struct compiled_expr : ::tp::compiled_expr { - /* = {"^" } */ - expr_native* ret = power(s); + expr_portable_expression_build_bindings m_bindings; + std::unique_ptr m_build_buffer; + size_t m_build_buffer_size; - const void* left_function = NUL; - expr_native* insertion = 0; + virtual size_t get_binding_array_size() const + { + return m_bindings.index_to_address.size(); + } - if (ret->type == (FUNCTION1 | FLAG_PURE) && - (ret->function == t_builtins::find_builtin_address("negate") || ret->function == t_builtins::find_builtin_address("logical_not") || - ret->function == t_builtins::find_builtin_address("logical_notnot") || ret->function == t_builtins::find_builtin_address("negate_logical_not") || - ret->function == t_builtins::find_builtin_address("negate_logical_notnot"))) + virtual const void* const* get_binding_addresses() const { - left_function = ret->function; - expr_native* se = ret->parameters[0]; - free(ret); - ret = se; + return (m_bindings.index_to_address.size() > 0) ? &(*m_bindings.index_to_address.cbegin()) : nullptr; } - while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("pow"))) + virtual const char* const* get_binding_names() const { - te_fun2 t = s->function; - next_token(s); + return &(*m_bindings.index_to_name_c_str.cbegin()); + } - if (insertion) - { - /* Make exponentiation go right-to-left. */ - expr_native* insert = NEW_EXPR(FUNCTION2 | FLAG_PURE, insertion->parameters[1], power(s)); - insert->function = t; - insertion->parameters[1] = insert; - insertion = insert; - } - else - { - ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, power(s)); - ret->function = t; - insertion = ret; - } + virtual size_t get_data_size() const + { + return m_build_buffer_size; } - if (left_function) + virtual const unsigned char* get_data() const { - ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, ret); - ret->function = left_function; + return m_build_buffer.get(); } + }; - return ret; - } -#else - static expr_native* factor(state* s) + static size_t + export_estimate(const expr_native* n, size_t& export_size, const variable* lookup, int lookup_len, name_map& name_map, index_map& index_map, int& index_counter) { - /* = {"^" } */ - expr_native* ret = power(s); + if (!n) + return export_size; - while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("pow"))) - { - te_fun2 t = (te_fun2)s->function; - next_token(s); - ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, power(s)); - ret->function = t; - } + export_size += sizeof(expr_native); - return ret; - } -#endif + auto eval_arg = [&](int e) { + export_estimate((const expr_native*)n->parameters[e], export_size, lookup, lookup_len, name_map, index_map, index_counter); + }; - static expr_native* term(state* s) - { - /* = {("*" | "/" | "%") } */ - expr_native* ret = factor(s); + auto handle_addr = [&](const variable* var) -> bool { + if (var) + { + auto itor = name_map.find(var->address); + if (itor == name_map.end()) + { + name_map.emplace(std::make_pair(var->address, std::string(var->name))); + index_map.insert(std::make_pair(var->address, index_counter++)); + } - while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("mul") || s->function == t_builtins::find_builtin_address("divide") || - s->function == t_builtins::find_builtin_address("fmod"))) - { - te_fun2 t = (te_fun2)s->function; - next_token(s); - ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, factor(s)); - ret->function = t; - } + if (var->type >= CLOSURE0 && var->type < CLOSURE_MAX) + { + auto itor = name_map.find(var->context); + if (itor == name_map.end()) + { + name_map.emplace(std::make_pair(var->context, std::string(var->name) + "_closure")); + index_map.insert(std::make_pair(var->context, index_counter++)); + } + } - return ret; - } + return true; + } + return false; + }; - static expr_native* sum_expr(state* s) - { - /* = {("+" | "-") } */ - expr_native* ret = term(s); + return eval_details::eval_generic( + n->type, [&]() { return export_size; }, + [&]() { + auto res = handle_addr(native::find_bind_by_addr(n->bound, lookup, lookup_len)); + assert(res); + return export_size; + }, + [&](int a) { + auto res = handle_addr(native::find_bind_or_any_by_addr(n->function, lookup, lookup_len)); + assert(res); + export_size += sizeof(n->parameters[0]) * a; - while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("add") || s->function == t_builtins::find_builtin_address("sub"))) - { - te_fun2 t = (te_fun2)s->function; - next_token(s); - ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, term(s)); - ret->function = t; - } + for (int i = 0; i < a; ++i) + { + eval_arg(i); + } + return export_size; + }, + [&](int a) { + auto res = handle_addr(native::find_bind_or_any_by_addr(n->function, lookup, lookup_len)); + assert(res); + export_size += sizeof(n->parameters[0]) * a; - return ret; + for (int i = 0; i < a; ++i) + { + eval_arg(i); + } + return export_size; + }, + [&]() { return export_size; }); } - static expr_native* test_expr(state* s) + template + static size_t + export_write(const expr_native* n, size_t& export_size, const variable* lookup, int lookup_len, const unsigned char* out_buffer, T_REGISTER_FUNC register_func) { - /* = {(">" | ">=" | "<" | "<=" | "==" | "!=") } */ - expr_native* ret = sum_expr(s); + if (!n) + return export_size; - while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("greater") || s->function == t_builtins::find_builtin_address("greater_eq") || - s->function == t_builtins::find_builtin_address("lower") || s->function == t_builtins::find_builtin_address("lower_eq") || - s->function == t_builtins::find_builtin_address("equal") || s->function == t_builtins::find_builtin_address("not_equal"))) - { - te_fun2 t = (te_fun2)s->function; - next_token(s); - ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, sum_expr(s)); - ret->function = t; - } + auto n_out = (expr_portable*)(out_buffer + export_size); - return ret; - } + export_size += sizeof(expr_native); + n_out->type = n->type; - static expr_native* expr(state* s) - { - /* = {("&&" | "||") } */ - expr_native* ret = test_expr(s); + auto eval_arg = [&](int e) { + return export_write((const expr_native*)n->parameters[e], export_size, lookup, lookup_len, out_buffer, register_func); + }; - while (s->type == (int)TOK::INFIX && (s->function == t_builtins::find_builtin_address("logical_and") || s->function == t_builtins::find_builtin_address("logical_or"))) - { - te_fun2 t = (te_fun2)s->function; - next_token(s); - ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, test_expr(s)); - ret->function = t; - } + return eval_details::eval_generic( + n->type, + [&]() { + n_out->value = n->value; + return export_size; + }, + [&]() { + register_func(n->bound, n_out, native::find_bind_by_addr(n->bound, lookup, lookup_len)); + return export_size; + }, + [&](int a) { + register_func(n->function, n_out, native::find_bind_or_any_by_addr(n->function, lookup, lookup_len)); - return ret; - } + export_size += sizeof(n->parameters[0]) * eval_details::arity(n->type); - static expr_native* list(state* s) - { - /* = {"," } */ - expr_native* ret = expr(s); + for (int i = 0; i < eval_details::arity(n->type); ++i) + { + n_out->parameters[i] = export_size; + eval_arg(i); + } + return export_size; + }, + [&](int a) { + register_func(n->function, n_out, native::find_bind_or_any_by_addr(n->function, lookup, lookup_len)); - while (s->type == (int)TOK::SEP) - { - next_token(s); - ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, expr(s)); - ret->function = t_builtins::find_builtin_address("comma"); - } + export_size += sizeof(n->parameters[0]) * eval_details::arity(n->type); - return ret; + for (int i = 0; i < eval_details::arity(n->type); ++i) + { + n_out->parameters[i] = export_size; + eval_arg(i); + } + return export_size; + }, + [&]() { return export_size; }); } - static t_vector eval_native(const expr_native* n) + static t_vector eval_compare(const expr_native* n, const expr_portable* n_portable, const unsigned char* expr_buffer, const void* const expr_context[]) { if (!n) - return t_builtins::nan(); + return t_traits::nan(); + + assert(n->type == n_portable->type); auto eval_arg = [&](int e) { - return eval_native((const expr_native*)n->parameters[e]); + return eval_compare((const expr_native*)n->parameters[e], (const expr_portable*)&expr_buffer[n_portable->parameters[e]], expr_buffer, expr_context); }; return eval_details::eval_generic( - n->type, [&]() { return n->value; }, [&]() { return *n->bound; }, - [&](int a) { return eval_details::eval_function(a, n->function, t_builtins::nan(), eval_arg); }, - [&](int a) { return eval_details::eval_closure(a, n->function, (void*)n->parameters[a], t_builtins::nan(), eval_arg); }, - [&]() { return t_builtins::nan(); }); - } - - static void optimize(expr_native* n) - { - /* Evaluates as much as possible. */ - if (n->type == CONSTANT) - return; - if (n->type == VARIABLE) - return; + n->type, [&]() { return n_portable->value; }, + [&]() { + assert(n->bound == expr_context[n_portable->bound]); + return t_traits::load_atom(*((const t_atom*)(expr_context[n_portable->bound]))); + }, + [&](int a) { + assert(n->function == expr_context[n_portable->function]); + return eval_details::eval_function(a, expr_context[n_portable->function], t_traits::nan(), eval_arg); + }, + [&](int a) { + assert(n->function == expr_context[n_portable->function]); + assert(n->parameters[arity(n->type)] == expr_context[n_portable->parameters[arity(n->type)]]); - /* Only optimize out functions flagged as pure. */ - if (is_pure(n->type)) - { - const int arity = eval_details::arity(n->type); - int known = 1; - int i; - for (i = 0; i < arity; ++i) - { - optimize((expr_native*)n->parameters[i]); - if (((expr_native*)(n->parameters[i]))->type != CONSTANT) - { - known = 0; - } - } - if (known) - { - const t_vector value = eval_native(n); - free_parameters(n); - n->type = CONSTANT; - n->value = value; - } - } + return eval_details::eval_closure( + a, expr_context[n_portable->function], (void*)expr_context[n_portable->parameters[arity(n->type)]], t_traits::nan(), eval_arg); + }, + [&]() { return t_traits::nan(); }); } - static expr_native* compile_native(const char* expression, const variable* variables, int var_count, int* error) + struct portable_compiled_program : compiled_program { - state s; - s.start = s.next = expression; - s.lookup = variables; - s.lookup_len = var_count; - - next_token(&s); - expr_native* root = list(&s); + std::vector program_statements; + std::vector binding_table; + std::vector binding_table_cstr; + std::vector address_table; + std::unique_ptr program_expression_buffer; + size_t program_expression_buffer_size = 0; - if (s.type != (int)TOK::END) + virtual size_t get_binding_array_size() const { - free_native(root); - if (error) - { - *error = static_cast(s.next - s.start); - if (*error == 0) - *error = 1; - } - return 0; + return address_table.size(); } - else - { - optimize(root); - if (error) - *error = 0; - return root; + virtual const void* const* get_binding_addresses() const + { + return &address_table[0]; } - } - static t_vector interp_native(const char* expression, int* error) - { - expr_native* n = compile_native(expression, 0, 0, error); - t_vector ret; - if (n) + virtual const char* const* get_binding_names() const { - ret = eval_native(n); - free_native(n); + return &binding_table_cstr[0]; } - else + + virtual size_t get_data_size() const { - ret = t_traits::nan(); + return program_expression_buffer_size; } - return ret; - } - - static void pn(const expr_native* n, int depth) - { - int i, arity; - printf("%*s", depth, ""); - - const auto t = type_mask(n->type); - if (t == CONSTANT) + virtual const unsigned char* get_data() const { - printf("%f\n", n->value); + return program_expression_buffer.get(); } - else if (t == VARIABLE) + + virtual size_t get_statement_array_size() const { - printf("bound %p\n", n->bound); + return (int)program_statements.size(); } - else if ((t >= FUNCTION0 && t < FUNCTION_MAX) || (t >= CLOSURE0 && t < CLOSURE_MAX)) + + virtual const statement* get_statements() const { - arity = arity(n->type); - printf("f%d", arity); - for (i = 0; i < arity; i++) - { - printf(" %p", n->parameters[i]); - } - printf("\n"); - for (i = 0; i < arity; i++) - { - pn((const expr_native*)n->parameters[i], depth + 1); - } + return &program_statements[0]; } - } + }; + }; - static void print(const expr_native* n) + namespace expr_details + { + template + compiled_expr* compile_using_indexer( + typename portable::expr_portable_expression_build_indexer& indexer, const char* expression, const variable* variables, int var_count, int* error) { - pn(n, 0); - } - - //// + typename native::expr_native* native_expr = native::compile_native(expression, variables, var_count, error); - static const variable* find_bind_by_addr(const void* addr, const variable* lookup, int lookup_len) - { - for (int i = 0; i < lookup_len; ++i) + if (native_expr) { - if (lookup[i].address == addr) - { - return &lookup[i]; - } - } - return nullptr; - } + auto expr = new typename portable::compiled_expr; - static const variable* find_closure_by_addr(const void* addr, const variable* lookup, int lookup_len) - { - for (int i = 0; i < lookup_len; ++i) - { - if (lookup[i].context == addr) + size_t export_size = 0; + portable::export_estimate(native_expr, export_size, variables, var_count, indexer.name_map, indexer.index_map, indexer.index_counter); + + expr->m_bindings.index_to_address.resize(indexer.index_counter); + for (const auto& itor : indexer.index_map) { - return &lookup[i]; + expr->m_bindings.index_to_address[itor.second] = itor.first; } - } - return nullptr; - } - static const variable* find_bind_or_any_by_addr(const void* addr, const variable* lookup, int lookup_len) - { - auto res = t_builtins::find_any_by_addr(addr); - if (!res) - { - res = find_bind_by_addr(addr, lookup, lookup_len); - if (!res) + expr->m_bindings.index_to_name.resize(indexer.index_counter); + expr->m_bindings.index_to_name_c_str.resize(indexer.index_counter); + for (int i = 0; i < indexer.index_counter; ++i) { - // maybe this is a closure? - res = find_closure_by_addr(addr, lookup, lookup_len); + auto itor = indexer.name_map.find(expr->m_bindings.index_to_address[i]); + assert(itor != indexer.name_map.end()); + expr->m_bindings.index_to_name[i] = itor->second; + expr->m_bindings.index_to_name_c_str[i] = expr->m_bindings.index_to_name[i].c_str(); } - } - return res; - } - }; - - template - struct portable - { - using t_traits = T_TRAITS; - using t_atom = typename T_TRAITS::t_atom; - using t_vector = typename T_TRAITS::t_vector; - using expr_native = typename native::expr_native; - using name_map = std::unordered_map; + expr->m_build_buffer.reset(new uint8_t[export_size]); + ::memset(expr->m_build_buffer.get(), 0x0, export_size); + expr->m_build_buffer_size = export_size; - using index_map = std::unordered_map; + size_t actual_export_size = 0; + portable::export_write( + native_expr, actual_export_size, variables, var_count, expr->m_build_buffer.get(), + [&](const void* addr, expr_portable* out, const variable* v) -> void { + assert(v != nullptr); + auto itor = indexer.index_map.find(addr); + assert(itor != indexer.index_map.end()); + out->function = itor->second; - struct expr_portable_expression_build_indexer - { - name_map name_map; - index_map index_map; - int index_counter = 0; + if (v->type >= CLOSURE0 && v->type < CLOSURE_MAX) + { + auto itor2 = indexer.index_map.find(v->context); + assert(itor2 != indexer.index_map.end()); + out->parameters[eval_details::arity(v->type)] = itor2->second; + } + }); - int add_referenced_variable(const variable* var) - { - int idx = index_counter++; - name_map.insert(std::make_pair(var->address, std::string(var->name))); - index_map.insert(std::make_pair(var->address, idx)); - return idx; + native::free_native(native_expr); + return expr; } + return nullptr; + } - std::vector get_binding_table() + template + compiled_expr* compile(const char* expression, const variable* variables, int var_count, int* error) + { + typename portable::expr_portable_expression_build_indexer indexer; + return compile_using_indexer(indexer, expression, variables, var_count, error); + } + + } // namespace expr_details + + namespace program_details + { + struct parser + { + static inline std::string_view trim_leading_space(std::string_view v) noexcept { - std::vector t; - t.resize(index_counter); - for (auto index_map_itor : index_map) + if (v.length() > 0) { - auto name_map_itor = name_map.find(index_map_itor.first); - t[index_map_itor.second] = name_map_itor->second; - } + for (size_t i = 0; i < v.length(); ++i) + { + if (!::isspace(v[i])) + { + return std::string_view(&v[i], v.length() - i); + } + } - return t; + return std::string_view(&v[v.length()], 0); + } + return v; } - std::vector get_address_table() + static inline std::string_view trim_trailing_space(std::string_view v) noexcept { - std::vector t; - t.resize(index_counter); - for (auto index_map_itor : index_map) + if (v.length() > 0) { - t[index_map_itor.second] = index_map_itor.first; - } - - return t; - } - }; + for (size_t i = v.length(); i > 0; --i) + { + if (!::isspace(v[i - 1])) + { + return std::string_view(&v[0], i); + } + } - struct expr_portable_expression_build_bindings - { - std::vector index_to_address; // this contains the native function/value address as originally compiled - std::vector index_to_name; - std::vector index_to_name_c_str; - }; + if (!::isspace(v[0])) + { + std::string_view(&v[0], 1); + } - struct compiled_expr : ::tp::compiled_expr - { - expr_portable_expression_build_bindings m_bindings; - std::unique_ptr m_build_buffer; - size_t m_build_buffer_size; + return std::string_view(&v[0], 0); + } + return v; + } - virtual size_t get_binding_array_size() const + static inline std::string_view trim_all_space(std::string_view v) noexcept { - return m_bindings.index_to_address.size(); + return trim_leading_space(trim_trailing_space(v)); } - virtual const void* const* get_binding_addresses() const + static inline std::tuple split_at_index(std::string_view s, size_t index) { - return (m_bindings.index_to_address.size() > 0) ? &(*m_bindings.index_to_address.cbegin()) : nullptr; + if (index < s.length()) + { + return {trim_all_space(std::string_view{&s[0], index}), trim_all_space(std::string_view{&s[index], s.length() - index})}; + } + return {s, std::string_view{}}; } - virtual const char* const* get_binding_names() const + static inline std::tuple split_at_index_excl(std::string_view s, size_t index) { - return &(*m_bindings.index_to_name_c_str.cbegin()); + auto [l, r] = split_at_index(s, index); + if (r.length() > 1) + { + return {trim_all_space(l), trim_all_space(std::string_view{&r[1], r.length() - 1})}; + } + return {l, std::string_view{}}; } - virtual size_t get_data_size() const + static inline std::tuple split_at_char(std::string_view program, char c) { - return m_build_buffer_size; + for (size_t i = 0; i < program.length(); ++i) + { + if (program[i] == c) + { + return split_at_index(program, i); + } + } + return {program, std::string_view{}}; } - virtual const unsigned char* get_data() const + static inline std::tuple split_at_char_excl(std::string_view program, char c) { - return m_build_buffer.get(); + for (size_t i = 0; i < program.length(); ++i) + { + if (program[i] == c) + { + return split_at_index_excl(program, i); + } + } + return {program, std::string_view{}}; } - }; - static size_t - export_estimate(const expr_native* n, size_t& export_size, const variable* lookup, int lookup_len, name_map& name_map, index_map& index_map, int& index_counter) - { - if (!n) - return export_size; + //// - export_size += sizeof(expr_native); + static inline const auto keyword_return = std::string_view("return"); + static inline const auto keyword_jump = std::string_view("jump"); + static inline const auto keyword_label = std::string_view("label"); - auto eval_arg = [&](int e) { - export_estimate((const expr_native*)n->parameters[e], export_size, lookup, lookup_len, name_map, index_map, index_counter); - }; + template + static inline void parse_statement( + std::string_view statement, T_ADD_LABEL add_label, T_ADD_JUMP add_jump, T_ADD_JUMP_IF add_jump_if, T_ADD_RETURN_VALUE add_return_value, T_ADD_ASSIGN add_assign, + T_ADD_CALL add_call) + { + auto [operation, expression] = split_at_char_excl(statement, ':'); - auto handle_addr = [&](const variable* var) -> bool { - if (var) + if (expression.length() == 0) { - auto itor = name_map.find(var->address); - if (itor == name_map.end()) + add_call(operation); + } + else + { + if (operation == keyword_label) { - name_map.emplace(std::make_pair(var->address, std::string(var->name))); - index_map.insert(std::make_pair(var->address, index_counter++)); + add_label(expression); } - - if (var->type >= CLOSURE0 && var->type < CLOSURE_MAX) + else if (operation == keyword_jump) { - auto itor = name_map.find(var->context); - if (itor == name_map.end()) + auto [jump_label, jump_condition] = split_at_char_excl(expression, '?'); + + if (jump_condition.length() > 0) { - name_map.emplace(std::make_pair(var->context, std::string(var->name) + "_closure")); - index_map.insert(std::make_pair(var->context, index_counter++)); + add_jump_if(jump_label, jump_condition); + } + else + { + add_jump(jump_label); } } - - return true; - } - return false; - }; - - return eval_details::eval_generic( - n->type, [&]() { return export_size; }, - [&]() { - auto res = handle_addr(native::find_bind_by_addr(n->bound, lookup, lookup_len)); - assert(res); - return export_size; - }, - [&](int a) { - auto res = handle_addr(native::find_bind_or_any_by_addr(n->function, lookup, lookup_len)); - assert(res); - export_size += sizeof(n->parameters[0]) * a; - - for (int i = 0; i < a; ++i) - { - eval_arg(i); - } - return export_size; - }, - [&](int a) { - auto res = handle_addr(native::find_bind_or_any_by_addr(n->function, lookup, lookup_len)); - assert(res); - export_size += sizeof(n->parameters[0]) * a; - - for (int i = 0; i < a; ++i) - { - eval_arg(i); - } - return export_size; - }, - [&]() { return export_size; }); - } - - template - static size_t - export_write(const expr_native* n, size_t& export_size, const variable* lookup, int lookup_len, const unsigned char* out_buffer, T_REGISTER_FUNC register_func) - { - if (!n) - return export_size; - - auto n_out = (expr_portable*)(out_buffer + export_size); - - export_size += sizeof(expr_native); - n_out->type = n->type; - - auto eval_arg = [&](int e) { - return export_write((const expr_native*)n->parameters[e], export_size, lookup, lookup_len, out_buffer, register_func); - }; - - return eval_details::eval_generic( - n->type, - [&]() { - n_out->value = n->value; - return export_size; - }, - [&]() { - register_func(n->bound, n_out, native::find_bind_by_addr(n->bound, lookup, lookup_len)); - return export_size; - }, - [&](int a) { - register_func(n->function, n_out, native::find_bind_or_any_by_addr(n->function, lookup, lookup_len)); - - export_size += sizeof(n->parameters[0]) * eval_details::arity(n->type); - - for (int i = 0; i < eval_details::arity(n->type); ++i) + else if (operation == keyword_return) { - n_out->parameters[i] = export_size; - eval_arg(i); + add_return_value(expression); } - return export_size; - }, - [&](int a) { - register_func(n->function, n_out, native::find_bind_or_any_by_addr(n->function, lookup, lookup_len)); - - export_size += sizeof(n->parameters[0]) * eval_details::arity(n->type); - - for (int i = 0; i < eval_details::arity(n->type); ++i) + else { - n_out->parameters[i] = export_size; - eval_arg(i); + add_assign(operation, expression); } - return export_size; - }, - [&]() { return export_size; }); - } + } + } + }; - static t_vector eval_compare(const expr_native* n, const expr_portable* n_portable, const unsigned char* expr_buffer, const void* const expr_context[]) + struct label_manager { - if (!n) - return t_traits::nan(); - - assert(n->type == n_portable->type); - - auto eval_arg = [&](int e) { - return eval_compare((const expr_native*)n->parameters[e], (const expr_portable*)&expr_buffer[n_portable->parameters[e]], expr_buffer, expr_context); - }; + using handle = int; + static inline constexpr int placeholder_index = -1; - return eval_details::eval_generic( - n->type, [&]() { return n_portable->value; }, - [&]() { - assert(n->bound == expr_context[n_portable->bound]); - return t_traits::load_atom(*((const t_atom*)(expr_context[n_portable->bound]))); - }, - [&](int a) { - assert(n->function == expr_context[n_portable->function]); - return eval_details::eval_function(a, expr_context[n_portable->function], t_traits::nan(), eval_arg); - }, - [&](int a) { - assert(n->function == expr_context[n_portable->function]); - assert(n->parameters[arity(n->type)] == expr_context[n_portable->parameters[arity(n->type)]]); + std::vector m_label_statement_indexes; + std::unordered_map m_label_handle_map; - return eval_details::eval_closure( - a, expr_context[n_portable->function], (void*)expr_context[n_portable->parameters[arity(n->type)]], t_traits::nan(), eval_arg); - }, - [&]() { return t_traits::nan(); }); - } + handle add_label(std::string_view label, int statement_index) + { + auto itor = m_label_handle_map.find(label); + if (itor != m_label_handle_map.end()) + { + handle label_handle = (*itor).second; - struct portable_compiled_program : compiled_program - { - std::vector program_statements; - std::vector binding_table; - std::vector binding_table_cstr; - std::vector address_table; - std::unique_ptr program_expression_buffer; - size_t program_expression_buffer_size = 0; + // Placeholder was filled, fill in the program counter + if (m_label_statement_indexes[label_handle] != placeholder_index) + { + // error, label repeated? + } + else + { + // The target of the jump label is the next statement of program + m_label_statement_indexes[label_handle] = statement_index; + } - virtual size_t get_binding_array_size() const - { - return address_table.size(); + return label_handle; + } + else + { + // The target of the jump label is the next statement of program + auto label_handle = (handle)m_label_statement_indexes.size(); + m_label_handle_map.insert(std::make_pair(label, label_handle)); + m_label_statement_indexes.push_back(statement_index); + return label_handle; + } } - virtual const void* const* get_binding_addresses() const + handle find_label(std::string_view label) { - return &address_table[0]; + auto itor = m_label_handle_map.find(label); + if (itor != m_label_handle_map.end()) + { + // label exists, use its index + return (handle)(*itor).second; + } + else + { + // add a temp label, which will be replaced then when the actual label is encountered + auto new_handle = (handle)m_label_statement_indexes.size(); + m_label_handle_map.insert(std::make_pair(label, new_handle)); + m_label_statement_indexes.push_back(placeholder_index); + return new_handle; + } } - virtual const char* const* get_binding_names() const + int get_label_statement_index(handle label) { - return &binding_table_cstr[0]; + return m_label_statement_indexes[label]; } + }; - virtual size_t get_data_size() const - { - return program_expression_buffer_size; - } + struct variable_manager + { + int m_variable_count = 0; + std::unordered_map m_variable_map; - virtual const unsigned char* get_data() const + int find_label(std::string_view name) { - return program_expression_buffer.get(); + auto idx = m_variable_count++; + m_variable_map.insert(std::make_pair(name, idx)); + return idx; } + }; - virtual size_t get_statement_array_size() const - { - return (int)program_statements.size(); - } + struct expression_manager + { + std::vector m_expressions; - virtual const statement* get_statements() const + int add_expression(std::string_view src) { - return &program_statements[0]; + auto idx = (int)m_expressions.size(); + m_expressions.push_back(src); + return idx; } }; - }; - namespace expr_details - { + struct jump_statement + { + label_manager::handle m_target_handle; // Pass 1: index isn't known isn't known until whole program is parsed + int m_expression_index; + + int m_target_index; // Pass 2: set from handle to index (of statement immediately following the label) + int m_expression_offset; + }; + + struct return_value_statement + { + int m_expression_index; + + int m_expression_offset; + }; + + struct assign_statement + { + int m_variable_index; + int m_expression_index; + + int m_expression_offset; + }; + + struct call_statement + { + int m_expression_index; + + int m_expression_offset; + }; + + using any_statement = std::variant; + template - compiled_expr* compile_using_indexer( - typename portable::expr_portable_expression_build_indexer& indexer, const char* expression, const variable* variables, int var_count, int* error) + auto compile(const char* text, const variable* variables, int var_count, int* error) -> typename portable::portable_compiled_program* { - typename native::expr_native* native_expr = native::compile_native(expression, variables, var_count, error); + auto program_src = parser::trim_all_space(std::string_view{text, strlen(text)}); + auto program_remaining = program_src; - if (native_expr) + std::vector program_statements; + using program_impl = typename portable::portable_compiled_program; + auto program = new program_impl(); + label_manager lm; + variable_manager vm; + expression_manager em; + + while (program_remaining.length() > 0) { - auto expr = new typename portable::compiled_expr; + auto [statement, remaining] = parser::split_at_char_excl(program_remaining, ';'); - size_t export_size = 0; - portable::export_estimate(native_expr, export_size, variables, var_count, indexer.name_map, indexer.index_map, indexer.index_counter); + parser::parse_statement( + statement, - expr->m_bindings.index_to_address.resize(indexer.index_counter); - for (const auto& itor : indexer.index_map) - { - expr->m_bindings.index_to_address[itor.second] = itor.first; - } + // label + [&](std::string_view label) { lm.add_label(label, (int)program_statements.size()); }, - expr->m_bindings.index_to_name.resize(indexer.index_counter); - expr->m_bindings.index_to_name_c_str.resize(indexer.index_counter); - for (int i = 0; i < indexer.index_counter; ++i) - { - auto itor = indexer.name_map.find(expr->m_bindings.index_to_address[i]); - assert(itor != indexer.name_map.end()); - expr->m_bindings.index_to_name[i] = itor->second; - expr->m_bindings.index_to_name_c_str[i] = expr->m_bindings.index_to_name[i].c_str(); - } + // jump + [&](std::string_view destination_label) { + any_statement s = jump_statement{lm.find_label(destination_label), -1}; + program_statements.push_back(s); + }, - expr->m_build_buffer.reset(new uint8_t[export_size]); - ::memset(expr->m_build_buffer.get(), 0x0, export_size); - expr->m_build_buffer_size = export_size; + // jump_if + [&](std::string_view destination_label, std::string_view condition) { + any_statement s = jump_statement{lm.find_label(destination_label), em.add_expression(condition)}; + program_statements.push_back(s); + }, - size_t actual_export_size = 0; - portable::export_write( - native_expr, actual_export_size, variables, var_count, expr->m_build_buffer.get(), - [&](const void* addr, expr_portable* out, const variable* v) -> void { - assert(v != nullptr); - auto itor = indexer.index_map.find(addr); - assert(itor != indexer.index_map.end()); - out->function = itor->second; + // return_value + [&](std::string_view expression) { + any_statement s = return_value_statement{em.add_expression(expression)}; + program_statements.push_back(s); + }, - if (v->type >= CLOSURE0 && v->type < CLOSURE_MAX) - { - auto itor2 = indexer.index_map.find(v->context); - assert(itor2 != indexer.index_map.end()); - out->parameters[eval_details::arity(v->type)] = itor2->second; - } + // assign + [&](std::string_view destination, std::string_view expression) { + any_statement s = assign_statement{vm.find_label(destination), em.add_expression(expression)}; + program_statements.push_back(s); + }, + + // call + [&](std::string_view expression) { + any_statement s = call_statement{em.add_expression(expression)}; + program_statements.push_back(s); }); - native::free_native(native_expr); - return expr; - } - return nullptr; - } + program_remaining = remaining; + } + + // Fixup jump indices + for (auto& s : program_statements) + { + if (std::holds_alternative(s)) + { + std::get(s).m_target_index = lm.get_label_statement_index(std::get(s).m_target_handle); + } + } + + // Add referenced variables to the lookup dict + typename portable::expr_portable_expression_build_indexer indexer; + for (auto itor : vm.m_variable_map) + { + auto name = itor.first; + auto index = itor.second; + + int final_index = -1; + + for (int var_idx = 0; var_idx < var_count; ++var_idx) + { + const auto& var = variables[var_idx]; - template - compiled_expr* compile(const char* expression, const variable* variables, int var_count, int* error) - { - typename portable::expr_portable_expression_build_indexer indexer; - return compile_using_indexer(indexer, expression, variables, var_count, error); - } + if (name == std::string_view(var.name)) + { + final_index = indexer.add_referenced_variable(&var); + break; + } + } - } // namespace expr_details + if (final_index == -1) + { + *error = -1; // TODO: variable not found + return nullptr; + } - namespace program_details - { - struct parser - { - static inline std::string_view trim_leading_space(std::string_view v) noexcept - { - if (v.length() > 0) + // Remap all indexes to the indexer value + for (auto s : program_statements) { - for (size_t i = 0; i < v.length(); ++i) + if (std::holds_alternative(s)) { - if (!::isspace(v[i])) + if (std::get(s).m_variable_index == index) { - return std::string_view(&v[i], v.length() - i); + std::get(s).m_variable_index = final_index; } } - - return std::string_view(&v[v.length()], 0); } - return v; } - static inline std::string_view trim_trailing_space(std::string_view v) noexcept + // Compile all the expressions, redirect the statement expression indexes to the compiled buffer offset + for (int expr_idx = 0; expr_idx < em.m_expressions.size(); ++expr_idx) { - if (v.length() > 0) + auto expr = em.m_expressions[expr_idx]; + auto compiled_expr = expr_details::compile_using_indexer(indexer, expr.data(), variables, var_count, error); + + if (compiled_expr) { - for (size_t i = v.length(); i > 0; --i) + const int current_expr_offset = (int)program->program_expression_buffer_size; + auto compiled_size = (int)compiled_expr->get_data_size(); + const int new_expr_offset = (int)program->program_expression_buffer_size + compiled_size; + + auto new_expr_buffer = new unsigned char[new_expr_offset]; + ::memcpy(new_expr_buffer, program->program_expression_buffer.get(), program->program_expression_buffer_size); + ::memcpy(new_expr_buffer + program->program_expression_buffer_size, compiled_expr->get_data(), compiled_size); + + program->program_expression_buffer_size = new_expr_offset; + + program->program_expression_buffer.reset(new_expr_buffer); + + // Fixup any expression indexes + for (auto& s : program_statements) { - if (!::isspace(v[i - 1])) + if (std::holds_alternative(s)) { - return std::string_view(&v[0], i); + if (std::get(s).m_expression_index == expr_idx) + { + std::get(s).m_expression_offset = current_expr_offset; + } } - } - if (!::isspace(v[0])) - { - std::string_view(&v[0], 1); + if (std::holds_alternative(s)) + { + if (std::get(s).m_expression_index == expr_idx) + { + std::get(s).m_expression_offset = current_expr_offset; + } + } + + if (std::holds_alternative(s)) + { + if (std::get(s).m_expression_index == expr_idx) + { + std::get(s).m_expression_offset = current_expr_offset; + } + } + + if (std::holds_alternative(s)) + { + if (std::get(s).m_expression_index == expr_idx) + { + std::get(s).m_expression_offset = current_expr_offset; + } + } } + } + else + { + *error = -1; // TODO: handle error + break; + } + } - return std::string_view(&v[0], 0); + for (auto s_in : program_statements) + { + statement s_out; + + if (std::holds_alternative(s_in)) + { + s_out.type = statement_type::call; + s_out.arg_a = std::get(s_in).m_expression_offset; + s_out.arg_b = -1; } - return v; + else if (std::holds_alternative(s_in)) + { + s_out.type = statement_type::assign; + s_out.arg_a = std::get(s_in).m_variable_index; + s_out.arg_b = std::get(s_in).m_expression_offset; + } + else if (std::holds_alternative(s_in)) + { + s_out.type = statement_type::return_value; + s_out.arg_a = std::get(s_in).m_expression_offset; + s_out.arg_b = -1; + } + else if (std::holds_alternative(s_in)) + { + s_out.type = statement_type::jump; + s_out.arg_a = std::get(s_in).m_target_index; + s_out.arg_b = std::get(s_in).m_expression_offset; + } + + program->program_statements.push_back(s_out); } - static inline std::string_view trim_all_space(std::string_view v) noexcept + program->binding_table = indexer.get_binding_table(); + for (const auto& n : program->binding_table) { - return trim_leading_space(trim_trailing_space(v)); + program->binding_table_cstr.push_back(n.c_str()); } - static inline std::tuple split_at_index(std::string_view s, size_t index) + program->address_table = indexer.get_address_table(); + + return program; + } + } // namespace program_details +} // namespace tp +#endif // #if (TP_COMPILER_ENABLED) + +#if TP_STANDARD_LIBRARY +namespace tp_stdlib +{ + template + struct native_builtins_impl; + + template<> + struct native_builtins_impl + { + static double pi(void) + { + return 3.14159265358979323846; + } + + static double e(void) + { + return 2.71828182845904523536; + } + + static double fac(double a) + { /* simplest version of fac */ + if (a < 0.0) + return nan(); + if (a > UINT_MAX) + return INFINITY; + unsigned int ua = (unsigned int)(a); + unsigned long int result = 1, i; + for (i = 1; i <= ua; i++) { - if (index < s.length()) - { - return {trim_all_space(std::string_view{&s[0], index}), trim_all_space(std::string_view{&s[index], s.length() - index})}; - } - return {s, std::string_view{}}; + if (i > ULONG_MAX / result) + return INFINITY; + result *= i; } + return (double)result; + } - static inline std::tuple split_at_index_excl(std::string_view s, size_t index) + static double ncr(double n, double r) + { + if (n < 0.0 || r < 0.0 || n < r) + return nan(); + if (n > UINT_MAX || r > UINT_MAX) + return INFINITY; + unsigned long int un = (unsigned int)(n), ur = (unsigned int)(r), i; + unsigned long int result = 1; + if (ur > un / 2) + ur = un - ur; + for (i = 1; i <= ur; i++) { - auto [l, r] = split_at_index(s, index); - if (r.length() > 1) - { - return {trim_all_space(l), trim_all_space(std::string_view{&r[1], r.length() - 1})}; - } - return {l, std::string_view{}}; + if (result > ULONG_MAX / (un - ur + i)) + return INFINITY; + result *= un - ur + i; + result /= i; } + return result; + } + + static double npr(double n, double r) + { + return ncr(n, r) * fac(r); + } + + static double fabs(double n) + { + return ::fabs(n); + } + + static double acos(double n) + { + return ::acos(n); + } + + static double cosh(double n) + { + return ::cosh(n); + } + + static double cos(double n) + { + return ::cos(n); + } + + static double exp(double n) + { + return ::exp(n); + } + + static double asin(double n) + { + return ::asin(n); + } + + static double sinh(double n) + { + return ::sinh(n); + } + + static double sin(double n) + { + return ::sin(n); + } - static inline std::tuple split_at_char(std::string_view program, char c) - { - for (size_t i = 0; i < program.length(); ++i) - { - if (program[i] == c) - { - return split_at_index(program, i); - } - } - return {program, std::string_view{}}; - } + static double sqrt(double n) + { + return ::sqrt(n); + } - static inline std::tuple split_at_char_excl(std::string_view program, char c) - { - for (size_t i = 0; i < program.length(); ++i) - { - if (program[i] == c) - { - return split_at_index_excl(program, i); - } - } - return {program, std::string_view{}}; - } + static double log(double n) + { + return ::log(n); + } - //// + static double log10(double n) + { + return ::log10(n); + } - static inline const auto keyword_return = std::string_view("return"); - static inline const auto keyword_jump = std::string_view("jump"); - static inline const auto keyword_label = std::string_view("label"); + static double atan(double n) + { + return ::atan(n); + } - template - static inline void parse_statement( - std::string_view statement, T_ADD_LABEL add_label, T_ADD_JUMP add_jump, T_ADD_JUMP_IF add_jump_if, T_ADD_RETURN_VALUE add_return_value, T_ADD_ASSIGN add_assign, - T_ADD_CALL add_call) - { - auto [operation, expression] = split_at_char_excl(statement, ':'); + static double tanh(double n) + { + return ::tanh(n); + } - if (expression.length() == 0) - { - add_call(operation); - } - else - { - if (operation == keyword_label) - { - add_label(expression); - } - else if (operation == keyword_jump) - { - auto [jump_label, jump_condition] = split_at_char_excl(expression, '?'); + static double fmod(double n, double m) + { + return ::fmod(n, m); + } - if (jump_condition.length() > 0) - { - add_jump_if(jump_label, jump_condition); - } - else - { - add_jump(jump_label); - } - } - else if (operation == keyword_return) - { - add_return_value(expression); - } - else - { - add_assign(operation, expression); - } - } - } - }; + static double tan(double n) + { + return ::tan(n); + } - struct label_manager + static double atan2(double n, double m) { - using handle = int; - static inline constexpr int placeholder_index = -1; + return ::atan2(n, m); + } - std::vector m_label_statement_indexes; - std::unordered_map m_label_handle_map; + static double pow(double n, double m) + { + return ::pow(n, m); + } - handle add_label(std::string_view label, int statement_index) - { - auto itor = m_label_handle_map.find(label); - if (itor != m_label_handle_map.end()) - { - handle label_handle = (*itor).second; + static double floor(double d) + { + return ::floor(d); + } - // Placeholder was filled, fill in the program counter - if (m_label_statement_indexes[label_handle] != placeholder_index) - { - // error, label repeated? - } - else - { - // The target of the jump label is the next statement of program - m_label_statement_indexes[label_handle] = statement_index; - } + static double ceil(double d) + { + return ::ceil(d); + } - return label_handle; - } - else - { - // The target of the jump label is the next statement of program - auto label_handle = (handle)m_label_statement_indexes.size(); - m_label_handle_map.insert(std::make_pair(label, label_handle)); - m_label_statement_indexes.push_back(statement_index); - return label_handle; - } - } + static double add(double a, double b) + { + return a + b; + } - handle find_label(std::string_view label) - { - auto itor = m_label_handle_map.find(label); - if (itor != m_label_handle_map.end()) - { - // label exists, use its index - return (handle)(*itor).second; - } - else - { - // add a temp label, which will be replaced then when the actual label is encountered - auto new_handle = (handle)m_label_statement_indexes.size(); - m_label_handle_map.insert(std::make_pair(label, new_handle)); - m_label_statement_indexes.push_back(placeholder_index); - return new_handle; - } - } + static double sub(double a, double b) + { + return a - b; + } - int get_label_statement_index(handle label) - { - return m_label_statement_indexes[label]; - } - }; + static double mul(double a, double b) + { + return a * b; + } - struct variable_manager + static double divide(double a, double b) { - int m_variable_count = 0; - std::unordered_map m_variable_map; + return a / b; + } - int find_label(std::string_view name) - { - auto idx = m_variable_count++; - m_variable_map.insert(std::make_pair(name, idx)); - return idx; - } - }; + static double negate(double a) + { + return -a; + } - struct expression_manager + static double comma(double a, double b) { - std::vector m_expressions; + (void)a; + return b; + } - int add_expression(std::string_view src) - { - auto idx = (int)m_expressions.size(); - m_expressions.push_back(src); - return idx; - } - }; + static double greater(double a, double b) + { + return a > b; + } - struct jump_statement + static double greater_eq(double a, double b) { - label_manager::handle m_target_handle; // Pass 1: index isn't known isn't known until whole program is parsed - int m_expression_index; + return a >= b; + } - int m_target_index; // Pass 2: set from handle to index (of statement immediately following the label) - int m_expression_offset; - }; + static double lower(double a, double b) + { + return a < b; + } - struct return_value_statement + static double lower_eq(double a, double b) { - int m_expression_index; + return a <= b; + } - int m_expression_offset; - }; + static double equal(double a, double b) + { + return a == b; + } - struct assign_statement + static double not_equal(double a, double b) { - int m_variable_index; - int m_expression_index; + return a != b; + } - int m_expression_offset; - }; + static double logical_and(double a, double b) + { + return a != 0.0 && b != 0.0; + } + + static double logical_or(double a, double b) + { + return a != 0.0 || b != 0.0; + } + + static double logical_not(double a) + { + return a == 0.0; + } - struct call_statement + static double logical_notnot(double a) { - int m_expression_index; - - int m_expression_offset; - }; - - using any_statement = std::variant; + return a != 0.0; + } - template - auto compile(const char* text, const variable* variables, int var_count, int* error) -> typename portable::portable_compiled_program* + static double negate_logical_not(double a) { - auto program_src = parser::trim_all_space(std::string_view{text, strlen(text)}); - auto program_remaining = program_src; + return -(a == 0.0); + } - std::vector program_statements; - using program_impl = typename portable::portable_compiled_program; - auto program = new program_impl(); - label_manager lm; - variable_manager vm; - expression_manager em; + static double negate_logical_notnot(double a) + { + return -(a != 0.0); + } - while (program_remaining.length() > 0) - { - auto [statement, remaining] = parser::split_at_char_excl(program_remaining, ';'); + static double nul() + { + return 0.0f; + } - parser::parse_statement( - statement, + static double nan() + { + return std::numeric_limits::quiet_NaN(); + } + }; - // label - [&](std::string_view label) { lm.add_label(label, (int)program_statements.size()); }, + template<> + struct native_builtins_impl + { + static float pi(void) + { + return 3.14159265358979323846f; + } - // jump - [&](std::string_view destination_label) { - any_statement s = jump_statement{lm.find_label(destination_label), -1}; - program_statements.push_back(s); - }, + static float e(void) + { + return 2.71828182845904523536f; + } - // jump_if - [&](std::string_view destination_label, std::string_view condition) { - any_statement s = jump_statement{lm.find_label(destination_label), em.add_expression(condition)}; - program_statements.push_back(s); - }, + static float fac(float a) + { /* simplest version of fac */ + if (a < 0.0f) + return nan(); + if (a > UINT_MAX) + return INFINITY; + unsigned int ua = (unsigned int)(a); + unsigned long int result = 1, i; + for (i = 1; i <= ua; i++) + { + if (i > ULONG_MAX / result) + return INFINITY; + result *= i; + } + return (float)result; + } - // return_value - [&](std::string_view expression) { - any_statement s = return_value_statement{em.add_expression(expression)}; - program_statements.push_back(s); - }, + static float ncr(float n, float r) + { + if (n < 0.0f || r < 0.0f || n < r) + return nan(); + if (n > UINT_MAX || r > UINT_MAX) + return INFINITY; + unsigned long int un = (unsigned int)(n), ur = (unsigned int)(r), i; + unsigned long int result = 1; + if (ur > un / 2) + ur = un - ur; + for (i = 1; i <= ur; i++) + { + if (result > ULONG_MAX / (un - ur + i)) + return INFINITY; + result *= un - ur + i; + result /= i; + } + return (float)result; + } - // assign - [&](std::string_view destination, std::string_view expression) { - any_statement s = assign_statement{vm.find_label(destination), em.add_expression(expression)}; - program_statements.push_back(s); - }, + static float npr(float n, float r) + { + return ncr(n, r) * fac(r); + } - // call - [&](std::string_view expression) { - any_statement s = call_statement{em.add_expression(expression)}; - program_statements.push_back(s); - }); + static float fabs(float n) + { + return ::fabsf(n); + } - program_remaining = remaining; - } + static float acos(float n) + { + return ::acosf(n); + } - // Fixup jump indices - for (auto& s : program_statements) - { - if (std::holds_alternative(s)) - { - std::get(s).m_target_index = lm.get_label_statement_index(std::get(s).m_target_handle); - } - } + static float cosh(float n) + { + return ::coshf(n); + } - // Add referenced variables to the lookup dict - typename portable::expr_portable_expression_build_indexer indexer; - for (auto itor : vm.m_variable_map) - { - auto name = itor.first; - auto index = itor.second; + static float cos(float n) + { + return ::cosf(n); + } - int final_index = -1; + static float exp(float n) + { + return ::expf(n); + } - for (int var_idx = 0; var_idx < var_count; ++var_idx) - { - const auto& var = variables[var_idx]; + static float asin(float n) + { + return ::asinf(n); + } - if (name == std::string_view(var.name)) - { - final_index = indexer.add_referenced_variable(&var); - break; - } - } + static float sinh(float n) + { + return ::sinhf(n); + } - if (final_index == -1) - { - *error = -1; // TODO: variable not found - return nullptr; - } + static float sin(float n) + { + return ::sinf(n); + } - // Remap all indexes to the indexer value - for (auto s : program_statements) - { - if (std::holds_alternative(s)) - { - if (std::get(s).m_variable_index == index) - { - std::get(s).m_variable_index = final_index; - } - } - } - } + static float sqrt(float n) + { + return ::sqrtf(n); + } - // Compile all the expressions, redirect the statement expression indexes to the compiled buffer offset - for (int expr_idx = 0; expr_idx < em.m_expressions.size(); ++expr_idx) - { - auto expr = em.m_expressions[expr_idx]; - auto compiled_expr = expr_details::compile_using_indexer(indexer, expr.data(), variables, var_count, error); + static float log(float n) + { + return ::logf(n); + } - if (compiled_expr) - { - const int current_expr_offset = (int)program->program_expression_buffer_size; - auto compiled_size = (int)compiled_expr->get_data_size(); - const int new_expr_offset = (int)program->program_expression_buffer_size + compiled_size; + static float log10(float n) + { + return ::log10f(n); + } - auto new_expr_buffer = new unsigned char[new_expr_offset]; - ::memcpy(new_expr_buffer, program->program_expression_buffer.get(), program->program_expression_buffer_size); - ::memcpy(new_expr_buffer + program->program_expression_buffer_size, compiled_expr->get_data(), compiled_size); + static float atan(float n) + { + return ::atanf(n); + } - program->program_expression_buffer_size = new_expr_offset; + static float tanh(float n) + { + return ::tanhf(n); + } - program->program_expression_buffer.reset(new_expr_buffer); + static float fmod(float n, float m) + { + return ::fmodf(n, m); + } - // Fixup any expression indexes - for (auto& s : program_statements) - { - if (std::holds_alternative(s)) - { - if (std::get(s).m_expression_index == expr_idx) - { - std::get(s).m_expression_offset = current_expr_offset; - } - } + static float tan(float n) + { + return ::tanf(n); + } - if (std::holds_alternative(s)) - { - if (std::get(s).m_expression_index == expr_idx) - { - std::get(s).m_expression_offset = current_expr_offset; - } - } + static float atan2(float n, float m) + { + return ::atan2f(n, m); + } - if (std::holds_alternative(s)) - { - if (std::get(s).m_expression_index == expr_idx) - { - std::get(s).m_expression_offset = current_expr_offset; - } - } + static float pow(float n, float m) + { + return ::powf(n, m); + } - if (std::holds_alternative(s)) - { - if (std::get(s).m_expression_index == expr_idx) - { - std::get(s).m_expression_offset = current_expr_offset; - } - } - } - } - else - { - *error = -1; // TODO: handle error - break; - } - } + static float floor(float d) + { + return ::floorf(d); + } - for (auto s_in : program_statements) - { - statement s_out; + static float ceil(float d) + { + return ::ceilf(d); + } - if (std::holds_alternative(s_in)) - { - s_out.type = statement_type::call; - s_out.arg_a = std::get(s_in).m_expression_offset; - s_out.arg_b = -1; - } - else if (std::holds_alternative(s_in)) - { - s_out.type = statement_type::assign; - s_out.arg_a = std::get(s_in).m_variable_index; - s_out.arg_b = std::get(s_in).m_expression_offset; - } - else if (std::holds_alternative(s_in)) - { - s_out.type = statement_type::return_value; - s_out.arg_a = std::get(s_in).m_expression_offset; - s_out.arg_b = -1; - } - else if (std::holds_alternative(s_in)) - { - s_out.type = statement_type::jump; - s_out.arg_a = std::get(s_in).m_target_index; - s_out.arg_b = std::get(s_in).m_expression_offset; - } + static float add(float a, float b) + { + return a + b; + } - program->program_statements.push_back(s_out); - } + static float sub(float a, float b) + { + return a - b; + } - program->binding_table = indexer.get_binding_table(); - for (const auto& n : program->binding_table) - { - program->binding_table_cstr.push_back(n.c_str()); - } + static float mul(float a, float b) + { + return a * b; + } - program->address_table = indexer.get_address_table(); + static float divide(float a, float b) + { + return a / b; + } - return program; + static float negate(float a) + { + return -a; } - } // namespace program_details -} // namespace tp -#endif // #if (TP_COMPILER_ENABLED) -namespace tp -{ - struct env_traits_f32 - { - using t_atom = float; - using t_vector = float; - using t_vector_int = int; - using t_atom_builtins = native_builtins; - using t_vector_builtins = native_builtins; + static float comma(float a, float b) + { + (void)a; + return b; + } - static inline t_vector load_atom(t_atom a) noexcept + static float greater(float a, float b) { - return a; + return a > b; } - static inline t_vector as_truth(t_vector a) noexcept + static float greater_eq(float a, float b) { - return (a != 0.0f) ? 1.0f : 0.0f; + return a >= b; } - static inline t_vector explicit_load_atom(double a) noexcept + static float lower(float a, float b) { - return (t_vector)a; + return a < b; } - static inline t_vector explicit_load_atom(int a) noexcept + static float lower_eq(float a, float b) { - return (t_vector)a; + return a <= b; } - static inline double explicit_store_double(t_vector a) + static float equal(float a, float b) { - return (double)a; + return a == b; } - static inline int explicit_store_int(t_vector a) + static float not_equal(float a, float b) { - return (int)a; + return a != b; } - }; - struct env_traits_d64 - { - using t_atom = double; - using t_vector = double; - using t_vector_int = int; - using t_atom_builtins = native_builtins; - using t_vector_builtins = native_builtins; + static float logical_and(float a, float b) + { + return a != 0.0f && b != 0.0f; + } - static inline t_vector load_atom(t_atom a) noexcept + static float logical_or(float a, float b) { - return a; + return a != 0.0f || b != 0.0f; } - static inline t_vector as_truth(t_vector a) noexcept + static float logical_not(float a) { - return (a != 0.0f) ? 1.0f : 0.0f; + return a == 0.0f; } - static inline t_vector explicit_load_atom(double a) noexcept + static float logical_notnot(float a) { - return (t_vector)a; + return a != 0.0f; } - static inline t_vector explicit_load_atom(int a) noexcept + static float negate_logical_not(float a) { - return (t_vector)a; + return (float)-(a == 0.0f); } - static inline double explicit_store_double(t_vector a) + static float negate_logical_notnot(float a) { - return (double)a; + return (float)-(a != 0.0f); } - static inline int explicit_store_int(t_vector a) + static float nul() { - return (int)a; + return 0.0f; + } + + static float nan() + { + return std::numeric_limits::quiet_NaN(); } }; + template + struct native_builtins : native_builtins_impl + { + using t_impl = native_builtins_impl; + + static inline constexpr tp::variable functions[] = {/* must be in alphabetical order */ + {"abs", t_impl::fabs, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"acos", t_impl::acos, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"asin", t_impl::asin, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"atan", t_impl::atan, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"atan2", t_impl::atan2, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"ceil", t_impl::ceil, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"cos", t_impl::cos, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"cosh", t_impl::cosh, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"e", t_impl::e, tp::FUNCTION0 | tp::FLAG_PURE, 0}, + {"exp", t_impl::exp, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"fac", t_impl::fac, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"floor", t_impl::floor, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"ln", t_impl::log, tp::FUNCTION1 | tp::FLAG_PURE, 0}, +#ifdef TP_NAT_LOG + {"log", t_impl::log, tp::FUNCTION1 | tp::FLAG_PURE, 0}, +#else + {"log", t_impl::log10, tp::FUNCTION1 | tp::FLAG_PURE, 0}, +#endif + {"log10", t_impl::log10, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"ncr", t_impl::ncr, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"npr", t_impl::npr, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"pi", t_impl::pi, tp::FUNCTION0 | tp::FLAG_PURE, 0}, + {"pow", t_impl::pow, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"sin", t_impl::sin, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"sinh", t_impl::sinh, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"sqrt", t_impl::sqrt, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"tan", t_impl::tan, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"tanh", t_impl::tanh, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {0, 0, 0, 0}}; + + static inline constexpr tp::variable operators[] = {/* must be in alphabetical order */ + {"add", t_impl::add, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"comma", t_impl::comma, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"divide", t_impl::divide, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"equal", t_impl::equal, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"fmod", t_impl::fmod, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"greater", t_impl::greater, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"greater_eq", t_impl::greater_eq, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"logical_and", t_impl::logical_and, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"logical_not", t_impl::logical_not, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"logical_notnot", t_impl::logical_notnot, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"logical_or", t_impl::logical_or, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"lower", t_impl::lower, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"lower_eq", t_impl::lower_eq, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"mul", t_impl::mul, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"negate", t_impl::negate, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"negate_logical_not", t_impl::negate_logical_not, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"negate_logical_notnot", t_impl::negate_logical_notnot, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"not_equal", t_impl::not_equal, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"pow", t_impl::pow, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"sub", t_impl::sub, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {0, 0, 0, 0}}; + }; +} // namespace tp_stdlib +#endif // #if TP_STANDARD_LIBRARY + +namespace tp +{ template struct impl { @@ -2806,6 +2752,96 @@ namespace tp }; } // namespace tp -using te = tp::impl; +#if TP_STANDARD_LIBRARY +namespace tp +{ + template typename T_NATIVE_BUILTINS> + struct env_traits_f32 + { + using t_atom = float; + using t_vector = float; + using t_vector_int = int; + using t_atom_builtins = T_NATIVE_BUILTINS; + using t_vector_builtins = T_NATIVE_BUILTINS; + + static inline t_vector load_atom(t_atom a) noexcept + { + return a; + } + + static inline t_vector as_truth(t_vector a) noexcept + { + return (a != 0.0f) ? 1.0f : 0.0f; + } + + static inline t_vector explicit_load_atom(double a) noexcept + { + return (t_vector)a; + } + + static inline t_vector explicit_load_atom(int a) noexcept + { + return (t_vector)a; + } + + static inline double explicit_store_double(t_vector a) + { + return (double)a; + } + + static inline int explicit_store_int(t_vector a) + { + return (int)a; + } + }; + + template typename T_NATIVE_BUILTINS> + struct env_traits_d64 + { + using t_atom = double; + using t_vector = double; + using t_vector_int = int; + using t_atom_builtins = T_NATIVE_BUILTINS; + using t_vector_builtins = T_NATIVE_BUILTINS; + + static inline t_vector load_atom(t_atom a) noexcept + { + return a; + } + + static inline t_vector as_truth(t_vector a) noexcept + { + return (a != 0.0f) ? 1.0f : 0.0f; + } + + static inline t_vector explicit_load_atom(double a) noexcept + { + return (t_vector)a; + } + + static inline t_vector explicit_load_atom(int a) noexcept + { + return (t_vector)a; + } + + static inline double explicit_store_double(t_vector a) + { + return (double)a; + } + + static inline int explicit_store_int(t_vector a) + { + return (int)a; + } + }; +} // namespace tp +#endif // #if TP_STANDARD_LIBRARY + +#if TP_TESTING +#if !TP_STANDARD_LIBRARY +#error TP_STANDARD_LIBRARY should be defined for testing +#endif // #if !TP_STANDARD_LIBRARY +using te = tp::impl>; +#endif // #if TP_TESTING #endif /*__TINYPROG_H__*/ diff --git a/test/benchmark.cpp b/test/benchmark.cpp index 79dfc3e..8a46718 100644 --- a/test/benchmark.cpp +++ b/test/benchmark.cpp @@ -31,7 +31,7 @@ #include #include -#define TP_COMPILER_ENABLED 1 +#define TP_TESTING 1 #include "tinyprog.h" #define loops 10000 diff --git a/test/example.cpp b/test/example.cpp index 93b0225..f651e68 100644 --- a/test/example.cpp +++ b/test/example.cpp @@ -1,4 +1,4 @@ -#define TP_COMPILER_ENABLED 1 +#define TP_TESTING 1 #include "tinyprog.h" #include diff --git a/test/example2.cpp b/test/example2.cpp index 3c5f4a5..5b7ce09 100644 --- a/test/example2.cpp +++ b/test/example2.cpp @@ -1,4 +1,4 @@ -#define TP_COMPILER_ENABLED 1 +#define TP_TESTING 1 #include "tinyprog.h" #include diff --git a/test/example3.cpp b/test/example3.cpp index 86b9080..61c0423 100644 --- a/test/example3.cpp +++ b/test/example3.cpp @@ -1,4 +1,4 @@ -#define TP_COMPILER_ENABLED 1 +#define TP_TESTING 1 #include "tinyprog.h" #include diff --git a/test/example_program.cpp b/test/example_program.cpp index f8f685e..8adc37d 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -1,4 +1,4 @@ -#define TP_COMPILER_ENABLED 1 +#define TP_TESTING 1 #include "tinyprog.h" #include diff --git a/test/test.cpp b/test/test.cpp index 3268d8a..36cdde8 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -27,7 +27,7 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#define TP_COMPILER_ENABLED 1 +#define TP_TESTING 1 #include "tinyprog.h" #include From df64005f03f292127baf650dd08029ad5e50e88e Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Fri, 24 Jul 2020 14:17:59 -0700 Subject: [PATCH 08/43] Consolidate more builtin standard library stuff --- include/tinyprog.h | 208 ++++++++++++++++++++++----------------------- 1 file changed, 103 insertions(+), 105 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index b68494d..727be59 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -2080,6 +2080,106 @@ namespace tp } // namespace tp #endif // #if (TP_COMPILER_ENABLED) +namespace tp +{ + template + struct impl + { + using env_traits = T_TRAITS; + using variable = ::tp::variable; + using t_atom = typename env_traits::t_atom; + using t_vector = typename env_traits::t_vector; + + static inline t_vector eval(const void* expr_buffer, const void* const expr_context[]) noexcept + { + return eval_details::eval_portable_impl((const expr_portable*)expr_buffer, (const unsigned char*)expr_buffer, expr_context); + } + + static inline t_vector eval_program(const statement* statement_array, int statement_array_size, const void* expr_buffer, const void* const expr_context[]) + { + for (int statement_index = 0; statement_index < statement_array_size; ++statement_index) + { + auto& statement = statement_array[statement_index]; + switch (statement.type) + { + case statement_type::jump: + if (statement.arg_b == -1 || + (0.0f != eval(((const char*)expr_buffer) + statement.arg_b, expr_context))) // TODO: traits function like nan for zero, or compare function? + { + statement_index = statement.arg_a; + } + break; + + case statement_type::return_value: + return eval(((const char*)expr_buffer) + statement.arg_a, expr_context); + + case statement_type::assign: + { + auto dest = (t_vector*)expr_context[statement.arg_a]; + *dest = eval(((const char*)expr_buffer) + statement.arg_b, expr_context); + } + break; + + case statement_type::call: + eval(((const char*)expr_buffer) + statement.arg_a, expr_context); + break; + + default: + // fatal error + return env_traits::t_vector_builtins::nan(); + } + } + return env_traits::t_vector_builtins::nan(); + } + +#if (TP_COMPILER_ENABLED) + static compiled_expr* compile(const char* expression, const variable* variables, int var_count, int* error) + { + return expr_details::compile(expression, variables, var_count, error); + } + + static compiled_program* compile_program(const char* program, const variable* variables, int var_count, int* error) + { + return (compiled_program*)program_details::compile(program, variables, var_count, error); + } + + static inline t_vector eval(const compiled_expr* n) + { + return eval(n->get_data(), n->get_binding_addresses()); + } + + static inline t_vector interp(const char* expression, int* error) + { + compiled_expr* n = compile(expression, 0, 0, error); + t_vector ret; + if (n) + { + ret = eval(n); + delete n; + } + else + { + ret = env_traits::t_vector_builtins::nan(); + } + return ret; + } + + static inline t_vector eval_program(compiled_program* prog) + { + auto array_size = prog->get_binding_array_size(); + auto binding_addrs = prog->get_binding_addresses(); + auto binding_names = prog->get_binding_names(); + auto data_size = prog->get_data_size(); + auto data = prog->get_data(); + auto num_statements = prog->get_statement_array_size(); + auto statements = prog->get_statements(); + + return eval_program(statements, (int)num_statements, data, binding_addrs); + } +#endif // #if (TP_COMPILER_ENABLED) + }; +} // namespace tp + #if TP_STANDARD_LIBRARY namespace tp_stdlib { @@ -2650,110 +2750,8 @@ namespace tp_stdlib {0, 0, 0, 0}}; }; } // namespace tp_stdlib -#endif // #if TP_STANDARD_LIBRARY - -namespace tp -{ - template - struct impl - { - using env_traits = T_TRAITS; - using variable = ::tp::variable; - using t_atom = typename env_traits::t_atom; - using t_vector = typename env_traits::t_vector; - - static inline t_vector eval(const void* expr_buffer, const void* const expr_context[]) noexcept - { - return eval_details::eval_portable_impl((const expr_portable*)expr_buffer, (const unsigned char*)expr_buffer, expr_context); - } - - static inline t_vector eval_program(const statement* statement_array, int statement_array_size, const void* expr_buffer, const void* const expr_context[]) - { - for (int statement_index = 0; statement_index < statement_array_size; ++statement_index) - { - auto& statement = statement_array[statement_index]; - switch (statement.type) - { - case statement_type::jump: - if (statement.arg_b == -1 || - (0.0f != eval(((const char*)expr_buffer) + statement.arg_b, expr_context))) // TODO: traits function like nan for zero, or compare function? - { - statement_index = statement.arg_a; - } - break; - - case statement_type::return_value: - return eval(((const char*)expr_buffer) + statement.arg_a, expr_context); - - case statement_type::assign: - { - auto dest = (t_vector*)expr_context[statement.arg_a]; - *dest = eval(((const char*)expr_buffer) + statement.arg_b, expr_context); - } - break; - - case statement_type::call: - eval(((const char*)expr_buffer) + statement.arg_a, expr_context); - break; - default: - // fatal error - return env_traits::t_vector_builtins::nan(); - } - } - return env_traits::t_vector_builtins::nan(); - } - -#if (TP_COMPILER_ENABLED) - static compiled_expr* compile(const char* expression, const variable* variables, int var_count, int* error) - { - return expr_details::compile(expression, variables, var_count, error); - } - - static compiled_program* compile_program(const char* program, const variable* variables, int var_count, int* error) - { - return (compiled_program*)program_details::compile(program, variables, var_count, error); - } - - static inline t_vector eval(const compiled_expr* n) - { - return eval(n->get_data(), n->get_binding_addresses()); - } - - static inline t_vector interp(const char* expression, int* error) - { - compiled_expr* n = compile(expression, 0, 0, error); - t_vector ret; - if (n) - { - ret = eval(n); - delete n; - } - else - { - ret = env_traits::t_vector_builtins::nan(); - } - return ret; - } - - static inline t_vector eval_program(compiled_program* prog) - { - auto array_size = prog->get_binding_array_size(); - auto binding_addrs = prog->get_binding_addresses(); - auto binding_names = prog->get_binding_names(); - auto data_size = prog->get_data_size(); - auto data = prog->get_data(); - auto num_statements = prog->get_statement_array_size(); - auto statements = prog->get_statements(); - - return eval_program(statements, (int)num_statements, data, binding_addrs); - } -#endif // #if (TP_COMPILER_ENABLED) - }; -} // namespace tp - -#if TP_STANDARD_LIBRARY -namespace tp +namespace tp_stdlib { template typename T_NATIVE_BUILTINS> struct env_traits_f32 @@ -2834,14 +2832,14 @@ namespace tp return (int)a; } }; -} // namespace tp +} // namespace tp_stdlib #endif // #if TP_STANDARD_LIBRARY #if TP_TESTING #if !TP_STANDARD_LIBRARY #error TP_STANDARD_LIBRARY should be defined for testing #endif // #if !TP_STANDARD_LIBRARY -using te = tp::impl>; +using te = tp::impl>; #endif // #if TP_TESTING #endif /*__TINYPROG_H__*/ From f38c9cff691bc6e1cf7e058f38cb986b3b49a597 Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Mon, 27 Jul 2020 12:25:15 -0700 Subject: [PATCH 09/43] Platforms are going to #define useful words that should be namespaced. Face to bloodshed. --- include/tinyprog.h | 126 ++++++++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 727be59..9688b36 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -451,17 +451,17 @@ namespace tp typedef t_vector (*te_fun2)(t_vector, t_vector); - enum class TOK : int - { - NUL = CLOSURE_MAX, - ERROR, - END, - SEP, - OPEN, - CLOSE, - NUMBER, - VARIABLE, - INFIX + enum + { + TOK_NUL = CLOSURE_MAX, + TOK_ERROR, + TOK_END, + TOK_SEP, + TOK_OPEN, + TOK_CLOSE, + TOK_NUMBER, + TOK_VARIABLE, + TOK_INFIX }; struct state @@ -561,13 +561,13 @@ namespace tp static void next_token(state* s) { - s->type = (int)TOK::NUL; + s->type = (int)TOK_NUL; do { if (!*s->next || *s->next == ';') { - s->type = (int)TOK::END; + s->type = (int)TOK_END; return; } @@ -575,7 +575,7 @@ namespace tp if ((s->next[0] >= '0' && s->next[0] <= '9') || s->next[0] == '.') { s->value = (t_atom)strtod(s->next, (char**)&s->next); - s->type = (int)TOK::NUMBER; + s->type = (int)TOK_NUMBER; } else { @@ -593,14 +593,14 @@ namespace tp if (!var) { - s->type = (int)TOK::ERROR; + s->type = (int)TOK_ERROR; } else { const auto t = eval_details::type_mask(var->type); if (t == VARIABLE) { - s->type = (int)TOK::VARIABLE; + s->type = (int)TOK_VARIABLE; s->bound = (const t_atom*)var->address; } else if (t >= FUNCTION0) @@ -628,109 +628,109 @@ namespace tp switch (s->next++[0]) { case '+': - s->type = (int)TOK::INFIX; + s->type = (int)TOK_INFIX; s->function = t_vector_builtins::find_builtin_address("add"); break; case '-': - s->type = (int)TOK::INFIX; + s->type = (int)TOK_INFIX; s->function = t_vector_builtins::find_builtin_address("sub"); break; case '*': - s->type = (int)TOK::INFIX; + s->type = (int)TOK_INFIX; s->function = t_vector_builtins::find_builtin_address("mul"); break; case '/': - s->type = (int)TOK::INFIX; + s->type = (int)TOK_INFIX; s->function = t_vector_builtins::find_builtin_address("divide"); break; case '^': - s->type = (int)TOK::INFIX; + s->type = (int)TOK_INFIX; s->function = t_vector_builtins::find_builtin_address("pow"); break; case '%': - s->type = (int)TOK::INFIX; + s->type = (int)TOK_INFIX; s->function = t_vector_builtins::find_builtin_address("fmod"); break; case '!': if (s->next++[0] == '=') { - s->type = (int)TOK::INFIX; + s->type = (int)TOK_INFIX; s->function = t_vector_builtins::find_builtin_address("not_equal"); } else { s->next--; - s->type = (int)TOK::INFIX; + s->type = (int)TOK_INFIX; s->function = t_vector_builtins::find_builtin_address("logical_not"); } break; case '=': if (s->next++[0] == '=') { - s->type = (int)TOK::INFIX; + s->type = (int)TOK_INFIX; s->function = t_vector_builtins::find_builtin_address("equal"); } else { - s->type = (int)TOK::ERROR; + s->type = (int)TOK_ERROR; } break; case '<': if (s->next++[0] == '=') { - s->type = (int)TOK::INFIX; + s->type = (int)TOK_INFIX; s->function = t_vector_builtins::find_builtin_address("lower_eq"); } else { s->next--; - s->type = (int)TOK::INFIX; + s->type = (int)TOK_INFIX; s->function = t_vector_builtins::find_builtin_address("lower"); } break; case '>': if (s->next++[0] == '=') { - s->type = (int)TOK::INFIX; + s->type = (int)TOK_INFIX; s->function = t_vector_builtins::find_builtin_address("greater_eq"); } else { s->next--; - s->type = (int)TOK::INFIX; + s->type = (int)TOK_INFIX; s->function = t_vector_builtins::find_builtin_address("greater"); } break; case '&': if (s->next++[0] == '&') { - s->type = (int)TOK::INFIX; + s->type = (int)TOK_INFIX; s->function = t_vector_builtins::find_builtin_address("logical_and"); } else { - s->type = (int)TOK::ERROR; + s->type = (int)TOK_ERROR; } break; case '|': if (s->next++[0] == '|') { - s->type = (int)TOK::INFIX; + s->type = (int)TOK_INFIX; s->function = t_vector_builtins::find_builtin_address("logical_or"); } else { - s->type = (int)TOK::ERROR; + s->type = (int)TOK_ERROR; } break; case '(': - s->type = (int)TOK::OPEN; + s->type = (int)TOK_OPEN; break; case ')': - s->type = (int)TOK::CLOSE; + s->type = (int)TOK_CLOSE; break; case ',': - s->type = (int)TOK::SEP; + s->type = (int)TOK_SEP; break; case ' ': case '\t': @@ -738,12 +738,12 @@ namespace tp case '\r': break; default: - s->type = (int)TOK::ERROR; + s->type = (int)TOK_ERROR; break; } } } - } while (s->type == (int)TOK::NUL); + } while (s->type == (int)TOK_NUL); } static expr_native* base(state* s) @@ -754,13 +754,13 @@ namespace tp const auto t = eval_details::type_mask(s->type); - if (t == (int)TOK::NUMBER) + if (t == (int)TOK_NUMBER) { ret = new_expr(CONSTANT, 0); ret->value = s->value; next_token(s); } - else if (t == (int)TOK::VARIABLE) + else if (t == (int)TOK_VARIABLE) { ret = new_expr(VARIABLE, 0); ret->bound = s->bound; @@ -776,12 +776,12 @@ namespace tp if (is_closure(s->type)) ret->parameters[0] = s->context; next_token(s); - if (s->type == (int)TOK::OPEN) + if (s->type == (int)TOK_OPEN) { next_token(s); - if (s->type != (int)TOK::CLOSE) + if (s->type != (int)TOK_CLOSE) { - s->type = (int)TOK::ERROR; + s->type = (int)TOK_ERROR; } else { @@ -806,9 +806,9 @@ namespace tp ret->parameters[arity] = s->context; next_token(s); - if (s->type != (int)TOK::OPEN) + if (s->type != (int)TOK_OPEN) { - s->type = (int)TOK::ERROR; + s->type = (int)TOK_ERROR; } else { @@ -817,14 +817,14 @@ namespace tp { next_token(s); ret->parameters[i] = expr(s); - if (s->type != (int)TOK::SEP) + if (s->type != (int)TOK_SEP) { break; } } - if (s->type != (int)TOK::CLOSE || i != arity - 1) + if (s->type != (int)TOK_CLOSE || i != arity - 1) { - s->type = (int)TOK::ERROR; + s->type = (int)TOK_ERROR; } else { @@ -833,13 +833,13 @@ namespace tp } } } - else if (t == (int)TOK::OPEN) + else if (t == (int)TOK_OPEN) { next_token(s); ret = list(s); - if (s->type != (int)TOK::CLOSE) + if (s->type != (int)TOK_CLOSE) { - s->type = (int)TOK::ERROR; + s->type = (int)TOK_ERROR; } else { @@ -849,7 +849,7 @@ namespace tp else { ret = new_expr(0, 0); - s->type = (int)TOK::ERROR; + s->type = (int)TOK_ERROR; ret->value = t_vector_builtins::nan(); } @@ -860,7 +860,7 @@ namespace tp { /* = {("-" | "+" | "!")} */ int sign = 1; - while (s->type == (int)TOK::INFIX && (s->function == t_vector_builtins::find_builtin_address("add") || s->function == t_vector_builtins::find_builtin_address("sub"))) + while (s->type == (int)TOK_INFIX && (s->function == t_vector_builtins::find_builtin_address("add") || s->function == t_vector_builtins::find_builtin_address("sub"))) { if (s->function == t_vector_builtins::find_builtin_address("sub")) sign = -sign; @@ -868,7 +868,7 @@ namespace tp } int logical = 0; - while (s->type == (int)TOK::INFIX && (s->function == t_vector_builtins::find_builtin_address("add") || s->function == t_vector_builtins::find_builtin_address("sub") || + while (s->type == (int)TOK_INFIX && (s->function == t_vector_builtins::find_builtin_address("add") || s->function == t_vector_builtins::find_builtin_address("sub") || s->function == t_vector_builtins::find_builtin_address("logical_not"))) { if (s->function == t_vector_builtins::find_builtin_address("logical_not")) @@ -946,7 +946,7 @@ namespace tp ret = se; } - while (s->type == (int)TOK::INFIX && (s->function == t_vector_builtins::find_builtin_address("pow"))) + while (s->type == (int)TOK_INFIX && (s->function == t_vector_builtins::find_builtin_address("pow"))) { te_fun2 t = s->function; next_token(s); @@ -981,7 +981,7 @@ namespace tp /* = {"^" } */ expr_native* ret = power(s); - while (s->type == (int)TOK::INFIX && (s->function == t_vector_builtins::find_builtin_address("pow"))) + while (s->type == (int)TOK_INFIX && (s->function == t_vector_builtins::find_builtin_address("pow"))) { te_fun2 t = (te_fun2)s->function; next_token(s); @@ -998,7 +998,7 @@ namespace tp /* = {("*" | "/" | "%") } */ expr_native* ret = factor(s); - while (s->type == (int)TOK::INFIX && + while (s->type == (int)TOK_INFIX && (s->function == t_vector_builtins::find_builtin_address("mul") || s->function == t_vector_builtins::find_builtin_address("divide") || s->function == t_vector_builtins::find_builtin_address("fmod"))) { @@ -1016,7 +1016,7 @@ namespace tp /* = {("+" | "-") } */ expr_native* ret = term(s); - while (s->type == (int)TOK::INFIX && (s->function == t_vector_builtins::find_builtin_address("add") || s->function == t_vector_builtins::find_builtin_address("sub"))) + while (s->type == (int)TOK_INFIX && (s->function == t_vector_builtins::find_builtin_address("add") || s->function == t_vector_builtins::find_builtin_address("sub"))) { te_fun2 t = (te_fun2)s->function; next_token(s); @@ -1032,7 +1032,7 @@ namespace tp /* = {(">" | ">=" | "<" | "<=" | "==" | "!=") } */ expr_native* ret = sum_expr(s); - while (s->type == (int)TOK::INFIX && + while (s->type == (int)TOK_INFIX && (s->function == t_vector_builtins::find_builtin_address("greater") || s->function == t_vector_builtins::find_builtin_address("greater_eq") || s->function == t_vector_builtins::find_builtin_address("lower") || s->function == t_vector_builtins::find_builtin_address("lower_eq") || s->function == t_vector_builtins::find_builtin_address("equal") || s->function == t_vector_builtins::find_builtin_address("not_equal"))) @@ -1051,7 +1051,7 @@ namespace tp /* = {("&&" | "||") } */ expr_native* ret = test_expr(s); - while (s->type == (int)TOK::INFIX && + while (s->type == (int)TOK_INFIX && (s->function == t_vector_builtins::find_builtin_address("logical_and") || s->function == t_vector_builtins::find_builtin_address("logical_or"))) { te_fun2 t = (te_fun2)s->function; @@ -1068,7 +1068,7 @@ namespace tp /* = {"," } */ expr_native* ret = expr(s); - while (s->type == (int)TOK::SEP) + while (s->type == (int)TOK_SEP) { next_token(s); ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, expr(s)); @@ -1136,7 +1136,7 @@ namespace tp next_token(&s); expr_native* root = list(&s); - if (s.type != (int)TOK::END) + if (s.type != (int)TOK_END) { free_native(root); if (error) From 7f0ef0d232393bf32bc5c2f3bed11ee8a0109a8b Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Mon, 27 Jul 2020 13:55:05 -0700 Subject: [PATCH 10/43] Warning cleanup --- include/tinyprog.h | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 9688b36..affe487 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -508,13 +508,16 @@ namespace tp const int psize = sizeof(void*) * arity; const int size = (sizeof(expr_native) - sizeof(void*)) + psize + (is_closure(type) ? sizeof(void*) : 0); expr_native* ret = (expr_native*)malloc(size); - memset(ret, 0, size); - if (arity && parameters) + if (ret) { - memcpy(ret->parameters, parameters, psize); + memset(ret, 0, size); + if (arity && parameters) + { + memcpy(ret->parameters, parameters, psize); + } + ret->type = type; + ret->bound = 0; } - ret->type = type; - ret->bound = 0; return ret; } @@ -1366,8 +1369,8 @@ namespace tp if (var->type >= CLOSURE0 && var->type < CLOSURE_MAX) { - auto itor = name_map.find(var->context); - if (itor == name_map.end()) + auto context_itor = name_map.find(var->context); + if (context_itor == name_map.end()) { name_map.emplace(std::make_pair(var->context, std::string(var->name) + "_closure")); index_map.insert(std::make_pair(var->context, index_counter++)); @@ -1618,7 +1621,7 @@ namespace tp { for (size_t i = 0; i < v.length(); ++i) { - if (!::isspace(v[i])) + if (!::isspace((unsigned char)v[i])) { return std::string_view(&v[i], v.length() - i); } @@ -1635,13 +1638,13 @@ namespace tp { for (size_t i = v.length(); i > 0; --i) { - if (!::isspace(v[i - 1])) + if (!::isspace((unsigned char)v[i - 1])) { return std::string_view(&v[0], i); } } - if (!::isspace(v[0])) + if (!::isspace((unsigned char)v[0])) { std::string_view(&v[0], 1); } From d6fd0c3e733d97dfb7b4cd2a5cf806162d8ffcd9 Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Tue, 28 Jul 2020 18:58:30 -0700 Subject: [PATCH 11/43] Fix compile warning --- include/tinyprog.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/tinyprog.h b/include/tinyprog.h index affe487..0b733b7 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -1387,11 +1387,13 @@ namespace tp [&]() { auto res = handle_addr(native::find_bind_by_addr(n->bound, lookup, lookup_len)); assert(res); + ((void)res); return export_size; }, [&](int a) { auto res = handle_addr(native::find_bind_or_any_by_addr(n->function, lookup, lookup_len)); assert(res); + ((void)res); export_size += sizeof(n->parameters[0]) * a; for (int i = 0; i < a; ++i) @@ -1403,6 +1405,7 @@ namespace tp [&](int a) { auto res = handle_addr(native::find_bind_or_any_by_addr(n->function, lookup, lookup_len)); assert(res); + ((void)res); export_size += sizeof(n->parameters[0]) * a; for (int i = 0; i < a; ++i) From b32d833a5b24eff3cd4bc5268da95f97a6178e10 Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Sat, 1 Aug 2020 14:33:30 -0700 Subject: [PATCH 12/43] variable helpers --- include/tinyprog.h | 84 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 5 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 0b733b7..146fac0 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -872,7 +872,7 @@ namespace tp int logical = 0; while (s->type == (int)TOK_INFIX && (s->function == t_vector_builtins::find_builtin_address("add") || s->function == t_vector_builtins::find_builtin_address("sub") || - s->function == t_vector_builtins::find_builtin_address("logical_not"))) + s->function == t_vector_builtins::find_builtin_address("logical_not"))) { if (s->function == t_vector_builtins::find_builtin_address("logical_not")) { @@ -2088,13 +2088,87 @@ namespace tp namespace tp { + namespace details + { + template + struct variable_helper + { + static inline constexpr variable readonly_var(const char* name, const T_VAL* v) noexcept + { + return {name, v}; // TODO: make distinction between rw and ro + } + + static inline constexpr variable readwrite_var(const char* name, T_VAL* v) noexcept + { + return {name, v}; // TODO: make distinction between rw and ro + } + + template + static inline constexpr variable function0(const char* name, T_FUNC func) + { + typedef T_VAL (*zeroargfn)(); + return {name, zeroargfn{func}, tp::FUNCTION0 | tp::FLAG_PURE, 0}; + } + + template + static inline constexpr variable function1(const char* name, T_FUNC func) + { + typedef T_VAL (*oneargfn)(T_VAL); + return {name, oneargfn{func}, tp::FUNCTION1 | tp::FLAG_PURE, 0}; + } + + template + static inline constexpr variable function2(const char* name, T_FUNC func) + { + typedef T_VAL (*twoargfn)(T_VAL, T_VAL); + return {name, twoargfn{func}, tp::FUNCTION2 | tp::FLAG_PURE, 0}; + } + + template + static inline constexpr variable function3(const char* name, T_FUNC func) + { + typedef T_VAL (*threeargfn)(T_VAL, T_VAL, T_VAL); + return {name, threeargfn{func}, tp::FUNCTION3 | tp::FLAG_PURE, 0}; + } + + template + static inline constexpr variable function4(const char* name, T_FUNC func) + { + typedef T_VAL (*fourargfn)(T_VAL, T_VAL, T_VAL, T_VAL); + return {name, fourargfn{func}, tp::FUNCTION4 | tp::FLAG_PURE, 0}; + } + + template + static inline constexpr variable function5(const char* name, T_FUNC func) + { + typedef T_VAL (*fiveargfn)(T_VAL, T_VAL, T_VAL, T_VAL, T_VAL); + return {name, fiveargfn{func}, tp::FUNCTION5 | tp::FLAG_PURE, 0}; + } + + template + static inline constexpr variable function6(const char* name, T_FUNC func) + { + typedef T_VAL (*sixargfn)(T_VAL, T_VAL, T_VAL, T_VAL, T_VAL, T_VAL); + return {name, sixargfn{func}, tp::FUNCTION6 | tp::FLAG_PURE, 0}; + } + + template + static inline constexpr variable function7(const char* name, T_FUNC func) + { + typedef T_VAL (*sevenargfn)(T_VAL, T_VAL, T_VAL, T_VAL, T_VAL, T_VAL, T_VAL); + return {name, sevenargfn{func}, tp::FUNCTION7 | tp::FLAG_PURE, 0}; + } + }; + } // namespace details + template struct impl { - using env_traits = T_TRAITS; - using variable = ::tp::variable; - using t_atom = typename env_traits::t_atom; - using t_vector = typename env_traits::t_vector; + using env_traits = T_TRAITS; + using variable = ::tp::variable; + using t_atom = typename env_traits::t_atom; + using t_vector = typename env_traits::t_vector; + using variable_factory = details::variable_helper; static inline t_vector eval(const void* expr_buffer, const void* const expr_context[]) noexcept { From b9a2fedfa116f94f7b263940b0d2c7e96bb6ed2c Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Sat, 1 Aug 2020 14:34:28 -0700 Subject: [PATCH 13/43] Compile programs with an indexer so multiple programs can use the same bindings. --- include/tinyprog.h | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 146fac0..e039cd5 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -1874,7 +1874,10 @@ namespace tp using any_statement = std::variant; template - auto compile(const char* text, const variable* variables, int var_count, int* error) -> typename portable::portable_compiled_program* + using t_indexer = typename portable::expr_portable_expression_build_indexer; + + template + auto compile_using_indexer(const char* text, const variable* variables, int var_count, int* error, typename t_indexer& indexer) -> typename portable::portable_compiled_program* { auto program_src = parser::trim_all_space(std::string_view{text, strlen(text)}); auto program_remaining = program_src; @@ -1939,7 +1942,6 @@ namespace tp } // Add referenced variables to the lookup dict - typename portable::expr_portable_expression_build_indexer indexer; for (auto itor : vm.m_variable_map) { auto name = itor.first; @@ -2082,6 +2084,14 @@ namespace tp return program; } + + template + auto compile(const char* text, const variable* variables, int var_count, int* error) -> typename portable::portable_compiled_program* + { + t_indexer indexer; + return compile_using_indexer(text, variables, var_count, error, indexer); + } + } // namespace program_details } // namespace tp #endif // #if (TP_COMPILER_ENABLED) @@ -2169,6 +2179,9 @@ namespace tp using t_atom = typename env_traits::t_atom; using t_vector = typename env_traits::t_vector; using variable_factory = details::variable_helper; +#if (TP_COMPILER_ENABLED) + using t_indexer = program_details::t_indexer; +#endif // #if (TP_COMPILER_ENABLED) static inline t_vector eval(const void* expr_buffer, const void* const expr_context[]) noexcept { @@ -2223,6 +2236,11 @@ namespace tp return (compiled_program*)program_details::compile(program, variables, var_count, error); } + static compiled_program* compile_program_using_indexer(const char* program, const variable* variables, int var_count, int* error, program_details::t_indexer& indexer) + { + return (compiled_program*)program_details::compile_using_indexer(program, variables, var_count, error, indexer); + } + static inline t_vector eval(const compiled_expr* n) { return eval(n->get_data(), n->get_binding_addresses()); From dae892683f19b9a0fc2ebd837b6745a8d0dfb6bc Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Sun, 2 Aug 2020 22:58:08 -0700 Subject: [PATCH 14/43] Multiple programs compiled with the same parameter cache is now actually working. --- include/tinyprog.h | 70 +++++++++++++++++++++++++++++----------- test/example_program.cpp | 60 +++++++++++++++++++++++++++------- 2 files changed, 100 insertions(+), 30 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index e039cd5..4bf5ac8 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -1272,12 +1272,24 @@ namespace tp index_map index_map; int index_counter = 0; + std::vector m_user_variables; + + void add_user_variable(const variable* var) + { + m_user_variables.push_back(*var); + } + int add_referenced_variable(const variable* var) { - int idx = index_counter++; - name_map.insert(std::make_pair(var->address, std::string(var->name))); - index_map.insert(std::make_pair(var->address, idx)); - return idx; + auto itor = index_map.find(var->address); + if (itor == index_map.end()) + { + int idx = index_counter++; + name_map.insert(std::make_pair(var->address, std::string(var->name))); + index_map.insert(std::make_pair(var->address, idx)); + return idx; + } + return itor->second; } std::vector get_binding_table() @@ -1551,8 +1563,11 @@ namespace tp { template compiled_expr* compile_using_indexer( - typename portable::expr_portable_expression_build_indexer& indexer, const char* expression, const variable* variables, int var_count, int* error) + typename portable::expr_portable_expression_build_indexer& indexer, const char* expression, int* error) { + auto variables = &indexer.m_user_variables[0]; + int var_count = (int)indexer.m_user_variables.size(); + typename native::expr_native* native_expr = native::compile_native(expression, variables, var_count, error); if (native_expr) @@ -1609,7 +1624,11 @@ namespace tp compiled_expr* compile(const char* expression, const variable* variables, int var_count, int* error) { typename portable::expr_portable_expression_build_indexer indexer; - return compile_using_indexer(indexer, expression, variables, var_count, error); + for (int v = 0; v < var_count; ++v) + { + indexer.add_user_variable(variables + v); + } + return compile_using_indexer(indexer, expression, error); } } // namespace expr_details @@ -1822,9 +1841,14 @@ namespace tp int find_label(std::string_view name) { - auto idx = m_variable_count++; - m_variable_map.insert(std::make_pair(name, idx)); - return idx; + auto itor = m_variable_map.find(name); + if (itor == m_variable_map.end()) + { + auto idx = m_variable_count++; + m_variable_map.insert(std::make_pair(name, idx)); + return idx; + } + return itor->second; } }; @@ -1858,9 +1882,10 @@ namespace tp struct assign_statement { - int m_variable_index; + int m_variable_build_index; int m_expression_index; + int m_variable_final_index; int m_expression_offset; }; @@ -1877,7 +1902,7 @@ namespace tp using t_indexer = typename portable::expr_portable_expression_build_indexer; template - auto compile_using_indexer(const char* text, const variable* variables, int var_count, int* error, typename t_indexer& indexer) -> typename portable::portable_compiled_program* + auto compile_using_indexer(const char* text, int* error, typename t_indexer& indexer) -> typename portable::portable_compiled_program* { auto program_src = parser::trim_all_space(std::string_view{text, strlen(text)}); auto program_remaining = program_src; @@ -1949,6 +1974,9 @@ namespace tp int final_index = -1; + auto variables = &indexer.m_user_variables[0]; + const int var_count = (int)indexer.m_user_variables.size(); + for (int var_idx = 0; var_idx < var_count; ++var_idx) { const auto& var = variables[var_idx]; @@ -1967,13 +1995,13 @@ namespace tp } // Remap all indexes to the indexer value - for (auto s : program_statements) + for (auto& s : program_statements) { if (std::holds_alternative(s)) { - if (std::get(s).m_variable_index == index) + if (std::get(s).m_variable_build_index == index) { - std::get(s).m_variable_index = final_index; + std::get(s).m_variable_final_index = final_index; } } } @@ -1983,7 +2011,7 @@ namespace tp for (int expr_idx = 0; expr_idx < em.m_expressions.size(); ++expr_idx) { auto expr = em.m_expressions[expr_idx]; - auto compiled_expr = expr_details::compile_using_indexer(indexer, expr.data(), variables, var_count, error); + auto compiled_expr = expr_details::compile_using_indexer(indexer, expr.data(), error); if (compiled_expr) { @@ -2055,7 +2083,7 @@ namespace tp else if (std::holds_alternative(s_in)) { s_out.type = statement_type::assign; - s_out.arg_a = std::get(s_in).m_variable_index; + s_out.arg_a = std::get(s_in).m_variable_final_index; s_out.arg_b = std::get(s_in).m_expression_offset; } else if (std::holds_alternative(s_in)) @@ -2089,7 +2117,11 @@ namespace tp auto compile(const char* text, const variable* variables, int var_count, int* error) -> typename portable::portable_compiled_program* { t_indexer indexer; - return compile_using_indexer(text, variables, var_count, error, indexer); + for (int v = 0; v < var_count; ++v) + { + indexer.add_user_variable(variables + v); + } + return compile_using_indexer(text, error, indexer); } } // namespace program_details @@ -2236,9 +2268,9 @@ namespace tp return (compiled_program*)program_details::compile(program, variables, var_count, error); } - static compiled_program* compile_program_using_indexer(const char* program, const variable* variables, int var_count, int* error, program_details::t_indexer& indexer) + static compiled_program* compile_program_using_indexer(const char* program, int* error, program_details::t_indexer& indexer) { - return (compiled_program*)program_details::compile_using_indexer(program, variables, var_count, error, indexer); + return (compiled_program*)program_details::compile_using_indexer(program, error, indexer); } static inline t_vector eval(const compiled_expr* n) diff --git a/test/example_program.cpp b/test/example_program.cpp index 8adc37d..bc67203 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -4,19 +4,57 @@ int main(int argc, char* argv[]) { - const char* p = - "x: sqrt(5^2+7^2+11^2+(8-2)^2);" - "jump: is_negative ? x < 0;" - "return: x;" - "label: is_negative;" - "return: -1 * x;"; - te::env_traits::t_atom x = 0.0f, y = 0.0f; te::variable vars[] = {{"x", &x}, {"y", &y}}; + te::t_indexer indexer; + + indexer.add_user_variable(vars + 0); + indexer.add_user_variable(vars + 1); + + auto prog1 = [&indexer]() { + const char* p1 = + "x: sqrt(5^2+7^2+11^2+(8-2)^2);" + "jump: is_negative ? x < 0;" + "return: x;" + "label: is_negative;" + "return: -1 * x;"; + int err1 = 0; + auto res = te::compile_program_using_indexer(p1, &err1, indexer); + assert(res); + return res; + }(); + auto result1 = te::eval_program(prog1); + + auto prog2 = [&indexer]() -> auto + { + const char* p2 = + "y: sqrt(5^2+7^2+11^2+(8-2)^2);" + "jump: is_negative ? y < 0;" + "return: y;" + "label: is_negative;" + "return: -1 * y;"; + int err2 = 0; + auto res = te::compile_program_using_indexer(p2, &err2, indexer); + assert(res); + assert(err2 == 0); + return res; + } + (); + auto result2 = te::eval_program(prog2); + + assert(result1 == result2); + assert(prog1->get_binding_array_size() <= prog2->get_binding_array_size()); + + auto ba1 = prog1->get_binding_addresses(); + auto ba2 = prog2->get_binding_addresses(); + + for (int i = 0; i < prog1->get_binding_array_size(); ++i) + { + assert(ba1[i] == ba2[i]); + } + + delete prog1; + delete prog2; - int err = 0; - auto prog = te::compile_program(p, vars, 2, &err); - auto result = te::eval_program(prog); - delete prog; return 0; } From 1acf1210ed7aa0901821882926c83c2171f266ca Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Mon, 3 Aug 2020 13:42:52 -0700 Subject: [PATCH 15/43] Variable declaration support --- include/tinyprog.h | 118 +++++++++++++++++++++++++-------------- test/example_program.cpp | 10 ++-- 2 files changed, 82 insertions(+), 46 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 4bf5ac8..eb3b286 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -1272,11 +1272,32 @@ namespace tp index_map index_map; int index_counter = 0; - std::vector m_user_variables; + std::vector m_env_variables; + + std::vector m_declared_variables; + std::vector m_declared_variable_names; + std::vector m_declared_variable_values; + + std::vector get_variable_array() const + { + std::vector combined; + combined.reserve(m_env_variables.size() + m_declared_variables.size()); + combined.insert(combined.end(), m_env_variables.begin(), m_env_variables.end()); + combined.insert(combined.end(), m_declared_variables.begin(), m_declared_variables.end()); + return combined; + } + + void add_declared_variable(std::string_view name, std::string_view /*scope*/) + { + auto idx = m_declared_variables.size(); + m_declared_variable_names.emplace_back(std::string(name)); + m_declared_variable_values.resize(m_declared_variable_names.size()); + m_declared_variables.push_back(variable{m_declared_variable_names[idx].c_str(), &m_declared_variable_values[idx]}); + } void add_user_variable(const variable* var) { - m_user_variables.push_back(*var); + m_env_variables.push_back(*var); } int add_referenced_variable(const variable* var) @@ -1562,12 +1583,12 @@ namespace tp namespace expr_details { template - compiled_expr* compile_using_indexer( - typename portable::expr_portable_expression_build_indexer& indexer, const char* expression, int* error) + compiled_expr* compile_using_indexer(typename portable::expr_portable_expression_build_indexer& indexer, const char* expression, int* error) { - auto variables = &indexer.m_user_variables[0]; - int var_count = (int)indexer.m_user_variables.size(); - + auto var_array = indexer.get_variable_array(); + auto variables = &var_array[0]; + int var_count = (int)var_array.size(); + typename native::expr_native* native_expr = native::compile_native(expression, variables, var_count, error); if (native_expr) @@ -1729,11 +1750,13 @@ namespace tp static inline const auto keyword_return = std::string_view("return"); static inline const auto keyword_jump = std::string_view("jump"); static inline const auto keyword_label = std::string_view("label"); + static inline const auto keyword_var = std::string_view("var"); - template + template< + typename T_ADD_VARIABLE, typename T_ADD_LABEL, typename T_ADD_JUMP, typename T_ADD_JUMP_IF, typename T_ADD_RETURN_VALUE, typename T_ADD_ASSIGN, typename T_ADD_CALL> static inline void parse_statement( - std::string_view statement, T_ADD_LABEL add_label, T_ADD_JUMP add_jump, T_ADD_JUMP_IF add_jump_if, T_ADD_RETURN_VALUE add_return_value, T_ADD_ASSIGN add_assign, - T_ADD_CALL add_call) + std::string_view statement, T_ADD_VARIABLE add_variable, T_ADD_LABEL add_label, T_ADD_JUMP add_jump, T_ADD_JUMP_IF add_jump_if, T_ADD_RETURN_VALUE add_return_value, + T_ADD_ASSIGN add_assign, T_ADD_CALL add_call) { auto [operation, expression] = split_at_char_excl(statement, ':'); @@ -1743,7 +1766,12 @@ namespace tp } else { - if (operation == keyword_label) + if (operation == keyword_var) + { + auto [var_name, var_scope] = split_at_char_excl(expression, '?'); + add_variable(var_name, var_scope); + } + else if (operation == keyword_label) { add_label(expression); } @@ -1900,7 +1928,7 @@ namespace tp template using t_indexer = typename portable::expr_portable_expression_build_indexer; - + template auto compile_using_indexer(const char* text, int* error, typename t_indexer& indexer) -> typename portable::portable_compiled_program* { @@ -1921,6 +1949,9 @@ namespace tp parser::parse_statement( statement, + // variable + [&](std::string_view name, std::string_view scope) { indexer.add_declared_variable(name, scope); }, + // label [&](std::string_view label) { lm.add_label(label, (int)program_statements.size()); }, @@ -1967,41 +1998,44 @@ namespace tp } // Add referenced variables to the lookup dict - for (auto itor : vm.m_variable_map) { - auto name = itor.first; - auto index = itor.second; - - int final_index = -1; + auto var_array = indexer.get_variable_array(); + for (auto itor : vm.m_variable_map) + { + auto name = itor.first; + auto index = itor.second; - auto variables = &indexer.m_user_variables[0]; - const int var_count = (int)indexer.m_user_variables.size(); + int final_index = -1; - for (int var_idx = 0; var_idx < var_count; ++var_idx) - { - const auto& var = variables[var_idx]; + auto variables = &var_array[0]; + int var_count = (int)var_array.size(); - if (name == std::string_view(var.name)) + for (int var_idx = 0; var_idx < var_count; ++var_idx) { - final_index = indexer.add_referenced_variable(&var); - break; + const auto& var = variables[var_idx]; + + if (name == std::string_view(var.name)) + { + final_index = indexer.add_referenced_variable(&var); + break; + } } - } - if (final_index == -1) - { - *error = -1; // TODO: variable not found - return nullptr; - } + if (final_index == -1) + { + *error = -1; // TODO: variable not found + return nullptr; + } - // Remap all indexes to the indexer value - for (auto& s : program_statements) - { - if (std::holds_alternative(s)) + // Remap all indexes to the indexer value + for (auto& s : program_statements) { - if (std::get(s).m_variable_build_index == index) + if (std::holds_alternative(s)) { - std::get(s).m_variable_final_index = final_index; + if (std::get(s).m_variable_build_index == index) + { + std::get(s).m_variable_final_index = final_index; + } } } } @@ -2112,15 +2146,15 @@ namespace tp return program; } - + template auto compile(const char* text, const variable* variables, int var_count, int* error) -> typename portable::portable_compiled_program* { t_indexer indexer; for (int v = 0; v < var_count; ++v) - { - indexer.add_user_variable(variables + v); - } + { + indexer.add_user_variable(variables + v); + } return compile_using_indexer(text, error, indexer); } @@ -2212,7 +2246,7 @@ namespace tp using t_vector = typename env_traits::t_vector; using variable_factory = details::variable_helper; #if (TP_COMPILER_ENABLED) - using t_indexer = program_details::t_indexer; + using t_indexer = program_details::t_indexer; #endif // #if (TP_COMPILER_ENABLED) static inline t_vector eval(const void* expr_buffer, const void* const expr_context[]) noexcept diff --git a/test/example_program.cpp b/test/example_program.cpp index bc67203..e7be51b 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -4,15 +4,16 @@ int main(int argc, char* argv[]) { - te::env_traits::t_atom x = 0.0f, y = 0.0f; - te::variable vars[] = {{"x", &x}, {"y", &y}}; + //te::env_traits::t_atom x = 0.0f, y = 0.0f; + //te::variable vars[] = {{"x", &x}, {"y", &y}}; te::t_indexer indexer; - indexer.add_user_variable(vars + 0); - indexer.add_user_variable(vars + 1); + //indexer.add_user_variable(vars + 0); + //indexer.add_user_variable(vars + 1); auto prog1 = [&indexer]() { const char* p1 = + "var: x;" "x: sqrt(5^2+7^2+11^2+(8-2)^2);" "jump: is_negative ? x < 0;" "return: x;" @@ -28,6 +29,7 @@ int main(int argc, char* argv[]) auto prog2 = [&indexer]() -> auto { const char* p2 = + "var: y;" "y: sqrt(5^2+7^2+11^2+(8-2)^2);" "jump: is_negative ? y < 0;" "return: y;" From d2eda832221c2543587e9a2251deecd57bbb2ab8 Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Mon, 3 Aug 2020 15:58:07 -0700 Subject: [PATCH 16/43] Fix for no user variables. Indexer reset helper. --- include/tinyprog.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index eb3b286..84877b4 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -1278,6 +1278,17 @@ namespace tp std::vector m_declared_variable_names; std::vector m_declared_variable_values; + void reset() + { + name_map.clear(); + index_map.clear(); + index_counter = 0; + m_env_variables.clear(); + m_declared_variables.clear(); + m_declared_variable_names.clear(); + m_declared_variable_values.clear(); + } + std::vector get_variable_array() const { std::vector combined; @@ -1586,8 +1597,8 @@ namespace tp compiled_expr* compile_using_indexer(typename portable::expr_portable_expression_build_indexer& indexer, const char* expression, int* error) { auto var_array = indexer.get_variable_array(); - auto variables = &var_array[0]; int var_count = (int)var_array.size(); + auto variables = (var_count > 0) ? &var_array[0] : nullptr; typename native::expr_native* native_expr = native::compile_native(expression, variables, var_count, error); From 95ef7cd2708bea8f7039bf60ba96931d0d1797d5 Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Mon, 3 Aug 2020 19:39:42 -0700 Subject: [PATCH 17/43] Vector resizes invalidate the string pointers, silly me. --- include/tinyprog.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 84877b4..6bb8e0d 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -1274,7 +1274,6 @@ namespace tp std::vector m_env_variables; - std::vector m_declared_variables; std::vector m_declared_variable_names; std::vector m_declared_variable_values; @@ -1284,7 +1283,6 @@ namespace tp index_map.clear(); index_counter = 0; m_env_variables.clear(); - m_declared_variables.clear(); m_declared_variable_names.clear(); m_declared_variable_values.clear(); } @@ -1292,18 +1290,22 @@ namespace tp std::vector get_variable_array() const { std::vector combined; - combined.reserve(m_env_variables.size() + m_declared_variables.size()); - combined.insert(combined.end(), m_env_variables.begin(), m_env_variables.end()); - combined.insert(combined.end(), m_declared_variables.begin(), m_declared_variables.end()); + for (auto var : m_env_variables) + { + combined.push_back(var); + } + + for (size_t v = 0; v < m_declared_variable_names.size(); ++v) + { + combined.push_back(variable{m_declared_variable_names[v].c_str(), &m_declared_variable_values[v]}); + } return combined; } void add_declared_variable(std::string_view name, std::string_view /*scope*/) { - auto idx = m_declared_variables.size(); - m_declared_variable_names.emplace_back(std::string(name)); + m_declared_variable_names.push_back(std::string(name)); m_declared_variable_values.resize(m_declared_variable_names.size()); - m_declared_variables.push_back(variable{m_declared_variable_names[idx].c_str(), &m_declared_variable_values[idx]}); } void add_user_variable(const variable* var) From 676fe0735bca31db7554041b6aebdf577badab0f Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Wed, 5 Aug 2020 21:59:36 -0700 Subject: [PATCH 18/43] Make the runtime lookup visible outside the compiler --- include/tinyprog.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 6bb8e0d..72a0372 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -274,13 +274,6 @@ namespace tp #endif // #if (TP_COMPILER_ENABLED) } // namespace tp -#if (TP_COMPILER_ENABLED) -#include -#include -#include -#include -#include - namespace tp { template @@ -427,7 +420,17 @@ namespace tp return var; } }; +} // namespace tp +#if (TP_COMPILER_ENABLED) +#include +#include +#include +#include +#include + +namespace tp +{ template struct native { From 0e361485c9e745529e05a82537e90351ffad824e Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Fri, 7 Aug 2020 14:16:02 -0700 Subject: [PATCH 19/43] Program serialization helper --- include/tinyprog.h | 239 ++++++++++++++++++++++++++++++++++++++- test/example_program.cpp | 107 ++++++++++++++++++ 2 files changed, 341 insertions(+), 5 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 72a0372..7814e45 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -2251,16 +2251,240 @@ namespace tp return {name, sevenargfn{func}, tp::FUNCTION7 | tp::FLAG_PURE, 0}; } }; + + struct serialized_program + { +#pragma pack(push, 1) + struct header_chunk + { + uint16_t magic; + uint16_t version; + uint16_t num_binding_names; + uint16_t padding; + }; + + struct chunk_header + { + uint16_t size; + uint16_t padding; + }; + + struct chunk : chunk_header + { + char data[1]; + }; + + struct statement_chunk : chunk + { + }; + + struct data_chunk : chunk + { + }; + + struct string_chunk : chunk + { + }; +#pragma pack(pop) + + template + static inline constexpr T round_up_to_multiple(T value, T multiple) noexcept + { + return ((value + multiple - 1) / multiple) * multiple; + } + + static inline constexpr uint16_t alignment{4}; + static inline constexpr auto out_header_size = uint16_t(sizeof(serialized_program::header_chunk)); + static_assert(out_header_size == round_up_to_multiple(uint16_t(sizeof(serialized_program::header_chunk)), alignment)); + + static inline constexpr auto statement_data_size = uint16_t(sizeof(serialized_program::chunk_header)); + static_assert(statement_data_size == round_up_to_multiple(uint16_t(sizeof(serialized_program::chunk_header)), alignment)); + + static inline constexpr auto expression_data_size = uint16_t(sizeof(serialized_program::chunk_header)); + static_assert(expression_data_size == round_up_to_multiple(uint16_t(sizeof(serialized_program::chunk_header)), alignment)); + + static inline constexpr auto string_header_size = uint16_t(sizeof(serialized_program::chunk_header)); + static_assert(string_header_size == round_up_to_multiple(uint16_t(sizeof(serialized_program::chunk_header)), alignment)); + + serialized_program(compiled_program* prog) + { + auto binding_name_count = prog->get_binding_array_size(); + auto binding_names = prog->get_binding_names(); + auto expression_size = prog->get_data_size(); + auto expression_src = prog->get_data(); + auto num_statements = prog->get_statement_array_size(); + auto statement_src = prog->get_statements(); + + // 2-pass iff-style + + size_t total_program_size = 0; + serialized_program::header_chunk out_header; + out_header.magic = uint16_t(0x1010); + out_header.version = uint16_t(0x0001); + out_header.num_binding_names = uint16_t(binding_name_count); + total_program_size += out_header_size; + + serialized_program::statement_chunk statement_data; + statement_data.size = uint16_t(num_statements * sizeof(statement_src[0])); + total_program_size += statement_data_size; + const auto statement_data_data_size = round_up_to_multiple(statement_data.size, alignment); + total_program_size += statement_data_data_size; + + serialized_program::data_chunk expression_data; + expression_data.size = uint16_t(expression_size); + total_program_size += expression_data_size; + const auto expression_data_data_size = round_up_to_multiple(expression_data.size, alignment); + total_program_size += expression_data_data_size; + + std::vector strs; + for (size_t i = 0; i < binding_name_count; ++i) + { + serialized_program::string_chunk chonk; + chonk.size = uint16_t(::strlen(binding_names[i]) + 1); + total_program_size += string_header_size; + total_program_size += round_up_to_multiple(chonk.size, uint16_t(alignment)); + strs.push_back(chonk); + } + + // + + char* const serialized_program = (char*)::malloc(total_program_size); + char* p = serialized_program; + + this->header = (header_chunk*)p; + ::memcpy(p, &out_header, out_header_size); + p += out_header_size; + + this->statements = (statement_chunk*)p; + ::memcpy(p, &statement_data, statement_data_size); + p += statement_data_size; + ::memcpy(p, &statement_src[0], statement_data.size); + p += statement_data_data_size; + + this->data = (data_chunk*)p; + ::memcpy(p, &expression_data, expression_data_size); + p += expression_data_size; + ::memcpy(p, &expression_src[0], expression_data_data_size); + p += expression_data_data_size; + + this->strings = (string_chunk*)p; + + for (size_t i = 0; i < binding_name_count; ++i) + { + ::memcpy(p, &strs[i], string_header_size); + p += string_header_size; + + ::memcpy(p, binding_names[i], strs[i].size - 1); + p[strs[i].size - 1] = '\0'; + p += round_up_to_multiple(strs[i].size, alignment); + } + + this->raw_data = serialized_program; + this->raw_data_size = total_program_size; + } + + serialized_program(const void* serialized_program, size_t total_program_size) + { + this->raw_data = nullptr; + this->raw_data_size = total_program_size; + + const char* p = (const char*)serialized_program; + + this->header = (header_chunk*)p; + p += out_header_size; + + this->statements = (statement_chunk*)p; + p += statement_data_size; + p += round_up_to_multiple(this->statements->size, alignment); + + this->data = (data_chunk*)p; + p += expression_data_size; + p += round_up_to_multiple(this->data->size, alignment); + + this->strings = (string_chunk*)p; + } + + ~serialized_program() + { + if (raw_data) + { + ::free(raw_data); + } + } + + const statement* get_statements_array() const noexcept + { + return reinterpret_cast(&statements->data[0]); + } + + size_t get_statements_array_size() const noexcept + { + return statements->size / sizeof(statement); + } + + const void* get_expression_data() const noexcept + { + return &data->data[0]; + } + + const size_t get_expression_size() const noexcept + { + return data->size; + } + + const size_t get_num_bindings() const noexcept + { + return this->header->num_binding_names; + } + + const char* get_binding_string(uint16_t index) const noexcept + { + if (this->header->num_binding_names > index) + { + auto p = (const char*)this->strings; + for (size_t i = 0; i < this->header->num_binding_names; ++i) + { + auto chonk = (const string_chunk*)p; + if (i == index) + { + return &chonk->data[0]; + } + + p += string_header_size; + p += round_up_to_multiple(chonk->size, alignment); + } + } + return nullptr; + } + + const size_t get_raw_data_size() const noexcept + { + return raw_data_size; + } + + const size_t get_raw_data() const noexcept + { + return raw_data_size; + } + + void* raw_data{nullptr}; + size_t raw_data_size{0}; + const header_chunk* header{nullptr}; + const statement_chunk* statements{nullptr}; + const data_chunk* data{nullptr}; + const string_chunk* strings{nullptr}; + }; } // namespace details template struct impl { - using env_traits = T_TRAITS; - using variable = ::tp::variable; - using t_atom = typename env_traits::t_atom; - using t_vector = typename env_traits::t_vector; - using variable_factory = details::variable_helper; + using env_traits = T_TRAITS; + using variable = ::tp::variable; + using t_atom = typename env_traits::t_atom; + using t_vector = typename env_traits::t_vector; + using variable_factory = details::variable_helper; + using serialized_program = details::serialized_program; #if (TP_COMPILER_ENABLED) using t_indexer = program_details::t_indexer; #endif // #if (TP_COMPILER_ENABLED) @@ -2307,6 +2531,11 @@ namespace tp return env_traits::t_vector_builtins::nan(); } + static inline t_vector eval_program(serialized_program& prog, const void* const* binding_addrs) + { + return eval_program(prog.get_statements_array(), (int)prog.get_statements_array_size(), prog.get_expression_data(), binding_addrs); + } + #if (TP_COMPILER_ENABLED) static compiled_expr* compile(const char* expression, const variable* variables, int var_count, int* error) { diff --git a/test/example_program.cpp b/test/example_program.cpp index e7be51b..b01a912 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -2,6 +2,112 @@ #include "tinyprog.h" #include +int main(int argc, char* argv[]) +{ + te::env_traits::t_atom x = 0.0f, y = 0.0f; + te::variable vars[] = {{"x", &x}, {"y", &y}}; + te::t_indexer indexer; + + indexer.add_user_variable(vars + 0); + indexer.add_user_variable(vars + 1); + + auto prog1 = [&indexer]() { + const char* p1 = + "x: sqrt(5^2+7^2+11^2+(8-2)^2);" + "jump: is_negative ? x < 0;" + "return: x;" + "label: is_negative;" + "return: -1 * x;"; + int err1 = 0; + auto res = te::compile_program_using_indexer(p1, &err1, indexer); + assert(res); + return res; + }(); + auto result1 = te::eval_program(prog1); + + auto prog2 = [&indexer]() -> auto + { + const char* p2 = + "y: sqrt(5^2+7^2+11^2+(8-2)^2);" + "jump: is_negative ? y < 0;" + "return: y;" + "label: is_negative;" + "return: -1 * y;"; + int err2 = 0; + auto res = te::compile_program_using_indexer(p2, &err2, indexer); + assert(res); + assert(err2 == 0); + return res; + } + (); + auto result2 = te::eval_program(prog2); + + assert(result1 == result2); + assert(prog1->get_binding_array_size() <= prog2->get_binding_array_size()); + + auto ba1 = prog1->get_binding_addresses(); + auto ba2 = prog2->get_binding_addresses(); + + for (int i = 0; i < prog1->get_binding_array_size(); ++i) + { + assert(ba1[i] == ba2[i]); + } + + using builtins = tp::compiler_builtins; + + { + std::vector binding_array; + te::serialized_program sp1(prog1); + + for (uint16_t i = 0; i < (uint16_t)sp1.get_num_bindings(); ++i) + { + auto name = sp1.get_binding_string(i); + if (_stricmp(name, "x") == 0) + { + binding_array.push_back(&x); + } + else if (_stricmp(name, "y") == 0) + { + binding_array.push_back(&y); + } + else + { + binding_array.push_back(builtins::find_builtin_address(name)); + } + } + auto spresult1 = te::eval_program(sp1, &binding_array[0]); + } + + { + std::vector binding_array; + te::serialized_program sp2(prog2); + + for (uint16_t i = 0; i < sp2.get_num_bindings(); ++i) + { + auto name = sp2.get_binding_string(i); + if (_stricmp(name, "x") == 0) + { + binding_array.push_back(&x); + } + else if (_stricmp(name, "y") == 0) + { + binding_array.push_back(&y); + } + else + { + binding_array.push_back(builtins::find_builtin_address(name)); + } + } + auto spresult2 = te::eval_program(sp2, &binding_array[0]); + } + + delete prog1; + delete prog2; + + return 0; +} + +#if 0 int main(int argc, char* argv[]) { //te::env_traits::t_atom x = 0.0f, y = 0.0f; @@ -60,3 +166,4 @@ int main(int argc, char* argv[]) return 0; } +#endif \ No newline at end of file From 5961b04f2b876e96acaeda9272b30061d7033af3 Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Fri, 7 Aug 2020 18:29:27 -0700 Subject: [PATCH 20/43] Delcared vars are working --- include/tinyprog.h | 111 +++++++++++++----- test/example_program.cpp | 237 +++++++++++++++++++++++++++------------ 2 files changed, 244 insertions(+), 104 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 7814e45..6423d15 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -2274,17 +2274,10 @@ namespace tp char data[1]; }; - struct statement_chunk : chunk - { - }; - - struct data_chunk : chunk - { - }; - - struct string_chunk : chunk - { - }; + using statement_chunk = chunk; + using data_chunk = chunk; + using string_chunk = chunk; + using user_var_chunk = chunk; #pragma pack(pop) template @@ -2294,19 +2287,22 @@ namespace tp } static inline constexpr uint16_t alignment{4}; - static inline constexpr auto out_header_size = uint16_t(sizeof(serialized_program::header_chunk)); - static_assert(out_header_size == round_up_to_multiple(uint16_t(sizeof(serialized_program::header_chunk)), alignment)); + static inline constexpr auto out_header_size = uint16_t(sizeof(header_chunk)); + static_assert(out_header_size == round_up_to_multiple(uint16_t(sizeof(header_chunk)), alignment)); + + static inline constexpr auto statement_data_size = uint16_t(sizeof(chunk_header)); + static_assert(statement_data_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment)); - static inline constexpr auto statement_data_size = uint16_t(sizeof(serialized_program::chunk_header)); - static_assert(statement_data_size == round_up_to_multiple(uint16_t(sizeof(serialized_program::chunk_header)), alignment)); + static inline constexpr auto expression_data_size = uint16_t(sizeof(chunk_header)); + static_assert(expression_data_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment)); - static inline constexpr auto expression_data_size = uint16_t(sizeof(serialized_program::chunk_header)); - static_assert(expression_data_size == round_up_to_multiple(uint16_t(sizeof(serialized_program::chunk_header)), alignment)); + static inline constexpr auto string_header_size = uint16_t(sizeof(chunk_header)); + static_assert(string_header_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment)); - static inline constexpr auto string_header_size = uint16_t(sizeof(serialized_program::chunk_header)); - static_assert(string_header_size == round_up_to_multiple(uint16_t(sizeof(serialized_program::chunk_header)), alignment)); + static inline constexpr auto user_var_size = uint16_t(sizeof(chunk_header)); + static_assert(user_var_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment)); - serialized_program(compiled_program* prog) + serialized_program(compiled_program* prog, std::vector& user_vars) { auto binding_name_count = prog->get_binding_array_size(); auto binding_names = prog->get_binding_names(); @@ -2314,38 +2310,63 @@ namespace tp auto expression_src = prog->get_data(); auto num_statements = prog->get_statement_array_size(); auto statement_src = prog->get_statements(); + auto user_var_count = user_vars.size(); // 2-pass iff-style - size_t total_program_size = 0; - serialized_program::header_chunk out_header; + size_t total_program_size = 0; + header_chunk out_header; out_header.magic = uint16_t(0x1010); out_header.version = uint16_t(0x0001); out_header.num_binding_names = uint16_t(binding_name_count); total_program_size += out_header_size; - serialized_program::statement_chunk statement_data; + statement_chunk statement_data; statement_data.size = uint16_t(num_statements * sizeof(statement_src[0])); total_program_size += statement_data_size; const auto statement_data_data_size = round_up_to_multiple(statement_data.size, alignment); total_program_size += statement_data_data_size; - serialized_program::data_chunk expression_data; + data_chunk expression_data; expression_data.size = uint16_t(expression_size); total_program_size += expression_data_size; const auto expression_data_data_size = round_up_to_multiple(expression_data.size, alignment); total_program_size += expression_data_data_size; - std::vector strs; + std::vector strs; for (size_t i = 0; i < binding_name_count; ++i) { - serialized_program::string_chunk chonk; + string_chunk chonk; chonk.size = uint16_t(::strlen(binding_names[i]) + 1); total_program_size += string_header_size; total_program_size += round_up_to_multiple(chonk.size, uint16_t(alignment)); strs.push_back(chonk); } + std::vector user_var_indexes; + user_var_indexes.resize(user_var_count); + std::fill(std::begin(user_var_indexes), std::end(user_var_indexes), -1); + for (size_t i = 0; i < binding_name_count; ++i) + { + for (size_t j = 0; j < user_var_count; ++j) + { + if (user_var_indexes[j] == -1) + { + if (_stricmp(binding_names[i], user_vars[j].c_str()) == 0) + { + user_var_indexes[j] = int(i); + break; + } + } + } + } + + user_var_chunk user_var_data; + user_var_data.size = uint16_t(sizeof(int) * user_var_count); + total_program_size += user_var_size; + const auto user_var_data_data_size = round_up_to_multiple(user_var_data.size, alignment); + total_program_size += user_var_data_data_size; + // char* const serialized_program = (char*)::malloc(total_program_size); @@ -2364,7 +2385,7 @@ namespace tp this->data = (data_chunk*)p; ::memcpy(p, &expression_data, expression_data_size); p += expression_data_size; - ::memcpy(p, &expression_src[0], expression_data_data_size); + ::memcpy(p, &expression_src[0], expression_data.size); p += expression_data_data_size; this->strings = (string_chunk*)p; @@ -2379,6 +2400,14 @@ namespace tp p += round_up_to_multiple(strs[i].size, alignment); } + this->user_vars = (user_var_chunk*)p; + ::memcpy(p, &user_var_data, user_var_size); + p += user_var_size; + ::memcpy(p, &user_var_indexes[0], user_var_data.size); + p += user_var_data_data_size; + + // + this->raw_data = serialized_program; this->raw_data_size = total_program_size; } @@ -2402,6 +2431,17 @@ namespace tp p += round_up_to_multiple(this->data->size, alignment); this->strings = (string_chunk*)p; + + for (size_t i = 0; i < this->header->num_binding_names; ++i) + { + auto chonk = (const string_chunk*)p; + p += string_header_size; + p += round_up_to_multiple(chonk->size, alignment); + } + + this->user_vars = (user_var_chunk*)p; + p += user_var_size; + p += round_up_to_multiple(this->user_vars->size, alignment); } ~serialized_program() @@ -2457,14 +2497,24 @@ namespace tp return nullptr; } - const size_t get_raw_data_size() const noexcept + size_t get_raw_data_size() const noexcept { return raw_data_size; } - const size_t get_raw_data() const noexcept + const void* get_raw_data() const noexcept { - return raw_data_size; + return raw_data; + } + + size_t get_num_user_vars() const noexcept + { + return (user_vars != nullptr) ? user_vars->size / sizeof(int) : 0; + } + + const int* get_user_vars() const noexcept + { + return (user_vars != nullptr) ? (int*)&user_vars->data[0] : nullptr; } void* raw_data{nullptr}; @@ -2473,6 +2523,7 @@ namespace tp const statement_chunk* statements{nullptr}; const data_chunk* data{nullptr}; const string_chunk* strings{nullptr}; + const user_var_chunk* user_vars{nullptr}; }; } // namespace details diff --git a/test/example_program.cpp b/test/example_program.cpp index b01a912..a5bcf1a 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -2,107 +2,196 @@ #include "tinyprog.h" #include -int main(int argc, char* argv[]) +bool serialize_program_to_disk(const char* file_name, const char* prog_text, te::t_indexer& indexer) { - te::env_traits::t_atom x = 0.0f, y = 0.0f; - te::variable vars[] = {{"x", &x}, {"y", &y}}; - te::t_indexer indexer; - - indexer.add_user_variable(vars + 0); - indexer.add_user_variable(vars + 1); + using builtins = tp::compiler_builtins; - auto prog1 = [&indexer]() { - const char* p1 = - "x: sqrt(5^2+7^2+11^2+(8-2)^2);" - "jump: is_negative ? x < 0;" - "return: x;" - "label: is_negative;" - "return: -1 * x;"; + auto prog = [prog_text, &indexer]() { int err1 = 0; - auto res = te::compile_program_using_indexer(p1, &err1, indexer); + auto res = te::compile_program_using_indexer(prog_text, &err1, indexer); assert(res); return res; }(); - auto result1 = te::eval_program(prog1); - auto prog2 = [&indexer]() -> auto + te::serialized_program sp(prog, indexer.m_declared_variable_names); + + FILE* f; + if (!::fopen_s(&f, file_name, "wb")) { - const char* p2 = - "y: sqrt(5^2+7^2+11^2+(8-2)^2);" - "jump: is_negative ? y < 0;" - "return: y;" - "label: is_negative;" - "return: -1 * y;"; - int err2 = 0; - auto res = te::compile_program_using_indexer(p2, &err2, indexer); - assert(res); - assert(err2 == 0); - return res; - } - (); - auto result2 = te::eval_program(prog2); + ::fwrite(sp.get_raw_data(), 1, sp.get_raw_data_size(), f); + ::fflush(f); + ::fclose(f); + f = nullptr; - assert(result1 == result2); - assert(prog1->get_binding_array_size() <= prog2->get_binding_array_size()); + return true; + } - auto ba1 = prog1->get_binding_addresses(); - auto ba2 = prog2->get_binding_addresses(); + return false; +} - for (int i = 0; i < prog1->get_binding_array_size(); ++i) +std::tuple serialize_from_disk(const char* file_name) +{ + FILE* f; + if (!::fopen_s(&f, file_name, "rb")) { - assert(ba1[i] == ba2[i]); + ::fseek(f, 0, SEEK_END); + auto s = ::ftell(f); + ::fseek(f, 0, SEEK_SET); + auto mem = ::malloc(s); + if (mem) + { + ::fread(mem, 1, s, f); + return {mem, s}; + } } + return {nullptr, 0}; +} +int main(int argc, char* argv[]) +{ using builtins = tp::compiler_builtins; + //te::env_traits::t_atom x = 0.0f, y = 0.0f; + //te::variable vars[] = {{"x", &x}, {"y", &y}}; + + //{ + // te::t_indexer indexer; + // //indexer.add_user_variable(vars + 0); + // //indexer.add_user_variable(vars + 1); + // + // const char* p1 = + // "var: x;" + // "x: sqrt(5^2+7^2+11^2+(8-2)^2);" + // "jump: is_negative ? x < 0;" + // "return: x;" + // "label: is_negative;" + // "return: -1 * x;"; + // + // serialize_program_to_disk("prog1.tpp", p1, indexer); + // + // const char* p2 = + // "var: y;" + // "y: sqrt(5^2+7^2+11^2+(8-2)^2);" + // "jump: is_negative ? y < 0;" + // "return: y;" + // "label: is_negative;" + // "return: -1 * y;"; + // + // serialize_program_to_disk("prog2.tpp", p2, indexer); + //} + { - std::vector binding_array; - te::serialized_program sp1(prog1); + auto [buffer, buffer_size] = serialize_from_disk("prog1.tpp"); + te::serialized_program prog(buffer, buffer_size); - for (uint16_t i = 0; i < (uint16_t)sp1.get_num_bindings(); ++i) + std::vector binding_array; + binding_array.resize(prog.get_num_bindings()); + std::fill_n(std::begin(binding_array), prog.get_num_bindings(), nullptr); + + std::vector user_var_array; + user_var_array.resize(prog.get_num_user_vars()); + + for (uint16_t i = 0; i < (uint16_t)prog.get_num_user_vars(); ++i) { - auto name = sp1.get_binding_string(i); - if (_stricmp(name, "x") == 0) - { - binding_array.push_back(&x); - } - else if (_stricmp(name, "y") == 0) - { - binding_array.push_back(&y); - } - else - { - binding_array.push_back(builtins::find_builtin_address(name)); - } + auto binding_idx = prog.get_user_vars()[i]; + binding_array[binding_idx] = &user_var_array[i]; } - auto spresult1 = te::eval_program(sp1, &binding_array[0]); - } - - { - std::vector binding_array; - te::serialized_program sp2(prog2); - for (uint16_t i = 0; i < sp2.get_num_bindings(); ++i) + for (uint16_t i = 0; i < (uint16_t)prog.get_num_bindings(); ++i) { - auto name = sp2.get_binding_string(i); - if (_stricmp(name, "x") == 0) - { - binding_array.push_back(&x); - } - else if (_stricmp(name, "y") == 0) + if (!binding_array[i]) { - binding_array.push_back(&y); - } - else - { - binding_array.push_back(builtins::find_builtin_address(name)); + auto name = prog.get_binding_string(i); + binding_array[i] = builtins::find_builtin_address(name); } + assert(binding_array[i] != nullptr); } - auto spresult2 = te::eval_program(sp2, &binding_array[0]); + + auto result = te::eval_program(prog, &binding_array[0]); + ::free(buffer); } - delete prog1; - delete prog2; + // + // auto prog1 = [&indexer]() { + // int err1 = 0; + // auto res = te::compile_program_using_indexer(p1, &err1, indexer); + // assert(res); + // return res; + //}(); + // auto result1 = te::eval_program(prog1); + // + // auto prog2 = [&indexer]() -> auto + //{ + // int err2 = 0; + // auto res = te::compile_program_using_indexer(p2, &err2, indexer); + // assert(res); + // assert(err2 == 0); + // return res; + //} + //(); + // auto result2 = te::eval_program(prog2); + // + // assert(result1 == result2); + // assert(prog1->get_binding_array_size() <= prog2->get_binding_array_size()); + // + // auto ba1 = prog1->get_binding_addresses(); + // auto ba2 = prog2->get_binding_addresses(); + // + // for (int i = 0; i < prog1->get_binding_array_size(); ++i) + //{ + // assert(ba1[i] == ba2[i]); + //} + // + // using builtins = tp::compiler_builtins; + // + //{ + // std::vector binding_array; + // te::serialized_program sp1(prog1); + // + // for (uint16_t i = 0; i < (uint16_t)sp1.get_num_bindings(); ++i) + // { + // auto name = sp1.get_binding_string(i); + // if (_stricmp(name, "x") == 0) + // { + // binding_array.push_back(&x); + // } + // else if (_stricmp(name, "y") == 0) + // { + // binding_array.push_back(&y); + // } + // else + // { + // binding_array.push_back(builtins::find_builtin_address(name)); + // } + // } + // auto spresult1 = te::eval_program(sp1, &binding_array[0]); + //} + // + //{ + // std::vector binding_array; + // te::serialized_program sp2(prog2); + // + // for (uint16_t i = 0; i < sp2.get_num_bindings(); ++i) + // { + // auto name = sp2.get_binding_string(i); + // if (_stricmp(name, "x") == 0) + // { + // binding_array.push_back(&x); + // } + // else if (_stricmp(name, "y") == 0) + // { + // binding_array.push_back(&y); + // } + // else + // { + // binding_array.push_back(builtins::find_builtin_address(name)); + // } + // } + // auto spresult2 = te::eval_program(sp2, &binding_array[0]); + //} + // + // delete prog1; + // delete prog2; return 0; } From e8e938843c6609912101d9e988f0f72cae7a882c Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Fri, 7 Aug 2020 23:01:35 -0700 Subject: [PATCH 21/43] Now possible to merge multiple programs into one --- include/tinyprog.h | 230 +++++++++++++++++++++++++++++---------- test/example_program.cpp | 60 +++++----- 2 files changed, 202 insertions(+), 88 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 6423d15..fa94549 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -2260,7 +2260,7 @@ namespace tp uint16_t magic; uint16_t version; uint16_t num_binding_names; - uint16_t padding; + uint16_t num_subprograms; }; struct chunk_header @@ -2274,6 +2274,7 @@ namespace tp char data[1]; }; + using program_chunk = chunk; using statement_chunk = chunk; using data_chunk = chunk; using string_chunk = chunk; @@ -2302,36 +2303,93 @@ namespace tp static inline constexpr auto user_var_size = uint16_t(sizeof(chunk_header)); static_assert(user_var_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment)); - serialized_program(compiled_program* prog, std::vector& user_vars) + struct subprogram { - auto binding_name_count = prog->get_binding_array_size(); - auto binding_names = prog->get_binding_names(); - auto expression_size = prog->get_data_size(); - auto expression_src = prog->get_data(); - auto num_statements = prog->get_statement_array_size(); - auto statement_src = prog->get_statements(); - auto user_var_count = user_vars.size(); + const statement_chunk* statements{nullptr}; + const data_chunk* data{nullptr}; + }; + + const string_chunk* strings{nullptr}; + const user_var_chunk* user_vars{nullptr}; + void* raw_data{nullptr}; + size_t raw_data_size{0}; + const header_chunk* header{nullptr}; + const statement_chunk* first_subprogram{nullptr}; + + serialized_program(compiled_program** programs, int num_programs, std::vector& user_vars_in) + { + std::vector subprograms; + subprograms.resize(num_programs); + + auto user_var_count = user_vars_in.size(); + + auto binding_name_count = programs[0]->get_binding_array_size(); + auto binding_names = programs[0]->get_binding_names(); + auto most_bindings_idx = 0; + for (int subprogram_idx = 1; subprogram_idx < num_programs; ++subprogram_idx) + { + auto next_binding_name_count = programs[0]->get_binding_array_size(); + auto next_binding_names = programs[0]->get_binding_names(); + + auto verify_bindings = [&]() { + for (auto i = std::min(binding_name_count, next_binding_name_count); i > 0; ++i) + { + if (strcmp(binding_names[i], next_binding_names[i]) != 0) + { + return false; + } + } + return true; + }; + assert(verify_bindings()); - // 2-pass iff-style + if (binding_name_count < next_binding_name_count) + { + most_bindings_idx = subprogram_idx; + binding_name_count = next_binding_name_count; + binding_names = next_binding_names; + } + } + + size_t total_program_size = 0; - size_t total_program_size = 0; header_chunk out_header; out_header.magic = uint16_t(0x1010); out_header.version = uint16_t(0x0001); out_header.num_binding_names = uint16_t(binding_name_count); + out_header.num_subprograms = uint16_t(num_programs); total_program_size += out_header_size; - statement_chunk statement_data; - statement_data.size = uint16_t(num_statements * sizeof(statement_src[0])); - total_program_size += statement_data_size; - const auto statement_data_data_size = round_up_to_multiple(statement_data.size, alignment); - total_program_size += statement_data_data_size; + struct program_state + { + statement_chunk statement_data; + data_chunk expression_data; + size_t statement_data_data_size; + size_t expression_data_data_size; + }; + + std::vector program_states; + program_states.resize(num_programs); - data_chunk expression_data; - expression_data.size = uint16_t(expression_size); - total_program_size += expression_data_size; - const auto expression_data_data_size = round_up_to_multiple(expression_data.size, alignment); - total_program_size += expression_data_data_size; + for (int subprogram_idx = 0; subprogram_idx < num_programs; ++subprogram_idx) + { + auto& prog_state = program_states[subprogram_idx]; + auto prog = programs[subprogram_idx]; + auto expression_size = prog->get_data_size(); + auto expression_src = prog->get_data(); + auto num_statements = prog->get_statement_array_size(); + auto statement_src = prog->get_statements(); + + prog_state.statement_data.size = uint16_t(num_statements * sizeof(statement_src[0])); + total_program_size += statement_data_size; + prog_state.statement_data_data_size = round_up_to_multiple(prog_state.statement_data.size, alignment); + total_program_size += prog_state.statement_data_data_size; + + prog_state.expression_data.size = uint16_t(expression_size); + total_program_size += expression_data_size; + prog_state.expression_data_data_size = round_up_to_multiple(prog_state.expression_data.size, alignment); + total_program_size += prog_state.expression_data_data_size; + } std::vector strs; for (size_t i = 0; i < binding_name_count; ++i) @@ -2352,7 +2410,7 @@ namespace tp { if (user_var_indexes[j] == -1) { - if (_stricmp(binding_names[i], user_vars[j].c_str()) == 0) + if (_stricmp(binding_names[i], user_vars_in[j].c_str()) == 0) { user_var_indexes[j] = int(i); break; @@ -2372,21 +2430,36 @@ namespace tp char* const serialized_program = (char*)::malloc(total_program_size); char* p = serialized_program; + // + this->header = (header_chunk*)p; ::memcpy(p, &out_header, out_header_size); p += out_header_size; - this->statements = (statement_chunk*)p; - ::memcpy(p, &statement_data, statement_data_size); - p += statement_data_size; - ::memcpy(p, &statement_src[0], statement_data.size); - p += statement_data_data_size; + first_subprogram = (statement_chunk*)p; - this->data = (data_chunk*)p; - ::memcpy(p, &expression_data, expression_data_size); - p += expression_data_size; - ::memcpy(p, &expression_src[0], expression_data.size); - p += expression_data_data_size; + for (int subprogram_idx = 0; subprogram_idx < num_programs; ++subprogram_idx) + { + auto& prog_state = program_states[subprogram_idx]; + auto& subprogram = subprograms[subprogram_idx]; + + auto prog = programs[subprogram_idx]; + auto statement_src = prog->get_statements(); + + auto expression_src = prog->get_data(); + + subprogram.statements = (statement_chunk*)p; + ::memcpy(p, &prog_state.statement_data, statement_data_size); + p += statement_data_size; + ::memcpy(p, &statement_src[0], prog_state.statement_data.size); + p += prog_state.statement_data_data_size; + + subprogram.data = (data_chunk*)p; + ::memcpy(p, &prog_state.expression_data, expression_data_size); + p += expression_data_size; + ::memcpy(p, &expression_src[0], prog_state.expression_data.size); + p += prog_state.expression_data_data_size; + } this->strings = (string_chunk*)p; @@ -2407,7 +2480,7 @@ namespace tp p += user_var_data_data_size; // - + this->raw_data = serialized_program; this->raw_data_size = total_program_size; } @@ -2422,28 +2495,47 @@ namespace tp this->header = (header_chunk*)p; p += out_header_size; - this->statements = (statement_chunk*)p; - p += statement_data_size; - p += round_up_to_multiple(this->statements->size, alignment); + first_subprogram = (statement_chunk*)p; - this->data = (data_chunk*)p; - p += expression_data_size; - p += round_up_to_multiple(this->data->size, alignment); + for (int subprogram_idx = 0; subprogram_idx < this->header->num_subprograms; ++subprogram_idx) + { + auto subprogram_statements = (statement_chunk*)p; + p += statement_data_size; + //::memcpy(p, &statement_src[0], prog_state.statement_data.size); + p += round_up_to_multiple(subprogram_statements->size, alignment); + + auto subprogram_data = (data_chunk*)p; + //::memcpy(p, &prog_state.expression_data, expression_data_size); + p += expression_data_size; + //::memcpy(p, &expression_src[0], prog_state.expression_data.size); + p += round_up_to_multiple(subprogram_data->size, alignment); + } this->strings = (string_chunk*)p; - - for (size_t i = 0; i < this->header->num_binding_names; ++i) + + for (size_t i = 0; i < header->num_binding_names; ++i) { + //::memcpy(p, &strs[i], string_header_size); auto chonk = (const string_chunk*)p; p += string_header_size; + + //::memcpy(p, binding_names[i], strs[i].size - 1); + //p[strs[i].size - 1] = '\0'; p += round_up_to_multiple(chonk->size, alignment); } this->user_vars = (user_var_chunk*)p; + //::memcpy(p, &user_var_data, user_var_size); p += user_var_size; + //::memcpy(p, &user_var_indexes[0], user_var_data.size); p += round_up_to_multiple(this->user_vars->size, alignment); } + serialized_program(std::tuple args) + : serialized_program(std::get<0>(args), std::get<1>(args)) + { + } + ~serialized_program() { if (raw_data) @@ -2452,24 +2544,54 @@ namespace tp } } - const statement* get_statements_array() const noexcept + const std::tuple get_subprogram_data(int subprogram_index) const noexcept + { + const char* p = (const char*)first_subprogram; + + for (int i = 0; i < this->header->num_subprograms; ++i) + { + auto statements = (statement_chunk*)p; + p += statement_data_size; + //::memcpy(p, &statement_src[0], prog_state.statement_data.size); + p += round_up_to_multiple(statements->size, alignment); + + auto subprogram_data = (data_chunk*)p; + //::memcpy(p, &prog_state.expression_data, expression_data_size); + p += expression_data_size; + //::memcpy(p, &expression_src[0], prog_state.expression_data.size); + p += round_up_to_multiple(subprogram_data->size, alignment); + + if (i == subprogram_index) + { + return {statements, subprogram_data}; + } + } + + return {nullptr, nullptr}; + } + + const statement* get_statements_array(int subprogram_index) const noexcept { + auto [statements, subprogram_data] = get_subprogram_data(subprogram_index); return reinterpret_cast(&statements->data[0]); } - size_t get_statements_array_size() const noexcept + size_t get_statements_array_size(int subprogram_index) const noexcept { + auto [statements, subprogram_data] = get_subprogram_data(subprogram_index); return statements->size / sizeof(statement); } - const void* get_expression_data() const noexcept + const void* get_expression_data(int subprogram_index) const noexcept { - return &data->data[0]; + auto [statements, subprogram_data] = get_subprogram_data(subprogram_index); + return &subprogram_data->data[0]; } - const size_t get_expression_size() const noexcept + const size_t get_expression_size(int subprogram_index) const noexcept { - return data->size; + auto [statements, subprogram_data] = get_subprogram_data(subprogram_index); + return subprogram_data->size; } const size_t get_num_bindings() const noexcept @@ -2511,19 +2633,11 @@ namespace tp { return (user_vars != nullptr) ? user_vars->size / sizeof(int) : 0; } - + const int* get_user_vars() const noexcept { return (user_vars != nullptr) ? (int*)&user_vars->data[0] : nullptr; } - - void* raw_data{nullptr}; - size_t raw_data_size{0}; - const header_chunk* header{nullptr}; - const statement_chunk* statements{nullptr}; - const data_chunk* data{nullptr}; - const string_chunk* strings{nullptr}; - const user_var_chunk* user_vars{nullptr}; }; } // namespace details @@ -2582,9 +2696,9 @@ namespace tp return env_traits::t_vector_builtins::nan(); } - static inline t_vector eval_program(serialized_program& prog, const void* const* binding_addrs) + static inline t_vector eval_program(serialized_program& prog, int subprogram, const void* const* binding_addrs) { - return eval_program(prog.get_statements_array(), (int)prog.get_statements_array_size(), prog.get_expression_data(), binding_addrs); + return eval_program(prog.get_statements_array(subprogram), (int)prog.get_statements_array_size(subprogram), prog.get_expression_data(subprogram), binding_addrs); } #if (TP_COMPILER_ENABLED) diff --git a/test/example_program.cpp b/test/example_program.cpp index a5bcf1a..8db221c 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -13,7 +13,7 @@ bool serialize_program_to_disk(const char* file_name, const char* prog_text, te: return res; }(); - te::serialized_program sp(prog, indexer.m_declared_variable_names); + te::serialized_program sp(&prog, 1, indexer.m_declared_variable_names); FILE* f; if (!::fopen_s(&f, file_name, "wb")) @@ -54,36 +54,36 @@ int main(int argc, char* argv[]) //te::env_traits::t_atom x = 0.0f, y = 0.0f; //te::variable vars[] = {{"x", &x}, {"y", &y}}; - //{ - // te::t_indexer indexer; - // //indexer.add_user_variable(vars + 0); - // //indexer.add_user_variable(vars + 1); - // - // const char* p1 = - // "var: x;" - // "x: sqrt(5^2+7^2+11^2+(8-2)^2);" - // "jump: is_negative ? x < 0;" - // "return: x;" - // "label: is_negative;" - // "return: -1 * x;"; - // - // serialize_program_to_disk("prog1.tpp", p1, indexer); - // - // const char* p2 = - // "var: y;" - // "y: sqrt(5^2+7^2+11^2+(8-2)^2);" - // "jump: is_negative ? y < 0;" - // "return: y;" - // "label: is_negative;" - // "return: -1 * y;"; - // - // serialize_program_to_disk("prog2.tpp", p2, indexer); - //} - { - auto [buffer, buffer_size] = serialize_from_disk("prog1.tpp"); - te::serialized_program prog(buffer, buffer_size); + te::t_indexer indexer; + //indexer.add_user_variable(vars + 0); + //indexer.add_user_variable(vars + 1); + + const char* p1 = + "var: x;" + "x: sqrt(5^2+7^2+11^2+(8-2)^2);" + "jump: is_negative ? x < 0;" + "return: x;" + "label: is_negative;" + "return: -1 * x;"; + + serialize_program_to_disk("prog1.tpp", p1, indexer); + + const char* p2 = + "var: y;" + "y: sqrt(5^2+7^2+11^2+(8-2)^2);" + "jump: is_negative ? y < 0;" + "return: y;" + "label: is_negative;" + "return: -1 * y;"; + + serialize_program_to_disk("prog2.tpp", p2, indexer); + } + { + auto [buffer, size] = serialize_from_disk("prog1.tpp"); + te::serialized_program prog(buffer, size); + std::vector binding_array; binding_array.resize(prog.get_num_bindings()); std::fill_n(std::begin(binding_array), prog.get_num_bindings(), nullptr); @@ -107,7 +107,7 @@ int main(int argc, char* argv[]) assert(binding_array[i] != nullptr); } - auto result = te::eval_program(prog, &binding_array[0]); + auto result = te::eval_program(prog, 0, &binding_array[0]); ::free(buffer); } From 3cf3eeb50ebd7fd3ee902dd90084d88e0c4a685e Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Fri, 7 Aug 2020 23:18:49 -0700 Subject: [PATCH 22/43] Multiprogram cleanup --- include/tinyprog.h | 22 +++++++++------------- test/example_program.cpp | 39 ++++++++++++++++++++++----------------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index fa94549..53a8448 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -2316,7 +2316,7 @@ namespace tp const header_chunk* header{nullptr}; const statement_chunk* first_subprogram{nullptr}; - serialized_program(compiled_program** programs, int num_programs, std::vector& user_vars_in) + serialized_program(const compiled_program* const* programs, int num_programs, std::vector& user_vars_in) { std::vector subprograms; subprograms.resize(num_programs); @@ -2328,20 +2328,16 @@ namespace tp auto most_bindings_idx = 0; for (int subprogram_idx = 1; subprogram_idx < num_programs; ++subprogram_idx) { - auto next_binding_name_count = programs[0]->get_binding_array_size(); - auto next_binding_names = programs[0]->get_binding_names(); - - auto verify_bindings = [&]() { - for (auto i = std::min(binding_name_count, next_binding_name_count); i > 0; ++i) + auto next_binding_name_count = programs[subprogram_idx]->get_binding_array_size(); + auto next_binding_names = programs[subprogram_idx]->get_binding_names(); + + for (auto i = std::min(binding_name_count, next_binding_name_count); i > 0; --i) + { + if (strcmp(binding_names[i - 1], next_binding_names[i - 1]) != 0) { - if (strcmp(binding_names[i], next_binding_names[i]) != 0) - { - return false; - } + assert(false); } - return true; - }; - assert(verify_bindings()); + } if (binding_name_count < next_binding_name_count) { diff --git a/test/example_program.cpp b/test/example_program.cpp index 8db221c..fa99caa 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -2,18 +2,22 @@ #include "tinyprog.h" #include -bool serialize_program_to_disk(const char* file_name, const char* prog_text, te::t_indexer& indexer) +bool serialize_programs_to_disk(const char* file_name, const char* prog_texts[], size_t num_prog_texts, te::t_indexer& indexer) { using builtins = tp::compiler_builtins; - auto prog = [prog_text, &indexer]() { - int err1 = 0; - auto res = te::compile_program_using_indexer(prog_text, &err1, indexer); - assert(res); - return res; - }(); + std::vector subprograms; + for (int i = 0; i < num_prog_texts; ++i) + { + subprograms.push_back([i, prog_texts, &indexer]() { + int err1 = 0; + auto res = te::compile_program_using_indexer(prog_texts[i], &err1, indexer); + assert(res); + return res; + }()); + } - te::serialized_program sp(&prog, 1, indexer.m_declared_variable_names); + te::serialized_program sp(&subprograms[0], int(num_prog_texts), indexer.m_declared_variable_names); FILE* f; if (!::fopen_s(&f, file_name, "wb")) @@ -67,8 +71,6 @@ int main(int argc, char* argv[]) "label: is_negative;" "return: -1 * x;"; - serialize_program_to_disk("prog1.tpp", p1, indexer); - const char* p2 = "var: y;" "y: sqrt(5^2+7^2+11^2+(8-2)^2);" @@ -76,21 +78,23 @@ int main(int argc, char* argv[]) "return: y;" "label: is_negative;" "return: -1 * y;"; - - serialize_program_to_disk("prog2.tpp", p2, indexer); + + const char* progs[] { p1, p2 }; + + serialize_programs_to_disk("progs.tpp", progs, 2, indexer); } { - auto [buffer, size] = serialize_from_disk("prog1.tpp"); + auto [buffer, size] = serialize_from_disk("progs.tpp"); te::serialized_program prog(buffer, size); - + std::vector binding_array; binding_array.resize(prog.get_num_bindings()); std::fill_n(std::begin(binding_array), prog.get_num_bindings(), nullptr); - + std::vector user_var_array; user_var_array.resize(prog.get_num_user_vars()); - + for (uint16_t i = 0; i < (uint16_t)prog.get_num_user_vars(); ++i) { auto binding_idx = prog.get_user_vars()[i]; @@ -107,7 +111,8 @@ int main(int argc, char* argv[]) assert(binding_array[i] != nullptr); } - auto result = te::eval_program(prog, 0, &binding_array[0]); + assert(te::eval_program(prog, 0, &binding_array[0]) == te::eval_program(prog, 1, &binding_array[0])); + ::free(buffer); } From e316f4f154bc27d12e257d4bb0526a1abe4e6bd3 Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Sat, 8 Aug 2020 00:08:15 -0700 Subject: [PATCH 23/43] Comment cleanup --- include/tinyprog.h | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 53a8448..395fe24 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -2309,12 +2309,12 @@ namespace tp const data_chunk* data{nullptr}; }; - const string_chunk* strings{nullptr}; - const user_var_chunk* user_vars{nullptr}; - void* raw_data{nullptr}; - size_t raw_data_size{0}; - const header_chunk* header{nullptr}; - const statement_chunk* first_subprogram{nullptr}; + const string_chunk* strings{nullptr}; + const user_var_chunk* user_vars{nullptr}; + void* raw_data{nullptr}; + size_t raw_data_size{0}; + const header_chunk* header{nullptr}; + const statement_chunk* first_subprogram{nullptr}; serialized_program(const compiled_program* const* programs, int num_programs, std::vector& user_vars_in) { @@ -2330,7 +2330,7 @@ namespace tp { auto next_binding_name_count = programs[subprogram_idx]->get_binding_array_size(); auto next_binding_names = programs[subprogram_idx]->get_binding_names(); - + for (auto i = std::min(binding_name_count, next_binding_name_count); i > 0; --i) { if (strcmp(binding_names[i - 1], next_binding_names[i - 1]) != 0) @@ -2476,7 +2476,7 @@ namespace tp p += user_var_data_data_size; // - + this->raw_data = serialized_program; this->raw_data_size = total_program_size; } @@ -2497,13 +2497,10 @@ namespace tp { auto subprogram_statements = (statement_chunk*)p; p += statement_data_size; - //::memcpy(p, &statement_src[0], prog_state.statement_data.size); p += round_up_to_multiple(subprogram_statements->size, alignment); auto subprogram_data = (data_chunk*)p; - //::memcpy(p, &prog_state.expression_data, expression_data_size); p += expression_data_size; - //::memcpy(p, &expression_src[0], prog_state.expression_data.size); p += round_up_to_multiple(subprogram_data->size, alignment); } @@ -2511,26 +2508,17 @@ namespace tp for (size_t i = 0; i < header->num_binding_names; ++i) { - //::memcpy(p, &strs[i], string_header_size); auto chonk = (const string_chunk*)p; p += string_header_size; - - //::memcpy(p, binding_names[i], strs[i].size - 1); - //p[strs[i].size - 1] = '\0'; p += round_up_to_multiple(chonk->size, alignment); } this->user_vars = (user_var_chunk*)p; - //::memcpy(p, &user_var_data, user_var_size); p += user_var_size; - //::memcpy(p, &user_var_indexes[0], user_var_data.size); p += round_up_to_multiple(this->user_vars->size, alignment); } - serialized_program(std::tuple args) - : serialized_program(std::get<0>(args), std::get<1>(args)) - { - } + serialized_program(std::tuple args) : serialized_program(std::get<0>(args), std::get<1>(args)) {} ~serialized_program() { @@ -2548,21 +2536,18 @@ namespace tp { auto statements = (statement_chunk*)p; p += statement_data_size; - //::memcpy(p, &statement_src[0], prog_state.statement_data.size); p += round_up_to_multiple(statements->size, alignment); auto subprogram_data = (data_chunk*)p; - //::memcpy(p, &prog_state.expression_data, expression_data_size); p += expression_data_size; - //::memcpy(p, &expression_src[0], prog_state.expression_data.size); p += round_up_to_multiple(subprogram_data->size, alignment); - + if (i == subprogram_index) { return {statements, subprogram_data}; } } - + return {nullptr, nullptr}; } From 85810e5e73107505bd992d1a5bfcd217fbccaaa8 Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Sat, 8 Aug 2020 15:04:45 -0700 Subject: [PATCH 24/43] Cleanup --- include/tinyprog.h | 14 ++- test/example_program.cpp | 249 +++++++++++---------------------------- 2 files changed, 78 insertions(+), 185 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 395fe24..dc5fbb5 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -2040,6 +2040,7 @@ namespace tp if (final_index == -1) { *error = -1; // TODO: variable not found + assert(!"Variable not found."); return nullptr; } @@ -2481,12 +2482,12 @@ namespace tp this->raw_data_size = total_program_size; } - serialized_program(const void* serialized_program, size_t total_program_size) + serialized_program(const void* data, size_t data_size) { this->raw_data = nullptr; - this->raw_data_size = total_program_size; + this->raw_data_size = data_size; - const char* p = (const char*)serialized_program; + const char* p = (const char*)data; this->header = (header_chunk*)p; p += out_header_size; @@ -2520,6 +2521,13 @@ namespace tp serialized_program(std::tuple args) : serialized_program(std::get<0>(args), std::get<1>(args)) {} + static inline serialized_program* create_using_buffer(void* data, size_t data_size) + { + auto prog = new serialized_program(data, data_size); + prog->raw_data = data; + return prog; + } + ~serialized_program() { if (raw_data) diff --git a/test/example_program.cpp b/test/example_program.cpp index fa99caa..c5f4977 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -2,10 +2,32 @@ #include "tinyprog.h" #include -bool serialize_programs_to_disk(const char* file_name, const char* prog_texts[], size_t num_prog_texts, te::t_indexer& indexer) +bool serialize_program_to_disk(const char* file_name, te::serialized_program* sp) +{ + FILE* f; + if (!::fopen_s(&f, file_name, "wb")) + { + ::fwrite(sp->get_raw_data(), 1, sp->get_raw_data_size(), f); + ::fflush(f); + ::fclose(f); + f = nullptr; + + return true; + } + + return false; +} + +te::serialized_program* create_program(const char* prog_texts[], size_t num_prog_texts, te::variable* vars, size_t num_vars) { using builtins = tp::compiler_builtins; + te::t_indexer indexer; + for (int i = 0; i < num_vars; ++i) + { + indexer.add_user_variable(vars + i); + } + std::vector subprograms; for (int i = 0; i < num_prog_texts; ++i) { @@ -17,23 +39,10 @@ bool serialize_programs_to_disk(const char* file_name, const char* prog_texts[], }()); } - te::serialized_program sp(&subprograms[0], int(num_prog_texts), indexer.m_declared_variable_names); - - FILE* f; - if (!::fopen_s(&f, file_name, "wb")) - { - ::fwrite(sp.get_raw_data(), 1, sp.get_raw_data_size(), f); - ::fflush(f); - ::fclose(f); - f = nullptr; - - return true; - } - - return false; + return new te::serialized_program(&subprograms[0], int(num_prog_texts), indexer.m_declared_variable_names); } -std::tuple serialize_from_disk(const char* file_name) +te::serialized_program* serialize_from_disk(const char* file_name) { FILE* f; if (!::fopen_s(&f, file_name, "rb")) @@ -45,24 +54,22 @@ std::tuple serialize_from_disk(const char* file_name) if (mem) { ::fread(mem, 1, s, f); - return {mem, s}; + return te::serialized_program::create_using_buffer(mem, s); } } - return {nullptr, 0}; + return nullptr; } int main(int argc, char* argv[]) { using builtins = tp::compiler_builtins; - //te::env_traits::t_atom x = 0.0f, y = 0.0f; - //te::variable vars[] = {{"x", &x}, {"y", &y}}; + te::env_traits::t_atom x = 0.0f, y = 0.0f; + te::variable vars[] = {{"xx", &x}, {"y", &y}}; + static constexpr size_t vars_count = sizeof(vars) / sizeof(vars[0]); + // compile & save to disk { - te::t_indexer indexer; - //indexer.add_user_variable(vars + 0); - //indexer.add_user_variable(vars + 1); - const char* p1 = "var: x;" "x: sqrt(5^2+7^2+11^2+(8-2)^2);" @@ -72,192 +79,70 @@ int main(int argc, char* argv[]) "return: -1 * x;"; const char* p2 = - "var: y;" "y: sqrt(5^2+7^2+11^2+(8-2)^2);" "jump: is_negative ? y < 0;" "return: y;" "label: is_negative;" "return: -1 * y;"; - + const char* progs[] { p1, p2 }; - - serialize_programs_to_disk("progs.tpp", progs, 2, indexer); + + auto prog = create_program(progs, 2, vars, vars_count); + assert(prog); + if (!serialize_program_to_disk("progs.tpp", prog)) + { + assert(0); + } + delete prog; } + // Load from disk, execute { - auto [buffer, size] = serialize_from_disk("progs.tpp"); - te::serialized_program prog(buffer, size); + te::serialized_program* prog = serialize_from_disk("progs.tpp");; + assert(prog); std::vector binding_array; - binding_array.resize(prog.get_num_bindings()); - std::fill_n(std::begin(binding_array), prog.get_num_bindings(), nullptr); + binding_array.resize(prog->get_num_bindings()); + std::fill_n(std::begin(binding_array), prog->get_num_bindings(), nullptr); std::vector user_var_array; - user_var_array.resize(prog.get_num_user_vars()); + user_var_array.resize(prog->get_num_user_vars()); + std::fill(std::begin(user_var_array), std::end(user_var_array), 0.0f); - for (uint16_t i = 0; i < (uint16_t)prog.get_num_user_vars(); ++i) + for (uint16_t i = 0; i < (uint16_t)prog->get_num_user_vars(); ++i) { - auto binding_idx = prog.get_user_vars()[i]; + auto binding_idx = prog->get_user_vars()[i]; binding_array[binding_idx] = &user_var_array[i]; } - for (uint16_t i = 0; i < (uint16_t)prog.get_num_bindings(); ++i) + for (uint16_t i = 0; i < (uint16_t)prog->get_num_bindings(); ++i) { if (!binding_array[i]) { - auto name = prog.get_binding_string(i); + auto name = prog->get_binding_string(i); binding_array[i] = builtins::find_builtin_address(name); + + if (!binding_array[i]) + { + for (uint16_t j = 0; j < vars_count; ++j) + { + if (_stricmp(vars[j].name, name) == 0) + { + binding_array[i] = vars[j].address; + } + } + } } + assert(binding_array[i] != nullptr); } - assert(te::eval_program(prog, 0, &binding_array[0]) == te::eval_program(prog, 1, &binding_array[0])); + auto a = te::eval_program(*prog, 0, &binding_array[0]) ; + auto b = te::eval_program(*prog, 1, &binding_array[0]) ; + assert(a == b); - ::free(buffer); - } - - // - // auto prog1 = [&indexer]() { - // int err1 = 0; - // auto res = te::compile_program_using_indexer(p1, &err1, indexer); - // assert(res); - // return res; - //}(); - // auto result1 = te::eval_program(prog1); - // - // auto prog2 = [&indexer]() -> auto - //{ - // int err2 = 0; - // auto res = te::compile_program_using_indexer(p2, &err2, indexer); - // assert(res); - // assert(err2 == 0); - // return res; - //} - //(); - // auto result2 = te::eval_program(prog2); - // - // assert(result1 == result2); - // assert(prog1->get_binding_array_size() <= prog2->get_binding_array_size()); - // - // auto ba1 = prog1->get_binding_addresses(); - // auto ba2 = prog2->get_binding_addresses(); - // - // for (int i = 0; i < prog1->get_binding_array_size(); ++i) - //{ - // assert(ba1[i] == ba2[i]); - //} - // - // using builtins = tp::compiler_builtins; - // - //{ - // std::vector binding_array; - // te::serialized_program sp1(prog1); - // - // for (uint16_t i = 0; i < (uint16_t)sp1.get_num_bindings(); ++i) - // { - // auto name = sp1.get_binding_string(i); - // if (_stricmp(name, "x") == 0) - // { - // binding_array.push_back(&x); - // } - // else if (_stricmp(name, "y") == 0) - // { - // binding_array.push_back(&y); - // } - // else - // { - // binding_array.push_back(builtins::find_builtin_address(name)); - // } - // } - // auto spresult1 = te::eval_program(sp1, &binding_array[0]); - //} - // - //{ - // std::vector binding_array; - // te::serialized_program sp2(prog2); - // - // for (uint16_t i = 0; i < sp2.get_num_bindings(); ++i) - // { - // auto name = sp2.get_binding_string(i); - // if (_stricmp(name, "x") == 0) - // { - // binding_array.push_back(&x); - // } - // else if (_stricmp(name, "y") == 0) - // { - // binding_array.push_back(&y); - // } - // else - // { - // binding_array.push_back(builtins::find_builtin_address(name)); - // } - // } - // auto spresult2 = te::eval_program(sp2, &binding_array[0]); - //} - // - // delete prog1; - // delete prog2; - - return 0; -} - -#if 0 -int main(int argc, char* argv[]) -{ - //te::env_traits::t_atom x = 0.0f, y = 0.0f; - //te::variable vars[] = {{"x", &x}, {"y", &y}}; - te::t_indexer indexer; - - //indexer.add_user_variable(vars + 0); - //indexer.add_user_variable(vars + 1); - - auto prog1 = [&indexer]() { - const char* p1 = - "var: x;" - "x: sqrt(5^2+7^2+11^2+(8-2)^2);" - "jump: is_negative ? x < 0;" - "return: x;" - "label: is_negative;" - "return: -1 * x;"; - int err1 = 0; - auto res = te::compile_program_using_indexer(p1, &err1, indexer); - assert(res); - return res; - }(); - auto result1 = te::eval_program(prog1); - - auto prog2 = [&indexer]() -> auto - { - const char* p2 = - "var: y;" - "y: sqrt(5^2+7^2+11^2+(8-2)^2);" - "jump: is_negative ? y < 0;" - "return: y;" - "label: is_negative;" - "return: -1 * y;"; - int err2 = 0; - auto res = te::compile_program_using_indexer(p2, &err2, indexer); - assert(res); - assert(err2 == 0); - return res; - } - (); - auto result2 = te::eval_program(prog2); - - assert(result1 == result2); - assert(prog1->get_binding_array_size() <= prog2->get_binding_array_size()); - - auto ba1 = prog1->get_binding_addresses(); - auto ba2 = prog2->get_binding_addresses(); - - for (int i = 0; i < prog1->get_binding_array_size(); ++i) - { - assert(ba1[i] == ba2[i]); + delete prog; } - delete prog1; - delete prog2; - return 0; } -#endif \ No newline at end of file From e9c7cc47ee85db3aa8a13ecbafbecdf3cc16c0d5 Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Sat, 8 Aug 2020 17:14:55 -0700 Subject: [PATCH 25/43] Streamline program usage a bit. --- include/tinyprog.h | 5 +++++ test/example_program.cpp | 29 ++++++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index dc5fbb5..26cd241 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -2627,6 +2627,11 @@ namespace tp { return (user_vars != nullptr) ? (int*)&user_vars->data[0] : nullptr; } + + uint16_t get_num_subprograms() const noexcept + { + return header->num_subprograms; + } }; } // namespace details diff --git a/test/example_program.cpp b/test/example_program.cpp index c5f4977..f30c550 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -70,8 +70,13 @@ int main(int argc, char* argv[]) // compile & save to disk { - const char* p1 = + const char* constructor = "var: x;" + "x: 255.0;" + "var: y;" + "y: 255.0;"; // will return nan + + const char* p1 = "x: sqrt(5^2+7^2+11^2+(8-2)^2);" "jump: is_negative ? x < 0;" "return: x;" @@ -85,9 +90,10 @@ int main(int argc, char* argv[]) "label: is_negative;" "return: -1 * y;"; - const char* progs[] { p1, p2 }; - - auto prog = create_program(progs, 2, vars, vars_count); + const char* progs[] { constructor, p1, p2 }; + const size_t num_progs = sizeof(progs) / sizeof(progs[0]); + + auto prog = create_program(progs, num_progs, vars, vars_count); assert(prog); if (!serialize_program_to_disk("progs.tpp", prog)) { @@ -137,10 +143,19 @@ int main(int argc, char* argv[]) assert(binding_array[i] != nullptr); } - auto a = te::eval_program(*prog, 0, &binding_array[0]) ; - auto b = te::eval_program(*prog, 1, &binding_array[0]) ; - assert(a == b); + float *results = new float[prog->get_num_subprograms()]; + float last_result = 0; + for (int i = 0; i < prog->get_num_subprograms(); ++i) + { + results[i] = te::eval_program(*prog, i, &binding_array[0]); + last_result = results[i]; + } + for (int i = 1; i < prog->get_num_subprograms(); ++i) + { + assert(results[i] == last_result); + } + delete prog; } From 00915f69c155d2aeb06c4a94b690ba20e9e56280 Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Sat, 8 Aug 2020 18:23:54 -0700 Subject: [PATCH 26/43] Improved test, make sure that overloads and vars work correctly. --- test/example_program.cpp | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/test/example_program.cpp b/test/example_program.cpp index f30c550..04a88f0 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -27,7 +27,7 @@ te::serialized_program* create_program(const char* prog_texts[], size_t num_prog { indexer.add_user_variable(vars + i); } - + std::vector subprograms; for (int i = 0; i < num_prog_texts; ++i) { @@ -64,17 +64,18 @@ int main(int argc, char* argv[]) { using builtins = tp::compiler_builtins; - te::env_traits::t_atom x = 0.0f, y = 0.0f; - te::variable vars[] = {{"xx", &x}, {"y", &y}}; + te::env_traits::t_atom x = 0.0f, y = -1.0f; + te::variable vars[] = {{"xx", &x}, {"y", &y}}; static constexpr size_t vars_count = sizeof(vars) / sizeof(vars[0]); - + // compile & save to disk { const char* constructor = "var: x;" "x: 255.0;" "var: y;" - "y: 255.0;"; // will return nan + "y: 255.0;" + "xx: 255.0;"; const char* p1 = "x: sqrt(5^2+7^2+11^2+(8-2)^2);" @@ -82,17 +83,17 @@ int main(int argc, char* argv[]) "return: x;" "label: is_negative;" "return: -1 * x;"; - + const char* p2 = "y: sqrt(5^2+7^2+11^2+(8-2)^2);" "jump: is_negative ? y < 0;" "return: y;" "label: is_negative;" "return: -1 * y;"; - - const char* progs[] { constructor, p1, p2 }; + + const char* progs[]{constructor, p1, p2}; const size_t num_progs = sizeof(progs) / sizeof(progs[0]); - + auto prog = create_program(progs, num_progs, vars, vars_count); assert(prog); if (!serialize_program_to_disk("progs.tpp", prog)) @@ -104,7 +105,7 @@ int main(int argc, char* argv[]) // Load from disk, execute { - te::serialized_program* prog = serialize_from_disk("progs.tpp");; + te::serialized_program* prog = serialize_from_disk("progs.tpp"); assert(prog); std::vector binding_array; @@ -117,7 +118,7 @@ int main(int argc, char* argv[]) for (uint16_t i = 0; i < (uint16_t)prog->get_num_user_vars(); ++i) { - auto binding_idx = prog->get_user_vars()[i]; + auto binding_idx = prog->get_user_vars()[i]; binding_array[binding_idx] = &user_var_array[i]; } @@ -125,9 +126,9 @@ int main(int argc, char* argv[]) { if (!binding_array[i]) { - auto name = prog->get_binding_string(i); + auto name = prog->get_binding_string(i); binding_array[i] = builtins::find_builtin_address(name); - + if (!binding_array[i]) { for (uint16_t j = 0; j < vars_count; ++j) @@ -142,20 +143,23 @@ int main(int argc, char* argv[]) assert(binding_array[i] != nullptr); } - - float *results = new float[prog->get_num_subprograms()]; - float last_result = 0; + + float* results = new float[prog->get_num_subprograms()]; + float last_result = 0; for (int i = 0; i < prog->get_num_subprograms(); ++i) { - results[i] = te::eval_program(*prog, i, &binding_array[0]); + results[i] = te::eval_program(*prog, i, &binding_array[0]); last_result = results[i]; } - + for (int i = 1; i < prog->get_num_subprograms(); ++i) { assert(results[i] == last_result); } + assert(x == 255.0f); // x should have been initialized to 255 in the constructor + assert(y == -1.0f); // y should have been overridden by the declared var + delete prog; } From cce3118283e21db70299fded0c2e1fe201ab3800 Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Sat, 8 Aug 2020 18:48:53 -0700 Subject: [PATCH 27/43] Better comments, binding precedence --- test/example_program.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/test/example_program.cpp b/test/example_program.cpp index 04a88f0..682fae9 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -103,7 +103,7 @@ int main(int argc, char* argv[]) delete prog; } - // Load from disk, execute + // Load from disk, setup bindings, execute { te::serialized_program* prog = serialize_from_disk("progs.tpp"); assert(prog); @@ -116,6 +116,9 @@ int main(int argc, char* argv[]) user_var_array.resize(prog->get_num_user_vars()); std::fill(std::begin(user_var_array), std::end(user_var_array), 0.0f); + // Binding precendence will be: declared->user->builtin + + // Declared vars come first for (uint16_t i = 0; i < (uint16_t)prog->get_num_user_vars(); ++i) { auto binding_idx = prog->get_user_vars()[i]; @@ -124,23 +127,28 @@ int main(int argc, char* argv[]) for (uint16_t i = 0; i < (uint16_t)prog->get_num_bindings(); ++i) { + // If the binding was not already set as a declared var if (!binding_array[i]) { - auto name = prog->get_binding_string(i); - binding_array[i] = builtins::find_builtin_address(name); + auto name = prog->get_binding_string(i); - if (!binding_array[i]) + // If not a builtin, see if we have a user binding + for (uint16_t j = 0; j < vars_count; ++j) { - for (uint16_t j = 0; j < vars_count; ++j) + if (_stricmp(vars[j].name, name) == 0) { - if (_stricmp(vars[j].name, name) == 0) - { - binding_array[i] = vars[j].address; - } + binding_array[i] = vars[j].address; } } + + if (!binding_array[i]) + { + // See if the binding is a builtin + binding_array[i] = builtins::find_builtin_address(name); + } } + // All bindings must be valid, otherwise the runtime will crash. assert(binding_array[i] != nullptr); } From 97988150da43be0dc75e825aaa0fb3bac088ce8d Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Sat, 8 Aug 2020 21:27:10 -0700 Subject: [PATCH 28/43] Isolate constructor scope and add error checks to fix msvc complaints. --- include/tinyprog.h | 203 ++++++++++++++++++++++++--------------------- 1 file changed, 107 insertions(+), 96 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 26cd241..69f3991 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -2348,15 +2348,6 @@ namespace tp } } - size_t total_program_size = 0; - - header_chunk out_header; - out_header.magic = uint16_t(0x1010); - out_header.version = uint16_t(0x0001); - out_header.num_binding_names = uint16_t(binding_name_count); - out_header.num_subprograms = uint16_t(num_programs); - total_program_size += out_header_size; - struct program_state { statement_chunk statement_data; @@ -2365,121 +2356,141 @@ namespace tp size_t expression_data_data_size; }; - std::vector program_states; - program_states.resize(num_programs); - - for (int subprogram_idx = 0; subprogram_idx < num_programs; ++subprogram_idx) + auto [total_program_size, out_header, program_states, strs, user_var_indexes, user_var_data, user_var_data_data_size] = [&]() -> std::tuple, std::vector, std::vector, user_var_chunk, size_t> { - auto& prog_state = program_states[subprogram_idx]; - auto prog = programs[subprogram_idx]; - auto expression_size = prog->get_data_size(); - auto expression_src = prog->get_data(); - auto num_statements = prog->get_statement_array_size(); - auto statement_src = prog->get_statements(); + std::vector program_states; + program_states.resize(num_programs); - prog_state.statement_data.size = uint16_t(num_statements * sizeof(statement_src[0])); - total_program_size += statement_data_size; - prog_state.statement_data_data_size = round_up_to_multiple(prog_state.statement_data.size, alignment); - total_program_size += prog_state.statement_data_data_size; + size_t total_program_size = 0; - prog_state.expression_data.size = uint16_t(expression_size); - total_program_size += expression_data_size; - prog_state.expression_data_data_size = round_up_to_multiple(prog_state.expression_data.size, alignment); - total_program_size += prog_state.expression_data_data_size; - } + header_chunk out_header; + out_header.magic = uint16_t(0x1010); + out_header.version = uint16_t(0x0001); + out_header.num_binding_names = uint16_t(binding_name_count); + out_header.num_subprograms = uint16_t(num_programs); - std::vector strs; - for (size_t i = 0; i < binding_name_count; ++i) - { - string_chunk chonk; - chonk.size = uint16_t(::strlen(binding_names[i]) + 1); - total_program_size += string_header_size; - total_program_size += round_up_to_multiple(chonk.size, uint16_t(alignment)); - strs.push_back(chonk); - } + total_program_size += out_header_size; - std::vector user_var_indexes; - user_var_indexes.resize(user_var_count); - std::fill(std::begin(user_var_indexes), std::end(user_var_indexes), -1); - for (size_t i = 0; i < binding_name_count; ++i) - { - for (size_t j = 0; j < user_var_count; ++j) + for (int subprogram_idx = 0; subprogram_idx < num_programs; ++subprogram_idx) { - if (user_var_indexes[j] == -1) + auto& prog_state = program_states[subprogram_idx]; + auto prog = programs[subprogram_idx]; + auto expression_size = prog->get_data_size(); + auto num_statements = prog->get_statement_array_size(); + auto statement_src = prog->get_statements(); + + prog_state.statement_data.size = uint16_t(num_statements * sizeof(statement_src[0])); + total_program_size += statement_data_size; + prog_state.statement_data_data_size = round_up_to_multiple(prog_state.statement_data.size, alignment); + total_program_size += prog_state.statement_data_data_size; + + prog_state.expression_data.size = uint16_t(expression_size); + total_program_size += expression_data_size; + prog_state.expression_data_data_size = round_up_to_multiple(prog_state.expression_data.size, alignment); + total_program_size += prog_state.expression_data_data_size; + } + + std::vector strs; + for (size_t i = 0; i < binding_name_count; ++i) + { + string_chunk chonk; + chonk.size = uint16_t(::strlen(binding_names[i]) + 1); + total_program_size += string_header_size; + total_program_size += round_up_to_multiple(chonk.size, uint16_t(alignment)); + strs.push_back(chonk); + } + + std::vector user_var_indexes; + user_var_indexes.resize(user_var_count); + std::fill(std::begin(user_var_indexes), std::end(user_var_indexes), -1); + for (size_t i = 0; i < binding_name_count; ++i) + { + for (size_t j = 0; j < user_var_count; ++j) { - if (_stricmp(binding_names[i], user_vars_in[j].c_str()) == 0) + if (user_var_indexes[j] == -1) { - user_var_indexes[j] = int(i); - break; + if (_stricmp(binding_names[i], user_vars_in[j].c_str()) == 0) + { + user_var_indexes[j] = int(i); + break; + } } } } - } - - user_var_chunk user_var_data; - user_var_data.size = uint16_t(sizeof(int) * user_var_count); - total_program_size += user_var_size; - const auto user_var_data_data_size = round_up_to_multiple(user_var_data.size, alignment); - total_program_size += user_var_data_data_size; - // + user_var_chunk user_var_data; + user_var_data.size = uint16_t(sizeof(int) * user_var_count); + total_program_size += user_var_size; + const auto user_var_data_data_size = round_up_to_multiple(user_var_data.size, alignment); + total_program_size += user_var_data_data_size; - char* const serialized_program = (char*)::malloc(total_program_size); - char* p = serialized_program; + return {total_program_size, out_header, program_states, strs, user_var_indexes, user_var_data, user_var_data_data_size}; + }(); // - this->header = (header_chunk*)p; - ::memcpy(p, &out_header, out_header_size); - p += out_header_size; + if (total_program_size > sizeof(out_header)) + { + assert(total_program_size > out_header_size + user_var_data_data_size + user_var_size); + char* const serialized_program = (char*)::malloc(total_program_size); + char* p = serialized_program; + if (p != nullptr) + { + // - first_subprogram = (statement_chunk*)p; + this->header = (header_chunk*)p; + ::memcpy(p, &out_header, sizeof(out_header)); + p += out_header_size; - for (int subprogram_idx = 0; subprogram_idx < num_programs; ++subprogram_idx) - { - auto& prog_state = program_states[subprogram_idx]; - auto& subprogram = subprograms[subprogram_idx]; + first_subprogram = (statement_chunk*)p; - auto prog = programs[subprogram_idx]; - auto statement_src = prog->get_statements(); + for (int subprogram_idx = 0; subprogram_idx < num_programs; ++subprogram_idx) + { + auto& prog_state = program_states[subprogram_idx]; + auto& subprogram = subprograms[subprogram_idx]; - auto expression_src = prog->get_data(); + auto prog = programs[subprogram_idx]; + auto statement_src = prog->get_statements(); - subprogram.statements = (statement_chunk*)p; - ::memcpy(p, &prog_state.statement_data, statement_data_size); - p += statement_data_size; - ::memcpy(p, &statement_src[0], prog_state.statement_data.size); - p += prog_state.statement_data_data_size; + auto expression_src = prog->get_data(); - subprogram.data = (data_chunk*)p; - ::memcpy(p, &prog_state.expression_data, expression_data_size); - p += expression_data_size; - ::memcpy(p, &expression_src[0], prog_state.expression_data.size); - p += prog_state.expression_data_data_size; - } + subprogram.statements = (statement_chunk*)p; + ::memcpy(p, &prog_state.statement_data, statement_data_size); + p += statement_data_size; + ::memcpy(p, &statement_src[0], prog_state.statement_data.size); + p += prog_state.statement_data_data_size; - this->strings = (string_chunk*)p; + subprogram.data = (data_chunk*)p; + ::memcpy(p, &prog_state.expression_data, expression_data_size); + p += expression_data_size; + ::memcpy(p, &expression_src[0], prog_state.expression_data.size); + p += prog_state.expression_data_data_size; + } - for (size_t i = 0; i < binding_name_count; ++i) - { - ::memcpy(p, &strs[i], string_header_size); - p += string_header_size; + this->strings = (string_chunk*)p; - ::memcpy(p, binding_names[i], strs[i].size - 1); - p[strs[i].size - 1] = '\0'; - p += round_up_to_multiple(strs[i].size, alignment); - } + for (size_t i = 0; i < binding_name_count; ++i) + { + ::memcpy(p, &strs[i], string_header_size); + p += string_header_size; - this->user_vars = (user_var_chunk*)p; - ::memcpy(p, &user_var_data, user_var_size); - p += user_var_size; - ::memcpy(p, &user_var_indexes[0], user_var_data.size); - p += user_var_data_data_size; + ::memcpy(p, binding_names[i], strs[i].size - 1); + p[strs[i].size - 1] = '\0'; + p += round_up_to_multiple(strs[i].size, alignment); + } - // + this->user_vars = (user_var_chunk*)p; + ::memcpy(p, &user_var_data, user_var_size); + p += user_var_size; + ::memcpy(p, &user_var_indexes[0], user_var_data.size); + p += user_var_data_data_size; + + // - this->raw_data = serialized_program; - this->raw_data_size = total_program_size; + this->raw_data = serialized_program; + this->raw_data_size = total_program_size; + } + } } serialized_program(const void* data, size_t data_size) From 65fe7e33e8a865449f39a240d92cb9fe98061496 Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Sun, 9 Aug 2020 00:21:37 -0700 Subject: [PATCH 29/43] the feature disable respecter has logged in. --- include/tinyprog.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/tinyprog.h b/include/tinyprog.h index 69f3991..22b14ca 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -2317,6 +2317,7 @@ namespace tp const header_chunk* header{nullptr}; const statement_chunk* first_subprogram{nullptr}; +#if (TP_COMPILER_ENABLED) serialized_program(const compiled_program* const* programs, int num_programs, std::vector& user_vars_in) { std::vector subprograms; @@ -2492,6 +2493,7 @@ namespace tp } } } +#endif // #if (TP_COMPILER_ENABLED) serialized_program(const void* data, size_t data_size) { From e265946fe739d508f3523ebc38605830318abb0e Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Sun, 9 Aug 2020 14:43:11 -0700 Subject: [PATCH 30/43] Add closures to the test. --- test/example_program.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/test/example_program.cpp b/test/example_program.cpp index 682fae9..57af41c 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -60,12 +60,18 @@ te::serialized_program* serialize_from_disk(const char* file_name) return nullptr; } +float test_closure(void* context, float arg) +{ + printf("Closure called: %llu, %f\n", (size_t)context, arg); + return arg; +} + int main(int argc, char* argv[]) { using builtins = tp::compiler_builtins; te::env_traits::t_atom x = 0.0f, y = -1.0f; - te::variable vars[] = {{"xx", &x}, {"y", &y}}; + te::variable vars[] = {{"xx", &x}, {"y", &y}, {"test_closure", test_closure, tp::CLOSURE1, (void*)0xf33db33ff33db33f}}; static constexpr size_t vars_count = sizeof(vars) / sizeof(vars[0]); // compile & save to disk @@ -79,6 +85,7 @@ int main(int argc, char* argv[]) const char* p1 = "x: sqrt(5^2+7^2+11^2+(8-2)^2);" + "test_closure(x);" "jump: is_negative ? x < 0;" "return: x;" "label: is_negative;" @@ -86,6 +93,7 @@ int main(int argc, char* argv[]) const char* p2 = "y: sqrt(5^2+7^2+11^2+(8-2)^2);" + "test_closure(y);" "jump: is_negative ? y < 0;" "return: y;" "label: is_negative;" @@ -125,6 +133,8 @@ int main(int argc, char* argv[]) binding_array[binding_idx] = &user_var_array[i]; } + std::unordered_map closure_contexts; + for (uint16_t i = 0; i < (uint16_t)prog->get_num_bindings(); ++i) { // If the binding was not already set as a declared var @@ -138,6 +148,11 @@ int main(int argc, char* argv[]) if (_stricmp(vars[j].name, name) == 0) { binding_array[i] = vars[j].address; + + if (vars[j].type >= tp::CLOSURE0 && vars[j].type < tp::CLOSURE_MAX) + { + closure_contexts.insert({std::string(vars[j].name) + "_closure", vars[j].context}); + } } } @@ -146,6 +161,16 @@ int main(int argc, char* argv[]) // See if the binding is a builtin binding_array[i] = builtins::find_builtin_address(name); } + + if (!binding_array[i]) + { + // look in the closure contexts + auto itor = closure_contexts.find(name); + if (itor != std::end(closure_contexts)) + { + binding_array[i] = itor->second; + } + } } // All bindings must be valid, otherwise the runtime will crash. From 0690d1fb64eb8016d5fc7ccb1e51e2f9ced4db43 Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Sun, 9 Aug 2020 14:51:55 -0700 Subject: [PATCH 31/43] Clean up documentation, closure contexts should be highest priority --- test/example_program.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/test/example_program.cpp b/test/example_program.cpp index 57af41c..ef526e5 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -142,7 +142,17 @@ int main(int argc, char* argv[]) { auto name = prog->get_binding_string(i); - // If not a builtin, see if we have a user binding + // Closure contexts are always first + if (!binding_array[i]) + { + auto itor = closure_contexts.find(name); + if (itor != std::end(closure_contexts)) + { + binding_array[i] = itor->second; + } + } + + // User bindings are next priority for (uint16_t j = 0; j < vars_count; ++j) { if (_stricmp(vars[j].name, name) == 0) @@ -151,26 +161,18 @@ int main(int argc, char* argv[]) if (vars[j].type >= tp::CLOSURE0 && vars[j].type < tp::CLOSURE_MAX) { + // Closures are always declared first, followed by a context, which is appended with "_closure". + // Add this to the lookup so we find it later. closure_contexts.insert({std::string(vars[j].name) + "_closure", vars[j].context}); } } } + // Builtins come last if (!binding_array[i]) { - // See if the binding is a builtin binding_array[i] = builtins::find_builtin_address(name); } - - if (!binding_array[i]) - { - // look in the closure contexts - auto itor = closure_contexts.find(name); - if (itor != std::end(closure_contexts)) - { - binding_array[i] = itor->second; - } - } } // All bindings must be valid, otherwise the runtime will crash. From 8183440d28b53b79e219f396573f7b4c2ae25d11 Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Sun, 9 Aug 2020 15:18:04 -0700 Subject: [PATCH 32/43] Test cleanup --- test/example_program.cpp | 80 +++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/test/example_program.cpp b/test/example_program.cpp index ef526e5..0c25be9 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -116,69 +116,75 @@ int main(int argc, char* argv[]) te::serialized_program* prog = serialize_from_disk("progs.tpp"); assert(prog); + // Reserve binding memory std::vector binding_array; binding_array.resize(prog->get_num_bindings()); std::fill_n(std::begin(binding_array), prog->get_num_bindings(), nullptr); + // Reserve memory for user vars std::vector user_var_array; user_var_array.resize(prog->get_num_user_vars()); std::fill(std::begin(user_var_array), std::end(user_var_array), 0.0f); - // Binding precendence will be: declared->user->builtin - - // Declared vars come first - for (uint16_t i = 0; i < (uint16_t)prog->get_num_user_vars(); ++i) + // Binding setup { - auto binding_idx = prog->get_user_vars()[i]; - binding_array[binding_idx] = &user_var_array[i]; - } - - std::unordered_map closure_contexts; + // Binding precendence will be: declared->user->builtin - for (uint16_t i = 0; i < (uint16_t)prog->get_num_bindings(); ++i) - { - // If the binding was not already set as a declared var - if (!binding_array[i]) + // Declared vars come first + for (uint16_t i = 0; i < (uint16_t)prog->get_num_user_vars(); ++i) { - auto name = prog->get_binding_string(i); + auto binding_idx = prog->get_user_vars()[i]; + binding_array[binding_idx] = &user_var_array[i]; + } - // Closure contexts are always first + std::unordered_map closure_contexts; + + for (uint16_t i = 0; i < (uint16_t)prog->get_num_bindings(); ++i) + { + // If the binding was not already set as a declared var if (!binding_array[i]) { - auto itor = closure_contexts.find(name); - if (itor != std::end(closure_contexts)) + auto name = prog->get_binding_string(i); + + // Closure contexts are always first + if (!binding_array[i]) { - binding_array[i] = itor->second; + auto itor = closure_contexts.find(name); + if (itor != std::end(closure_contexts)) + { + binding_array[i] = itor->second; + } } - } - // User bindings are next priority - for (uint16_t j = 0; j < vars_count; ++j) - { - if (_stricmp(vars[j].name, name) == 0) + // User bindings are next priority + for (uint16_t j = 0; j < vars_count; ++j) { - binding_array[i] = vars[j].address; - - if (vars[j].type >= tp::CLOSURE0 && vars[j].type < tp::CLOSURE_MAX) + if (_stricmp(vars[j].name, name) == 0) { - // Closures are always declared first, followed by a context, which is appended with "_closure". - // Add this to the lookup so we find it later. - closure_contexts.insert({std::string(vars[j].name) + "_closure", vars[j].context}); + binding_array[i] = vars[j].address; + + if (vars[j].type >= tp::CLOSURE0 && vars[j].type < tp::CLOSURE_MAX) + { + // Closures are always declared first, followed by a context, which is appended with "_closure". + // Add this to the lookup so we find it later. + closure_contexts.insert({std::string(vars[j].name) + "_closure", vars[j].context}); + } } } - } - // Builtins come last - if (!binding_array[i]) - { - binding_array[i] = builtins::find_builtin_address(name); + // Builtins come last + if (!binding_array[i]) + { + binding_array[i] = builtins::find_builtin_address(name); + } } - } - // All bindings must be valid, otherwise the runtime will crash. - assert(binding_array[i] != nullptr); + // All bindings must be valid, otherwise the runtime will crash. + assert(binding_array[i] != nullptr); + } } + // Execute the test programs float* results = new float[prog->get_num_subprograms()]; float last_result = 0; for (int i = 0; i < prog->get_num_subprograms(); ++i) From 5a0dea18b7254a3f085594673c802852a8fe4fb3 Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Sun, 9 Aug 2020 21:41:01 -0700 Subject: [PATCH 33/43] Get the interpreter building on c++ 14 --- include/tinyprog.h | 198 ++++++++++++++++++++++++++------------------- 1 file changed, 113 insertions(+), 85 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 22b14ca..1f75af8 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -32,6 +32,7 @@ #include #include +#include #ifndef TP_TESTING #define TP_TESTING 0 @@ -50,6 +51,13 @@ #define TP_STANDARD_LIBRARY 0 #endif // #ifndef TP_STANDARD_LIBRARY +#if TP_COMPILER_ENABLED +#if (_MSVC_LANG < 201703L) + #error C++ 17 is required for the compiler. +#endif +#include +#endif // #if TP_COMPILER_ENABLED + namespace tp { enum @@ -1729,7 +1737,10 @@ namespace tp static inline std::tuple split_at_index_excl(std::string_view s, size_t index) { - auto [l, r] = split_at_index(s, index); + auto tup = split_at_index(s, index); + auto& l = std::get<0>(tup); + auto& r = std::get<1>(tup); + if (r.length() > 1) { return {trim_all_space(l), trim_all_space(std::string_view{&r[1], r.length() - 1})}; @@ -1763,17 +1774,17 @@ namespace tp //// - static inline const auto keyword_return = std::string_view("return"); - static inline const auto keyword_jump = std::string_view("jump"); - static inline const auto keyword_label = std::string_view("label"); - static inline const auto keyword_var = std::string_view("var"); - template< typename T_ADD_VARIABLE, typename T_ADD_LABEL, typename T_ADD_JUMP, typename T_ADD_JUMP_IF, typename T_ADD_RETURN_VALUE, typename T_ADD_ASSIGN, typename T_ADD_CALL> static inline void parse_statement( - std::string_view statement, T_ADD_VARIABLE add_variable, T_ADD_LABEL add_label, T_ADD_JUMP add_jump, T_ADD_JUMP_IF add_jump_if, T_ADD_RETURN_VALUE add_return_value, - T_ADD_ASSIGN add_assign, T_ADD_CALL add_call) + std::string_view statement, T_ADD_VARIABLE add_variable, T_ADD_LABEL add_label, T_ADD_JUMP add_jump, T_ADD_JUMP_IF add_jump_if, + T_ADD_RETURN_VALUE add_return_value, T_ADD_ASSIGN add_assign, T_ADD_CALL add_call) { + const auto keyword_return = std::string_view("return"); + const auto keyword_jump = std::string_view("jump"); + const auto keyword_label = std::string_view("label"); + const auto keyword_var = std::string_view("var"); + auto [operation, expression] = split_at_char_excl(statement, ':'); if (expression.length() == 0) @@ -1818,10 +1829,10 @@ namespace tp struct label_manager { - using handle = int; - static inline constexpr int placeholder_index = -1; + using handle = int; + static /*inline*/ constexpr int placeholder_index = -1; - std::vector m_label_statement_indexes; + std::vector m_label_statement_indexes; std::unordered_map m_label_handle_map; handle add_label(std::string_view label, int statement_index) @@ -1880,7 +1891,7 @@ namespace tp struct variable_manager { - int m_variable_count = 0; + int m_variable_count = 0; std::unordered_map m_variable_map; int find_label(std::string_view name) @@ -1960,7 +1971,9 @@ namespace tp while (program_remaining.length() > 0) { - auto [statement, remaining] = parser::split_at_char_excl(program_remaining, ';'); + auto tup = parser::split_at_char_excl(program_remaining, ';'); + auto& statement = std::get<0>(tup); + auto& remaining = std::get<1>(tup); parser::parse_statement( statement, @@ -2288,21 +2301,21 @@ namespace tp return ((value + multiple - 1) / multiple) * multiple; } - static inline constexpr uint16_t alignment{4}; - static inline constexpr auto out_header_size = uint16_t(sizeof(header_chunk)); - static_assert(out_header_size == round_up_to_multiple(uint16_t(sizeof(header_chunk)), alignment)); + static /*inline*/ constexpr uint16_t alignment{4}; + static /*inline*/ constexpr auto out_header_size = uint16_t(sizeof(header_chunk)); + static_assert(out_header_size == round_up_to_multiple(uint16_t(sizeof(header_chunk)), alignment), ""); - static inline constexpr auto statement_data_size = uint16_t(sizeof(chunk_header)); - static_assert(statement_data_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment)); + static /*inline*/ constexpr auto statement_data_size = uint16_t(sizeof(chunk_header)); + static_assert(statement_data_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment), ""); - static inline constexpr auto expression_data_size = uint16_t(sizeof(chunk_header)); - static_assert(expression_data_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment)); + static /*inline*/ constexpr auto expression_data_size = uint16_t(sizeof(chunk_header)); + static_assert(expression_data_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment), ""); - static inline constexpr auto string_header_size = uint16_t(sizeof(chunk_header)); - static_assert(string_header_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment)); + static /*inline*/ constexpr auto string_header_size = uint16_t(sizeof(chunk_header)); + static_assert(string_header_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment), ""); - static inline constexpr auto user_var_size = uint16_t(sizeof(chunk_header)); - static_assert(user_var_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment)); + static /*inline*/ constexpr auto user_var_size = uint16_t(sizeof(chunk_header)); + static_assert(user_var_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment), ""); struct subprogram { @@ -2357,12 +2370,11 @@ namespace tp size_t expression_data_data_size; }; - auto [total_program_size, out_header, program_states, strs, user_var_indexes, user_var_data, user_var_data_data_size] = [&]() -> std::tuple, std::vector, std::vector, user_var_chunk, size_t> - { + auto tup = [&]() -> std::tuple, std::vector, std::vector, user_var_chunk, size_t> { std::vector program_states; program_states.resize(num_programs); - size_t total_program_size = 0; + size_t total_program_size = 0; header_chunk out_header; out_header.magic = uint16_t(0x1010); @@ -2428,10 +2440,18 @@ namespace tp return {total_program_size, out_header, program_states, strs, user_var_indexes, user_var_data, user_var_data_data_size}; }(); + auto& total_program_size = std::get<0>(tup); + auto& out_header = std::get<1>(tup); + auto& program_states = std::get<2>(tup); + auto& strs = std::get<3>(tup); + auto& user_var_indexes = std::get<4>(tup); + auto& user_var_data = std::get<5>(tup); + auto& user_var_data_data_size = std::get<6>(tup); + // if (total_program_size > sizeof(out_header)) - { + { assert(total_program_size > out_header_size + user_var_data_data_size + user_var_size); char* const serialized_program = (char*)::malloc(total_program_size); char* p = serialized_program; @@ -2536,11 +2556,11 @@ namespace tp static inline serialized_program* create_using_buffer(void* data, size_t data_size) { - auto prog = new serialized_program(data, data_size); + auto prog = new serialized_program(data, data_size); prog->raw_data = data; return prog; } - + ~serialized_program() { if (raw_data) @@ -2574,25 +2594,33 @@ namespace tp const statement* get_statements_array(int subprogram_index) const noexcept { - auto [statements, subprogram_data] = get_subprogram_data(subprogram_index); + auto tup = get_subprogram_data(subprogram_index); + auto& statements = std::get<0>(tup); + auto& subprogram_data = std::get<1>(tup); return reinterpret_cast(&statements->data[0]); } size_t get_statements_array_size(int subprogram_index) const noexcept { - auto [statements, subprogram_data] = get_subprogram_data(subprogram_index); + auto tup = get_subprogram_data(subprogram_index); + auto& statements = std::get<0>(tup); + auto& subprogram_data = std::get<1>(tup); return statements->size / sizeof(statement); } const void* get_expression_data(int subprogram_index) const noexcept { - auto [statements, subprogram_data] = get_subprogram_data(subprogram_index); + auto tup = get_subprogram_data(subprogram_index); + auto& statements = std::get<0>(tup); + auto& subprogram_data = std::get<1>(tup); return &subprogram_data->data[0]; } const size_t get_expression_size(int subprogram_index) const noexcept { - auto [statements, subprogram_data] = get_subprogram_data(subprogram_index); + auto tup = get_subprogram_data(subprogram_index); + auto& statements = std::get<0>(tup); + auto& subprogram_data = std::get<1>(tup); return subprogram_data->size; } @@ -2640,7 +2668,7 @@ namespace tp { return (user_vars != nullptr) ? (int*)&user_vars->data[0] : nullptr; } - + uint16_t get_num_subprograms() const noexcept { return header->num_subprograms; @@ -3276,59 +3304,59 @@ namespace tp_stdlib { using t_impl = native_builtins_impl; - static inline constexpr tp::variable functions[] = {/* must be in alphabetical order */ - {"abs", t_impl::fabs, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"acos", t_impl::acos, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"asin", t_impl::asin, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"atan", t_impl::atan, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"atan2", t_impl::atan2, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"ceil", t_impl::ceil, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"cos", t_impl::cos, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"cosh", t_impl::cosh, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"e", t_impl::e, tp::FUNCTION0 | tp::FLAG_PURE, 0}, - {"exp", t_impl::exp, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"fac", t_impl::fac, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"floor", t_impl::floor, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"ln", t_impl::log, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + static /*inline*/ constexpr tp::variable functions[] = {/* must be in alphabetical order */ + {"abs", t_impl::fabs, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"acos", t_impl::acos, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"asin", t_impl::asin, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"atan", t_impl::atan, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"atan2", t_impl::atan2, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"ceil", t_impl::ceil, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"cos", t_impl::cos, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"cosh", t_impl::cosh, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"e", t_impl::e, tp::FUNCTION0 | tp::FLAG_PURE, 0}, + {"exp", t_impl::exp, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"fac", t_impl::fac, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"floor", t_impl::floor, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"ln", t_impl::log, tp::FUNCTION1 | tp::FLAG_PURE, 0}, #ifdef TP_NAT_LOG - {"log", t_impl::log, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"log", t_impl::log, tp::FUNCTION1 | tp::FLAG_PURE, 0}, #else - {"log", t_impl::log10, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"log", t_impl::log10, tp::FUNCTION1 | tp::FLAG_PURE, 0}, #endif - {"log10", t_impl::log10, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"ncr", t_impl::ncr, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"npr", t_impl::npr, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"pi", t_impl::pi, tp::FUNCTION0 | tp::FLAG_PURE, 0}, - {"pow", t_impl::pow, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"sin", t_impl::sin, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"sinh", t_impl::sinh, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"sqrt", t_impl::sqrt, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"tan", t_impl::tan, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"tanh", t_impl::tanh, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {0, 0, 0, 0}}; - - static inline constexpr tp::variable operators[] = {/* must be in alphabetical order */ - {"add", t_impl::add, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"comma", t_impl::comma, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"divide", t_impl::divide, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"equal", t_impl::equal, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"fmod", t_impl::fmod, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"greater", t_impl::greater, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"greater_eq", t_impl::greater_eq, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"logical_and", t_impl::logical_and, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"logical_not", t_impl::logical_not, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"logical_notnot", t_impl::logical_notnot, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"logical_or", t_impl::logical_or, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"lower", t_impl::lower, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"lower_eq", t_impl::lower_eq, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"mul", t_impl::mul, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"negate", t_impl::negate, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"negate_logical_not", t_impl::negate_logical_not, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"negate_logical_notnot", t_impl::negate_logical_notnot, tp::FUNCTION1 | tp::FLAG_PURE, 0}, - {"not_equal", t_impl::not_equal, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"pow", t_impl::pow, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {"sub", t_impl::sub, tp::FUNCTION2 | tp::FLAG_PURE, 0}, - {0, 0, 0, 0}}; + {"log10", t_impl::log10, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"ncr", t_impl::ncr, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"npr", t_impl::npr, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"pi", t_impl::pi, tp::FUNCTION0 | tp::FLAG_PURE, 0}, + {"pow", t_impl::pow, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"sin", t_impl::sin, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"sinh", t_impl::sinh, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"sqrt", t_impl::sqrt, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"tan", t_impl::tan, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"tanh", t_impl::tanh, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {0, 0, 0, 0}}; + + static /*inline*/ constexpr tp::variable operators[] = {/* must be in alphabetical order */ + {"add", t_impl::add, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"comma", t_impl::comma, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"divide", t_impl::divide, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"equal", t_impl::equal, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"fmod", t_impl::fmod, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"greater", t_impl::greater, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"greater_eq", t_impl::greater_eq, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"logical_and", t_impl::logical_and, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"logical_not", t_impl::logical_not, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"logical_notnot", t_impl::logical_notnot, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"logical_or", t_impl::logical_or, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"lower", t_impl::lower, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"lower_eq", t_impl::lower_eq, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"mul", t_impl::mul, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"negate", t_impl::negate, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"negate_logical_not", t_impl::negate_logical_not, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"negate_logical_notnot", t_impl::negate_logical_notnot, tp::FUNCTION1 | tp::FLAG_PURE, 0}, + {"not_equal", t_impl::not_equal, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"pow", t_impl::pow, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {"sub", t_impl::sub, tp::FUNCTION2 | tp::FLAG_PURE, 0}, + {0, 0, 0, 0}}; }; } // namespace tp_stdlib From e9cb710c6aac292420f440e38d844dabdb53e660 Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Sun, 9 Aug 2020 21:49:36 -0700 Subject: [PATCH 34/43] c++ 17 is only required for the compiler, revert some of the changes. --- include/tinyprog.h | 44 +++++++++++++++------------------------- test/example_program.cpp | 22 +++++++++++++++----- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 1f75af8..1fcdf27 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -53,7 +53,7 @@ #if TP_COMPILER_ENABLED #if (_MSVC_LANG < 201703L) - #error C++ 17 is required for the compiler. +#error C++ 17 is required for the compiler. #endif #include #endif // #if TP_COMPILER_ENABLED @@ -1737,10 +1737,7 @@ namespace tp static inline std::tuple split_at_index_excl(std::string_view s, size_t index) { - auto tup = split_at_index(s, index); - auto& l = std::get<0>(tup); - auto& r = std::get<1>(tup); - + auto [l, r] = split_at_index(s, index); if (r.length() > 1) { return {trim_all_space(l), trim_all_space(std::string_view{&r[1], r.length() - 1})}; @@ -1774,17 +1771,17 @@ namespace tp //// + static inline const auto keyword_return = std::string_view("return"); + static inline const auto keyword_jump = std::string_view("jump"); + static inline const auto keyword_label = std::string_view("label"); + static inline const auto keyword_var = std::string_view("var"); + template< typename T_ADD_VARIABLE, typename T_ADD_LABEL, typename T_ADD_JUMP, typename T_ADD_JUMP_IF, typename T_ADD_RETURN_VALUE, typename T_ADD_ASSIGN, typename T_ADD_CALL> static inline void parse_statement( - std::string_view statement, T_ADD_VARIABLE add_variable, T_ADD_LABEL add_label, T_ADD_JUMP add_jump, T_ADD_JUMP_IF add_jump_if, - T_ADD_RETURN_VALUE add_return_value, T_ADD_ASSIGN add_assign, T_ADD_CALL add_call) + std::string_view statement, T_ADD_VARIABLE add_variable, T_ADD_LABEL add_label, T_ADD_JUMP add_jump, T_ADD_JUMP_IF add_jump_if, T_ADD_RETURN_VALUE add_return_value, + T_ADD_ASSIGN add_assign, T_ADD_CALL add_call) { - const auto keyword_return = std::string_view("return"); - const auto keyword_jump = std::string_view("jump"); - const auto keyword_label = std::string_view("label"); - const auto keyword_var = std::string_view("var"); - auto [operation, expression] = split_at_char_excl(statement, ':'); if (expression.length() == 0) @@ -1829,10 +1826,10 @@ namespace tp struct label_manager { - using handle = int; - static /*inline*/ constexpr int placeholder_index = -1; + using handle = int; + static inline constexpr int placeholder_index = -1; - std::vector m_label_statement_indexes; + std::vector m_label_statement_indexes; std::unordered_map m_label_handle_map; handle add_label(std::string_view label, int statement_index) @@ -1891,7 +1888,7 @@ namespace tp struct variable_manager { - int m_variable_count = 0; + int m_variable_count = 0; std::unordered_map m_variable_map; int find_label(std::string_view name) @@ -1971,9 +1968,7 @@ namespace tp while (program_remaining.length() > 0) { - auto tup = parser::split_at_char_excl(program_remaining, ';'); - auto& statement = std::get<0>(tup); - auto& remaining = std::get<1>(tup); + auto [statement, remaining] = parser::split_at_char_excl(program_remaining, ';'); parser::parse_statement( statement, @@ -2370,7 +2365,8 @@ namespace tp size_t expression_data_data_size; }; - auto tup = [&]() -> std::tuple, std::vector, std::vector, user_var_chunk, size_t> { + auto [total_program_size, out_header, program_states, strs, user_var_indexes, user_var_data, user_var_data_data_size] = + [&]() -> std::tuple, std::vector, std::vector, user_var_chunk, size_t> { std::vector program_states; program_states.resize(num_programs); @@ -2440,14 +2436,6 @@ namespace tp return {total_program_size, out_header, program_states, strs, user_var_indexes, user_var_data, user_var_data_data_size}; }(); - auto& total_program_size = std::get<0>(tup); - auto& out_header = std::get<1>(tup); - auto& program_states = std::get<2>(tup); - auto& strs = std::get<3>(tup); - auto& user_var_indexes = std::get<4>(tup); - auto& user_var_data = std::get<5>(tup); - auto& user_var_data_data_size = std::get<6>(tup); - // if (total_program_size > sizeof(out_header)) diff --git a/test/example_program.cpp b/test/example_program.cpp index 0c25be9..8ae1763 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -1,7 +1,16 @@ +#include +#include +#include + +#include +#include + #define TP_TESTING 1 #include "tinyprog.h" -#include +using te = tp::impl>; + +#if TP_COMPILER_ENABLED bool serialize_program_to_disk(const char* file_name, te::serialized_program* sp) { FILE* f; @@ -41,6 +50,7 @@ te::serialized_program* create_program(const char* prog_texts[], size_t num_prog return new te::serialized_program(&subprograms[0], int(num_prog_texts), indexer.m_declared_variable_names); } +#endif te::serialized_program* serialize_from_disk(const char* file_name) { @@ -75,6 +85,7 @@ int main(int argc, char* argv[]) static constexpr size_t vars_count = sizeof(vars) / sizeof(vars[0]); // compile & save to disk +#if TP_COMPILER_ENABLED { const char* constructor = "var: x;" @@ -82,7 +93,7 @@ int main(int argc, char* argv[]) "var: y;" "y: 255.0;" "xx: 255.0;"; - + const char* p1 = "x: sqrt(5^2+7^2+11^2+(8-2)^2);" "test_closure(x);" @@ -90,7 +101,7 @@ int main(int argc, char* argv[]) "return: x;" "label: is_negative;" "return: -1 * x;"; - + const char* p2 = "y: sqrt(5^2+7^2+11^2+(8-2)^2);" "test_closure(y);" @@ -98,10 +109,10 @@ int main(int argc, char* argv[]) "return: y;" "label: is_negative;" "return: -1 * y;"; - + const char* progs[]{constructor, p1, p2}; const size_t num_progs = sizeof(progs) / sizeof(progs[0]); - + auto prog = create_program(progs, num_progs, vars, vars_count); assert(prog); if (!serialize_program_to_disk("progs.tpp", prog)) @@ -110,6 +121,7 @@ int main(int argc, char* argv[]) } delete prog; } +#endif // #if TP_COMPILER_ENABLED // Load from disk, setup bindings, execute { From c86b2033d461b33bf217021477a2fbc24d98c86b Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Tue, 11 Aug 2020 19:24:50 -0700 Subject: [PATCH 35/43] cxx14/nonconforming old compiler fixes --- include/tinyprog.h | 120 +++++++++++++++++++++------------------------ 1 file changed, 57 insertions(+), 63 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 1fcdf27..7bacabc 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -51,11 +51,18 @@ #define TP_STANDARD_LIBRARY 0 #endif // #ifndef TP_STANDARD_LIBRARY -#if TP_COMPILER_ENABLED #if (_MSVC_LANG < 201703L) -#error C++ 17 is required for the compiler. +#define TP_MODERN_CPP 0 +#else +#define TP_MODERN_CPP 1 #endif + +#if TP_COMPILER_ENABLED +#if TP_MODERN_CPP #include +#else +#error C++ 17 is required for the compiler. +#endif #endif // #if TP_COMPILER_ENABLED namespace tp @@ -218,7 +225,7 @@ namespace tp template static inline auto eval_portable_impl(const expr_portable* n_portable, const unsigned char* expr_buffer, const void* const expr_context[]) noexcept -> - typename T_VECTOR + T_VECTOR { using t_atom = T_ATOM; using t_vector = T_VECTOR; @@ -226,7 +233,7 @@ namespace tp using t_vector_builtins = typename T_TRAITS::t_vector_builtins; auto eval_arg = [&](int e) { - return eval_portable_impl((const expr_portable*)&expr_buffer[n_portable->parameters[e]], expr_buffer, expr_context); + return eval_portable_impl((const expr_portable*)&expr_buffer[n_portable->parameters[e]], expr_buffer, expr_context); }; return eval_generic( @@ -2191,7 +2198,8 @@ namespace tp { namespace details { - template +#if TP_MODERN_CPP + template < typename T_VAL> struct variable_helper { static inline constexpr variable readonly_var(const char* name, const T_VAL* v) noexcept @@ -2260,6 +2268,7 @@ namespace tp return {name, sevenargfn{func}, tp::FUNCTION7 | tp::FLAG_PURE, 0}; } }; +#endif struct serialized_program { @@ -2280,6 +2289,7 @@ namespace tp struct chunk : chunk_header { + using header = chunk_header; char data[1]; }; @@ -2296,21 +2306,7 @@ namespace tp return ((value + multiple - 1) / multiple) * multiple; } - static /*inline*/ constexpr uint16_t alignment{4}; - static /*inline*/ constexpr auto out_header_size = uint16_t(sizeof(header_chunk)); - static_assert(out_header_size == round_up_to_multiple(uint16_t(sizeof(header_chunk)), alignment), ""); - - static /*inline*/ constexpr auto statement_data_size = uint16_t(sizeof(chunk_header)); - static_assert(statement_data_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment), ""); - - static /*inline*/ constexpr auto expression_data_size = uint16_t(sizeof(chunk_header)); - static_assert(expression_data_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment), ""); - - static /*inline*/ constexpr auto string_header_size = uint16_t(sizeof(chunk_header)); - static_assert(string_header_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment), ""); - - static /*inline*/ constexpr auto user_var_size = uint16_t(sizeof(chunk_header)); - static_assert(user_var_size == round_up_to_multiple(uint16_t(sizeof(chunk_header)), alignment), ""); + static inline uint16_t alignment() { return 4; } struct subprogram { @@ -2378,7 +2374,7 @@ namespace tp out_header.num_binding_names = uint16_t(binding_name_count); out_header.num_subprograms = uint16_t(num_programs); - total_program_size += out_header_size; + total_program_size += sizeof(header_chunk); for (int subprogram_idx = 0; subprogram_idx < num_programs; ++subprogram_idx) { @@ -2389,13 +2385,13 @@ namespace tp auto statement_src = prog->get_statements(); prog_state.statement_data.size = uint16_t(num_statements * sizeof(statement_src[0])); - total_program_size += statement_data_size; - prog_state.statement_data_data_size = round_up_to_multiple(prog_state.statement_data.size, alignment); + total_program_size += sizeof(chunk_header); + prog_state.statement_data_data_size = round_up_to_multiple(prog_state.statement_data.size, alignment()); total_program_size += prog_state.statement_data_data_size; prog_state.expression_data.size = uint16_t(expression_size); - total_program_size += expression_data_size; - prog_state.expression_data_data_size = round_up_to_multiple(prog_state.expression_data.size, alignment); + total_program_size += sizeof(chunk_header); + prog_state.expression_data_data_size = round_up_to_multiple(prog_state.expression_data.size, alignment()); total_program_size += prog_state.expression_data_data_size; } @@ -2404,8 +2400,8 @@ namespace tp { string_chunk chonk; chonk.size = uint16_t(::strlen(binding_names[i]) + 1); - total_program_size += string_header_size; - total_program_size += round_up_to_multiple(chonk.size, uint16_t(alignment)); + total_program_size += sizeof(chunk_header); + total_program_size += round_up_to_multiple(chonk.size, uint16_t(alignment())); strs.push_back(chonk); } @@ -2429,8 +2425,8 @@ namespace tp user_var_chunk user_var_data; user_var_data.size = uint16_t(sizeof(int) * user_var_count); - total_program_size += user_var_size; - const auto user_var_data_data_size = round_up_to_multiple(user_var_data.size, alignment); + total_program_size += sizeof(chunk_header); + const auto user_var_data_data_size = round_up_to_multiple(user_var_data.size, alignment()); total_program_size += user_var_data_data_size; return {total_program_size, out_header, program_states, strs, user_var_indexes, user_var_data, user_var_data_data_size}; @@ -2440,7 +2436,7 @@ namespace tp if (total_program_size > sizeof(out_header)) { - assert(total_program_size > out_header_size + user_var_data_data_size + user_var_size); + assert(total_program_size > sizeof(header_chunk) + user_var_data_data_size + sizeof(chunk_header)); char* const serialized_program = (char*)::malloc(total_program_size); char* p = serialized_program; if (p != nullptr) @@ -2449,7 +2445,7 @@ namespace tp this->header = (header_chunk*)p; ::memcpy(p, &out_header, sizeof(out_header)); - p += out_header_size; + p += sizeof(header_chunk); first_subprogram = (statement_chunk*)p; @@ -2464,14 +2460,14 @@ namespace tp auto expression_src = prog->get_data(); subprogram.statements = (statement_chunk*)p; - ::memcpy(p, &prog_state.statement_data, statement_data_size); - p += statement_data_size; + ::memcpy(p, &prog_state.statement_data, sizeof(chunk_header)); + p += sizeof(chunk_header); ::memcpy(p, &statement_src[0], prog_state.statement_data.size); p += prog_state.statement_data_data_size; subprogram.data = (data_chunk*)p; - ::memcpy(p, &prog_state.expression_data, expression_data_size); - p += expression_data_size; + ::memcpy(p, &prog_state.expression_data, sizeof(chunk_header)); + p += sizeof(chunk_header); ::memcpy(p, &expression_src[0], prog_state.expression_data.size); p += prog_state.expression_data_data_size; } @@ -2480,17 +2476,17 @@ namespace tp for (size_t i = 0; i < binding_name_count; ++i) { - ::memcpy(p, &strs[i], string_header_size); - p += string_header_size; + ::memcpy(p, &strs[i], sizeof(chunk_header)); + p += sizeof(chunk_header); ::memcpy(p, binding_names[i], strs[i].size - 1); p[strs[i].size - 1] = '\0'; - p += round_up_to_multiple(strs[i].size, alignment); + p += round_up_to_multiple(strs[i].size, alignment()); } this->user_vars = (user_var_chunk*)p; - ::memcpy(p, &user_var_data, user_var_size); - p += user_var_size; + ::memcpy(p, &user_var_data, sizeof(chunk_header)); + p += sizeof(chunk_header); ::memcpy(p, &user_var_indexes[0], user_var_data.size); p += user_var_data_data_size; @@ -2511,19 +2507,19 @@ namespace tp const char* p = (const char*)data; this->header = (header_chunk*)p; - p += out_header_size; + p += sizeof(header_chunk); first_subprogram = (statement_chunk*)p; for (int subprogram_idx = 0; subprogram_idx < this->header->num_subprograms; ++subprogram_idx) { auto subprogram_statements = (statement_chunk*)p; - p += statement_data_size; - p += round_up_to_multiple(subprogram_statements->size, alignment); + p += sizeof(statement_chunk::header); + p += round_up_to_multiple(subprogram_statements->size, alignment()); auto subprogram_data = (data_chunk*)p; - p += expression_data_size; - p += round_up_to_multiple(subprogram_data->size, alignment); + p += sizeof(data_chunk::header); + p += round_up_to_multiple(subprogram_data->size, alignment()); } this->strings = (string_chunk*)p; @@ -2531,13 +2527,13 @@ namespace tp for (size_t i = 0; i < header->num_binding_names; ++i) { auto chonk = (const string_chunk*)p; - p += string_header_size; - p += round_up_to_multiple(chonk->size, alignment); + p += sizeof(string_chunk::header); + p += round_up_to_multiple(chonk->size, alignment()); } this->user_vars = (user_var_chunk*)p; - p += user_var_size; - p += round_up_to_multiple(this->user_vars->size, alignment); + p += sizeof(user_var_chunk::header); + p += round_up_to_multiple(this->user_vars->size, alignment()); } serialized_program(std::tuple args) : serialized_program(std::get<0>(args), std::get<1>(args)) {} @@ -2564,27 +2560,26 @@ namespace tp for (int i = 0; i < this->header->num_subprograms; ++i) { auto statements = (statement_chunk*)p; - p += statement_data_size; - p += round_up_to_multiple(statements->size, alignment); + p += sizeof(statement_chunk::header); + p += round_up_to_multiple(statements->size, alignment()); auto subprogram_data = (data_chunk*)p; - p += expression_data_size; - p += round_up_to_multiple(subprogram_data->size, alignment); + p += sizeof(data_chunk::header); + p += round_up_to_multiple(subprogram_data->size, alignment()); if (i == subprogram_index) { - return {statements, subprogram_data}; + return std::tuple(statements, subprogram_data); } } - return {nullptr, nullptr}; + return std::tuple(nullptr, nullptr); } const statement* get_statements_array(int subprogram_index) const noexcept { auto tup = get_subprogram_data(subprogram_index); auto& statements = std::get<0>(tup); - auto& subprogram_data = std::get<1>(tup); return reinterpret_cast(&statements->data[0]); } @@ -2592,27 +2587,24 @@ namespace tp { auto tup = get_subprogram_data(subprogram_index); auto& statements = std::get<0>(tup); - auto& subprogram_data = std::get<1>(tup); return statements->size / sizeof(statement); } const void* get_expression_data(int subprogram_index) const noexcept { auto tup = get_subprogram_data(subprogram_index); - auto& statements = std::get<0>(tup); auto& subprogram_data = std::get<1>(tup); return &subprogram_data->data[0]; } - const size_t get_expression_size(int subprogram_index) const noexcept + size_t get_expression_size(int subprogram_index) const noexcept { auto tup = get_subprogram_data(subprogram_index); - auto& statements = std::get<0>(tup); auto& subprogram_data = std::get<1>(tup); return subprogram_data->size; } - const size_t get_num_bindings() const noexcept + size_t get_num_bindings() const noexcept { return this->header->num_binding_names; } @@ -2630,8 +2622,8 @@ namespace tp return &chonk->data[0]; } - p += string_header_size; - p += round_up_to_multiple(chonk->size, alignment); + p += sizeof(string_chunk::header); + p += round_up_to_multiple(chonk->size, alignment()); } } return nullptr; @@ -2671,7 +2663,9 @@ namespace tp using variable = ::tp::variable; using t_atom = typename env_traits::t_atom; using t_vector = typename env_traits::t_vector; +#if TP_MODERN_CPP using variable_factory = details::variable_helper; +#endif // #if TP_MODERN_CPP using serialized_program = details::serialized_program; #if (TP_COMPILER_ENABLED) using t_indexer = program_details::t_indexer; From 832ed6cc1f45accc684ef1b15db82e8b128d98b7 Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Wed, 12 Aug 2020 22:40:56 -0700 Subject: [PATCH 36/43] Don't assert on variable errors. --- include/tinyprog.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 7bacabc..e456cd9 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -2055,7 +2055,7 @@ namespace tp if (final_index == -1) { *error = -1; // TODO: variable not found - assert(!"Variable not found."); + //assert(!"Variable not found."); return nullptr; } From d7f94b3f5239ef927aaccd1b36748bbaac53e1a2 Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Thu, 20 Aug 2020 21:34:57 -0700 Subject: [PATCH 37/43] Lots of cleanup and simplification regarding runtime lookup --- include/tinyprog.h | 581 +++++++++++++++++---------------------- test/example_program.cpp | 28 +- 2 files changed, 269 insertions(+), 340 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index e456cd9..c68b702 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -104,6 +104,12 @@ namespace tp void* context; }; + struct variable_lookup + { + const variable* lookup; + int lookup_len; + }; + template struct expr_portable { @@ -224,13 +230,11 @@ namespace tp } template - static inline auto eval_portable_impl(const expr_portable* n_portable, const unsigned char* expr_buffer, const void* const expr_context[]) noexcept -> - T_VECTOR + static inline auto eval_portable_impl(const expr_portable* n_portable, const unsigned char* expr_buffer, const void* const expr_context[]) noexcept -> T_VECTOR { - using t_atom = T_ATOM; - using t_vector = T_VECTOR; - using t_traits = T_TRAITS; - using t_vector_builtins = typename T_TRAITS::t_vector_builtins; + using t_atom = T_ATOM; + using t_vector = T_VECTOR; + using t_traits = T_TRAITS; auto eval_arg = [&](int e) { return eval_portable_impl((const expr_portable*)&expr_buffer[n_portable->parameters[e]], expr_buffer, expr_context); @@ -238,12 +242,10 @@ namespace tp return eval_generic( n_portable->type, [&]() { return t_traits::load_atom(n_portable->value); }, - [&]() { return t_traits::load_atom((expr_context != nullptr) ? *((const t_vector*)(expr_context[n_portable->bound])) : t_vector_builtins::nan()); }, - [&](int a) { return eval_function(a, expr_context[n_portable->function], t_vector_builtins::nan(), eval_arg); }, - [&](int a) { - return eval_closure(a, expr_context[n_portable->function], (void*)expr_context[n_portable->parameters[a]], t_vector_builtins::nan(), eval_arg); - }, - [&]() { return t_vector_builtins::nan(); }); + [&]() { return t_traits::load_atom((expr_context != nullptr) ? *((const t_vector*)(expr_context[n_portable->bound])) : t_traits::nan()); }, + [&](int a) { return eval_function(a, expr_context[n_portable->function], t_traits::nan(), eval_arg); }, + [&](int a) { return eval_closure(a, expr_context[n_portable->function], (void*)expr_context[n_portable->parameters[a]], t_traits::nan(), eval_arg); }, + [&]() { return t_traits::nan(); }); } } // namespace eval_details @@ -289,154 +291,6 @@ namespace tp #endif // #if (TP_COMPILER_ENABLED) } // namespace tp -namespace tp -{ - template - struct compiler_builtins : T_NATIVE - { - using t_base = T_NATIVE; - - static inline int name_compare(const char* a, const char* b, size_t n) - { - while (n && *a && (*a == *b)) - { - ++a; - ++b; - --n; - } - if (n == 0) - { - return 0; - } - else - { - return (*(unsigned char*)a - *(unsigned char*)b); - } - } - - static inline const variable* find_builtin_function(const char* name, int len) - { - int imin = 0; - int imax = sizeof(t_base::functions) / sizeof(variable) - 2; - - /*Binary search.*/ - while (imax >= imin) - { - const int i = (imin + ((imax - imin) / 2)); - int c = name_compare(name, t_base::functions[i].name, len); - if (!c) - c = '\0' - t_base::functions[i].name[len]; - if (c == 0) - { - return t_base::functions + i; - } - else if (c > 0) - { - imin = i + 1; - } - else - { - imax = i - 1; - } - } - return nullptr; - } - - static inline const variable* find_builtin_operator(const char* name, int len) - { - int imin = 0; - int imax = sizeof(t_base::operators) / sizeof(variable) - 2; - - /*Binary search.*/ - while (imax >= imin) - { - const int i = (imin + ((imax - imin) / 2)); - int c = name_compare(name, t_base::operators[i].name, len); - - if (!c) - c = '\0' - t_base::operators[i].name[len]; - - if (c == 0) - { - return t_base::operators + i; - } - else if (c > 0) - { - imin = i + 1; - } - else - { - imax = i - 1; - } - } - return nullptr; - } - - static inline const variable* find_builtin(const char* name, int len) - { - auto res = find_builtin_function(name, len); - if (!res) - { - res = find_builtin_operator(name, len); - } - return res; - } - - static inline const variable* find_builtin(const char* name) - { - return find_builtin(name, static_cast(::strlen(name))); - } - - static inline const void* find_builtin_address(const char* name) - { - auto b = find_builtin(name, static_cast(::strlen(name))); - if (b) - { - return b->address; - } - return nullptr; - } - - static inline const variable* find_function_by_addr(const void* addr) - { - for (auto var = &t_base::functions[0]; var->name != 0; ++var) - { - if (var->address == addr) - { - return var; - } - } - return nullptr; - } - - static inline const variable* find_operator_by_addr(const void* addr) - { - for (auto var = &t_base::operators[0]; var->name != 0; ++var) - { - if (var->address == addr) - { - return var; - } - } - return nullptr; - } - - static inline const variable* find_any_by_addr(const void* addr) - { - const variable* var = find_function_by_addr(addr); - if (!var) - { - var = find_operator_by_addr(addr); - if (!var) - { - return find_builtin("nul"); - } - } - return var; - } - }; -} // namespace tp - #if (TP_COMPILER_ENABLED) #include #include @@ -449,11 +303,9 @@ namespace tp template struct native { - using t_traits = T_TRAITS; - using t_atom = typename T_TRAITS::t_atom; - using t_vector = typename T_TRAITS::t_vector; - using t_atom_builtins = compiler_builtins; - using t_vector_builtins = compiler_builtins; + using t_traits = T_TRAITS; + using t_atom = typename T_TRAITS::t_atom; + using t_vector = typename T_TRAITS::t_vector; struct expr_native { @@ -495,8 +347,7 @@ namespace tp }; void* context; - const variable* lookup; - int lookup_len; + variable_lookup lookup; }; static inline bool is_pure(int t) noexcept @@ -558,26 +409,10 @@ namespace tp free(n); } - static const variable* find_lookup(const variable* lookup, int lookup_len, const char* name, int len) - { - if (!lookup) - return 0; - - const variable* var = lookup; - int iters = lookup_len; - for (; iters; ++var, --iters) - { - if (strncmp(name, var->name, len) == 0 && var->name[len] == '\0') - { - return var; - } - } - return 0; - } - - static const variable* find_lookup(const state* s, const char* name, int len) + static inline const void* find_wrapper(const char* name, state* s) { - return find_lookup(s->lookup, s->lookup_len, name, len); + auto var = t_traits::find_by_name(name, int(strlen(name)), &s->lookup); + return var ? var->address : nullptr; } static void next_token(state* s) @@ -608,9 +443,7 @@ namespace tp while ((s->next[0] >= 'a' && s->next[0] <= 'z') || (s->next[0] >= '0' && s->next[0] <= '9') || (s->next[0] == '_')) s->next++; - const variable* var = find_lookup(s, start, static_cast(s->next - start)); - if (!var) - var = t_vector_builtins::find_builtin(start, static_cast(s->next - start)); + const variable* var = t_traits::find_by_name(start, static_cast(s->next - start), &s->lookup); if (!var) { @@ -650,46 +483,46 @@ namespace tp { case '+': s->type = (int)TOK_INFIX; - s->function = t_vector_builtins::find_builtin_address("add"); + s->function = find_wrapper("add", s); break; case '-': s->type = (int)TOK_INFIX; - s->function = t_vector_builtins::find_builtin_address("sub"); + s->function = find_wrapper("sub", s); break; case '*': s->type = (int)TOK_INFIX; - s->function = t_vector_builtins::find_builtin_address("mul"); + s->function = find_wrapper("mul", s); break; case '/': s->type = (int)TOK_INFIX; - s->function = t_vector_builtins::find_builtin_address("divide"); + s->function = find_wrapper("divide", s); break; case '^': s->type = (int)TOK_INFIX; - s->function = t_vector_builtins::find_builtin_address("pow"); + s->function = find_wrapper("pow", s); break; case '%': s->type = (int)TOK_INFIX; - s->function = t_vector_builtins::find_builtin_address("fmod"); + s->function = find_wrapper("fmod", s); break; case '!': if (s->next++[0] == '=') { s->type = (int)TOK_INFIX; - s->function = t_vector_builtins::find_builtin_address("not_equal"); + s->function = find_wrapper("not_equal", s); } else { s->next--; s->type = (int)TOK_INFIX; - s->function = t_vector_builtins::find_builtin_address("logical_not"); + s->function = find_wrapper("logical_not", s); } break; case '=': if (s->next++[0] == '=') { s->type = (int)TOK_INFIX; - s->function = t_vector_builtins::find_builtin_address("equal"); + s->function = find_wrapper("equal", s); } else { @@ -700,33 +533,33 @@ namespace tp if (s->next++[0] == '=') { s->type = (int)TOK_INFIX; - s->function = t_vector_builtins::find_builtin_address("lower_eq"); + s->function = find_wrapper("lower_eq", s); } else { s->next--; s->type = (int)TOK_INFIX; - s->function = t_vector_builtins::find_builtin_address("lower"); + s->function = find_wrapper("lower", s); } break; case '>': if (s->next++[0] == '=') { s->type = (int)TOK_INFIX; - s->function = t_vector_builtins::find_builtin_address("greater_eq"); + s->function = find_wrapper("greater_eq", s); } else { s->next--; s->type = (int)TOK_INFIX; - s->function = t_vector_builtins::find_builtin_address("greater"); + s->function = find_wrapper("greater", s); } break; case '&': if (s->next++[0] == '&') { s->type = (int)TOK_INFIX; - s->function = t_vector_builtins::find_builtin_address("logical_and"); + s->function = find_wrapper("logical_and", s); } else { @@ -737,7 +570,7 @@ namespace tp if (s->next++[0] == '|') { s->type = (int)TOK_INFIX; - s->function = t_vector_builtins::find_builtin_address("logical_or"); + s->function = find_wrapper("logical_or", s); } else { @@ -871,7 +704,7 @@ namespace tp { ret = new_expr(0, 0); s->type = (int)TOK_ERROR; - ret->value = t_vector_builtins::nan(); + ret->value = t_traits::nan(); } return ret; @@ -881,18 +714,17 @@ namespace tp { /* = {("-" | "+" | "!")} */ int sign = 1; - while (s->type == (int)TOK_INFIX && (s->function == t_vector_builtins::find_builtin_address("add") || s->function == t_vector_builtins::find_builtin_address("sub"))) + while (s->type == (int)TOK_INFIX && (s->function == find_wrapper("add", s) || s->function == find_wrapper("sub", s))) { - if (s->function == t_vector_builtins::find_builtin_address("sub")) + if (s->function == find_wrapper("sub", s)) sign = -sign; next_token(s); } int logical = 0; - while (s->type == (int)TOK_INFIX && (s->function == t_vector_builtins::find_builtin_address("add") || s->function == t_vector_builtins::find_builtin_address("sub") || - s->function == t_vector_builtins::find_builtin_address("logical_not"))) + while (s->type == (int)TOK_INFIX && (s->function == find_wrapper("add", s) || s->function == find_wrapper("sub", s) || s->function == find_wrapper("logical_not", s))) { - if (s->function == t_vector_builtins::find_builtin_address("logical_not")) + if (s->function == find_wrapper("logical_not", s)) { if (logical == 0) { @@ -917,12 +749,12 @@ namespace tp else if (logical == -1) { ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); - ret->function = t_vector_builtins::find_builtin_address("logical_not"); + ret->function = find_wrapper("logical_not", s); } else { ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); - ret->function = t_vector_builtins::find_builtin_address("logical_notnot"); + ret->function = find_wrapper("logical_notnot", s); } } else @@ -930,17 +762,17 @@ namespace tp if (logical == 0) { ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); - ret->function = t_vector_builtins::find_builtin_address("negate"); + ret->function = find_wrapper("negate", s); } else if (logical == -1) { ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); - ret->function = t_vector_builtins::find_builtin_address("negate_logical_not"); + ret->function = find_wrapper("negate_logical_not", s); } else { ret = NEW_EXPR(FUNCTION1 | FLAG_PURE, base(s)); - ret->function = t_vector_builtins::find_builtin_address("negate_logical_notnot"); + ret->function = find_wrapper("negate_logical_notnot", s); } } @@ -957,9 +789,8 @@ namespace tp expr_native* insertion = 0; if (ret->type == (FUNCTION1 | FLAG_PURE) && - (ret->function == t_vector_builtins::find_builtin_address("negate") || ret->function == t_vector_builtins::find_builtin_address("logical_not") || - ret->function == t_vector_builtins::find_builtin_address("logical_notnot") || ret->function == t_vector_builtins::find_builtin_address("negate_logical_not") || - ret->function == t_vector_builtins::find_builtin_address("negate_logical_notnot"))) + (ret->function == find_wrapper("negate", s) || ret->function == find_wrapper("logical_not", s) || ret->function == find_wrapper("logical_notnot", s) || + ret->function == find_wrapper("negate_logical_not", s) || ret->function == find_wrapper("negate_logical_notnot", s))) { left_function = ret->function; expr_native* se = ret->parameters[0]; @@ -967,7 +798,7 @@ namespace tp ret = se; } - while (s->type == (int)TOK_INFIX && (s->function == t_vector_builtins::find_builtin_address("pow"))) + while (s->type == (int)TOK_INFIX && (s->function == find_wrapper("pow", s))) { te_fun2 t = s->function; next_token(s); @@ -1002,7 +833,7 @@ namespace tp /* = {"^" } */ expr_native* ret = power(s); - while (s->type == (int)TOK_INFIX && (s->function == t_vector_builtins::find_builtin_address("pow"))) + while (s->type == (int)TOK_INFIX && (s->function == find_wrapper("pow", s))) { te_fun2 t = (te_fun2)s->function; next_token(s); @@ -1019,9 +850,7 @@ namespace tp /* = {("*" | "/" | "%") } */ expr_native* ret = factor(s); - while (s->type == (int)TOK_INFIX && - (s->function == t_vector_builtins::find_builtin_address("mul") || s->function == t_vector_builtins::find_builtin_address("divide") || - s->function == t_vector_builtins::find_builtin_address("fmod"))) + while (s->type == (int)TOK_INFIX && (s->function == find_wrapper("mul", s) || s->function == find_wrapper("divide", s) || s->function == find_wrapper("fmod", s))) { te_fun2 t = (te_fun2)s->function; next_token(s); @@ -1037,7 +866,7 @@ namespace tp /* = {("+" | "-") } */ expr_native* ret = term(s); - while (s->type == (int)TOK_INFIX && (s->function == t_vector_builtins::find_builtin_address("add") || s->function == t_vector_builtins::find_builtin_address("sub"))) + while (s->type == (int)TOK_INFIX && (s->function == find_wrapper("add", s) || s->function == find_wrapper("sub", s))) { te_fun2 t = (te_fun2)s->function; next_token(s); @@ -1054,9 +883,8 @@ namespace tp expr_native* ret = sum_expr(s); while (s->type == (int)TOK_INFIX && - (s->function == t_vector_builtins::find_builtin_address("greater") || s->function == t_vector_builtins::find_builtin_address("greater_eq") || - s->function == t_vector_builtins::find_builtin_address("lower") || s->function == t_vector_builtins::find_builtin_address("lower_eq") || - s->function == t_vector_builtins::find_builtin_address("equal") || s->function == t_vector_builtins::find_builtin_address("not_equal"))) + (s->function == find_wrapper("greater", s) || s->function == find_wrapper("greater_eq", s) || s->function == find_wrapper("lower", s) || + s->function == find_wrapper("lower_eq", s) || s->function == find_wrapper("equal", s) || s->function == find_wrapper("not_equal", s))) { te_fun2 t = (te_fun2)s->function; next_token(s); @@ -1072,8 +900,7 @@ namespace tp /* = {("&&" | "||") } */ expr_native* ret = test_expr(s); - while (s->type == (int)TOK_INFIX && - (s->function == t_vector_builtins::find_builtin_address("logical_and") || s->function == t_vector_builtins::find_builtin_address("logical_or"))) + while (s->type == (int)TOK_INFIX && (s->function == find_wrapper("logical_and", s) || s->function == find_wrapper("logical_or", s))) { te_fun2 t = (te_fun2)s->function; next_token(s); @@ -1093,7 +920,7 @@ namespace tp { next_token(s); ret = NEW_EXPR(FUNCTION2 | FLAG_PURE, ret, expr(s)); - ret->function = t_vector_builtins::find_builtin_address("comma"); + ret->function = find_wrapper("comma", s); } return ret; @@ -1102,7 +929,7 @@ namespace tp static t_vector eval_native(const expr_native* n) { if (!n) - return t_vector_builtins::nan(); + return t_traits::nan(); auto eval_arg = [&](int e) { return eval_native((const expr_native*)n->parameters[e]); @@ -1110,9 +937,8 @@ namespace tp return eval_details::eval_generic( n->type, [&]() { return n->value; }, [&]() { return *n->bound; }, - [&](int a) { return eval_details::eval_function(a, n->function, t_vector_builtins::nan(), eval_arg); }, - [&](int a) { return eval_details::eval_closure(a, n->function, (void*)n->parameters[a], t_vector_builtins::nan(), eval_arg); }, - [&]() { return t_vector_builtins::nan(); }); + [&](int a) { return eval_details::eval_function(a, n->function, t_traits::nan(), eval_arg); }, + [&](int a) { return eval_details::eval_closure(a, n->function, (void*)n->parameters[a], t_traits::nan(), eval_arg); }, [&]() { return t_traits::nan(); }); } static void optimize(expr_native* n) @@ -1150,9 +976,9 @@ namespace tp static expr_native* compile_native(const char* expression, const variable* variables, int var_count, int* error) { state s; - s.start = s.next = expression; - s.lookup = variables; - s.lookup_len = var_count; + s.start = s.next = expression; + s.lookup.lookup = variables; + s.lookup.lookup_len = var_count; next_token(&s); expr_native* root = list(&s); @@ -1229,47 +1055,6 @@ namespace tp { pn(n, 0); } - - //// - - static const variable* find_bind_by_addr(const void* addr, const variable* lookup, int lookup_len) - { - for (int i = 0; i < lookup_len; ++i) - { - if (lookup[i].address == addr) - { - return &lookup[i]; - } - } - return nullptr; - } - - static const variable* find_closure_by_addr(const void* addr, const variable* lookup, int lookup_len) - { - for (int i = 0; i < lookup_len; ++i) - { - if (lookup[i].context == addr) - { - return &lookup[i]; - } - } - return nullptr; - } - - static const variable* find_bind_or_any_by_addr(const void* addr, const variable* lookup, int lookup_len) - { - auto res = t_vector_builtins::find_any_by_addr(addr); - if (!res) - { - res = find_bind_by_addr(addr, lookup, lookup_len); - if (!res) - { - // maybe this is a closure? - res = find_closure_by_addr(addr, lookup, lookup_len); - } - } - return res; - } }; template @@ -1409,8 +1194,7 @@ namespace tp } }; - static size_t - export_estimate(const expr_native* n, size_t& export_size, const variable* lookup, int lookup_len, name_map& name_map, index_map& index_map, int& index_counter) + static size_t export_estimate(const expr_native* n, size_t& export_size, const variable_lookup* lookup, name_map& name_map, index_map& index_map, int& index_counter) { if (!n) return export_size; @@ -1418,7 +1202,7 @@ namespace tp export_size += sizeof(expr_native); auto eval_arg = [&](int e) { - export_estimate((const expr_native*)n->parameters[e], export_size, lookup, lookup_len, name_map, index_map, index_counter); + export_estimate((const expr_native*)n->parameters[e], export_size, lookup, name_map, index_map, index_counter); }; auto handle_addr = [&](const variable* var) -> bool { @@ -1449,13 +1233,13 @@ namespace tp return eval_details::eval_generic( n->type, [&]() { return export_size; }, [&]() { - auto res = handle_addr(native::find_bind_by_addr(n->bound, lookup, lookup_len)); + auto res = handle_addr(t_traits::find_by_addr(n->bound, lookup)); assert(res); ((void)res); return export_size; }, [&](int a) { - auto res = handle_addr(native::find_bind_or_any_by_addr(n->function, lookup, lookup_len)); + auto res = handle_addr(t_traits::find_by_addr(n->function, lookup)); assert(res); ((void)res); export_size += sizeof(n->parameters[0]) * a; @@ -1467,7 +1251,7 @@ namespace tp return export_size; }, [&](int a) { - auto res = handle_addr(native::find_bind_or_any_by_addr(n->function, lookup, lookup_len)); + auto res = handle_addr(t_traits::find_by_addr(n->function, lookup)); assert(res); ((void)res); export_size += sizeof(n->parameters[0]) * a; @@ -1482,8 +1266,7 @@ namespace tp } template - static size_t - export_write(const expr_native* n, size_t& export_size, const variable* lookup, int lookup_len, const unsigned char* out_buffer, T_REGISTER_FUNC register_func) + static size_t export_write(const expr_native* n, size_t& export_size, const variable_lookup* lookup, const unsigned char* out_buffer, T_REGISTER_FUNC register_func) { if (!n) return export_size; @@ -1494,7 +1277,7 @@ namespace tp n_out->type = n->type; auto eval_arg = [&](int e) { - return export_write((const expr_native*)n->parameters[e], export_size, lookup, lookup_len, out_buffer, register_func); + return export_write((const expr_native*)n->parameters[e], export_size, lookup, out_buffer, register_func); }; return eval_details::eval_generic( @@ -1504,11 +1287,11 @@ namespace tp return export_size; }, [&]() { - register_func(n->bound, n_out, native::find_bind_by_addr(n->bound, lookup, lookup_len)); + register_func(n->bound, n_out, t_traits::find_by_addr(n->bound, lookup)); return export_size; }, [&](int a) { - register_func(n->function, n_out, native::find_bind_or_any_by_addr(n->function, lookup, lookup_len)); + register_func(n->function, n_out, t_traits::find_by_addr(n->function, lookup)); export_size += sizeof(n->parameters[0]) * eval_details::arity(n->type); @@ -1520,7 +1303,7 @@ namespace tp return export_size; }, [&](int a) { - register_func(n->function, n_out, native::find_bind_or_any_by_addr(n->function, lookup, lookup_len)); + register_func(n->function, n_out, t_traits::find_by_addr(n->function, lookup)); export_size += sizeof(n->parameters[0]) * eval_details::arity(n->type); @@ -1616,18 +1399,17 @@ namespace tp template compiled_expr* compile_using_indexer(typename portable::expr_portable_expression_build_indexer& indexer, const char* expression, int* error) { - auto var_array = indexer.get_variable_array(); - int var_count = (int)var_array.size(); - auto variables = (var_count > 0) ? &var_array[0] : nullptr; + auto var_array = indexer.get_variable_array(); + variable_lookup variables{(var_array.size() > 0) ? &var_array[0] : nullptr, (int)var_array.size()}; - typename native::expr_native* native_expr = native::compile_native(expression, variables, var_count, error); + typename native::expr_native* native_expr = native::compile_native(expression, variables.lookup, variables.lookup_len, error); if (native_expr) { auto expr = new typename portable::compiled_expr; size_t export_size = 0; - portable::export_estimate(native_expr, export_size, variables, var_count, indexer.name_map, indexer.index_map, indexer.index_counter); + portable::export_estimate(native_expr, export_size, &variables, indexer.name_map, indexer.index_map, indexer.index_counter); expr->m_bindings.index_to_address.resize(indexer.index_counter); for (const auto& itor : indexer.index_map) @@ -1651,8 +1433,7 @@ namespace tp size_t actual_export_size = 0; portable::export_write( - native_expr, actual_export_size, variables, var_count, expr->m_build_buffer.get(), - [&](const void* addr, expr_portable* out, const variable* v) -> void { + native_expr, actual_export_size, &variables, expr->m_build_buffer.get(), [&](const void* addr, expr_portable* out, const variable* v) -> void { assert(v != nullptr); auto itor = indexer.index_map.find(addr); assert(itor != indexer.index_map.end()); @@ -2055,7 +1836,7 @@ namespace tp if (final_index == -1) { *error = -1; // TODO: variable not found - //assert(!"Variable not found."); + // assert(!"Variable not found."); return nullptr; } @@ -2199,7 +1980,7 @@ namespace tp namespace details { #if TP_MODERN_CPP - template < typename T_VAL> + template struct variable_helper { static inline constexpr variable readonly_var(const char* name, const T_VAL* v) noexcept @@ -2306,7 +2087,10 @@ namespace tp return ((value + multiple - 1) / multiple) * multiple; } - static inline uint16_t alignment() { return 4; } + static inline uint16_t alignment() + { + return 4; + } struct subprogram { @@ -2578,15 +2362,15 @@ namespace tp const statement* get_statements_array(int subprogram_index) const noexcept { - auto tup = get_subprogram_data(subprogram_index); - auto& statements = std::get<0>(tup); + auto tup = get_subprogram_data(subprogram_index); + auto& statements = std::get<0>(tup); return reinterpret_cast(&statements->data[0]); } size_t get_statements_array_size(int subprogram_index) const noexcept { - auto tup = get_subprogram_data(subprogram_index); - auto& statements = std::get<0>(tup); + auto tup = get_subprogram_data(subprogram_index); + auto& statements = std::get<0>(tup); return statements->size / sizeof(statement); } @@ -2659,12 +2443,13 @@ namespace tp template struct impl { - using env_traits = T_TRAITS; - using variable = ::tp::variable; - using t_atom = typename env_traits::t_atom; - using t_vector = typename env_traits::t_vector; + using env_traits = T_TRAITS; + using variable = ::tp::variable; + using variable_lookup = ::tp::variable_lookup; + using t_atom = typename env_traits::t_atom; + using t_vector = typename env_traits::t_vector; #if TP_MODERN_CPP - using variable_factory = details::variable_helper; + using variable_factory = details::variable_helper; #endif // #if TP_MODERN_CPP using serialized_program = details::serialized_program; #if (TP_COMPILER_ENABLED) @@ -2707,10 +2492,10 @@ namespace tp default: // fatal error - return env_traits::t_vector_builtins::nan(); + return env_traits::nan(); } } - return env_traits::t_vector_builtins::nan(); + return env_traits::nan(); } static inline t_vector eval_program(serialized_program& prog, int subprogram, const void* const* binding_addrs) @@ -2750,7 +2535,7 @@ namespace tp } else { - ret = env_traits::t_vector_builtins::nan(); + ret = env_traits::nan(); } return ret; } @@ -3340,18 +3125,123 @@ namespace tp_stdlib {"sub", t_impl::sub, tp::FUNCTION2 | tp::FLAG_PURE, 0}, {0, 0, 0, 0}}; }; -} // namespace tp_stdlib -namespace tp_stdlib -{ - template typename T_NATIVE_BUILTINS> + template + struct compiler_builtins : T_NATIVE + { + using t_base = T_NATIVE; + + static inline int name_compare(const char* a, const char* b, size_t n) + { + while (n && *a && (*a == *b)) + { + ++a; + ++b; + --n; + } + if (n == 0) + { + return 0; + } + else + { + return (*(unsigned char*)a - *(unsigned char*)b); + } + } + + static inline const ::tp::variable* find_in_sorted_array(const char* name, int len, const ::tp::variable* vars, int vars_len) + { + int imin = 0; + int imax = vars_len - 2; + + /*Binary search.*/ + while (imax >= imin) + { + const int i = (imin + ((imax - imin) / 2)); + int c = name_compare(name, vars[i].name, len); + if (!c) + c = '\0' - vars[i].name[len]; + if (c == 0) + { + return vars + i; + } + else if (c > 0) + { + imin = i + 1; + } + else + { + imax = i - 1; + } + } + return nullptr; + } + + static inline const ::tp::variable* find_by_name(const char* name, int len, const ::tp::variable_lookup* lookup) + { + if (lookup) + { + const ::tp::variable* var = lookup->lookup; + int iters = lookup->lookup_len; + for (; iters; ++var, --iters) + { + if (name_compare(name, var->name, len) == 0 && var->name[len] == '\0') + { + return var; + } + } + } + + auto res = find_in_sorted_array(name, len, t_base::functions, int(sizeof(t_base::functions) / sizeof(::tp::variable))); + if (!res) + { + res = find_in_sorted_array(name, len, t_base::operators, int(sizeof(t_base::operators) / sizeof(::tp::variable))); + } + return res; + } + + static const ::tp::variable* find_by_addr(const void* addr, const ::tp::variable_lookup* lookup) + { + if (lookup) + { + for (int i = 0; i < lookup->lookup_len; ++i) + { + if (lookup->lookup[i].address == addr) + { + return &lookup->lookup[i]; + } + } + } + + for (auto var = &t_base::functions[0]; var->name != 0; ++var) + { + if (var->address == addr) + { + return var; + } + } + + for (auto var = &t_base::operators[0]; var->name != 0; ++var) + { + if (var->address == addr) + { + return var; + } + } + + return nullptr; + } + }; + struct env_traits_f32 { - using t_atom = float; - using t_vector = float; - using t_vector_int = int; - using t_atom_builtins = T_NATIVE_BUILTINS; - using t_vector_builtins = T_NATIVE_BUILTINS; + using t_atom = float; + using t_vector = float; + using t_vector_int = int; + +#if TP_COMPILER_ENABLED + using t_vector_builtins = compiler_builtins>; +#endif static inline t_vector load_atom(t_atom a) noexcept { @@ -3382,16 +3272,34 @@ namespace tp_stdlib { return (int)a; } + + static inline t_vector nan() + { + return std::numeric_limits::quiet_NaN(); + } + +#if TP_COMPILER_ENABLED + static inline const ::tp::variable* find_by_name(const char* name, int len, const ::tp::variable_lookup* lookup) + { + return t_vector_builtins::find_by_name(name, len, lookup); + } + + static const ::tp::variable* find_by_addr(const void* addr, const ::tp::variable_lookup* lookup) + { + return t_vector_builtins::find_by_addr(addr, lookup); + } +#endif // #if TP_COMPILER_ENABLED }; - template typename T_NATIVE_BUILTINS> struct env_traits_d64 { - using t_atom = double; - using t_vector = double; - using t_vector_int = int; - using t_atom_builtins = T_NATIVE_BUILTINS; - using t_vector_builtins = T_NATIVE_BUILTINS; + using t_atom = double; + using t_vector = double; + using t_vector_int = int; + +#if TP_COMPILER_ENABLED + using t_vector_builtins = compiler_builtins>; +#endif static inline t_vector load_atom(t_atom a) noexcept { @@ -3422,6 +3330,23 @@ namespace tp_stdlib { return (int)a; } + + static inline t_vector nan() + { + return std::numeric_limits::quiet_NaN(); + } + +#if TP_COMPILER_ENABLED + static inline const ::tp::variable* find_by_name(const char* name, int len, const ::tp::variable_lookup* lookup) + { + return t_vector_builtins::find_by_name(name, len, lookup); + } + + static const ::tp::variable* find_by_addr(const void* addr, const ::tp::variable_lookup* lookup) + { + return t_vector_builtins::find_by_addr(addr, lookup); + } +#endif // #if TP_COMPILER_ENABLED }; } // namespace tp_stdlib #endif // #if TP_STANDARD_LIBRARY @@ -3430,7 +3355,7 @@ namespace tp_stdlib #if !TP_STANDARD_LIBRARY #error TP_STANDARD_LIBRARY should be defined for testing #endif // #if !TP_STANDARD_LIBRARY -using te = tp::impl>; +using te = tp::impl; #endif // #if TP_TESTING #endif /*__TINYPROG_H__*/ diff --git a/test/example_program.cpp b/test/example_program.cpp index 8ae1763..ece38d3 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -5,10 +5,11 @@ #include #include -#define TP_TESTING 1 +#define TP_COMPILER_ENABLED 1 +#define TP_STANDARD_LIBRARY 1 #include "tinyprog.h" -using te = tp::impl>; +using te = tp::impl; #if TP_COMPILER_ENABLED bool serialize_program_to_disk(const char* file_name, te::serialized_program* sp) @@ -29,7 +30,7 @@ bool serialize_program_to_disk(const char* file_name, te::serialized_program* sp te::serialized_program* create_program(const char* prog_texts[], size_t num_prog_texts, te::variable* vars, size_t num_vars) { - using builtins = tp::compiler_builtins; + using builtins = te::env_traits::t_vector_builtins; te::t_indexer indexer; for (int i = 0; i < num_vars; ++i) @@ -78,7 +79,7 @@ float test_closure(void* context, float arg) int main(int argc, char* argv[]) { - using builtins = tp::compiler_builtins; + using builtins = tp_stdlib::compiler_builtins>; te::env_traits::t_atom x = 0.0f, y = -1.0f; te::variable vars[] = {{"xx", &x}, {"y", &y}, {"test_closure", test_closure, tp::CLOSURE1, (void*)0xf33db33ff33db33f}}; @@ -95,12 +96,13 @@ int main(int argc, char* argv[]) "xx: 255.0;"; const char* p1 = - "x: sqrt(5^2+7^2+11^2+(8-2)^2);" - "test_closure(x);" - "jump: is_negative ? x < 0;" - "return: x;" + "var: x_tmp ? local;" + "x_tmp: sqrt(5^2+7^2+11^2+(8-2)^2);" + "test_closure(x_tmp);" + "jump: is_negative ? x_tmp < 0;" + "return: x_tmp;" "label: is_negative;" - "return: -1 * x;"; + "return: -1 * x_tmp;"; const char* p2 = "y: sqrt(5^2+7^2+11^2+(8-2)^2);" @@ -187,12 +189,14 @@ int main(int argc, char* argv[]) // Builtins come last if (!binding_array[i]) { - binding_array[i] = builtins::find_builtin_address(name); + auto t = builtins::find_by_name(name, int(strlen(name)), nullptr); + binding_array[i] = t ? t->address : nullptr; } + + // All bindings must be valid, otherwise the runtime will crash. + assert(binding_array[i] != nullptr); } - // All bindings must be valid, otherwise the runtime will crash. - assert(binding_array[i] != nullptr); } } From 39792448e24dc3a0d6afb9c2ba178eab587ae848 Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Thu, 20 Aug 2020 22:33:55 -0700 Subject: [PATCH 38/43] Reduce memory churn on temporary variable lookup arrays. --- include/tinyprog.h | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index c68b702..68d6074 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -973,12 +973,18 @@ namespace tp } } - static expr_native* compile_native(const char* expression, const variable* variables, int var_count, int* error) + static expr_native* compile_native(const char* expression, const variable_lookup* lookup, int* error) { state s; s.start = s.next = expression; - s.lookup.lookup = variables; - s.lookup.lookup_len = var_count; + if (lookup) + { + s.lookup = *lookup; + } + else + { + s.lookup = { 0, 0 }; + } next_token(&s); expr_native* root = list(&s); @@ -1006,7 +1012,7 @@ namespace tp static t_vector interp_native(const char* expression, int* error) { - expr_native* n = compile_native(expression, 0, 0, error); + expr_native* n = compile_native(expression, 0, error); t_vector ret; if (n) { @@ -1090,17 +1096,27 @@ namespace tp m_declared_variable_values.clear(); } - std::vector get_variable_array() const + struct variable_lookup_temp { - std::vector combined; + std::vector data; + + variable_lookup get_lookup() const + { + return variable_lookup { (data.size() > 0) ? &data[0] : nullptr, int(data.size()) }; + } + }; + + std::unique_ptr get_variable_array() const + { + std::unique_ptr combined(new variable_lookup_temp()); for (auto var : m_env_variables) { - combined.push_back(var); + combined->data.push_back(var); } for (size_t v = 0; v < m_declared_variable_names.size(); ++v) { - combined.push_back(variable{m_declared_variable_names[v].c_str(), &m_declared_variable_values[v]}); + combined->data.push_back(variable{m_declared_variable_names[v].c_str(), &m_declared_variable_values[v]}); } return combined; } @@ -1400,9 +1416,9 @@ namespace tp compiled_expr* compile_using_indexer(typename portable::expr_portable_expression_build_indexer& indexer, const char* expression, int* error) { auto var_array = indexer.get_variable_array(); - variable_lookup variables{(var_array.size() > 0) ? &var_array[0] : nullptr, (int)var_array.size()}; + auto variables = var_array->get_lookup(); - typename native::expr_native* native_expr = native::compile_native(expression, variables.lookup, variables.lookup_len, error); + typename native::expr_native* native_expr = native::compile_native(expression, &variables, error); if (native_expr) { @@ -1811,7 +1827,8 @@ namespace tp // Add referenced variables to the lookup dict { - auto var_array = indexer.get_variable_array(); + auto var_array_tmp = indexer.get_variable_array(); + auto var_lookup = var_array_tmp->get_lookup(); for (auto itor : vm.m_variable_map) { auto name = itor.first; @@ -1819,12 +1836,9 @@ namespace tp int final_index = -1; - auto variables = &var_array[0]; - int var_count = (int)var_array.size(); - - for (int var_idx = 0; var_idx < var_count; ++var_idx) + for (int var_idx = 0; var_idx < var_lookup.lookup_len; ++var_idx) { - const auto& var = variables[var_idx]; + const auto& var = var_lookup.lookup[var_idx]; if (name == std::string_view(var.name)) { From c930ad361f20a1d94d6a85a72eac3c836d5e705b Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Sat, 22 Aug 2020 21:13:02 -0700 Subject: [PATCH 39/43] Added support for _ prefixed variables and api defined constant vars. --- include/tinyprog.h | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 68d6074..d07251b 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -436,7 +436,7 @@ namespace tp else { /* Look for a variable or builtin function call. */ - if (s->next[0] >= 'a' && s->next[0] <= 'z') + if ((s->next[0] >= 'a' && s->next[0] <= 'z') || (s->next[0] == '_')) { const char* start; start = s->next; @@ -452,7 +452,12 @@ namespace tp else { const auto t = eval_details::type_mask(var->type); - if (t == VARIABLE) + if (t == CONSTANT) + { + s->type = (int)TOK_VARIABLE; + s->bound = (const t_atom*)var->address; + } + else if (t == VARIABLE) { s->type = (int)TOK_VARIABLE; s->bound = (const t_atom*)var->address; @@ -945,9 +950,21 @@ namespace tp { /* Evaluates as much as possible. */ if (n->type == CONSTANT) + { return; + } + if (n->type == VARIABLE) + { + const variable* v = t_traits::find_by_addr(n->bound, nullptr); + if (v && (v->type == CONSTANT)) + { + free_parameters(n); + n->type = CONSTANT; + n->value = *((t_vector*)n->bound); + } return; + } /* Only optimize out functions flagged as pure. */ if (is_pure(n->type)) From e34fb2b1a042e1cdeb0430c0a490e3e653ee95df Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Mon, 31 Aug 2020 12:09:19 -0700 Subject: [PATCH 40/43] Fixing duplicate user vars. --- include/tinyprog.h | 11 ++++++++--- test/example_program.cpp | 4 ++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index d07251b..507e2a8 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -1138,10 +1138,15 @@ namespace tp return combined; } - void add_declared_variable(std::string_view name, std::string_view /*scope*/) + void add_declared_variable(std::string_view name_view, std::string_view /*scope*/) { - m_declared_variable_names.push_back(std::string(name)); - m_declared_variable_values.resize(m_declared_variable_names.size()); + std::string name(name_view); + auto itor = std::find(m_declared_variable_names.begin(), m_declared_variable_names.end(), name); + if (itor == m_declared_variable_names.end()) + { + m_declared_variable_names.push_back(name); + m_declared_variable_values.resize(m_declared_variable_names.size()); + } } void add_user_variable(const variable* var) diff --git a/test/example_program.cpp b/test/example_program.cpp index ece38d3..927e143 100644 --- a/test/example_program.cpp +++ b/test/example_program.cpp @@ -105,6 +105,7 @@ int main(int argc, char* argv[]) "return: -1 * x_tmp;"; const char* p2 = + "var: y;" "y: sqrt(5^2+7^2+11^2+(8-2)^2);" "test_closure(y);" "jump: is_negative ? y < 0;" @@ -121,6 +122,9 @@ int main(int argc, char* argv[]) { assert(0); } + + assert(prog->get_num_user_vars() == 3); + delete prog; } #endif // #if TP_COMPILER_ENABLED From 2a56b4cf0d909fd50706a421c4e29949a0fb5fbb Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Mon, 31 Aug 2020 18:31:40 -0700 Subject: [PATCH 41/43] Fix the duplicate var binding problem. I really need to refactor this thing. --- include/tinyprog.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 507e2a8..4f11f40 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -1100,8 +1100,8 @@ namespace tp std::vector m_env_variables; - std::vector m_declared_variable_names; - std::vector m_declared_variable_values; + std::vector m_declared_variable_names; + std::vector> m_declared_variable_values; void reset() { @@ -1126,15 +1126,17 @@ namespace tp std::unique_ptr get_variable_array() const { std::unique_ptr combined(new variable_lookup_temp()); - for (auto var : m_env_variables) + + for (size_t v = 0; v < m_declared_variable_names.size(); ++v) { - combined->data.push_back(var); + combined->data.push_back(variable{m_declared_variable_names[v].c_str(), m_declared_variable_values[v].get()}); } - for (size_t v = 0; v < m_declared_variable_names.size(); ++v) + for (auto var : m_env_variables) { - combined->data.push_back(variable{m_declared_variable_names[v].c_str(), &m_declared_variable_values[v]}); + combined->data.push_back(var); } + return combined; } @@ -1145,7 +1147,7 @@ namespace tp if (itor == m_declared_variable_names.end()) { m_declared_variable_names.push_back(name); - m_declared_variable_values.resize(m_declared_variable_names.size()); + m_declared_variable_values.emplace_back(new t_atom); } } From 78016f368e6c5ac509ad9783305ad1bc68618729 Mon Sep 17 00:00:00 2001 From: nrausch_shg Date: Tue, 1 Sep 2020 13:13:54 -0700 Subject: [PATCH 42/43] Fixed jump statements. --- include/tinyprog.h | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/include/tinyprog.h b/include/tinyprog.h index 4f11f40..79afb1c 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -2501,38 +2501,47 @@ namespace tp static inline t_vector eval_program(const statement* statement_array, int statement_array_size, const void* expr_buffer, const void* const expr_context[]) { - for (int statement_index = 0; statement_index < statement_array_size; ++statement_index) + for (int statement_index = 0; statement_index < statement_array_size;) { auto& statement = statement_array[statement_index]; - switch (statement.type) - { - case statement_type::jump: + + if (statement.type == statement_type::jump) + { if (statement.arg_b == -1 || (0.0f != eval(((const char*)expr_buffer) + statement.arg_b, expr_context))) // TODO: traits function like nan for zero, or compare function? { statement_index = statement.arg_a; + continue; } - break; - - case statement_type::return_value: + else + { + ++statement_index; + } + } + else if (statement.type == statement_type::return_value) + { return eval(((const char*)expr_buffer) + statement.arg_a, expr_context); - - case statement_type::assign: + } + else if (statement.type == statement_type::assign) { auto dest = (t_vector*)expr_context[statement.arg_a]; *dest = eval(((const char*)expr_buffer) + statement.arg_b, expr_context); + ++statement_index; } - break; - - case statement_type::call: + else if (statement.type == statement_type::call) + { eval(((const char*)expr_buffer) + statement.arg_a, expr_context); - break; - - default: - // fatal error + ++statement_index; + } + else + { + // fatal, unknown statement + assert(0); return env_traits::nan(); } } + + // TODO: should probably make this a std::optional or something to indicate success or faillure return env_traits::nan(); } From 8818dc870cb1509e14b73e30d4e4d0d65e33efb6 Mon Sep 17 00:00:00 2001 From: Nathan Rausch <1158984+loopunit@users.noreply.github.com> Date: Fri, 25 Sep 2020 15:58:36 -0700 Subject: [PATCH 43/43] Rework project, using TheLartians/ModernCppStarter as a template. --- .clang-format | 80 ++++-------------- .cmake-format | 56 ++++++++++++ .github/workflows/documentation.yaml | 29 +++++++ .github/workflows/install.yml | 37 ++++++++ .github/workflows/macos.yml | 31 +++++++ .github/workflows/standalone.yml | 26 ++++++ .github/workflows/style.yml | 28 ++++++ .github/workflows/ubuntu.yml | 35 ++++++++ .github/workflows/windows.yml | 31 +++++++ .gitignore | 5 +- CMakeLists.txt | 117 ++++++++++++-------------- cmake/CPM.cmake | 19 +++++ cmake/tools.cmake | 74 ++++++++++++++++ codecov.yaml | 5 ++ documentation/CMakeLists.txt | 37 ++++++++ documentation/Doxyfile | 31 +++++++ documentation/conf.py | 19 +++++ documentation/pages/about.dox | 5 ++ include/tinyprog.h | 4 +- standalone/CMakeLists.txt | 25 ++++++ standalone/source/main.cpp | 8 ++ test/CMakeLists.txt | 110 ++++++++++++------------ test/{ => source}/benchmark.cpp | 16 ++-- test/{ => source}/example.cpp | 5 +- test/{ => source}/example2.cpp | 14 +-- test/{ => source}/example3.cpp | 6 +- test/{ => source}/example_program.cpp | 6 +- test/source/main.cpp | 3 + test/{ => source}/minctest.h | 0 test/{ => source}/test.cpp | 6 +- 30 files changed, 654 insertions(+), 214 deletions(-) create mode 100644 .cmake-format create mode 100644 .github/workflows/documentation.yaml create mode 100644 .github/workflows/install.yml create mode 100644 .github/workflows/macos.yml create mode 100644 .github/workflows/standalone.yml create mode 100644 .github/workflows/style.yml create mode 100644 .github/workflows/ubuntu.yml create mode 100644 .github/workflows/windows.yml create mode 100644 cmake/CPM.cmake create mode 100644 cmake/tools.cmake create mode 100644 codecov.yaml create mode 100644 documentation/CMakeLists.txt create mode 100644 documentation/Doxyfile create mode 100644 documentation/conf.py create mode 100644 documentation/pages/about.dox create mode 100644 standalone/CMakeLists.txt create mode 100644 standalone/source/main.cpp rename test/{ => source}/benchmark.cpp (93%) rename test/{ => source}/example.cpp (78%) rename test/{ => source}/example2.cpp (83%) rename test/{ => source}/example3.cpp (92%) rename test/{ => source}/example_program.cpp (98%) create mode 100644 test/source/main.cpp rename test/{ => source}/minctest.h (100%) rename test/{ => source}/test.cpp (99%) diff --git a/.clang-format b/.clang-format index 8c1f86c..cb03ae8 100644 --- a/.clang-format +++ b/.clang-format @@ -1,66 +1,16 @@ ---- -BasedOnStyle: LLVM -AccessModifierOffset: '-4' -AlignAfterOpenBracket: AlwaysBreak -AlignConsecutiveMacros: 'true' -AlignConsecutiveAssignments: 'true' -AlignConsecutiveDeclarations: 'true' -AlignEscapedNewlines: Right -AlignOperands: 'true' -AlignTrailingComments: 'true' -AllowAllArgumentsOnNextLine: 'true' -AllowAllConstructorInitializersOnNextLine: 'true' -AllowAllParametersOfDeclarationOnNextLine: 'true' -AllowShortBlocksOnASingleLine: 'false' -AllowShortCaseLabelsOnASingleLine: 'false' -AllowShortFunctionsOnASingleLine: Empty -AllowShortIfStatementsOnASingleLine: Never -AllowShortLambdasOnASingleLine: Inline -AllowShortLoopsOnASingleLine: 'false' -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: 'true' -AlwaysBreakTemplateDeclarations: 'Yes' -BinPackArguments: 'true' -BinPackParameters: 'true' -BreakBeforeBraces: Allman -BreakBeforeTernaryOperators: 'true' -BreakConstructorInitializers: BeforeComma -BreakInheritanceList: BeforeComma -BreakStringLiterals: 'true' -ColumnLimit: '180' -CompactNamespaces: 'false' -ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' -Cpp11BracedListStyle: 'true' -FixNamespaceComments: 'true' -IncludeBlocks: Preserve -IndentCaseLabels: 'false' -IndentPPDirectives: None -IndentWidth: '4' -IndentWrappedFunctionNames: 'false' -KeepEmptyLinesAtTheStartOfBlocks: 'false' -NamespaceIndentation: All -PointerAlignment: Left -ReflowComments: 'true' -SortIncludes: 'false' -SortUsingDeclarations: 'false' -SpaceAfterCStyleCast: 'false' -SpaceAfterLogicalNot: 'false' -SpaceAfterTemplateKeyword: 'false' -SpaceBeforeAssignmentOperators: 'true' -SpaceBeforeCpp11BracedList: 'false' -SpaceBeforeCtorInitializerColon: 'true' -SpaceBeforeInheritanceColon: 'true' -SpaceBeforeParens: ControlStatements -SpaceBeforeRangeBasedForLoopColon: 'true' -SpaceInEmptyParentheses: 'false' -SpacesBeforeTrailingComments: '1' -SpacesInAngles: 'false' -SpacesInCStyleCastParentheses: 'false' -SpacesInContainerLiterals: 'false' -SpacesInParentheses: 'false' -SpacesInSquareBrackets: 'false' -Standard: Cpp11 -TabWidth: '4' -UseTab: Always - +--- + BasedOnStyle: Google + AccessModifierOffset: '-2' + AlignTrailingComments: 'true' + AllowAllParametersOfDeclarationOnNextLine: 'false' + AlwaysBreakTemplateDeclarations: 'No' + BreakBeforeBraces: Attach + ColumnLimit: '100' + ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' + IncludeBlocks: Regroup + IndentPPDirectives: AfterHash + IndentWidth: '2' + NamespaceIndentation: All + BreakBeforeBinaryOperators: All + BreakBeforeTernaryOperators: 'true' ... diff --git a/.cmake-format b/.cmake-format new file mode 100644 index 0000000..a52c7d6 --- /dev/null +++ b/.cmake-format @@ -0,0 +1,56 @@ +format: + tab_size: 2 + line_width: 100 + dangle_parens: true + +parse: + additional_commands: + cpmaddpackage: + pargs: + nargs: '*' + flags: [] + spelling: CPMAddPackage + kwargs: &cpmaddpackagekwargs + NAME: 1 + FORCE: 1 + VERSION: 1 + GIT_TAG: 1 + DOWNLOAD_ONLY: 1 + GITHUB_REPOSITORY: 1 + GITLAB_REPOSITORY: 1 + GIT_REPOSITORY: 1 + SVN_REPOSITORY: 1 + SVN_REVISION: 1 + SOURCE_DIR: 1 + DOWNLOAD_COMMAND: 1 + FIND_PACKAGE_ARGUMENTS: 1 + NO_CACHE: 1 + GIT_SHALLOW: 1 + URL: 1 + URL_HASH: 1 + URL_MD5: 1 + DOWNLOAD_NAME: 1 + DOWNLOAD_NO_EXTRACT: 1 + HTTP_USERNAME: 1 + HTTP_PASSWORD: 1 + OPTIONS: + + cpmfindpackage: + pargs: + nargs: '*' + flags: [] + spelling: CPMFindPackage + kwargs: *cpmaddpackagekwargs + packageproject: + pargs: + nargs: '*' + flags: [] + spelling: packageProject + kwargs: + NAME: 1 + VERSION: 1 + INCLUDE_DIR: 1 + INCLUDE_DESTINATION: 1 + BINARY_DIR: 1 + COMPATIBILITY: 1 + VERSION_HEADER: 1 + DEPENDENCIES: + diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml new file mode 100644 index 0000000..3ee6d30 --- /dev/null +++ b/.github/workflows/documentation.yaml @@ -0,0 +1,29 @@ +name: Documentation + +on: + push: + tags: + - '*' + +jobs: + build: + name: Build and publish documentation + runs-on: macos-latest + steps: + - uses: actions/checkout@v1 + + - name: Install dependencies + run: | + brew install doxygen + pip3 install jinja2 Pygments + + - name: Build + run: | + cmake -Hdocumentation -Bbuild + cmake --build build --target GenerateDocs + + - name: Publish + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./build/doxygen/html diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml new file mode 100644 index 0000000..997da70 --- /dev/null +++ b/.github/workflows/install.yml @@ -0,0 +1,37 @@ +name: Install + +on: + push: + branches: + - master + pull_request: + branches: + - master + +env: + CTEST_OUTPUT_ON_FAILURE: 1 + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + + - name: build and install library + run: | + cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Release + sudo cmake --build build --target install + rm -rf build + + - name: configure + run: cmake -Htest -Bbuild -DTEST_INSTALLED_VERSION=1 + + - name: build + run: cmake --build build --config Debug -j4 + + - name: test + run: | + cd build + ctest --build-config Debug diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000..cc82132 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,31 @@ +name: MacOS + +on: + push: + branches: + - master + pull_request: + branches: + - master + +env: + CTEST_OUTPUT_ON_FAILURE: 1 + +jobs: + build: + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v1 + + - name: configure + run: cmake -Htest -Bbuild + + - name: build + run: cmake --build build --config Debug -j4 + + - name: test + run: | + cd build + ctest --build-config Debug diff --git a/.github/workflows/standalone.yml b/.github/workflows/standalone.yml new file mode 100644 index 0000000..91cc536 --- /dev/null +++ b/.github/workflows/standalone.yml @@ -0,0 +1,26 @@ +name: Standalone + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + + - name: configure + run: cmake -Hstandalone -Bbuild + + - name: build + run: cmake --build build -j4 + + - name: run + run: ./build/tinyprog diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml new file mode 100644 index 0000000..48d1fa5 --- /dev/null +++ b/.github/workflows/style.yml @@ -0,0 +1,28 @@ +name: Style + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v1 + + - name: Install format dependencies + run: | + brew install clang-format + pip3 install cmake_format==0.6.11 pyyaml + + - name: configure + run: cmake -Htest -Bbuild + + - name: check style + run: cmake --build build --target check-format diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml new file mode 100644 index 0000000..dd62cc8 --- /dev/null +++ b/.github/workflows/ubuntu.yml @@ -0,0 +1,35 @@ +name: Ubuntu + +on: + push: + branches: + - master + pull_request: + branches: + - master + +env: + CTEST_OUTPUT_ON_FAILURE: 1 + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + + - name: configure + run: cmake -Htest -Bbuild -DENABLE_TEST_COVERAGE=1 + + - name: build + run: cmake --build build --config Debug -j4 + + - name: test + run: | + cd build + ctest --build-config Debug + + - name: collect code coverage + run: bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports" diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000..8f6d2b2 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,31 @@ +name: Windows + +on: + push: + branches: + - master + pull_request: + branches: + - master + +env: + CTEST_OUTPUT_ON_FAILURE: 1 + +jobs: + build: + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v1 + + - name: configure + run: cmake -Htest -Bbuild + + - name: build + run: cmake --build build --config Debug -j4 + + - name: test + run: | + cd build + ctest --build-config Debug diff --git a/.gitignore b/.gitignore index 7289112..a4cfc5d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -vcpkg_root/ -CMakeSettings.json +/build* +/.vscode +.DS_Store \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b17055..50e6bd9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,71 +1,64 @@ -cmake_minimum_required(VERSION 3.17 FATAL_ERROR) - -project(tinyprog VERSION 0.0 LANGUAGES CXX) - -include(FetchContent) - -set(VCPKG_DEVELOP_TRIPLET x64-windows-static-md) - -macro(vcpkg_setup_ez) - cmake_parse_arguments( - "_arg" - "" - "REPO;TAG;DIR;UPDATE_DISCONNECTED" - "" - ${ARGN} - ) - - if(NOT _arg_TAG) - set(vcpkg_ez_SOURCE_DIR ${_arg_DIR}) - else() - FetchContent_Declare( - vcpkg_ez - GIT_REPOSITORY ${_arg_REPO} - GIT_TAG ${_arg_TAG} - SOURCE_DIR ${_arg_DIR} - UPDATE_DISCONNECTED ${_arg_UPDATE_DISCONNECTED} - ) - - FetchContent_GetProperties(vcpkg_ez) - - if(NOT vcpkg_ez_POPULATED) - FetchContent_Populate(vcpkg_ez) - endif() - endif() -endmacro() - -if(NOT VCPKG_DEVELOP_ROOT_DIR) - set(VCPKG_DEVELOP_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/vcpkg_root) -endif() +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +# ---- Project ---- + +# Note: update this to your new project's name and version +project( + tinyprog + VERSION 0.0 + LANGUAGES CXX +) -if(VCPKG_DEVELOP_EZ_DIR) - vcpkg_setup_ez(DIR ${VCPKG_DEVELOP_EZ_DIR}) -else() - vcpkg_setup_ez( - REPO https://gitlab.ct.activision.com/shg_audio/vcpkg_ez.git - TAG HEAD - DIR ${VCPKG_DEVELOP_ROOT_DIR}/vcpkg_ez - ) +# ---- Include guards ---- + +if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) + message( + FATAL_ERROR + "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there." + ) endif() -include(${vcpkg_ez_SOURCE_DIR}/vcpkg_ez.cmake) +# ---- Add dependencies via CPM ---- +# see https://github.com/TheLartians/CPM.cmake for more info -option(TE_POW_FROM_RIGHT "Evaluate exponents from right to left." OFF) -option(TE_NAT_LOG "Define the log function as natural logarithm." OFF) +include(cmake/CPM.cmake) -vcpkg_interface_library(${PROJECT_NAME} - INTERFACE_HEADERS - include/tinyprog.h - INTERFACE_INCLUDES - ${CMAKE_CURRENT_SOURCE_DIR}/include +# PackageProject.cmake will be used to make our target installable +CPMAddPackage( + NAME PackageProject.cmake + GITHUB_REPOSITORY TheLartians/PackageProject.cmake + VERSION 1.3 ) -target_compile_definitions(${PROJECT_NAME} - INTERFACE - $,"TE_POW_FROM_RIGHT",> - $,"TE_NAT_LOG",> +# ---- Add source files ---- + +# Note: globbing sources is considered bad practice as CMake's generators may not detect new files +# automatically. Keep that in mind when changing files, or explicitly mention them here. +file(GLOB_RECURSE headers CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h") + +# ---- Create library ---- + +add_library(tinyprog INTERFACE) + +# being a cross-platform target, we enforce standards conformance on MSVC +target_compile_options(tinyprog INTERFACE "$<$:/permissive->") + +target_include_directories( + tinyprog INTERFACE $ + $ ) -if(ENABLE_TESTING) - add_subdirectory(test) -endif() \ No newline at end of file +# ---- Create an installable target ---- +# this allows users to install and find the library via `find_package()`. + +# the location where the project's version header will be placed should match the project's regular +# header paths + +packageProject( + NAME ${PROJECT_NAME} + VERSION ${PROJECT_VERSION} + BINARY_DIR ${PROJECT_BINARY_DIR} + INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include + INCLUDE_DESTINATION include/${PROJECT_NAME}-${PROJECT_VERSION} + DEPENDENCIES "" +) diff --git a/cmake/CPM.cmake b/cmake/CPM.cmake new file mode 100644 index 0000000..bffba54 --- /dev/null +++ b/cmake/CPM.cmake @@ -0,0 +1,19 @@ +set(CPM_DOWNLOAD_VERSION 0.27.2) + +if(CPM_SOURCE_CACHE) + set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +elseif(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +else() + set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +endif() + +if(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION})) + message(STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}") + file(DOWNLOAD + https://github.com/TheLartians/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} + ) +endif() + +include(${CPM_DOWNLOAD_LOCATION}) diff --git a/cmake/tools.cmake b/cmake/tools.cmake new file mode 100644 index 0000000..4029e37 --- /dev/null +++ b/cmake/tools.cmake @@ -0,0 +1,74 @@ +# this file contains a list of tools that can be activated and downloaded on-demand each tool is +# enabled during configuration by passing an additional `-DUSE_=` argument to CMake + +# only activate tools for top level project +if(NOT PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + return() +endif() + +include(${CMAKE_CURRENT_LIST_DIR}/CPM.cmake) + +# enables sanitizers support using the the `USE_SANITIZER` flag available values are: Address, +# Memory, MemoryWithOrigins, Undefined, Thread, Leak, 'Address;Undefined' +if(USE_SANITIZER OR USE_STATIC_ANALYZER) + CPMAddPackage( + NAME StableCoder-cmake-scripts + GITHUB_REPOSITORY StableCoder/cmake-scripts + GIT_TAG 3d2d5a9fb26f0ce24e3e4eaeeff686ec2ecfb3fb + ) + + if(USE_SANITIZER) + include(${StableCoder-cmake-scripts_SOURCE_DIR}/sanitizers.cmake) + endif() + + if(USE_STATIC_ANALYZER) + if("clang-tidy" IN_LIST USE_STATIC_ANALYZER) + set(CLANG_TIDY + ON + CACHE INTERNAL "" + ) + else() + set(CLANG_TIDY + OFF + CACHE INTERNAL "" + ) + endif() + if("iwyu" IN_LIST USE_STATIC_ANALYZER) + set(IWYU + ON + CACHE INTERNAL "" + ) + else() + set(IWYU + OFF + CACHE INTERNAL "" + ) + endif() + if("cppcheck" IN_LIST USE_STATIC_ANALYZER) + set(CPPCHECK + ON + CACHE INTERNAL "" + ) + else() + set(CPPCHECK + OFF + CACHE INTERNAL "" + ) + endif() + + include(${StableCoder-cmake-scripts_SOURCE_DIR}/tools.cmake) + + clang_tidy(${CLANG_TIDY_ARGS}) + include_what_you_use(${IWYU_ARGS}) + cppcheck(${CPPCHECK_ARGS}) + endif() +endif() + +# enables CCACHE support through the USE_CCACHE flag possible values are: YES, NO or equivalent +if(USE_CCACHE) + CPMAddPackage( + NAME Ccache.cmake + GITHUB_REPOSITORY TheLartians/Ccache.cmake + VERSION 1.2.1 + ) +endif() diff --git a/codecov.yaml b/codecov.yaml new file mode 100644 index 0000000..dae89fd --- /dev/null +++ b/codecov.yaml @@ -0,0 +1,5 @@ +ignore: + - "test" + +comment: + require_changes: true \ No newline at end of file diff --git a/documentation/CMakeLists.txt b/documentation/CMakeLists.txt new file mode 100644 index 0000000..6284d19 --- /dev/null +++ b/documentation/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +project(tinyprogDocs) + +# ---- Dependencies ---- + +include(../cmake/CPM.cmake) + +CPMAddPackage(NAME tinyprog SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/..) + +CPMAddPackage( + NAME MCSS + DOWNLOAD_ONLY YES + # patched version until https://github.com/mosra/m.css/pull/171 is resolved + GITHUB_REPOSITORY TheLartians/m.css + GIT_TAG 1bf162b96d5bfefc9967a80cef138f1270ffa415 +) + +# ---- Doxygen variables ---- + +# set Doxyfile variables +set(DOXYGEN_PROJECT_NAME tinyprog) +set(DOXYGEN_PROJECT_VERSION ${tinyprog_VERSION}) +set(DOXYGEN_PROJECT_ROOT "${CMAKE_CURRENT_LIST_DIR}/..") +set(DOXYGEN_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/doxygen") + +configure_file(${CMAKE_CURRENT_LIST_DIR}/Doxyfile ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + +configure_file(${CMAKE_CURRENT_LIST_DIR}/conf.py ${CMAKE_CURRENT_BINARY_DIR}/conf.py) + +add_custom_target( + GenerateDocs + ${CMAKE_COMMAND} -E make_directory "${DOXYGEN_OUTPUT_DIRECTORY}" + COMMAND "${MCSS_SOURCE_DIR}/documentation/doxygen.py" "${CMAKE_CURRENT_BINARY_DIR}/conf.py" + COMMAND echo "Docs written to: ${DOXYGEN_OUTPUT_DIRECTORY}" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" +) diff --git a/documentation/Doxyfile b/documentation/Doxyfile new file mode 100644 index 0000000..2c33e00 --- /dev/null +++ b/documentation/Doxyfile @@ -0,0 +1,31 @@ +# Configuration for Doxygen for use with CMake +# Only options that deviate from the default are included +# To create a new Doxyfile containing all available options, call `doxygen -g` + +# Get Project name and version from CMake +PROJECT_NAME = @DOXYGEN_PROJECT_NAME@ +PROJECT_NUMBER = @DOXYGEN_PROJECT_VERSION@ + +# Add sources +INPUT = @DOXYGEN_PROJECT_ROOT@/README.md @DOXYGEN_PROJECT_ROOT@/include @DOXYGEN_PROJECT_ROOT@/documentation/pages +EXTRACT_ALL = YES +RECURSIVE = YES +OUTPUT_DIRECTORY = @DOXYGEN_OUTPUT_DIRECTORY@ + +# Use the README as a main page +USE_MDFILE_AS_MAINPAGE = @DOXYGEN_PROJECT_ROOT@/README.md + +# set relative include paths +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = @DOXYGEN_PROJECT_ROOT@/include @DOXYGEN_PROJECT_ROOT@ + +# We use m.css to generate the html documentation, so we only need XML output +GENERATE_XML = YES +GENERATE_HTML = NO +GENERATE_LATEX = NO +XML_PROGRAMLISTING = NO +CREATE_SUBDIRS = NO + +# Include all directories, files and namespaces in the documentation +# Disable to include only explicitly documented objects +M_SHOW_UNDOCUMENTED = YES diff --git a/documentation/conf.py b/documentation/conf.py new file mode 100644 index 0000000..b5840db --- /dev/null +++ b/documentation/conf.py @@ -0,0 +1,19 @@ +DOXYFILE = 'Doxyfile' + +LINKS_NAVBAR1 = [ + (None, 'pages', [(None, 'about')]), + (None, 'namespaces', []), +] + +# Add your own navbar links using the code below. +# To find the valid link names, you can inspect the URL of a generated documentation site. + +# LINKS_NAVBAR1 = [ +# (None, 'pages', [(None, 'about')]), +# (None, 'namespaces', [(None, 'namespacetinyprog')]), +# ] +# +# LINKS_NAVBAR2 = [ +# (None, 'annotated', [(None, 'classtinyprog_1_1_tinyprog')]), +# (None, 'files', [(None, 'tinyprog_8h')]), +# ] diff --git a/documentation/pages/about.dox b/documentation/pages/about.dox new file mode 100644 index 0000000..8ca02c0 --- /dev/null +++ b/documentation/pages/about.dox @@ -0,0 +1,5 @@ +/** @page about About + @section doc ModernCppStarter Documentation + This is the auto-generated documentation for the initial project of the ModernCppStater. + It shows how we can use Doxygen to automatically build a browsable documentation for your projects. +*/ diff --git a/include/tinyprog.h b/include/tinyprog.h index 79afb1c..9a57baf 100644 --- a/include/tinyprog.h +++ b/include/tinyprog.h @@ -1330,7 +1330,7 @@ namespace tp register_func(n->bound, n_out, t_traits::find_by_addr(n->bound, lookup)); return export_size; }, - [&](int a) { + [&](int) { register_func(n->function, n_out, t_traits::find_by_addr(n->function, lookup)); export_size += sizeof(n->parameters[0]) * eval_details::arity(n->type); @@ -1342,7 +1342,7 @@ namespace tp } return export_size; }, - [&](int a) { + [&](int) { register_func(n->function, n_out, t_traits::find_by_addr(n->function, lookup)); export_size += sizeof(n->parameters[0]) * eval_details::arity(n->type); diff --git a/standalone/CMakeLists.txt b/standalone/CMakeLists.txt new file mode 100644 index 0000000..96f92d5 --- /dev/null +++ b/standalone/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +project(tinyprogStandalone LANGUAGES CXX) + +# --- Import tools ---- + +include(../cmake/tools.cmake) + +# ---- Dependencies ---- + +include(../cmake/CPM.cmake) + +CPMAddPackage(NAME tinyprog SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/..) + +# ---- Create standalone executable ---- + +file(GLOB sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp) + +add_executable(tinyprogStandalone ${sources}) + +set_target_properties(tinyprogStandalone PROPERTIES CXX_STANDARD 17 OUTPUT_NAME "tinyprog") + +target_link_libraries(tinyprogStandalone tinyprog) + +set_target_properties(tinyprogStandalone PROPERTIES CXX_STANDARD 17) \ No newline at end of file diff --git a/standalone/source/main.cpp b/standalone/source/main.cpp new file mode 100644 index 0000000..2fdcbdd --- /dev/null +++ b/standalone/source/main.cpp @@ -0,0 +1,8 @@ +#include +#include +#include + + +int main(int argc, char** argv) { + return 0; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 74244f5..1539d3e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,62 +1,64 @@ -cmake_minimum_required(VERSION 3.17 FATAL_ERROR) - -option(build_tinyprog_test "Build TinyProg tests." ON) -option(build_tinyprog_bench "Build TinyProg benchmark." ON) -option(build_tinyprog_example "Build TinyProg example." ON) -option(build_tinyprog_example2 "Build TinyProg example 2." ON) -option(build_tinyprog_example3 "Build TinyProg example 3." ON) - -if (build_tinyprog_test) - vcpkg_executable(tinyprog_test - SOURCES - test.cpp - PRIVATE_DEPENDENCIES - tinyprog - ) -endif() +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) -if (build_tinyprog_bench) - vcpkg_executable(tinyprog_benchmark - SOURCES - benchmark.cpp - PRIVATE_DEPENDENCIES - tinyprog - ) -endif() +project(tinyprogTests LANGUAGES CXX) -if (build_tinyprog_example) - vcpkg_executable(tinyprog_example - SOURCES - example.cpp - PRIVATE_DEPENDENCIES - tinyprog - ) -endif() +# ---- Options ---- -if (build_tinyprog_example2) - vcpkg_executable(tinyprog_example2 - SOURCES - example2.cpp - PRIVATE_DEPENDENCIES - tinyprog - ) -endif() +option(ENABLE_TEST_COVERAGE "Enable test coverage" OFF) +option(TEST_INSTALLED_VERSION "Test the version found by find_package" OFF) + +# --- Import tools ---- + +include(../cmake/tools.cmake) + +# ---- Dependencies ---- + +include(../cmake/CPM.cmake) -if (build_tinyprog_example3) - vcpkg_executable(tinyprog_example3 - SOURCES - example3.cpp - PRIVATE_DEPENDENCIES - tinyprog - ) +CPMAddPackage( + NAME doctest + GITHUB_REPOSITORY onqtam/doctest + GIT_TAG 2.3.7 +) + +if(TEST_INSTALLED_VERSION) + find_package(tinyprog REQUIRED) +else() + CPMAddPackage(NAME tinyprog SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/..) endif() -if (build_tinyprog_example) - vcpkg_executable(tinyprog_example_program - SOURCES - example_program.cpp - PRIVATE_DEPENDENCIES - tinyprog - ) +CPMAddPackage( + NAME Format.cmake + GITHUB_REPOSITORY TheLartians/Format.cmake + VERSION 1.6 + OPTIONS # enable cmake formatting + "FORMAT_CHECK_CMAKE ON" +) + +# ---- Create binary ---- + +file(GLOB sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp) +add_executable(tinyprogTests ${sources}) +target_link_libraries(tinyprogTests doctest tinyprog) + +set_target_properties(tinyprogTests PROPERTIES CXX_STANDARD 17) + +# enable compiler warnings +if(NOT TEST_INSTALLED_VERSION) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") + target_compile_options(tinyprog INTERFACE -Wall -pedantic -Wextra -Werror) + elseif(MSVC) + target_compile_options(tinyprog INTERFACE /W4 /WX) + target_compile_definitions(tinyprogTests PUBLIC DOCTEST_CONFIG_USE_STD_HEADERS) + endif() endif() +# ---- Add tinyprogTests ---- + +enable_testing() + +# Note: doctest and similar testing frameworks can automatically configure CMake tests For other +# testing frameworks add the tests target instead: ADD_TEST(tinyprogTests tinyprogTests) + +include(${doctest_SOURCE_DIR}/scripts/cmake/doctest.cmake) +doctest_discover_tests(tinyprogTests) diff --git a/test/benchmark.cpp b/test/source/benchmark.cpp similarity index 93% rename from test/benchmark.cpp rename to test/source/benchmark.cpp index 8a46718..9bfe639 100644 --- a/test/benchmark.cpp +++ b/test/source/benchmark.cpp @@ -1,3 +1,5 @@ +#include + /* * TinyProg - a minimalist shader-like scripting language. * @@ -118,13 +120,11 @@ te::env_traits::t_atom al(te::env_traits::t_atom a) return (1 / (a + 1) + 2 / (a + 2) + 3 / (a + 3)); } -int main(int argc, char* argv[]) +TEST_CASE("Bench") { - bench("sqrt(a^1.5+a^2.5)", as); - bench("a+5", a5); - bench("a+(5*2)", a10); - bench("(a+5)*2", a52); - bench("(1/(a+1)+2/(a+2)+3/(a+3))", al); - - return 0; +// bench("sqrt(a^1.5+a^2.5)", as); +// bench("a+5", a5); +// bench("a+(5*2)", a10); +// bench("(a+5)*2", a52); +// bench("(1/(a+1)+2/(a+2)+3/(a+3))", al); } diff --git a/test/example.cpp b/test/source/example.cpp similarity index 78% rename from test/example.cpp rename to test/source/example.cpp index f651e68..f48287c 100644 --- a/test/example.cpp +++ b/test/source/example.cpp @@ -1,11 +1,12 @@ +#include + #define TP_TESTING 1 #include "tinyprog.h" #include -int main(int argc, char *argv[]) +TEST_CASE("example_expression") { const char *c = "sqrt(5^2+7^2+11^2+(8-2)^2)"; te::env_traits::t_atom r = te::interp(c, 0); printf("The expression:\n\t%s\nevaluates to:\n\t%f\n", c, r); - return 0; } diff --git a/test/example2.cpp b/test/source/example2.cpp similarity index 83% rename from test/example2.cpp rename to test/source/example2.cpp index 5b7ce09..750bea2 100644 --- a/test/example2.cpp +++ b/test/source/example2.cpp @@ -1,16 +1,12 @@ +#include + #define TP_TESTING 1 #include "tinyprog.h" #include -int main(int argc, char* argv[]) +TEST_CASE("example2") { - if (argc < 2) - { - printf("Usage: example2 \"expression\"\n"); - return 0; - } - - const char* expression = argv[1]; + const char* expression = "x + y * 0.2"; printf("Evaluating:\n\t%s\n", expression); /* This shows an example where the variables @@ -39,6 +35,4 @@ int main(int argc, char* argv[]) /* Show the user where the error is at. */ printf("\t%*s^\nError near here", err - 1, ""); } - - return 0; } diff --git a/test/example3.cpp b/test/source/example3.cpp similarity index 92% rename from test/example3.cpp rename to test/source/example3.cpp index 61c0423..62f8e15 100644 --- a/test/example3.cpp +++ b/test/source/example3.cpp @@ -1,3 +1,5 @@ +#include + #define TP_TESTING 1 #include "tinyprog.h" #include @@ -9,7 +11,7 @@ te::env_traits::t_atom my_sum(te::env_traits::t_atom a, te::env_traits::t_atom b return a + b; } -int main(int argc, char* argv[]) +TEST_CASE("example3") { te::variable vars[] = {{"mysum", my_sum, tp::FUNCTION2}}; @@ -30,6 +32,4 @@ int main(int argc, char* argv[]) /* Show the user where the error is at. */ printf("\t%*s^\nError near here", err - 1, ""); } - - return 0; } diff --git a/test/example_program.cpp b/test/source/example_program.cpp similarity index 98% rename from test/example_program.cpp rename to test/source/example_program.cpp index 927e143..6e3d9f4 100644 --- a/test/example_program.cpp +++ b/test/source/example_program.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -77,7 +79,7 @@ float test_closure(void* context, float arg) return arg; } -int main(int argc, char* argv[]) +TEST_CASE("example_program") { using builtins = tp_stdlib::compiler_builtins>; @@ -223,6 +225,4 @@ int main(int argc, char* argv[]) delete prog; } - - return 0; } diff --git a/test/source/main.cpp b/test/source/main.cpp new file mode 100644 index 0000000..af24eeb --- /dev/null +++ b/test/source/main.cpp @@ -0,0 +1,3 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN + +#include diff --git a/test/minctest.h b/test/source/minctest.h similarity index 100% rename from test/minctest.h rename to test/source/minctest.h diff --git a/test/test.cpp b/test/source/test.cpp similarity index 99% rename from test/test.cpp rename to test/source/test.cpp index 36cdde8..7c01dd3 100644 --- a/test/test.cpp +++ b/test/source/test.cpp @@ -27,6 +27,8 @@ * 3. This notice may not be removed or altered from any source distribution. */ +#include + #define TP_TESTING 1 #include "tinyprog.h" @@ -804,7 +806,7 @@ void test_logic() } } -int main(int argc, char* argv[]) +TEST_CASE("expression_test") { lrun("Results", test_results); lrun("Syntax", test_syntax); @@ -819,6 +821,4 @@ int main(int argc, char* argv[]) lrun("Combinatorics", test_combinatorics); lrun("Logic", test_logic); lresults(); - - return lfails != 0; }