MTMS main
Modern Turing Machine Simulator
Loading...
Searching...
No Matches
project.cpp
Go to the documentation of this file.
1
11#include "project.hpp"
12
13#include <fstream>
14#include <print>
15#include <set>
16#include <vector>
17
18#define TOML_HEADER_ONLY 1
19
20bool Project::parse_metadata(const toml::table &config) noexcept
21{
22 this->name_ = config["metadata"]["name"].value_or("Untitled TM");
23 this->description_ = config["metadata"]["description"].value_or("");
24 return true;
25}
26
28 const toml::table &config,
29 std::set<State> &parsed_states,
30 State &initial_state,
31 std::unordered_map<std::string, State> &state_map
32) noexcept(false)
33{
34 std::string init_state_name = config["machine"]["initial_state"].value_or("");
35
36 if (auto states_array = config["states"].as_array())
37 {
38 for (auto &&node : *states_array)
39 {
40 auto &&table = *node.as_table();
41 std::string name = table["name"].value_or("");
42 bool is_accept = table["is_accept"].value_or(false);
43
44 State s(name, is_accept);
45 if (state_map.contains(name))
46 {
47 std::println(stderr, "[!] Duplicate state '{}'.", name);
48 return false;
49 }
50 parsed_states.insert(s);
51 state_map.emplace(name, s);
52
53 if (name == init_state_name)
54 {
55 initial_state = s;
56 }
57 }
58 }
59 return true;
60}
61
63 const toml::table &config,
64 Alphabet &input_alpha,
65 Alphabet &tape_alpha
66) noexcept(false)
67{
68 std::set<Symbol> input_symbols;
69 if (auto input_arr = config["machine"]["input_alphabet"].as_array())
70 {
71 for (auto &&node : *input_arr)
72 {
73 std::string sym_str = node.value_or("");
74 if (!sym_str.empty())
75 {
76 Symbol sym(sym_str[0]);
77 if (sym.get_char() == kBlank)
78 {
79 std::println(
80 stderr,
81 "[!] Input alphabet (Sigma) cannot contain the blank symbol."
82 );
83 return false;
84 }
85 input_symbols.insert(sym);
86 }
87 }
88 }
89
90 std::set<Symbol> tape_symbols;
91 if (auto tape_arr = config["machine"]["tape_alphabet"].as_array())
92 {
93 for (auto &&node : *tape_arr)
94 {
95 std::string sym_str = node.value_or(" ");
96 Symbol sym(sym_str.empty() ? kBlank : sym_str[0]);
97 tape_symbols.insert(sym);
98 }
99 }
100 tape_symbols.insert(Symbol(kBlank));
101
102 input_alpha = Alphabet(input_symbols);
103 tape_alpha = Alphabet(tape_symbols);
104 return true;
105}
106
108 const toml::table &config,
109 std::size_t tape_count,
110 const std::unordered_map<std::string, State> &state_map,
111 const std::shared_ptr<TuringMachine> &machine,
112 std::unordered_map<std::string, std::vector<std::string>> &graph
113) noexcept(false)
114{
115 if (auto trans_array = config["transitions"].as_array())
116 {
117 std::set<std::pair<std::string, std::string>> seen;
118 for (auto &&node : *trans_array)
119 {
120 auto &&table = *node.as_table();
121
122 std::string curr_name = table["current_state"].value_or("");
123 std::string next_name = table["next_state"].value_or("");
124
125 auto curr_it = state_map.find(curr_name);
126 auto next_it = state_map.find(next_name);
127 if (curr_it == state_map.end() || next_it == state_map.end())
128 {
129 std::println(stderr, "[!] Transition references an undefined state.");
130 return false;
131 }
132
133 std::vector<Symbol> read_vec;
134 std::vector<Symbol> write_vec;
135 std::vector<Direction> dir_vec;
136 if (auto read_arr = table["read"].as_array())
137 {
138 for (auto &&s_node : *read_arr)
139 {
140 std::string s_str = s_node.value_or(" ");
141 read_vec.emplace_back(s_str.empty() ? kBlank : s_str[0]);
142 }
143 }
144 if (auto write_arr = table["write"].as_array())
145 {
146 for (auto &&s_node : *write_arr)
147 {
148 std::string s_str = s_node.value_or(" ");
149 write_vec.emplace_back(s_str.empty() ? kBlank : s_str[0]);
150 }
151 }
152 if (auto dir_arr = table["direction"].as_array())
153 {
154 for (auto &&d_node : *dir_arr)
155 {
156 std::string d_str = d_node.value_or("STAY");
157 dir_vec.push_back(
158 (d_str == "LEFT") ? Direction::LEFT
159 : (d_str == "RIGHT") ? Direction::RIGHT
161 );
162 }
163 }
164
165 if (read_vec.size() != tape_count || write_vec.size() != tape_count ||
166 dir_vec.size() != tape_count)
167 {
168 std::println(
169 stderr,
170 "[!] Invalid transition in state '{}'. Tape dimension mismatch.",
171 curr_name
172 );
173 return false;
174 }
175
176 std::string key_state = curr_name + "|";
177 std::string key_read;
178
179 for (const auto &s : read_vec)
180 {
181 key_read += s.get_char();
182 }
183
184 if (!seen.emplace(key_state, key_read).second)
185 {
186 std::println(stderr, "[!] Non-deterministic TM detected at state '{}'.", curr_name);
187 return false;
188 }
189
190 machine->add_transition(
191 curr_it->second,
192 next_it->second,
193 std::move(read_vec),
194 std::move(write_vec),
195 std::move(dir_vec)
196 );
197 graph[curr_name].push_back(next_name);
198 }
199 }
200 return true;
201}
202
203std::unordered_set<std::string> Project::compute_reachability(
204 const std::string &start_state,
205 const std::unordered_map<std::string, std::vector<std::string>> &graph
206) noexcept(false)
207{
208 std::unordered_set<std::string> reachable;
209 std::vector<std::string> stack;
210
211 stack.push_back(start_state);
212 reachable.insert(start_state);
213
214 while (!stack.empty())
215 {
216 std::string cur = stack.back();
217 stack.pop_back();
218
219 auto it = graph.find(cur);
220 if (it != graph.end())
221 {
222 for (const auto &n : it->second)
223 {
224 if (!reachable.contains(n))
225 {
226 reachable.insert(n);
227 stack.push_back(n);
228 }
229 }
230 }
231 }
232 return reachable;
233}
234
236 const std::set<State> &parsed_states,
237 const std::unordered_set<std::string> &reachable,
238 const std::unordered_map<std::string, std::vector<std::string>> &graph
239) noexcept(false)
240{
241 for (const auto &state : parsed_states)
242 {
243 std::string state_name(state.get_label());
244 if (!reachable.contains(state_name))
245 {
246 std::println(stderr, "[-] State '{}' is unreachable from initial state.", state_name);
247 }
248
249 auto it = graph.find(state_name);
250 bool has_any_transition = (it != graph.end() && !it->second.empty());
251
252 if (!has_any_transition && !state.is_accept())
253 {
254 std::println(
255 stderr,
256 "[!] Incomplete TM. Non-accepting state '{}' has no outgoing transitions.",
257 state_name
258 );
259 return false;
260 }
261 }
262 return true;
263}
264
265bool Project::load_project(const std::filesystem::path &filepath) noexcept(false)
266{
267 try
268 {
269 auto config = toml::parse_file(filepath.string());
270 if (!this->parse_metadata(config))
271 {
272 return false;
273 }
274
275 // Get Machine Specs
276 std::size_t tape_count = config["machine"]["tape_count"].value_or(1);
277 std::string init_state_name = config["machine"]["initial_state"].value_or("");
278
279 std::set<State> parsed_states;
280 State initial_state;
281 std::unordered_map<std::string, State> state_map;
282 if (!parse_states(config, parsed_states, initial_state, state_map))
283 {
284 return false;
285 }
286
287 Alphabet input_alpha, tape_alpha;
288 if (!parse_alphabets(config, input_alpha, tape_alpha))
289 {
290 return false;
291 }
292
293 this->machine_ = std::make_shared<TuringMachine>(
294 input_alpha,
295 tape_alpha,
296 parsed_states,
297 initial_state,
298 tape_count
299 );
300
301 std::unordered_map<std::string, std::vector<std::string>> graph;
302 if (!parse_transitions(config, tape_count, state_map, this->machine_, graph))
303 {
304 return false;
305 }
306
307 // Mathematical Inclusion Validation (Σ ⊂ Γ)
308 for (const auto &sym : input_alpha)
309 {
310 if (!tape_alpha.contains(sym))
311 {
312 std::println(
313 stderr,
314 "[!] Input alphabet symbol '{}' is missing from the tape alphabet (Gamma).",
315 sym.get_char()
316 );
317 return false;
318 }
319 }
320
321 auto reachable = compute_reachability(init_state_name, graph);
322 return validate_machine_structure(parsed_states, reachable, graph);
323 }
324 catch (const toml::parse_error &err)
325 {
326 std::println(stderr, "[!] TOML parsing failed: {}", err.description());
327 return false;
328 }
329}
330
331bool Project::save_project(const std::filesystem::path &filepath) const noexcept(false)
332{
333 if (!this->has_active_machine())
334 {
335 return false;
336 }
337
338 try
339 {
340 // Build Metadata block
341 toml::table root;
342 root.insert_or_assign(
343 "metadata",
344 toml::table{{"name", this->name_}, {"description", this->description_}}
345 );
346
347 // Build Machine block mapping configuration markers
348 auto machine = this->machine_;
349 toml::array input_alpha_arr;
350 for (const auto &sym : machine->get_input_alphabet().get_set())
351 {
352 input_alpha_arr.push_back(std::string(1, sym.get_char()));
353 }
354
355 toml::array tape_alpha_arr;
356 for (const auto &sym : machine->get_tape_alphabet().get_set())
357 {
358 tape_alpha_arr.push_back(std::string(1, sym.get_char()));
359 }
360
361 root.insert_or_assign(
362 "machine",
363 toml::table{
364 {"tape_count", static_cast<int64_t>(machine->get_tape_count())},
365 {"initial_state", machine->get_start_state().get_label()},
366 {"input_alphabet", input_alpha_arr},
367 {"tape_alphabet", tape_alpha_arr}
368 }
369 );
370
371 // Serialize States array
372 toml::array states_arr;
373 for (const auto &state : machine->get_states())
374 {
375 states_arr.push_back(
376 toml::table{{"name", state.get_label()}, {"is_accept", state.is_accept()}}
377 );
378 }
379 root.insert_or_assign("states", states_arr);
380
381 // Serialize Transitions array matrix
382 toml::array trans_arr;
383 for (const auto &[id, trans] : machine->get_transitions())
384 {
385 toml::array read_sub_arr;
386 for (const auto &sym : trans.get_read_symbols())
387 {
388 read_sub_arr.push_back(std::string(1, sym.get_char()));
389 }
390
391 toml::array write_sub_arr;
392 for (const auto &sym : trans.get_write_symbols())
393 {
394 write_sub_arr.push_back(std::string(1, sym.get_char()));
395 }
396
397 toml::array dir_sub_arr;
398 for (const auto &dir : trans.get_directions())
399 {
400 std::string d_str = (dir == Direction::LEFT) ? "LEFT"
401 : (dir == Direction::RIGHT) ? "RIGHT"
402 : "STAY";
403 dir_sub_arr.push_back(d_str);
404 }
405
406 trans_arr.push_back(
407 toml::table{
408 {"current_state", trans.get_current_state().get_label()},
409 {"next_state", trans.get_next_state().get_label()},
410 {"read", read_sub_arr},
411 {"write", write_sub_arr},
412 {"direction", dir_sub_arr}
413 }
414 );
415 }
416 root.insert_or_assign("transitions", trans_arr);
417
418 // Open stream pipeline and dump the structured tables
419 std::ofstream file(filepath, std::ios::out | std::ios::trunc);
420 if (!file.is_open())
421 {
422 return false;
423 }
424
425 file << root << "\n";
426 return true;
427 }
428 catch (const std::exception &e)
429 {
430 std::println(stderr, "[!] TOML saving failed: {}", e.what());
431 return false;
432 }
433}
Represents the formal alphabet ( or ) of a Turing Machine.
Definition alphabet.hpp:27
bool contains(const Symbol &symbol) const noexcept
Checks if a specific Symbol belongs to this alphabet.
Definition alphabet.hpp:65
static bool validate_machine_structure(const std::set< State > &parsed_states, const std::unordered_set< std::string > &reachable, const std::unordered_map< std::string, std::vector< std::string > > &graph) noexcept(false)
Verifies structural integrity rules against isolated or broken nodes.
Definition project.cpp:235
static bool parse_transitions(const toml::table &config, std::size_t tape_count, const std::unordered_map< std::string, State > &state_map, const std::shared_ptr< TuringMachine > &machine, std::unordered_map< std::string, std::vector< std::string > > &graph) noexcept(false)
Decodes the matrix table containing legal execution step pathways.
Definition project.cpp:107
bool parse_metadata(const toml::table &config) noexcept
Extracts project identification block from TOML file.
Definition project.cpp:20
static bool parse_alphabets(const toml::table &config, Alphabet &input_alpha, Alphabet &tape_alpha) noexcept(false)
Validates and builds formal inclusion alphabets (Sigma and Gamma matrices).
Definition project.cpp:62
bool load_project(const std::filesystem::path &filepath) noexcept(false)
Parses a TM configuration file using toml++ and instantiates the TuringMachine.
Definition project.cpp:265
static std::unordered_set< std::string > compute_reachability(const std::string &start_state, const std::unordered_map< std::string, std::vector< std::string > > &graph) noexcept(false)
Computes reaching pathways from the start state over the adjacency graph via DFS.
Definition project.cpp:203
static bool parse_states(const toml::table &config, std::set< State > &parsed_states, State &initial_state, std::unordered_map< std::string, State > &state_map) noexcept(false)
Parses and instantiates structural computational states from TOML configuration array.
Definition project.cpp:27
bool save_project(const std::filesystem::path &filepath) const noexcept(false)
Exports the current machine configuration snapshot back to a TOML file.
Definition project.cpp:331
Represents a single state in the finite set of states (Q) of the Turing Machine.
Definition state.hpp:26
Represents a single symbol on the Turing Machine's tape.
Definition symbol.hpp:30
char get_char() const noexcept
Getter for the underlying raw character.
Definition symbol.hpp:47
@ RIGHT
Move the tape head one cell to the right.
@ LEFT
Move the tape head one cell to the left.
@ STAY
Keep the tape head on the current cell.
Session manager controlling serialization pipelines via toml++.
constexpr char kBlank
Representation of the default blank symbol used to fill the Turing Machine's tape.
Definition symbol.hpp:20