From e7b0a375ffda6211f7bb7969c5b0a923ec5a5384 Mon Sep 17 00:00:00 2001 From: Daniel Ruoso Date: Sat, 18 Jan 2025 19:30:20 -0500 Subject: [PATCH 01/11] use gtest_discover_tests --- .github/workflows/c-cpp.yml | 8 ++++---- tests/CMakeLists.txt | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index e8440e4..3980be3 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -14,10 +14,10 @@ jobs: steps: - uses: actions/checkout@v3 - name: build-depends - run: sudo apt-get -y install libgtest-dev build-essential cmake libgtest-dev libuv1-dev + run: sudo apt update && sudo apt -y install libgtest-dev build-essential cmake libgtest-dev libuv1-dev && sudo apt clean - name: configure - run: mkdir build && cd build && cmake ../ + run: mkdir build && cd build && cmake ../ -DCMAKE_BUILD_TYPE=Debug - name: make - run: make -C build/ VERBOSE=1 + run: make -j8 -C build/ VERBOSE=1 - name: make test - run: make -C build/ test VERBOSE=1 + run: ctest --test-dir build/ --output-on-failure diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 32d022a..50f28e9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,6 +16,9 @@ add_library( uv INTERFACE ) target_include_directories( uv INTERFACE ${LIBUV_INCLUDE_DIR} ) target_link_libraries( uv INTERFACE ${LIBUV_LIBRARIES} ) +include(CTest) +enable_testing() + foreach( TEST 001-empty @@ -53,11 +56,8 @@ foreach( uv ${GTEST_BOTH_LIBRARIES} ) - add_test( - NAME ${TEST} - COMMAND - ${CMAKE_COMMAND} -E env - TSAN_OPTIONS=suppressions=${CMAKE_SOURCE_DIR}/tsan_suppressions.txt - $ + gtest_discover_tests(${TEST}.t + PROPERTIES + ENVIRONMENT "TSAN_OPTIONS=suppressions=${CMAKE_SOURCE_DIR}/tsan_suppressions.txt" ) endforeach() From ff03bb1bd7fcf8b83191c41d36ffda15ade73426 Mon Sep 17 00:00:00 2001 From: Daniel Ruoso Date: Fri, 13 Dec 2024 12:13:29 -0500 Subject: [PATCH 02/11] Small fix for the test data --- tests/023-grammar-complete.cpp | 4 ++-- tests/data/023-source-code-http-client-server.txt | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/023-grammar-complete.cpp b/tests/023-grammar-complete.cpp index ee042d4..f3e16dd 100644 --- a/tests/023-grammar-complete.cpp +++ b/tests/023-grammar-complete.cpp @@ -28,7 +28,7 @@ TEST(MessageTest, Message) { auto req = protocol_description->at("HTTP Request"); ASSERT_EQ(8, req->parts->size()); - ASSERT_EQ(6, req->data->size()); + ASSERT_EQ(5, req->data->size()); ASSERT_EQ("HTTP Request", req->name->value); ASSERT_EQ("Open", req->when->name); ASSERT_EQ("AwaitResponse", req->then->name); @@ -36,7 +36,7 @@ TEST(MessageTest, Message) { auto res = protocol_description->at("HTTP Response"); ASSERT_EQ(8, res->parts->size()); - ASSERT_EQ(6, res->data->size()); + ASSERT_EQ(5, res->data->size()); ASSERT_EQ("HTTP Response", res->name->value); ASSERT_EQ("AwaitResponse", res->when->name); ASSERT_EQ("Open", res->then->name); diff --git a/tests/data/023-source-code-http-client-server.txt b/tests/data/023-source-code-http-client-server.txt index 2851605..63202b4 100644 --- a/tests/data/023-source-code-http-client-server.txt +++ b/tests/data/023-source-code-http-client-server.txt @@ -3,7 +3,7 @@ message "HTTP Request" { then: AwaitResponse; agent: Client; data: { - method: str; request_target: str>, sizing=Dynamic, max_length=100>; - contents: stream; } parts { tokens { verb } @@ -71,7 +70,6 @@ message "HTTP Response" { max_length=32768>>, sizing=Dynamic, max_length=100>; - contents: stream; } parts { tokens { From 2a71acd50db4b5c04b185ed354f06215e0bd73dd Mon Sep 17 00:00:00 2001 From: Daniel Ruoso Date: Mon, 20 Jan 2025 11:30:40 -0500 Subject: [PATCH 03/11] handle character escaping --- src/networkprotocoldsl/lexer/tokenize.cpp | 40 +++++++++++++++++++++-- tests/024-sema-analyze.cpp | 18 +++++----- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/networkprotocoldsl/lexer/tokenize.cpp b/src/networkprotocoldsl/lexer/tokenize.cpp index c18b1f4..6d87d8d 100644 --- a/src/networkprotocoldsl/lexer/tokenize.cpp +++ b/src/networkprotocoldsl/lexer/tokenize.cpp @@ -6,7 +6,9 @@ #include #include #include +#include #include +#include #include #include @@ -15,6 +17,40 @@ namespace networkprotocoldsl::lexer { namespace { +// Function to unescape string literals +std::string unescape_string(const std::string &str) { + std::string result; + result.reserve(str.size()); + for (size_t i = 0; i < str.size(); ++i) { + if (str[i] == '\\' && i + 1 < str.size()) { + switch (str[i + 1]) { + case 'n': + result += '\n'; + break; + case 't': + result += '\t'; + break; + case 'r': + result += '\r'; + break; + case '\\': + result += '\\'; + break; + case '\"': + result += '\"'; + break; + default: + result += str[i + 1]; + break; + } + ++i; + } else { + result += str[i]; + } + } + return result; +} + struct TokenRule { std::string_view pattern; std::function &, const std::string_view &)> pusher; @@ -36,10 +72,10 @@ const std::vector token_rules = { [](TP_ARGS) { tokens.push_back(token::literal::Integer{std::atoi(str.data())}); }}, - {"\\\"[^\"]*\\\"", + {"\\\"([^\"\\\\]|\\\\.)*\\\"", [](TP_ARGS) { tokens.push_back(token::literal::String{ - std::string(str.substr(1, str.length() - 2))}); + unescape_string(std::string(str.substr(1, str.length() - 2)))}); }}, {">", [](TP_ARGS) { diff --git a/tests/024-sema-analyze.cpp b/tests/024-sema-analyze.cpp index b26b523..6267880 100644 --- a/tests/024-sema-analyze.cpp +++ b/tests/024-sema-analyze.cpp @@ -129,7 +129,7 @@ TEST(MessageTest, Message) { auto write_static_octets_8 = std::get>( client_send_request_actions[8]); - ASSERT_EQ(write_static_octets_8->octets, "\\r\\n"); + ASSERT_EQ(write_static_octets_8->octets, "\r\n"); ASSERT_TRUE( std::holds_alternative>( @@ -138,7 +138,7 @@ TEST(MessageTest, Message) { client_send_request_actions[9]); ASSERT_EQ(client_loop_9->variable->name, "header"); ASSERT_EQ(client_loop_9->collection->name, "headers"); - ASSERT_EQ(client_loop_9->terminator, "\\r\\n"); + ASSERT_EQ(client_loop_9->terminator, "\r\n"); ASSERT_EQ(client_loop_9->actions.size(), 4); ASSERT_TRUE(std::holds_alternative< std::shared_ptr>( @@ -174,7 +174,7 @@ TEST(MessageTest, Message) { auto client_loop_action_3 = std::get>( client_loop_9->actions[3]); - ASSERT_EQ(client_loop_action_3->octets, "\\r\\n"); + ASSERT_EQ(client_loop_action_3->octets, "\r\n"); auto &client_closes_transition_pair = client_open_transitions.at("Client Closes Connection"); @@ -236,7 +236,7 @@ TEST(MessageTest, Message) { auto read_octets_until_terminator_4 = std::get< std::shared_ptr>( server_receive_request_actions[4]); - ASSERT_EQ(read_octets_until_terminator_4->terminator, "\\r\\n"); + ASSERT_EQ(read_octets_until_terminator_4->terminator, "\r\n"); ASSERT_EQ(read_octets_until_terminator_4->identifier->name, "minor_version"); ASSERT_TRUE( @@ -246,7 +246,7 @@ TEST(MessageTest, Message) { server_receive_request_actions[5]); ASSERT_EQ(server_loop_5->variable->name, "header"); ASSERT_EQ(server_loop_5->collection->name, "headers"); - ASSERT_EQ(server_loop_5->terminator, "\\r\\n"); + ASSERT_EQ(server_loop_5->terminator, "\r\n"); ASSERT_EQ(server_loop_5->actions.size(), 2); ASSERT_TRUE( std::holds_alternative< @@ -267,7 +267,7 @@ TEST(MessageTest, Message) { auto server_loop_action_1 = std::get< std::shared_ptr>( server_loop_5->actions[1]); - ASSERT_EQ(server_loop_action_1->terminator, "\\r\\n"); + ASSERT_EQ(server_loop_action_1->terminator, "\r\n"); ASSERT_EQ(server_loop_action_1->identifier->name, "header"); ASSERT_TRUE(server_loop_action_1->identifier->member.has_value()); ASSERT_EQ(server_loop_action_1->identifier->member.value()->name, "value"); @@ -418,7 +418,7 @@ TEST(MessageTest, Message) { auto server_write_static_octets_8 = std::get>( server_send_response_actions[8]); - ASSERT_EQ(server_write_static_octets_8->octets, "\\r\\n"); + ASSERT_EQ(server_write_static_octets_8->octets, "\r\n"); ASSERT_TRUE( std::holds_alternative>( @@ -427,7 +427,7 @@ TEST(MessageTest, Message) { server_send_response_actions[9]); ASSERT_EQ(server_loop_9->variable->name, "header"); ASSERT_EQ(server_loop_9->collection->name, "headers"); - ASSERT_EQ(server_loop_9->terminator, "\\r\\n"); + ASSERT_EQ(server_loop_9->terminator, "\r\n"); ASSERT_EQ(server_loop_9->actions.size(), 4); ASSERT_TRUE(std::holds_alternative< std::shared_ptr>( @@ -465,5 +465,5 @@ TEST(MessageTest, Message) { auto server_write_loop_action_3 = std::get>( server_loop_9->actions[3]); - ASSERT_EQ(server_write_loop_action_3->octets, "\\r\\n"); + ASSERT_EQ(server_write_loop_action_3->octets, "\r\n"); } From 2cb66426f3f0abd883b09dbbaf85612b199bdf8c Mon Sep 17 00:00:00 2001 From: Daniel Ruoso Date: Mon, 20 Jan 2025 12:58:55 -0500 Subject: [PATCH 04/11] new constructors for value to make it easier to use --- src/networkprotocoldsl/value.hpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/networkprotocoldsl/value.hpp b/src/networkprotocoldsl/value.hpp index 862e79a..0893661 100644 --- a/src/networkprotocoldsl/value.hpp +++ b/src/networkprotocoldsl/value.hpp @@ -46,10 +46,29 @@ struct Callable { struct DynamicList { std::shared_ptr> values; + DynamicList() = default; + explicit DynamicList(std::vector &&vals) + : values(std::make_shared>(std::move(vals))) {} + explicit DynamicList(std::initializer_list init) + : values(std::make_shared>(init)) {} + explicit DynamicList(const std::vector &vals) + : values(std::make_shared>(vals)) {} + explicit DynamicList(std::shared_ptr> vals) + : values(vals) {} + explicit DynamicList(std::shared_ptr> vals) + : values(std::const_pointer_cast>(vals)) {} }; struct Octets { std::shared_ptr data; + Octets() = default; + explicit Octets(std::string &&d) + : data(std::make_shared(std::move(d))) {} + explicit Octets(const char *d) + : data(std::make_shared(d)) {} + explicit Octets(const std::string &d) + : data(std::make_shared(d)) {} + explicit Octets(std::shared_ptr d) : data(d) {} }; } // namespace value From f07cd84f884ed8e2083ea45dc20db6083e8a2a4c Mon Sep 17 00:00:00 2001 From: Daniel Ruoso Date: Sat, 18 Jan 2025 19:03:36 -0500 Subject: [PATCH 05/11] Minor fixes in operations. --- src/networkprotocoldsl/operation/add.cpp | 29 ++++----------- src/networkprotocoldsl/operation/eq.cpp | 21 ++--------- .../operation/functioncall.cpp | 36 ++++++------------- .../operation/functioncallforeach.cpp | 14 +++++--- .../operation/functioncallforeach.hpp | 1 + .../operation/generatelist.cpp | 4 +++ .../operation/opsequence.cpp | 6 +++- .../operation/terminatelistifreadahead.cpp | 3 ++ 8 files changed, 44 insertions(+), 70 deletions(-) diff --git a/src/networkprotocoldsl/operation/add.cpp b/src/networkprotocoldsl/operation/add.cpp index ad2a7d1..ec312e2 100644 --- a/src/networkprotocoldsl/operation/add.cpp +++ b/src/networkprotocoldsl/operation/add.cpp @@ -5,36 +5,21 @@ namespace networkprotocoldsl::operation { static Value _add(int32_t lhs, int32_t rhs) { return rhs + lhs; } -static Value _add(int32_t lhs, auto rhs) { - return value::RuntimeError::TypeError; -} - -static Value _add(int32_t lhs, value::RuntimeError rhs) { return rhs; } - -static Value _add(int32_t lhs, value::ControlFlowInstruction rhs) { - return rhs; -} - -static Value _add(int32_t lhs, Value rhs) { - return std::visit([&lhs](auto rhs_v) -> Value { return _add(lhs, rhs_v); }, - rhs); -} - -static Value _add(value::Callable lhs, auto rhs) { - return value::RuntimeError::TypeError; -} +static Value _add(auto &, auto &) { return value::RuntimeError::TypeError; } static Value _add(value::RuntimeError lhs, auto rhs) { return lhs; } static Value _add(value::ControlFlowInstruction lhs, auto rhs) { return lhs; } -template static Value _add(LHS lhs, RHS rhs) { - return value::RuntimeError::TypeError; +static Value _add(int32_t lhs, value::RuntimeError rhs) { return rhs; } + +static Value _add(int32_t lhs, value::ControlFlowInstruction rhs) { + return rhs; } Value Add::operator()(Arguments a) const { - return std::visit([&a](auto lhs) { return _add(lhs, std::get<1>(a)); }, - std::get<0>(a)); + return std::visit([](auto lhs, auto rhs) { return _add(lhs, rhs); }, + std::get<0>(a), std::get<1>(a)); } } // namespace networkprotocoldsl::operation diff --git a/src/networkprotocoldsl/operation/eq.cpp b/src/networkprotocoldsl/operation/eq.cpp index 7560dd9..f7dc1c6 100644 --- a/src/networkprotocoldsl/operation/eq.cpp +++ b/src/networkprotocoldsl/operation/eq.cpp @@ -5,34 +5,19 @@ namespace networkprotocoldsl::operation { static Value _eq(int32_t lhs, int32_t rhs) { return (bool)(rhs == lhs); } -static Value _eq(int32_t lhs, value::Callable rhs) { - return value::RuntimeError::TypeError; -} +static Value _eq(auto &, auto &) { return value::RuntimeError::TypeError; } static Value _eq(int32_t lhs, value::RuntimeError rhs) { return rhs; } static Value _eq(int32_t lhs, value::ControlFlowInstruction rhs) { return rhs; } -static Value _eq(int32_t lhs, Value rhs) { - return std::visit([&lhs](auto rhs_v) -> Value { return _eq(lhs, rhs_v); }, - rhs); -} - -static Value _eq(value::Callable lhs, auto rhs) { - return value::RuntimeError::TypeError; -} - static Value _eq(value::RuntimeError lhs, auto rhs) { return lhs; } static Value _eq(value::ControlFlowInstruction lhs, auto rhs) { return lhs; } -template static Value _eq(LHS lhs, RHS rhs) { - return value::RuntimeError::TypeError; -} - Value Eq::operator()(Arguments a) const { - return std::visit([&a](auto lhs) { return _eq(lhs, std::get<1>(a)); }, - std::get<0>(a)); + return std::visit([&a](auto lhs, auto rhs) { return _eq(lhs, rhs); }, + std::get<0>(a), std::get<1>(a)); } } // namespace networkprotocoldsl::operation diff --git a/src/networkprotocoldsl/operation/functioncall.cpp b/src/networkprotocoldsl/operation/functioncall.cpp index a39d0b7..255b461 100644 --- a/src/networkprotocoldsl/operation/functioncall.cpp +++ b/src/networkprotocoldsl/operation/functioncall.cpp @@ -8,36 +8,22 @@ namespace networkprotocoldsl::operation { -static OperationResult _function_call2(ControlFlowOperationContext &ctx, - value::Callable callable, - value::DynamicList arglist) { +static OperationResult _function_call(ControlFlowOperationContext &ctx, + value::Callable callable, + value::DynamicList arglist) { ctx.callable = callable; ctx.arglist = arglist.values; return ReasonForBlockedOperation::WaitingForCallableInvocation; } -static OperationResult _function_call2(ControlFlowOperationContext &ctx, - value::Callable a, - value::RuntimeError b) { - return b; -} - -static OperationResult _function_call2(ControlFlowOperationContext &ctx, - value::Callable a, auto b) { - return value::RuntimeError::TypeError; -} - static OperationResult _function_call(ControlFlowOperationContext &ctx, - value::Callable callable, Value arglist) { - return std::visit( - [&ctx, &callable](auto arglist_visited) { - return _function_call2(ctx, callable, arglist_visited); - }, - arglist); + value::Callable a, + value::RuntimeError b) { + return b; } -static OperationResult _function_call(ControlFlowOperationContext &ctx, auto a, - auto b) { +static OperationResult _function_call(ControlFlowOperationContext &ctx, auto &, + auto &) { return value::RuntimeError::TypeError; } @@ -55,10 +41,10 @@ OperationResult FunctionCall::operator()(ControlFlowOperationContext &ctx, } } else { return std::visit( - [&ctx, &a](auto &callable) { - return _function_call(ctx, callable, std::get<1>(a)); + [&ctx](auto &callable, auto &arglist) { + return _function_call(ctx, callable, arglist); }, - std::get<0>(a)); + std::get<0>(a), std::get<1>(a)); } } diff --git a/src/networkprotocoldsl/operation/functioncallforeach.cpp b/src/networkprotocoldsl/operation/functioncallforeach.cpp index c4c86f6..4f1d68b 100644 --- a/src/networkprotocoldsl/operation/functioncallforeach.cpp +++ b/src/networkprotocoldsl/operation/functioncallforeach.cpp @@ -78,10 +78,16 @@ void FunctionCallForEach::set_callable_return(ControlFlowOperationContext &ctx, std::shared_ptr> FunctionCallForEach::get_argument_list(ControlFlowOperationContext &ctx) const { - // This presumes that this is a list of lists. A different operation would be - // needed in order to support cases where that's not going to be true. - return std::get(ctx.arglist->at(ctx.accumulator->size())) - .values; + if (element_is_single_argument) { + return std::make_shared>( + std::vector{ctx.arglist->at(ctx.accumulator->size())}); + } else { + // This presumes that this is a list of lists. A different operation would + // be needed in order to support cases where that's not going to be true. + return std::get( + ctx.arglist->at(ctx.accumulator->size())) + .values; + } } } // namespace networkprotocoldsl::operation diff --git a/src/networkprotocoldsl/operation/functioncallforeach.hpp b/src/networkprotocoldsl/operation/functioncallforeach.hpp index f66892f..35c2ecd 100644 --- a/src/networkprotocoldsl/operation/functioncallforeach.hpp +++ b/src/networkprotocoldsl/operation/functioncallforeach.hpp @@ -7,6 +7,7 @@ namespace networkprotocoldsl::operation { class FunctionCallForEach { public: + bool element_is_single_argument = false; using Arguments = std::tuple; OperationResult operator()(ControlFlowOperationContext &ctx, Arguments a) const; diff --git a/src/networkprotocoldsl/operation/generatelist.cpp b/src/networkprotocoldsl/operation/generatelist.cpp index f50fbac..33813c3 100644 --- a/src/networkprotocoldsl/operation/generatelist.cpp +++ b/src/networkprotocoldsl/operation/generatelist.cpp @@ -35,6 +35,10 @@ OperationResult GenerateList::operator()(ControlFlowOperationContext &ctx, return value::DynamicList{ctx.accumulator}; } } + // if it's a runtime error, we should also finish the list. + if (std::holds_alternative(ctx.value.value())) { + return ctx.value.value(); + } // if it's not the control flow instruction, we reset the context. ctx.accumulator->push_back(ctx.value.value()); ctx.callable_invoked = false; diff --git a/src/networkprotocoldsl/operation/opsequence.cpp b/src/networkprotocoldsl/operation/opsequence.cpp index 1040db3..5371f1e 100644 --- a/src/networkprotocoldsl/operation/opsequence.cpp +++ b/src/networkprotocoldsl/operation/opsequence.cpp @@ -5,7 +5,11 @@ namespace networkprotocoldsl::operation { Value OpSequence::operator()(std::shared_ptr> args) const { - return args->back(); + if (args->empty()) { + return false; + } else { + return args->back(); + } } } // namespace networkprotocoldsl::operation \ No newline at end of file diff --git a/src/networkprotocoldsl/operation/terminatelistifreadahead.cpp b/src/networkprotocoldsl/operation/terminatelistifreadahead.cpp index cf40b67..c7fef84 100644 --- a/src/networkprotocoldsl/operation/terminatelistifreadahead.cpp +++ b/src/networkprotocoldsl/operation/terminatelistifreadahead.cpp @@ -9,6 +9,9 @@ TerminateListIfReadAhead::operator()(InputOutputOperationContext &ctx, Arguments a) const { if (ctx.buffer.length() == 0) { // nothing to compare yet. + if (ctx.eof) { + return value::RuntimeError::ProtocolMismatchError; + } return ReasonForBlockedOperation::WaitingForRead; } else if (ctx.buffer.length() < terminator.length()) { // we don't have the complete thing yet, but if it already mismatches we From c12fb8474966856f24e5cd55e86b52ed6459fb32 Mon Sep 17 00:00:00 2001 From: Daniel Ruoso Date: Sat, 18 Jan 2025 19:05:00 -0500 Subject: [PATCH 06/11] Use unordered_maps in the AST --- src/networkprotocoldsl/sema/analyze.cpp | 9 +++++---- src/networkprotocoldsl/sema/ast/agent.hpp | 3 ++- src/networkprotocoldsl/sema/ast/state.hpp | 5 +++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/networkprotocoldsl/sema/analyze.cpp b/src/networkprotocoldsl/sema/analyze.cpp index 632935d..57b8bb6 100644 --- a/src/networkprotocoldsl/sema/analyze.cpp +++ b/src/networkprotocoldsl/sema/analyze.cpp @@ -32,11 +32,11 @@ static std::optional analyze_read_message( ast::ReadTransition{message->data, maybe_actions.value()}); } -static std::optional> +static std::optional> analyze_transitions( std::shared_ptr &protocol, const std::string &agent_name) { - auto transitions = std::map(); + auto transitions = std::unordered_map(); for (auto &message : *protocol) { auto maybe_transition = message.second->agent->name == agent_name ? analyze_write_message(message.second) @@ -61,7 +61,7 @@ static std::optional> analyze_agent( auto &transitions = maybe_transitions.value(); // assemble the states and transitions together - std::map> states; + std::unordered_map> states; for (auto &message : *protocol) { auto when_state = message.second->when->name; auto then_state = message.second->then->name; @@ -77,7 +77,8 @@ static std::optional> analyze_agent( std::make_pair(transitions[message.first], then_state); } // assemble a map to const states, now that we have the data complete. - std::map> const_states; + std::unordered_map> + const_states; for (const auto &state : states) { const_states[state.first] = state.second; } diff --git a/src/networkprotocoldsl/sema/ast/agent.hpp b/src/networkprotocoldsl/sema/ast/agent.hpp index e1d97d4..d9beba5 100644 --- a/src/networkprotocoldsl/sema/ast/agent.hpp +++ b/src/networkprotocoldsl/sema/ast/agent.hpp @@ -2,13 +2,14 @@ #define INCLUDED_NETWORKPROTOCOLDSL_SEMA_AST_AGENT_HPP #include +#include #include namespace networkprotocoldsl::sema::ast { struct Agent { - std::map> states; + std::unordered_map> states; }; } // namespace networkprotocoldsl::sema::ast diff --git a/src/networkprotocoldsl/sema/ast/state.hpp b/src/networkprotocoldsl/sema/ast/state.hpp index 747a1fc..6eec2be 100644 --- a/src/networkprotocoldsl/sema/ast/state.hpp +++ b/src/networkprotocoldsl/sema/ast/state.hpp @@ -1,15 +1,16 @@ #ifndef INCLUDED_NETWORKPROTOCOLDSL_SEMA_AST_STATE_HPP #define INCLUDED_NETWORKPROTOCOLDSL_SEMA_AST_STATE_HPP -#include #include +#include #include namespace networkprotocoldsl::sema::ast { struct State { - std::map> transitions; + std::unordered_map> + transitions; }; } // namespace networkprotocoldsl::sema::ast From a42b448adf6a72fa73c87cac655f228f4b106fc0 Mon Sep 17 00:00:00 2001 From: Daniel Ruoso Date: Sat, 18 Jan 2025 19:05:48 -0500 Subject: [PATCH 07/11] Wake up all signals in case of EOF --- src/networkprotocoldsl/interpreterrunner.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/networkprotocoldsl/interpreterrunner.cpp b/src/networkprotocoldsl/interpreterrunner.cpp index 75dc9a8..d8387aa 100644 --- a/src/networkprotocoldsl/interpreterrunner.cpp +++ b/src/networkprotocoldsl/interpreterrunner.cpp @@ -42,6 +42,10 @@ static bool handle_read(InterpreterContext &context, } else { if (context.eof.load()) { context.interpreter.handle_eof(); + signals.wake_up_for_output.notify(); + signals.wake_up_interpreter.notify(); + signals.wake_up_for_input.notify(); + signals.wake_up_for_callback.notify(); } else { signals.wake_up_for_input.notify(); } @@ -52,6 +56,10 @@ static bool handle_read(InterpreterContext &context, bool handle_write(InterpreterContext &context, InterpreterSignals &signals) { if (context.eof.load()) { context.interpreter.handle_eof(); + signals.wake_up_for_output.notify(); + signals.wake_up_interpreter.notify(); + signals.wake_up_for_input.notify(); + signals.wake_up_for_callback.notify(); return true; } else { auto buffer = context.interpreter.get_write_buffer(); From 4b19f5bce2409e4455ea51b0b234886166b2c971 Mon Sep 17 00:00:00 2001 From: Daniel Ruoso Date: Sat, 18 Jan 2025 19:08:53 -0500 Subject: [PATCH 08/11] Support for stringifying the optree --- CMakeLists.txt | 2 + src/networkprotocoldsl/operation/add.hpp | 1 + .../operation/dynamiclist.hpp | 1 + src/networkprotocoldsl/operation/eq.hpp | 1 + .../operation/functioncall.hpp | 1 + .../operation/functioncallforeach.hpp | 7 +++ .../operation/generatelist.hpp | 1 + src/networkprotocoldsl/operation/if.hpp | 1 + .../operation/int32literal.hpp | 3 ++ .../operation/inttoascii.hpp | 1 + .../operation/lesserequal.hpp | 1 + .../operation/lexicalpadget.hpp | 3 ++ .../operation/lexicalpadinitialize.hpp | 3 ++ .../operation/lexicalpadinitializeglobal.hpp | 3 ++ .../operation/lexicalpadset.hpp | 3 ++ src/networkprotocoldsl/operation/multiply.hpp | 3 +- .../operation/opsequence.hpp | 1 + .../operation/readint32native.hpp | 2 + .../operation/readintfromascii.hpp | 2 + .../operation/readoctetsuntilterminator.hpp | 4 ++ .../operation/readstaticoctets.hpp | 4 ++ .../operation/staticcallable.cpp | 19 ++++++++ .../operation/staticcallable.hpp | 1 + src/networkprotocoldsl/operation/subtract.hpp | 1 + .../operation/terminatelistifreadahead.hpp | 4 ++ .../operation/unarycallback.hpp | 4 ++ .../operation/writeint32native.hpp | 2 + .../operation/writeoctets.hpp | 2 + .../operation/writestaticoctets.hpp | 4 ++ src/networkprotocoldsl/operationconcepts.hpp | 9 ++++ src/networkprotocoldsl/print_optreenode.cpp | 43 +++++++++++++++++++ src/networkprotocoldsl/print_optreenode.hpp | 17 ++++++++ 32 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 src/networkprotocoldsl/print_optreenode.cpp create mode 100644 src/networkprotocoldsl/print_optreenode.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bdb7f3c..7d0fc34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -195,6 +195,8 @@ add_library( src/networkprotocoldsl/operationconcepts.hpp src/networkprotocoldsl/optree.cpp src/networkprotocoldsl/optree.hpp + src/networkprotocoldsl/print_optreenode.cpp + src/networkprotocoldsl/print_optreenode.hpp src/networkprotocoldsl/value.cpp src/networkprotocoldsl/value.hpp ) diff --git a/src/networkprotocoldsl/operation/add.hpp b/src/networkprotocoldsl/operation/add.hpp index 24aaadf..9259827 100644 --- a/src/networkprotocoldsl/operation/add.hpp +++ b/src/networkprotocoldsl/operation/add.hpp @@ -21,6 +21,7 @@ class Add { public: using Arguments = std::tuple; Value operator()(Arguments a) const; + std::string stringify() const { return "Add{}"; } }; static_assert(InterpretedOperationConcept); diff --git a/src/networkprotocoldsl/operation/dynamiclist.hpp b/src/networkprotocoldsl/operation/dynamiclist.hpp index 3f0a636..a46ca34 100644 --- a/src/networkprotocoldsl/operation/dynamiclist.hpp +++ b/src/networkprotocoldsl/operation/dynamiclist.hpp @@ -8,6 +8,7 @@ namespace networkprotocoldsl::operation { class DynamicList { public: Value operator()(std::shared_ptr> args) const; + std::string stringify() const { return "DynamicList{}"; } }; static_assert(DynamicInputOperationConcept); diff --git a/src/networkprotocoldsl/operation/eq.hpp b/src/networkprotocoldsl/operation/eq.hpp index 2b4c8ee..36fc5c5 100644 --- a/src/networkprotocoldsl/operation/eq.hpp +++ b/src/networkprotocoldsl/operation/eq.hpp @@ -21,6 +21,7 @@ class Eq { public: using Arguments = std::tuple; Value operator()(Arguments a) const; + std::string stringify() const { return "Eq{}"; } }; static_assert(InterpretedOperationConcept); diff --git a/src/networkprotocoldsl/operation/functioncall.hpp b/src/networkprotocoldsl/operation/functioncall.hpp index 0ea0825..f634a9a 100644 --- a/src/networkprotocoldsl/operation/functioncall.hpp +++ b/src/networkprotocoldsl/operation/functioncall.hpp @@ -15,6 +15,7 @@ class FunctionCall { get_argument_list(ControlFlowOperationContext &ctx) const; void set_callable_invoked(ControlFlowOperationContext &ctx) const; void set_callable_return(ControlFlowOperationContext &ctx, Value v) const; + std::string stringify() const { return "FunctionCall{}"; } }; static_assert(ControlFlowOperationConcept); diff --git a/src/networkprotocoldsl/operation/functioncallforeach.hpp b/src/networkprotocoldsl/operation/functioncallforeach.hpp index 35c2ecd..30bb9f3 100644 --- a/src/networkprotocoldsl/operation/functioncallforeach.hpp +++ b/src/networkprotocoldsl/operation/functioncallforeach.hpp @@ -16,6 +16,13 @@ class FunctionCallForEach { get_argument_list(ControlFlowOperationContext &ctx) const; void set_callable_invoked(ControlFlowOperationContext &ctx) const; void set_callable_return(ControlFlowOperationContext &ctx, Value v) const; + std::string stringify() const { + if (element_is_single_argument) { + return "FunctionCallForEach{element_is_single_argument: true}"; + } else { + return "FunctionCallForEach{element_is_single_argument: false}"; + } + } }; static_assert(ControlFlowOperationConcept); diff --git a/src/networkprotocoldsl/operation/generatelist.hpp b/src/networkprotocoldsl/operation/generatelist.hpp index 2ead4fe..0920a68 100644 --- a/src/networkprotocoldsl/operation/generatelist.hpp +++ b/src/networkprotocoldsl/operation/generatelist.hpp @@ -15,6 +15,7 @@ class GenerateList { get_argument_list(ControlFlowOperationContext &ctx) const; void set_callable_invoked(ControlFlowOperationContext &ctx) const; void set_callable_return(ControlFlowOperationContext &ctx, Value v) const; + std::string stringify() const { return "GenerateList{}"; } }; static_assert(ControlFlowOperationConcept); diff --git a/src/networkprotocoldsl/operation/if.hpp b/src/networkprotocoldsl/operation/if.hpp index c124a20..07a3ebd 100644 --- a/src/networkprotocoldsl/operation/if.hpp +++ b/src/networkprotocoldsl/operation/if.hpp @@ -28,6 +28,7 @@ class If { get_argument_list(ControlFlowOperationContext &ctx) const; void set_callable_invoked(ControlFlowOperationContext &ctx) const; void set_callable_return(ControlFlowOperationContext &ctx, Value v) const; + std::string stringify() const { return "If{}"; } }; static_assert(ControlFlowOperationConcept); diff --git a/src/networkprotocoldsl/operation/int32literal.hpp b/src/networkprotocoldsl/operation/int32literal.hpp index e8f7722..5f160dc 100644 --- a/src/networkprotocoldsl/operation/int32literal.hpp +++ b/src/networkprotocoldsl/operation/int32literal.hpp @@ -24,6 +24,9 @@ class Int32Literal { using Arguments = std::tuple<>; Int32Literal(int32_t v) : int32_v(v) {} Value operator()(Arguments a) const { return int32_v; } + std::string stringify() const { + return "Int32Literal{int32_v: " + std::to_string(int32_v) + "}"; + } }; static_assert(InterpretedOperationConcept); diff --git a/src/networkprotocoldsl/operation/inttoascii.hpp b/src/networkprotocoldsl/operation/inttoascii.hpp index ea707de..0c5e55f 100644 --- a/src/networkprotocoldsl/operation/inttoascii.hpp +++ b/src/networkprotocoldsl/operation/inttoascii.hpp @@ -15,6 +15,7 @@ class IntToAscii { public: using Arguments = std::tuple; Value operator()(Arguments a) const; + std::string stringify() const { return "IntToAscii{}"; } }; static_assert(InterpretedOperationConcept); diff --git a/src/networkprotocoldsl/operation/lesserequal.hpp b/src/networkprotocoldsl/operation/lesserequal.hpp index aa74757..93bb471 100644 --- a/src/networkprotocoldsl/operation/lesserequal.hpp +++ b/src/networkprotocoldsl/operation/lesserequal.hpp @@ -21,6 +21,7 @@ class LesserEqual { public: using Arguments = std::tuple; Value operator()(Arguments a) const; + std::string stringify() const { return "LesserEqual{}"; } }; static_assert(InterpretedOperationConcept); diff --git a/src/networkprotocoldsl/operation/lexicalpadget.hpp b/src/networkprotocoldsl/operation/lexicalpadget.hpp index 7d9ebee..17e7da0 100644 --- a/src/networkprotocoldsl/operation/lexicalpadget.hpp +++ b/src/networkprotocoldsl/operation/lexicalpadget.hpp @@ -16,6 +16,9 @@ class LexicalPadGet { Value operator()(Arguments args, std::shared_ptr pad) const { return pad->get(name); } + std::string stringify() const { + return "LexicalPadGet{name: \"" + name + "\"}"; + } }; static_assert(LexicalPadOperationConcept); diff --git a/src/networkprotocoldsl/operation/lexicalpadinitialize.hpp b/src/networkprotocoldsl/operation/lexicalpadinitialize.hpp index 9be78ad..a1bd8a5 100644 --- a/src/networkprotocoldsl/operation/lexicalpadinitialize.hpp +++ b/src/networkprotocoldsl/operation/lexicalpadinitialize.hpp @@ -17,6 +17,9 @@ class LexicalPadInitialize { pad->initialize(name, std::get<0>(args)); return std::get<0>(args); } + std::string stringify() const { + return "LexicalPadInitialize{name: \"" + name + "\"}"; + } }; static_assert(LexicalPadOperationConcept); diff --git a/src/networkprotocoldsl/operation/lexicalpadinitializeglobal.hpp b/src/networkprotocoldsl/operation/lexicalpadinitializeglobal.hpp index d4956d0..0d5f5fc 100644 --- a/src/networkprotocoldsl/operation/lexicalpadinitializeglobal.hpp +++ b/src/networkprotocoldsl/operation/lexicalpadinitializeglobal.hpp @@ -17,6 +17,9 @@ class LexicalPadInitializeGlobal { pad->initialize_global(name, std::get<0>(args)); return std::get<0>(args); } + std::string stringify() const { + return "LexicalPadInitializeGlobal{name: \"" + name + "\"}"; + } }; static_assert(LexicalPadOperationConcept); diff --git a/src/networkprotocoldsl/operation/lexicalpadset.hpp b/src/networkprotocoldsl/operation/lexicalpadset.hpp index 190a3e1..fd1b1de 100644 --- a/src/networkprotocoldsl/operation/lexicalpadset.hpp +++ b/src/networkprotocoldsl/operation/lexicalpadset.hpp @@ -16,6 +16,9 @@ class LexicalPadSet { Value operator()(Arguments args, std::shared_ptr pad) const { return pad->set(name, std::get<0>(args)); } + std::string stringify() const { + return "LexicalPadSet{name: \"" + name + "\"}"; + } }; static_assert(LexicalPadOperationConcept); diff --git a/src/networkprotocoldsl/operation/multiply.hpp b/src/networkprotocoldsl/operation/multiply.hpp index 47f2de3..f8b0a6a 100644 --- a/src/networkprotocoldsl/operation/multiply.hpp +++ b/src/networkprotocoldsl/operation/multiply.hpp @@ -21,6 +21,7 @@ class Multiply { public: using Arguments = std::tuple; Value operator()(Arguments a) const; + std::string stringify() const { return "Multiply{}"; } }; static_assert(InterpretedOperationConcept); @@ -28,4 +29,4 @@ static_assert(InterpretedOperationConcept); } // namespace networkprotocoldsl -#endif // NETWORKPROTOCOLDSL_OPERATION_HPP +#endif // NETWORKPROTOCOLDSL_OPERATION_MULTIPLY_HPP diff --git a/src/networkprotocoldsl/operation/opsequence.hpp b/src/networkprotocoldsl/operation/opsequence.hpp index a69153a..2da6c20 100644 --- a/src/networkprotocoldsl/operation/opsequence.hpp +++ b/src/networkprotocoldsl/operation/opsequence.hpp @@ -21,6 +21,7 @@ namespace operation { class OpSequence { public: Value operator()(std::shared_ptr> args) const; + std::string stringify() const { return "OpSequence{}"; } }; static_assert(DynamicInputOperationConcept); diff --git a/src/networkprotocoldsl/operation/readint32native.hpp b/src/networkprotocoldsl/operation/readint32native.hpp index 8b70265..d202f8d 100644 --- a/src/networkprotocoldsl/operation/readint32native.hpp +++ b/src/networkprotocoldsl/operation/readint32native.hpp @@ -28,6 +28,8 @@ class ReadInt32Native { std::string_view get_write_buffer(InputOutputOperationContext &ctx) const; size_t handle_write(InputOutputOperationContext &ctx, size_t s) const; + + std::string stringify() const { return "ReadInt32Native{}"; } }; static_assert(InputOutputOperationConcept); diff --git a/src/networkprotocoldsl/operation/readintfromascii.hpp b/src/networkprotocoldsl/operation/readintfromascii.hpp index 05d18dc..7daa76f 100644 --- a/src/networkprotocoldsl/operation/readintfromascii.hpp +++ b/src/networkprotocoldsl/operation/readintfromascii.hpp @@ -30,6 +30,8 @@ class ReadIntFromAscii { std::string_view get_write_buffer(InputOutputOperationContext &ctx) const; size_t handle_write(InputOutputOperationContext &ctx, size_t s) const; + + std::string stringify() const { return "ReadIntFromAscii{}"; } }; static_assert(InputOutputOperationConcept); diff --git a/src/networkprotocoldsl/operation/readoctetsuntilterminator.hpp b/src/networkprotocoldsl/operation/readoctetsuntilterminator.hpp index a237c3c..071559e 100644 --- a/src/networkprotocoldsl/operation/readoctetsuntilterminator.hpp +++ b/src/networkprotocoldsl/operation/readoctetsuntilterminator.hpp @@ -31,6 +31,10 @@ class ReadOctetsUntilTerminator { void handle_eof(InputOutputOperationContext &ctx) const; size_t handle_write(InputOutputOperationContext &ctx, size_t s) const; + + std::string stringify() const { + return "ReadOctetsUntilTerminator{terminator: \"" + terminator + "\"}"; + } }; static_assert(InputOutputOperationConcept); diff --git a/src/networkprotocoldsl/operation/readstaticoctets.hpp b/src/networkprotocoldsl/operation/readstaticoctets.hpp index abc99c6..8bdf2bf 100644 --- a/src/networkprotocoldsl/operation/readstaticoctets.hpp +++ b/src/networkprotocoldsl/operation/readstaticoctets.hpp @@ -31,6 +31,10 @@ class ReadStaticOctets { void handle_eof(InputOutputOperationContext &ctx) const; size_t handle_write(InputOutputOperationContext &ctx, size_t s) const; + + std::string stringify() const { + return "ReadStaticOctets{contents: \"" + contents + "\"}"; + } }; static_assert(InputOutputOperationConcept); diff --git a/src/networkprotocoldsl/operation/staticcallable.cpp b/src/networkprotocoldsl/operation/staticcallable.cpp index e6553d6..832aaa8 100644 --- a/src/networkprotocoldsl/operation/staticcallable.cpp +++ b/src/networkprotocoldsl/operation/staticcallable.cpp @@ -1,4 +1,5 @@ #include +#include namespace networkprotocoldsl::operation { @@ -6,4 +7,22 @@ Value StaticCallable::operator()(Arguments a) const { return value::Callable(optree, argument_names, inherits_lexical_pad); } +std::string StaticCallable::stringify() const { + std::ostringstream os; + os << "StaticCallable{\n"; + os << " inherits_lexical_pad: " << std::to_string(inherits_lexical_pad) + << "\n"; + os << " argument_names: ["; + for (size_t i = 0; i < argument_names.size(); ++i) { + os << "\"" << argument_names[i] << "\""; + if (i < argument_names.size() - 1) { + os << ", "; + } + } + os << "]\n optree: \n"; + print_optreenode(optree->root, os, " │ ", " │ "); + os << "}"; + return os.str(); +} + } // namespace networkprotocoldsl::operation diff --git a/src/networkprotocoldsl/operation/staticcallable.hpp b/src/networkprotocoldsl/operation/staticcallable.hpp index 36ad45b..b930590 100644 --- a/src/networkprotocoldsl/operation/staticcallable.hpp +++ b/src/networkprotocoldsl/operation/staticcallable.hpp @@ -27,6 +27,7 @@ class StaticCallable { : optree(o), argument_names(n), inherits_lexical_pad(i) {} using Arguments = std::tuple<>; Value operator()(Arguments a) const; + std::string stringify() const; }; static_assert(InterpretedOperationConcept); diff --git a/src/networkprotocoldsl/operation/subtract.hpp b/src/networkprotocoldsl/operation/subtract.hpp index 55a98a9..6a9cd5b 100644 --- a/src/networkprotocoldsl/operation/subtract.hpp +++ b/src/networkprotocoldsl/operation/subtract.hpp @@ -21,6 +21,7 @@ class Subtract { public: using Arguments = std::tuple; Value operator()(Arguments a) const; + std::string stringify() const { return "Subtract{}"; } }; static_assert(InterpretedOperationConcept); diff --git a/src/networkprotocoldsl/operation/terminatelistifreadahead.hpp b/src/networkprotocoldsl/operation/terminatelistifreadahead.hpp index 8446cf5..a650d77 100644 --- a/src/networkprotocoldsl/operation/terminatelistifreadahead.hpp +++ b/src/networkprotocoldsl/operation/terminatelistifreadahead.hpp @@ -24,6 +24,10 @@ class TerminateListIfReadAhead { void handle_eof(InputOutputOperationContext &ctx) const; size_t handle_write(InputOutputOperationContext &ctx, size_t s) const; + + std::string stringify() const { + return "TerminateListIfReadAhead{terminator: \"" + terminator + "\"}"; + } }; static_assert(InputOutputOperationConcept); diff --git a/src/networkprotocoldsl/operation/unarycallback.hpp b/src/networkprotocoldsl/operation/unarycallback.hpp index 4b8c4fc..3c5f10e 100644 --- a/src/networkprotocoldsl/operation/unarycallback.hpp +++ b/src/networkprotocoldsl/operation/unarycallback.hpp @@ -24,6 +24,10 @@ class UnaryCallback { std::string callback_key(CallbackOperationContext &ctx) const; void set_callback_return(CallbackOperationContext &ctx, Value v) const; void set_callback_called(CallbackOperationContext &ctx) const; + + std::string stringify() const { + return "UnaryCallback{key: \"" + key + "\"}"; + } }; static_assert(CallbackOperationConcept); diff --git a/src/networkprotocoldsl/operation/writeint32native.hpp b/src/networkprotocoldsl/operation/writeint32native.hpp index cfa6b62..22d7acb 100644 --- a/src/networkprotocoldsl/operation/writeint32native.hpp +++ b/src/networkprotocoldsl/operation/writeint32native.hpp @@ -29,6 +29,8 @@ class WriteInt32Native { void handle_eof(InputOutputOperationContext &ctx) const; size_t handle_write(InputOutputOperationContext &ctx, size_t s) const; + + std::string stringify() const { return "WriteInt32Native{}"; } }; static_assert(InputOutputOperationConcept); diff --git a/src/networkprotocoldsl/operation/writeoctets.hpp b/src/networkprotocoldsl/operation/writeoctets.hpp index 48919e1..f0d6298 100644 --- a/src/networkprotocoldsl/operation/writeoctets.hpp +++ b/src/networkprotocoldsl/operation/writeoctets.hpp @@ -29,6 +29,8 @@ class WriteOctets { void handle_eof(InputOutputOperationContext &ctx) const; size_t handle_write(InputOutputOperationContext &ctx, size_t s) const; + + std::string stringify() const { return "WriteOctets{}"; } }; static_assert(InputOutputOperationConcept); diff --git a/src/networkprotocoldsl/operation/writestaticoctets.hpp b/src/networkprotocoldsl/operation/writestaticoctets.hpp index fb54dfb..fc04d8f 100644 --- a/src/networkprotocoldsl/operation/writestaticoctets.hpp +++ b/src/networkprotocoldsl/operation/writestaticoctets.hpp @@ -31,6 +31,10 @@ class WriteStaticOctets { void handle_eof(InputOutputOperationContext &ctx) const; size_t handle_write(InputOutputOperationContext &ctx, size_t s) const; + + std::string stringify() const { + return "WriteStaticOctets{contents: \"" + contents + "\"}"; + } }; static_assert(InputOutputOperationConcept); diff --git a/src/networkprotocoldsl/operationconcepts.hpp b/src/networkprotocoldsl/operationconcepts.hpp index b99421b..4c0ceae 100644 --- a/src/networkprotocoldsl/operationconcepts.hpp +++ b/src/networkprotocoldsl/operationconcepts.hpp @@ -153,6 +153,15 @@ concept LexicalPadOperationConcept = requires(OT op, { op(args, pad) } -> std::convertible_to; }; +/** + * The Stringification operation concept offers an interface for + * converting the operation to a string representation. + */ +template +concept StringifiableOperationConcept = requires(OT op) { + { op.stringify() } -> std::convertible_to; +}; + } // namespace networkprotocoldsl #endif diff --git a/src/networkprotocoldsl/print_optreenode.cpp b/src/networkprotocoldsl/print_optreenode.cpp new file mode 100644 index 0000000..5e38b2c --- /dev/null +++ b/src/networkprotocoldsl/print_optreenode.cpp @@ -0,0 +1,43 @@ +#include + +#include +#include + +#include +#include + +namespace networkprotocoldsl { + +void print_optreenode(const OpTreeNode &node, std::ostream &os, + const std::string &prefix_firstline, + const std::string &prefix_others) { + + std::visit( + [&](const auto &op) { + if constexpr (StringifiableOperationConcept) { + std::string opStr = op.stringify(); + std::istringstream stream(opStr); + std::string line; + bool first = true; + while (std::getline(stream, line)) { + os << (first ? prefix_firstline : prefix_others) << line + << std::endl; + first = false; + } + } else { + os << prefix_firstline << "" << std::endl; + } + }, + node.operation); + + for (size_t i = 0; i < node.children.size(); ++i) { + const auto &child = node.children[i]; + std::string new_prefix_firstline = + prefix_others + (i == node.children.size() - 1 ? " └── " : " ├── "); + std::string new_prefix_others = + prefix_others + (i == node.children.size() - 1 ? " " : " │ "); + print_optreenode(child, os, new_prefix_firstline, new_prefix_others); + } +} + +} // namespace networkprotocoldsl diff --git a/src/networkprotocoldsl/print_optreenode.hpp b/src/networkprotocoldsl/print_optreenode.hpp new file mode 100644 index 0000000..beb0e36 --- /dev/null +++ b/src/networkprotocoldsl/print_optreenode.hpp @@ -0,0 +1,17 @@ +#ifndef NETWORKPROTOCOLDSL_PRINT_OPTREENODE_HPP +#define NETWORKPROTOCOLDSL_PRINT_OPTREENODE_HPP + +#include +#include +#include +#include + +namespace networkprotocoldsl { + +void print_optreenode(const OpTreeNode &node, std::ostream &os, + const std::string &prefix_firstline, + const std::string &prefix_others); + +} // namespace networkprotocoldsl + +#endif // NETWORKPROTOCOLDSL_PRINT_OPTREENODE_HPP From 2b2365820b9d65c337c7bb80c500b28fe9444822 Mon Sep 17 00:00:00 2001 From: Daniel Ruoso Date: Sat, 18 Jan 2025 19:09:47 -0500 Subject: [PATCH 09/11] Write debugging output to a tmp file This is not a valid idea for production code, but for the purposes of this exercise, it is fine. --- .../executionstackframe.cpp | 21 +++++++++++++++++++ .../executionstackframe.hpp | 3 +++ 2 files changed, 24 insertions(+) diff --git a/src/networkprotocoldsl/executionstackframe.cpp b/src/networkprotocoldsl/executionstackframe.cpp index 8d8c19e..a45ab58 100644 --- a/src/networkprotocoldsl/executionstackframe.cpp +++ b/src/networkprotocoldsl/executionstackframe.cpp @@ -1,9 +1,13 @@ #include #include +#include #include #include +#include +#include #include +#include namespace networkprotocoldsl { @@ -141,8 +145,25 @@ bool ExecutionStackFrame::has_arguments_ready() { std::shared_ptr ExecutionStackFrame::get_pad() { return pad; } +static std::string get_debug_stream_file_name() { + std::thread::id this_id = std::this_thread::get_id(); + std::ostringstream ss; + ss << "/tmp/thread_"; + ss << this_id; + ss << "_debug.log"; + return ss.str(); +} + +static void do_debug(ExecutionStackFrame *frame) { + thread_local static std::ofstream debug_file(get_debug_stream_file_name(), + std::ios_base::app); + print_optreenode(frame->optreenode, debug_file, "> ", "| "); +} + OperationResult ExecutionStackFrame::execute() { assert(has_arguments_ready()); + + do_debug(this); return std::visit( [this](auto &o) { return execute_specific_operation(this, o); }, optreenode.operation); diff --git a/src/networkprotocoldsl/executionstackframe.hpp b/src/networkprotocoldsl/executionstackframe.hpp index 68cbca5..04cc352 100644 --- a/src/networkprotocoldsl/executionstackframe.hpp +++ b/src/networkprotocoldsl/executionstackframe.hpp @@ -22,7 +22,10 @@ using OperationContextVariant = * only happen when the right number of inputs has been provided. */ class ExecutionStackFrame { +public: const OpTreeNode &optreenode; + +private: std::shared_ptr> accumulator; OperationContextVariant ctx; std::shared_ptr pad; From e13b5d44fae2a03cca7ba4c230f788877122bad3 Mon Sep 17 00:00:00 2001 From: Daniel Ruoso Date: Sat, 18 Jan 2025 19:12:08 -0500 Subject: [PATCH 10/11] Add a new Dictionary type, with the appropriate operations --- CMakeLists.txt | 3 ++ src/networkprotocoldsl/operation.hpp | 6 ++- .../operation/dictionaryget.cpp | 1 + .../operation/dictionaryget.hpp | 44 +++++++++++++++ .../operation/dictionaryinitialize.cpp | 1 + .../operation/dictionaryinitialize.hpp | 28 ++++++++++ .../operation/dictionaryset.cpp | 1 + .../operation/dictionaryset.hpp | 53 +++++++++++++++++++ src/networkprotocoldsl/value.hpp | 23 ++++++-- 9 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 src/networkprotocoldsl/operation/dictionaryget.cpp create mode 100644 src/networkprotocoldsl/operation/dictionaryget.hpp create mode 100644 src/networkprotocoldsl/operation/dictionaryinitialize.cpp create mode 100644 src/networkprotocoldsl/operation/dictionaryinitialize.hpp create mode 100644 src/networkprotocoldsl/operation/dictionaryset.cpp create mode 100644 src/networkprotocoldsl/operation/dictionaryset.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d0fc34..58d593f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,9 @@ add_library( src/networkprotocoldsl/operation.hpp src/networkprotocoldsl/operation/add.cpp src/networkprotocoldsl/operation/add.hpp + src/networkprotocoldsl/operation/dictionaryget.cpp + src/networkprotocoldsl/operation/dictionaryinitialize.cpp + src/networkprotocoldsl/operation/dictionaryset.cpp src/networkprotocoldsl/operation/dynamiclist.cpp src/networkprotocoldsl/operation/dynamiclist.hpp src/networkprotocoldsl/operation/eq.cpp diff --git a/src/networkprotocoldsl/operation.hpp b/src/networkprotocoldsl/operation.hpp index 23e2f1e..70feb25 100644 --- a/src/networkprotocoldsl/operation.hpp +++ b/src/networkprotocoldsl/operation.hpp @@ -2,6 +2,9 @@ #define NETWORKPROTOCOLDSL_OPERATION_HPP #include +#include +#include +#include #include #include #include @@ -52,7 +55,8 @@ using Operation = std::variant< operation::StaticCallable, operation::Subtract, operation::TerminateListIfReadAhead, operation::UnaryCallback, operation::WriteInt32Native, operation::WriteOctets, - operation::WriteStaticOctets>; + operation::WriteStaticOctets, operation::DictionaryInitialize, + operation::DictionarySet, operation::DictionaryGet>; } // namespace networkprotocoldsl diff --git a/src/networkprotocoldsl/operation/dictionaryget.cpp b/src/networkprotocoldsl/operation/dictionaryget.cpp new file mode 100644 index 0000000..ec9ffc5 --- /dev/null +++ b/src/networkprotocoldsl/operation/dictionaryget.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/src/networkprotocoldsl/operation/dictionaryget.hpp b/src/networkprotocoldsl/operation/dictionaryget.hpp new file mode 100644 index 0000000..cc3531f --- /dev/null +++ b/src/networkprotocoldsl/operation/dictionaryget.hpp @@ -0,0 +1,44 @@ +#ifndef NETWORKPROTOCOLDSL_OPERATION_DICTIONARYGET_HPP +#define NETWORKPROTOCOLDSL_OPERATION_DICTIONARYGET_HPP + +#include +#include + +namespace networkprotocoldsl::operation { + +struct DictionaryGet { +private: + Value get(const value::Dictionary &dict) const { + auto it = dict.members->find(key); + if (it != dict.members->end()) { + return it->second; + } + return value::RuntimeError::NameError; + } + + Value get(auto &) const { return value::RuntimeError::TypeError; } + + Value get(value::RuntimeError &err) const { return err; } + +public: + std::string key; + DictionaryGet(const std::string &key) : key(key) {} + + using Arguments = std::tuple; + + Value operator()(const Arguments &args) const { + return std::visit([&](auto &&dict) -> Value { return get(dict); }, + std::get<0>(args)); + } + + std::string stringify() const { + return "DictionaryGet{key: \"" + key + "\"}"; + } +}; + +static_assert(InterpretedOperationConcept, + "DictionaryGet must conform to InterpretedOperationConcept"); + +} // namespace networkprotocoldsl::operation + +#endif // NETWORKPROTOCOLDSL_OPERATION_DICTIONARYGET_HPP \ No newline at end of file diff --git a/src/networkprotocoldsl/operation/dictionaryinitialize.cpp b/src/networkprotocoldsl/operation/dictionaryinitialize.cpp new file mode 100644 index 0000000..27a9321 --- /dev/null +++ b/src/networkprotocoldsl/operation/dictionaryinitialize.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/src/networkprotocoldsl/operation/dictionaryinitialize.hpp b/src/networkprotocoldsl/operation/dictionaryinitialize.hpp new file mode 100644 index 0000000..5795566 --- /dev/null +++ b/src/networkprotocoldsl/operation/dictionaryinitialize.hpp @@ -0,0 +1,28 @@ +#ifndef NETWORKPROTOCOLDSL_OPERATION_DICTIONARYINITIALIZE_HPP +#define NETWORKPROTOCOLDSL_OPERATION_DICTIONARYINITIALIZE_HPP + +#include +#include + +namespace networkprotocoldsl::operation { + +struct DictionaryInitialize { + DictionaryInitialize() {} + + using Arguments = std::tuple<>; + + Value operator()(const Arguments &args) const { + return value::Dictionary{ + std::make_shared>()}; + } + + std::string stringify() const { return "DictionaryInitialize{}"; } +}; + +static_assert( + InterpretedOperationConcept, + "DictionaryInitialize must conform to InterpretedOperationConcept"); + +} // namespace networkprotocoldsl::operation + +#endif // NETWORKPROTOCOLDSL_OPERATION_DICTIONARYINITIALIZE_HPP \ No newline at end of file diff --git a/src/networkprotocoldsl/operation/dictionaryset.cpp b/src/networkprotocoldsl/operation/dictionaryset.cpp new file mode 100644 index 0000000..fdbc79e --- /dev/null +++ b/src/networkprotocoldsl/operation/dictionaryset.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/src/networkprotocoldsl/operation/dictionaryset.hpp b/src/networkprotocoldsl/operation/dictionaryset.hpp new file mode 100644 index 0000000..e70be01 --- /dev/null +++ b/src/networkprotocoldsl/operation/dictionaryset.hpp @@ -0,0 +1,53 @@ +#ifndef NETWORKPROTOCOLDSL_OPERATION_DICTIONARYSET_HPP +#define NETWORKPROTOCOLDSL_OPERATION_DICTIONARYSET_HPP + +#include +#include + +namespace networkprotocoldsl::operation { + +struct DictionarySet { +private: + Value set(const value::Dictionary &dict, const auto &value) const { + auto new_dict = std::make_shared(); + for (const auto &[k, v] : *dict.members) { + if (k != key) { + new_dict->emplace(k, v); + } + } + new_dict->emplace(key, value); + return value::Dictionary(new_dict); + } + Value set(const value::Dictionary &dict, + const value::RuntimeError &err) const { + return err; + } + Value set(const value::RuntimeError &err, const auto &) const { return err; } + Value set(const auto &, const auto &) const { + return value::RuntimeError::TypeError; + } + +public: + std::string key; + Value value; + DictionarySet(const std::string &key) : key(key) {} + + using Arguments = std::tuple; + + Value operator()(const Arguments &args) const { + return std::visit( + [&](auto &&dict, auto &&value) -> Value { return set(dict, value); }, + std::get<0>(args), std::get<1>(args)); + } + + std::string stringify() const { + return "DictionarySet(key: \"" + key + "\")"; + } +}; + +static_assert(InterpretedOperationConcept, + "DictionarySet must conform to InterpretedOperationConcept"); + +} // namespace networkprotocoldsl::operation + +#endif // NETWORKPROTOCOLDSL_OPERATION_DICTIONARYSET_HPP diff --git a/src/networkprotocoldsl/value.hpp b/src/networkprotocoldsl/value.hpp index 0893661..4e0537d 100644 --- a/src/networkprotocoldsl/value.hpp +++ b/src/networkprotocoldsl/value.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -20,12 +21,13 @@ enum class ControlFlowInstruction; struct Callable; struct DynamicList; struct Octets; +struct Dictionary; } // namespace value // variant of all types possible -using Value = std::variant; +using Value = std::variant; namespace value { @@ -71,6 +73,21 @@ struct Octets { explicit Octets(std::shared_ptr d) : data(d) {} }; +struct Dictionary { + using Type = std::unordered_map; + std::shared_ptr members; + Dictionary() = default; + explicit Dictionary(Type &&m) + : members(std::make_shared(std::move(m))) {} + explicit Dictionary(std::initializer_list init) + : members(std::make_shared(init)) {} + explicit Dictionary(const Type &m) + : members(std::make_shared(m)) {} + explicit Dictionary(std::shared_ptr m) : members(m) {} + explicit Dictionary(std::shared_ptr m) + : members(std::const_pointer_cast(m)) {} +}; + } // namespace value } // namespace networkprotocoldsl From e8bd411338b810a3fff778121cfdb37112bd4a5c Mon Sep 17 00:00:00 2001 From: Daniel Ruoso Date: Sat, 18 Jan 2025 19:19:04 -0500 Subject: [PATCH 11/11] Introduce the LexicalPadAsDict operation --- CMakeLists.txt | 2 ++ src/networkprotocoldsl/lexicalpad.cpp | 5 ++++ src/networkprotocoldsl/lexicalpad.hpp | 1 + src/networkprotocoldsl/operation.hpp | 4 +++- .../operation/lexicalpadasdict.cpp | 2 ++ .../operation/lexicalpadasdict.hpp | 23 +++++++++++++++++++ 6 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/networkprotocoldsl/operation/lexicalpadasdict.cpp create mode 100644 src/networkprotocoldsl/operation/lexicalpadasdict.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 58d593f..acb6af0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,6 +113,8 @@ add_library( src/networkprotocoldsl/operation/writeoctets.hpp src/networkprotocoldsl/operation/writestaticoctets.cpp src/networkprotocoldsl/operation/writestaticoctets.hpp + src/networkprotocoldsl/operation/lexicalpadasdict.cpp + src/networkprotocoldsl/operation/lexicalpadasdict.hpp src/networkprotocoldsl/parser/grammar/identifier.hpp src/networkprotocoldsl/parser/grammar/identifier.cpp src/networkprotocoldsl/parser/grammar/literals.hpp diff --git a/src/networkprotocoldsl/lexicalpad.cpp b/src/networkprotocoldsl/lexicalpad.cpp index 3c8f36d..b9a53a1 100644 --- a/src/networkprotocoldsl/lexicalpad.cpp +++ b/src/networkprotocoldsl/lexicalpad.cpp @@ -42,4 +42,9 @@ void LexicalPad::initialize_global(const std::string &name, Value v) { } } +Value LexicalPad::as_dict() const { + return value::Dictionary{ + std::make_shared>(pad)}; +} + } // namespace networkprotocoldsl diff --git a/src/networkprotocoldsl/lexicalpad.hpp b/src/networkprotocoldsl/lexicalpad.hpp index cdc3243..7c8b89b 100644 --- a/src/networkprotocoldsl/lexicalpad.hpp +++ b/src/networkprotocoldsl/lexicalpad.hpp @@ -22,6 +22,7 @@ class LexicalPad { Value set(const std::string &name, Value v); void initialize(const std::string &name, Value v); void initialize_global(const std::string &name, Value v); + Value as_dict() const; }; } // namespace networkprotocoldsl diff --git a/src/networkprotocoldsl/operation.hpp b/src/networkprotocoldsl/operation.hpp index 70feb25..bee13c5 100644 --- a/src/networkprotocoldsl/operation.hpp +++ b/src/networkprotocoldsl/operation.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -56,7 +57,8 @@ using Operation = std::variant< operation::TerminateListIfReadAhead, operation::UnaryCallback, operation::WriteInt32Native, operation::WriteOctets, operation::WriteStaticOctets, operation::DictionaryInitialize, - operation::DictionarySet, operation::DictionaryGet>; + operation::DictionarySet, operation::DictionaryGet, + operation::LexicalPadAsDict>; } // namespace networkprotocoldsl diff --git a/src/networkprotocoldsl/operation/lexicalpadasdict.cpp b/src/networkprotocoldsl/operation/lexicalpadasdict.cpp new file mode 100644 index 0000000..79d9eb0 --- /dev/null +++ b/src/networkprotocoldsl/operation/lexicalpadasdict.cpp @@ -0,0 +1,2 @@ + +#include \ No newline at end of file diff --git a/src/networkprotocoldsl/operation/lexicalpadasdict.hpp b/src/networkprotocoldsl/operation/lexicalpadasdict.hpp new file mode 100644 index 0000000..e4f69c4 --- /dev/null +++ b/src/networkprotocoldsl/operation/lexicalpadasdict.hpp @@ -0,0 +1,23 @@ +#ifndef INCLUDED_NETWORKPROTOCOLDSL_OPERATION_LEXICALPADASDICT_HPP +#define INCLUDED_NETWORKPROTOCOLDSL_OPERATION_LEXICALPADASDICT_HPP + +#include +#include + +#include + +namespace networkprotocoldsl::operation { + +class LexicalPadAsDict { +public: + using Arguments = std::tuple<>; + Value operator()(Arguments args, std::shared_ptr pad) const { + return pad->as_dict(); + } + std::string stringify() const { return "LexicalPadAsDict{}"; } +}; +static_assert(LexicalPadOperationConcept); + +} // namespace networkprotocoldsl::operation + +#endif \ No newline at end of file