Skip to content

Commit 1f8742b

Browse files
committed
Introduce the StateMachineOPeration that handles high-level states
1 parent 3c19cb1 commit 1f8742b

File tree

7 files changed

+489
-1
lines changed

7 files changed

+489
-1
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ add_library(
101101
src/networkprotocoldsl/operation/readstaticoctets.hpp
102102
src/networkprotocoldsl/operation/staticcallable.cpp
103103
src/networkprotocoldsl/operation/staticcallable.hpp
104+
src/networkprotocoldsl/operation/statemachineoperation.cpp
105+
src/networkprotocoldsl/operation/statemachineoperation.hpp
104106
src/networkprotocoldsl/operation/subtract.cpp
105107
src/networkprotocoldsl/operation/subtract.hpp
106108
src/networkprotocoldsl/operation/terminatelistifreadahead.cpp

src/networkprotocoldsl/operation.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <networkprotocoldsl/operation/readintfromascii.hpp>
2626
#include <networkprotocoldsl/operation/readoctetsuntilterminator.hpp>
2727
#include <networkprotocoldsl/operation/readstaticoctets.hpp>
28+
#include <networkprotocoldsl/operation/statemachineoperation.hpp>
2829
#include <networkprotocoldsl/operation/staticcallable.hpp>
2930
#include <networkprotocoldsl/operation/subtract.hpp>
3031
#include <networkprotocoldsl/operation/terminatelistifreadahead.hpp>
@@ -59,7 +60,8 @@ using Operation = std::variant<
5960
operation::WriteInt32Native, operation::WriteOctets,
6061
operation::WriteStaticOctets, operation::DictionaryInitialize,
6162
operation::DictionarySet, operation::DictionaryGet,
62-
operation::LexicalPadAsDict, operation::TransitionLookahead>;
63+
operation::LexicalPadAsDict, operation::TransitionLookahead,
64+
operation::StateMachineOperation>;
6365

6466
} // namespace networkprotocoldsl
6567

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
#include <networkprotocoldsl/operation/statemachineoperation.hpp>
2+
3+
#include <networkprotocoldsl/operation/staticcallable.hpp>
4+
#include <networkprotocoldsl/operationconcepts.hpp>
5+
#include <networkprotocoldsl/print_optreenode.hpp>
6+
#include <networkprotocoldsl/value.hpp>
7+
8+
#include <ostream>
9+
#include <thread>
10+
11+
#include <cassert>
12+
13+
//#define DEBUG(x)
14+
#define DEBUG(x) \
15+
std::cerr << std::this_thread::get_id() << ": " << x << std::endl;
16+
17+
namespace {
18+
19+
using namespace networkprotocoldsl;
20+
using namespace networkprotocoldsl::operation;
21+
22+
enum class ProcessingMode {
23+
Transition,
24+
State,
25+
};
26+
27+
using StatePair = std::pair<std::string, StateMachineOperation::StateInfo>;
28+
using TransitionPair =
29+
std::pair<std::string, StateMachineOperation::TransitionInfo>;
30+
31+
struct ProcessingInfo {
32+
ProcessingMode processing_mode;
33+
std::optional<StatePair> from_state;
34+
std::optional<TransitionPair> transition;
35+
StatePair to_state;
36+
};
37+
38+
} // namespace
39+
40+
namespace networkprotocoldsl::operation {
41+
42+
static std::optional<std::shared_ptr<std::vector<Value>>>
43+
_extract_arguments(const std::vector<std::string> &names,
44+
const value::Dictionary &d) {
45+
DEBUG("Entering _extract_arguments");
46+
auto args = std::make_shared<std::vector<Value>>();
47+
for (const auto &arg_name : names) {
48+
auto it = d.members->find(arg_name);
49+
if (it == d.members->end()) {
50+
return std::nullopt;
51+
} else {
52+
args->push_back(it->second);
53+
}
54+
}
55+
return args;
56+
}
57+
58+
static OperationResult
59+
_start_state_callback(ControlFlowOperationContext &ctx,
60+
const StateMachineOperation::StateMap &states) {
61+
DEBUG("Entering _start_state_callback");
62+
ProcessingInfo &info = std::any_cast<ProcessingInfo &>(ctx.additional_info);
63+
auto &s = info.to_state.second;
64+
ctx.callable = value::Callable{s.callback_optree, {"dictionary"}, true};
65+
ctx.value = std::nullopt;
66+
info.processing_mode = ProcessingMode::State;
67+
return ReasonForBlockedOperation::WaitingForCallableInvocation;
68+
}
69+
70+
static OperationResult
71+
_start_transition_callback(ControlFlowOperationContext &ctx,
72+
const StateMachineOperation::StateMap &states) {
73+
DEBUG("Entering _start_transition_callback");
74+
ProcessingInfo &info = std::any_cast<ProcessingInfo &>(ctx.additional_info);
75+
assert(info.transition.has_value());
76+
auto &t = info.transition.value().second;
77+
ctx.callable = value::Callable{t.callback_optree, t.argument_names, true};
78+
ctx.value = std::nullopt;
79+
info.processing_mode = ProcessingMode::Transition;
80+
return ReasonForBlockedOperation::WaitingForCallableInvocation;
81+
}
82+
83+
static OperationResult
84+
_process_state_return_with_args(ControlFlowOperationContext &ctx,
85+
const StateMachineOperation::StateMap &states,
86+
const value::Octets t,
87+
const value::Dictionary d) {
88+
DEBUG("Entering _process_state_return_with_args (Octets, Dictionary)");
89+
ProcessingInfo &info = std::any_cast<ProcessingInfo &>(ctx.additional_info);
90+
if (info.to_state.first == "Closed") {
91+
return d;
92+
}
93+
auto &s = info.to_state.second;
94+
info.from_state = info.to_state;
95+
auto it = s.transitions.find(*t.data);
96+
if (it == s.transitions.end()) {
97+
return value::RuntimeError::NameError;
98+
}
99+
auto target_state_name = it->second.target_state;
100+
auto it2 = states.find(target_state_name);
101+
if (it2 == states.end()) {
102+
return value::RuntimeError::NameError;
103+
}
104+
auto maybe_args = _extract_arguments(it->second.argument_names, d);
105+
if (!maybe_args.has_value()) {
106+
return value::RuntimeError::TypeError;
107+
}
108+
ctx.accumulator = maybe_args.value();
109+
info.transition = *it;
110+
info.to_state = *it2;
111+
return _start_transition_callback(ctx, states);
112+
}
113+
114+
static OperationResult
115+
_process_state_return_with_args(ControlFlowOperationContext &ctx,
116+
const StateMachineOperation::StateMap &states,
117+
const auto &, const auto &) {
118+
DEBUG("Entering _process_state_return_with_args (auto, auto)");
119+
return value::RuntimeError::TypeError;
120+
}
121+
122+
static OperationResult
123+
_process_state_return_dynlist(ControlFlowOperationContext &ctx,
124+
const StateMachineOperation::StateMap &states,
125+
const value::DynamicList v) {
126+
DEBUG("Entering _process_state_return_dynlist (DynamicList)");
127+
if (v.values->size() != 2) {
128+
return value::RuntimeError::TypeError;
129+
}
130+
return std::visit(
131+
[&](auto &t, auto &d) {
132+
return _process_state_return_with_args(ctx, states, t, d);
133+
},
134+
v.values->at(0), v.values->at(1));
135+
}
136+
137+
static OperationResult
138+
_process_state_return_dynlist(ControlFlowOperationContext &ctx,
139+
const StateMachineOperation::StateMap &states,
140+
const value::RuntimeError err) {
141+
DEBUG("Entering _process_state_return_dynlist (RuntimeError)");
142+
return err;
143+
}
144+
145+
static OperationResult
146+
_process_state_return_dynlist(ControlFlowOperationContext &ctx,
147+
const StateMachineOperation::StateMap &states,
148+
const auto &) {
149+
DEBUG("Entering _process_state_return_dynlist (auto)");
150+
return value::RuntimeError::TypeError;
151+
}
152+
153+
static OperationResult
154+
_process_state_return(ControlFlowOperationContext &ctx,
155+
const StateMachineOperation::StateMap &states) {
156+
DEBUG("Entering _process_state_return");
157+
assert(ctx.value.has_value());
158+
return std::visit(
159+
[&](auto &v) { return _process_state_return_dynlist(ctx, states, v); },
160+
ctx.value.value());
161+
}
162+
163+
static OperationResult _process_transition_return_with_args(
164+
ControlFlowOperationContext &ctx,
165+
const StateMachineOperation::StateMap &states, const value::Dictionary &d) {
166+
DEBUG("Entering _process_transition_return_with_args (Dictionary)");
167+
ctx.accumulator = std::make_shared<std::vector<Value>>(std::vector<Value>{d});
168+
return _start_state_callback(ctx, states);
169+
}
170+
171+
static OperationResult _process_transition_return_with_args(
172+
ControlFlowOperationContext &ctx,
173+
const StateMachineOperation::StateMap &states,
174+
const value::RuntimeError &err) {
175+
DEBUG("Entering _process_transition_return_with_args (RuntimeError)");
176+
return err;
177+
}
178+
179+
static OperationResult _process_transition_return_with_args(
180+
ControlFlowOperationContext &ctx,
181+
const StateMachineOperation::StateMap &states, const auto &) {
182+
DEBUG("Entering _process_transition_return_with_args (auto)");
183+
return value::RuntimeError::TypeError;
184+
}
185+
186+
static OperationResult _process_transition_return_dynlist(
187+
ControlFlowOperationContext &ctx,
188+
const StateMachineOperation::StateMap &states,
189+
const value::DynamicList &l) {
190+
DEBUG("Entering _process_transition_return_dynlist (DynamicList)");
191+
if (l.values->size() != 1) {
192+
return value::RuntimeError::TypeError;
193+
}
194+
return std::visit(
195+
[&](auto &v) {
196+
return _process_transition_return_with_args(ctx, states, v);
197+
},
198+
l.values->at(0));
199+
}
200+
201+
static OperationResult _process_transition_return_dynlist(
202+
ControlFlowOperationContext &ctx,
203+
const StateMachineOperation::StateMap &states,
204+
const value::RuntimeError err) {
205+
DEBUG("Entering _process_transition_return_dynlist (RuntimeError)");
206+
return err;
207+
}
208+
209+
static OperationResult _process_transition_return_dynlist(
210+
ControlFlowOperationContext &ctx,
211+
const StateMachineOperation::StateMap &states, const auto &) {
212+
DEBUG("Entering _process_transition_return_dynlist (auto)");
213+
return value::RuntimeError::TypeError;
214+
}
215+
216+
static OperationResult
217+
_process_transition_return(ControlFlowOperationContext &ctx,
218+
const StateMachineOperation::StateMap &states) {
219+
DEBUG("Entering _process_transition_return");
220+
assert(ctx.value.has_value());
221+
return std::visit(
222+
[&](auto &v) {
223+
return _process_transition_return_dynlist(ctx, states, v);
224+
},
225+
ctx.value.value());
226+
}
227+
228+
OperationResult
229+
StateMachineOperation::operator()(ControlFlowOperationContext &ctx,
230+
Arguments a) const {
231+
DEBUG("Entering StateMachineOperation::operator()");
232+
if (ctx.callable.has_value()) {
233+
if (ctx.callable_invoked) {
234+
if (ctx.value.has_value()) {
235+
ProcessingInfo &info =
236+
std::any_cast<ProcessingInfo &>(ctx.additional_info);
237+
if (info.processing_mode == ProcessingMode::Transition) {
238+
if (!info.transition.has_value()) {
239+
return value::RuntimeError::NameError;
240+
}
241+
return _process_transition_return(ctx, states);
242+
} else {
243+
return _process_state_return(ctx, states);
244+
}
245+
} else {
246+
return ReasonForBlockedOperation::WaitingForCallableResult;
247+
}
248+
} else {
249+
return ReasonForBlockedOperation::WaitingForCallableInvocation;
250+
}
251+
} else {
252+
auto it = states.find("Open");
253+
if (it == states.end()) {
254+
return value::RuntimeError::NameError;
255+
}
256+
ctx.accumulator = std::make_shared<std::vector<Value>>(
257+
std::vector<Value>{value::Dictionary()});
258+
ctx.additional_info = ProcessingInfo{ProcessingMode::Transition,
259+
std::nullopt, std::nullopt, *it};
260+
return _start_state_callback(ctx, states);
261+
}
262+
}
263+
264+
Value StateMachineOperation::get_callable(
265+
ControlFlowOperationContext &ctx) const {
266+
DEBUG("Entering StateMachineOperation::get_callable");
267+
assert(ctx.callable.has_value());
268+
return ctx.callable.value();
269+
}
270+
271+
std::shared_ptr<const std::vector<Value>>
272+
StateMachineOperation::get_argument_list(
273+
ControlFlowOperationContext &ctx) const {
274+
DEBUG("Entering StateMachineOperation::get_argument_list");
275+
return ctx.accumulator;
276+
}
277+
278+
void StateMachineOperation::set_callable_invoked(
279+
ControlFlowOperationContext &ctx) const {
280+
DEBUG("Entering StateMachineOperation::set_callable_invoked");
281+
ctx.callable_invoked = true;
282+
}
283+
284+
void StateMachineOperation::set_callable_return(
285+
ControlFlowOperationContext &ctx, Value v) const {
286+
DEBUG("Entering StateMachineOperation::set_callable_return");
287+
ctx.value = v;
288+
}
289+
290+
std::string StateMachineOperation::stringify() const {
291+
DEBUG("Entering StateMachineOperation::stringify");
292+
std::ostringstream os;
293+
os << "StateMachineOperation{\n";
294+
for (const auto &[state_name, state_info] : states) {
295+
os << " State: " << state_name << "\n";
296+
os << " Callback Optree:\n";
297+
if (state_info.callback_optree) {
298+
print_optreenode(state_info.callback_optree->root, os, "",
299+
"");
300+
} else {
301+
os << " <None>\n";
302+
}
303+
os << " Transitions:\n";
304+
for (const auto &[transition_name, transition_info] :
305+
state_info.transitions) {
306+
os << " Transition: " << transition_name << " -> "
307+
<< transition_info.target_state << "\n";
308+
os << " Argument Names: [";
309+
for (size_t i = 0; i < transition_info.argument_names.size(); ++i) {
310+
os << "\"" << transition_info.argument_names[i] << "\"";
311+
if (i < transition_info.argument_names.size() - 1) {
312+
os << ", ";
313+
}
314+
}
315+
os << "]\n";
316+
os << " Callback Optree:\n";
317+
if (transition_info.callback_optree) {
318+
print_optreenode(transition_info.callback_optree->root, os,
319+
"", "");
320+
} else {
321+
os << " <None>\n";
322+
}
323+
}
324+
}
325+
os << "}";
326+
return os.str();
327+
}
328+
329+
} // namespace networkprotocoldsl::operation

0 commit comments

Comments
 (0)