MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
automaton.h
Go to the documentation of this file.
1#pragma once
2
3#include <concepts>
4
5#include <algorithm>
6#include <list>
7#include <map>
8#include <ostream>
9#include <print>
10#include <set>
11#include <vector>
12
14
15namespace automaton {
16
17class DFANode;
18class NFANode;
19
20template<class NodeType>
22public:
23 AutomatonBase() = default;
24 AutomatonBase(const AutomatonBase&) = delete;
26
27 NodeType* add_state() {
28 nodes_.emplace_back(id_++);
29 return &nodes_.back();
30 }
31
32 void set_start(const NodeType* start) { start_ = start; }
33
34 const NodeType* get_start() const { return start_; }
35 size_t num_states() const { return nodes_.size(); }
36
37 /// Ordered by NodeType::Lt (i.e. by id) so that iteration is deterministic.
38 std::set<const NodeType*, typename NodeType::Lt> get_reachable_states() const {
39 std::set<const NodeType*, typename NodeType::Lt> reachableStates;
40 std::vector<const NodeType*> workList;
41 workList.push_back(get_start());
42 while (!workList.empty()) {
43 auto state = workList.back();
44 workList.pop_back();
45 reachableStates.insert(state);
46 state->for_transitions([&](auto, auto to) {
47 if (!reachableStates.contains(to)) workList.push_back(to);
48 });
49 }
50 return reachableStates;
51 }
52
53 void print(std::ostream& os) const {
54 if constexpr (std::is_same_v<NodeType, DFANode>)
55 std::println(os, "digraph dfa {{");
56 else if constexpr (std::is_same_v<NodeType, NFANode>)
57 std::println(os, "digraph nfa {{");
58 else
59 std::println(os, "digraph automaton {{");
60 std::println(os, " start -> \"{}\";", start_->id());
61
62 for (const auto& node : nodes_)
63 node.print(os);
64 std::println(os, "}}");
65 }
66
67 friend std::ostream& operator<<(std::ostream& os, const AutomatonBase& automaton) {
68 automaton.print(os);
69 return os;
70 }
71
72private:
73 std::list<NodeType> nodes_;
74 const NodeType* start_ = nullptr;
75 int id_ = 0;
76};
77
78template<class NodeType>
79void print_node(std::ostream& os, const NodeType& node, std::invocable<std::uint64_t> auto print_char) {
80 if (node.is_accepting()) std::println(os, " \"{}\" [shape=doublecircle];", node.id());
81 if (node.is_erroring()) std::println(os, " \"{}\" [shape=square];", node.id());
82
83 std::map<const NodeType*, std::vector<Range>, typename NodeType::Lt> node2transitions;
84 node.for_transitions([&](auto c, auto to) { node2transitions[to].push_back({c, c}); });
85
86 for (auto& [to, ranges] : node2transitions) {
87 std::sort(ranges.begin(), ranges.end(), RangeCompare{});
88 ranges = merge_ranges(ranges);
89 for (auto& [lo, hi] : ranges) {
90 auto chars = lo == hi ? print_char(lo) : std::format("{}-{}", print_char(lo), print_char(hi));
91 auto nums = lo == hi ? std::format("{}", lo) : std::format("{}-{}", lo, hi);
92 std::println(os, " \"{}\" -> \"{}\" [label=\"{} ({})\"];", node.id(), to->id(), chars, nums);
93 }
94 }
95}
96
97} // namespace automaton
size_t num_states() const
Definition automaton.h:35
void set_start(const NodeType *start)
Definition automaton.h:32
NodeType * add_state()
Definition automaton.h:27
std::set< const NodeType *, typename NodeType::Lt > get_reachable_states() const
Ordered by NodeType::Lt (i.e. by id) so that iteration is deterministic.
Definition automaton.h:38
const NodeType * get_start() const
Definition automaton.h:34
void print(std::ostream &os) const
Definition automaton.h:53
friend std::ostream & operator<<(std::ostream &os, const AutomatonBase &automaton)
Definition automaton.h:67
AutomatonBase(const AutomatonBase &)=delete
AutomatonBase & operator=(const AutomatonBase &)=delete
void print_node(std::ostream &os, const NodeType &node, std::invocable< std::uint64_t > auto print_char)
Definition automaton.h:79
std::optional< Range > merge_ranges(Range a, Range b) noexcept