18#define TOML_HEADER_ONLY 1
22 this->name_ = config[
"metadata"][
"name"].value_or(
"Untitled TM");
23 this->description_ = config[
"metadata"][
"description"].value_or(
"");
28 const toml::table &config,
29 std::set<State> &parsed_states,
31 std::unordered_map<std::string, State> &state_map
34 std::string init_state_name = config[
"machine"][
"initial_state"].value_or(
"");
36 if (
auto states_array = config[
"states"].as_array())
38 for (
auto &&node : *states_array)
40 auto &&table = *node.as_table();
41 std::string name = table[
"name"].value_or(
"");
42 bool is_accept = table[
"is_accept"].value_or(
false);
44 State s(name, is_accept);
45 if (state_map.contains(name))
47 std::println(stderr,
"[!] Duplicate state '{}'.", name);
50 parsed_states.insert(s);
51 state_map.emplace(name, s);
53 if (name == init_state_name)
63 const toml::table &config,
68 std::set<Symbol> input_symbols;
69 if (
auto input_arr = config[
"machine"][
"input_alphabet"].as_array())
71 for (
auto &&node : *input_arr)
73 std::string sym_str = node.value_or(
"");
81 "[!] Input alphabet (Sigma) cannot contain the blank symbol."
85 input_symbols.insert(sym);
90 std::set<Symbol> tape_symbols;
91 if (
auto tape_arr = config[
"machine"][
"tape_alphabet"].as_array())
93 for (
auto &&node : *tape_arr)
95 std::string sym_str = node.value_or(
" ");
97 tape_symbols.insert(sym);
102 input_alpha =
Alphabet(input_symbols);
103 tape_alpha =
Alphabet(tape_symbols);
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
115 if (
auto trans_array = config[
"transitions"].as_array())
117 std::set<std::pair<std::string, std::string>> seen;
118 for (
auto &&node : *trans_array)
120 auto &&table = *node.as_table();
122 std::string curr_name = table[
"current_state"].value_or(
"");
123 std::string next_name = table[
"next_state"].value_or(
"");
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())
129 std::println(stderr,
"[!] Transition references an undefined state.");
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())
138 for (
auto &&s_node : *read_arr)
140 std::string s_str = s_node.value_or(
" ");
141 read_vec.emplace_back(s_str.empty() ?
kBlank : s_str[0]);
144 if (
auto write_arr = table[
"write"].as_array())
146 for (
auto &&s_node : *write_arr)
148 std::string s_str = s_node.value_or(
" ");
149 write_vec.emplace_back(s_str.empty() ?
kBlank : s_str[0]);
152 if (
auto dir_arr = table[
"direction"].as_array())
154 for (
auto &&d_node : *dir_arr)
156 std::string d_str = d_node.value_or(
"STAY");
165 if (read_vec.size() != tape_count || write_vec.size() != tape_count ||
166 dir_vec.size() != tape_count)
170 "[!] Invalid transition in state '{}'. Tape dimension mismatch.",
176 std::string key_state = curr_name +
"|";
177 std::string key_read;
179 for (
const auto &s : read_vec)
181 key_read += s.get_char();
184 if (!seen.emplace(key_state, key_read).second)
186 std::println(stderr,
"[!] Non-deterministic TM detected at state '{}'.", curr_name);
190 machine->add_transition(
194 std::move(write_vec),
197 graph[curr_name].push_back(next_name);
204 const std::string &start_state,
205 const std::unordered_map<std::string, std::vector<std::string>> &graph
208 std::unordered_set<std::string> reachable;
209 std::vector<std::string> stack;
211 stack.push_back(start_state);
212 reachable.insert(start_state);
214 while (!stack.empty())
216 std::string cur = stack.back();
219 auto it = graph.find(cur);
220 if (it != graph.end())
222 for (
const auto &n : it->second)
224 if (!reachable.contains(n))
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
241 for (
const auto &state : parsed_states)
243 std::string state_name(state.get_label());
244 if (!reachable.contains(state_name))
246 std::println(stderr,
"[-] State '{}' is unreachable from initial state.", state_name);
249 auto it = graph.find(state_name);
250 bool has_any_transition = (it != graph.end() && !it->second.empty());
252 if (!has_any_transition && !state.is_accept())
256 "[!] Incomplete TM. Non-accepting state '{}' has no outgoing transitions.",
269 auto config = toml::parse_file(filepath.string());
270 if (!this->parse_metadata(config))
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(
"");
279 std::set<State> parsed_states;
281 std::unordered_map<std::string, State> state_map;
282 if (!parse_states(config, parsed_states, initial_state, state_map))
288 if (!parse_alphabets(config, input_alpha, tape_alpha))
293 this->machine_ = std::make_shared<TuringMachine>(
301 std::unordered_map<std::string, std::vector<std::string>> graph;
302 if (!parse_transitions(config, tape_count, state_map, this->machine_, graph))
308 for (
const auto &sym : input_alpha)
314 "[!] Input alphabet symbol '{}' is missing from the tape alphabet (Gamma).",
321 auto reachable = compute_reachability(init_state_name, graph);
322 return validate_machine_structure(parsed_states, reachable, graph);
324 catch (
const toml::parse_error &err)
326 std::println(stderr,
"[!] TOML parsing failed: {}", err.description());
333 if (!this->has_active_machine())
342 root.insert_or_assign(
344 toml::table{{
"name", this->name_}, {
"description", this->description_}}
348 auto machine = this->machine_;
349 toml::array input_alpha_arr;
350 for (
const auto &sym : machine->get_input_alphabet().get_set())
352 input_alpha_arr.push_back(std::string(1, sym.get_char()));
355 toml::array tape_alpha_arr;
356 for (
const auto &sym : machine->get_tape_alphabet().get_set())
358 tape_alpha_arr.push_back(std::string(1, sym.get_char()));
361 root.insert_or_assign(
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}
372 toml::array states_arr;
373 for (
const auto &state : machine->get_states())
375 states_arr.push_back(
376 toml::table{{
"name", state.get_label()}, {
"is_accept", state.is_accept()}}
379 root.insert_or_assign(
"states", states_arr);
382 toml::array trans_arr;
383 for (
const auto &[
id, trans] : machine->get_transitions())
385 toml::array read_sub_arr;
386 for (
const auto &sym : trans.get_read_symbols())
388 read_sub_arr.push_back(std::string(1, sym.get_char()));
391 toml::array write_sub_arr;
392 for (
const auto &sym : trans.get_write_symbols())
394 write_sub_arr.push_back(std::string(1, sym.get_char()));
397 toml::array dir_sub_arr;
398 for (
const auto &dir : trans.get_directions())
403 dir_sub_arr.push_back(d_str);
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}
416 root.insert_or_assign(
"transitions", trans_arr);
419 std::ofstream file(filepath, std::ios::out | std::ios::trunc);
425 file << root <<
"\n";
428 catch (
const std::exception &e)
430 std::println(stderr,
"[!] TOML saving failed: {}", e.what());
Represents the formal alphabet ( or ) of a Turing Machine.
bool contains(const Symbol &symbol) const noexcept
Checks if a specific Symbol belongs to this alphabet.
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.
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.
bool parse_metadata(const toml::table &config) noexcept
Extracts project identification block from TOML file.
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).
bool load_project(const std::filesystem::path &filepath) noexcept(false)
Parses a TM configuration file using toml++ and instantiates the TuringMachine.
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.
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.
bool save_project(const std::filesystem::path &filepath) const noexcept(false)
Exports the current machine configuration snapshot back to a TOML file.
Represents a single state in the finite set of states (Q) of the Turing Machine.
Represents a single symbol on the Turing Machine's tape.
char get_char() const noexcept
Getter for the underlying raw character.
@ 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.