P4C
The P4 Compiler
Loading...
Searching...
No Matches
json_generator.h
1/*
2 * SPDX-FileCopyrightText: 2013 Barefoot Networks, Inc.
3 * Copyright 2013-present Barefoot Networks, Inc.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8#ifndef IR_JSON_GENERATOR_H_
9#define IR_JSON_GENERATOR_H_
10
11#include <optional>
12#include <string>
13#include <string_view>
14#include <unordered_set>
15#include <variant>
16
17#include "ir/node.h"
18#include "lib/bitvec.h"
19#include "lib/cstring.h"
20#include "lib/indent.h"
21#include "lib/ltbitmatrix.h"
22#include "lib/match.h"
23#include "lib/ordered_map.h"
24#include "lib/ordered_set.h"
25#include "lib/safe_vector.h"
26#include "lib/string_map.h"
27
28namespace P4 {
29
30class JSONGenerator {
31 std::unordered_set<int> node_refs;
32 std::ostream &out;
33 bool dumpSourceInfo;
34
35 template <typename T>
36 class has_toJSON {
37 typedef char small;
38 typedef struct {
39 char c[2];
40 } big;
41
42 template <typename C>
43 static small test(decltype(&C::toJSON));
44 template <typename C>
45 static big test(...);
46
47 public:
48 static const bool value = sizeof(test<T>(0)) == sizeof(char);
49 };
50
51 indent_t indent;
52 enum output_state_t {
53 TOP,
54 VEC_START,
55 VEC_MID,
56 OBJ_START,
57 OBJ_AFTERTAG,
58 OBJ_MID,
59 OBJ_END
60 } output_state = TOP;
61
62 enum state_restore_kind { NONE, OBJECT, VECTOR };
63 class state_restore_t {
64 output_state_t prev_state;
65 JSONGenerator &gen;
66 state_restore_kind kind;
67 friend class JSONGenerator;
68
69 state_restore_t(JSONGenerator &gen, state_restore_kind kind)
70 : prev_state(gen.output_state), gen(gen), kind(kind) {}
71 state_restore_t(const state_restore_t &) = delete;
72 state_restore_t(state_restore_t &&a) : prev_state(a.prev_state), gen(a.gen), kind(a.kind) {
73 a.kind = NONE;
74 }
75
76 public:
77 ~state_restore_t() {
78 if (kind == OBJECT) gen.end_object(*this);
79 if (kind == VECTOR) gen.end_vector(*this);
80 }
81 };
82
83 public:
84 explicit JSONGenerator(std::ostream &out, bool dumpSourceInfo = false)
85 : out(out), dumpSourceInfo(dumpSourceInfo) {}
86
87 state_restore_t begin_vector() {
88 if (output_state == OBJ_START) output_state = OBJ_END;
89 BUG_CHECK(output_state != VEC_START, "invalid json output state in begin_vector");
90 state_restore_t rv(*this, VECTOR);
91 output_state = VEC_START;
92 out << '[';
93 ++indent;
94 return rv;
95 }
96
97 void end_vector(state_restore_t &prev) {
98 BUG_CHECK(prev.kind == VECTOR, "invalid previous state in end_vector");
99 prev.kind = NONE;
100 --indent;
101 if (output_state == VEC_MID)
102 out << std::endl << indent;
103 else if (output_state != VEC_START)
104 BUG("invalid json output state in end_vector");
105 out << ']';
106 if ((output_state = prev.prev_state) == OBJ_AFTERTAG) output_state = OBJ_MID;
107 }
108
109 state_restore_t begin_object() {
110 BUG_CHECK(output_state != OBJ_START && output_state != OBJ_MID && output_state != OBJ_END,
111 "invalid json output state in begin_object");
112 state_restore_t rv(*this, OBJECT);
113 output_state = OBJ_START;
114 return rv;
115 }
116
117 void end_object(state_restore_t &prev) {
118 BUG_CHECK(prev.kind == OBJECT, "invalid previous state in end_object");
119 prev.kind = NONE;
120 switch (output_state) {
121 case OBJ_START:
122 out << "{}";
123 break;
124 case OBJ_MID:
125 out << std::endl << --indent << '}';
126 break;
127 case OBJ_END:
128 break;
129 case TOP:
130 case VEC_START:
131 case VEC_MID:
132 case OBJ_AFTERTAG:
133 BUG("invalid json output state in end_object");
134 break;
135 }
136 if ((output_state = prev.prev_state) == OBJ_AFTERTAG) output_state = OBJ_MID;
137 }
138
139 template <typename T>
140 void emit(const T &val) {
141 switch (output_state) {
142 case VEC_MID:
143 out << ',';
144 /* fall through */
145 case VEC_START:
146 out << std::endl << indent;
147 output_state = VEC_MID;
148 break;
149 case OBJ_AFTERTAG:
150 output_state = OBJ_MID;
151 break;
152 case TOP:
153 break;
154 case OBJ_START:
155 output_state = OBJ_END;
156 break;
157 case OBJ_MID:
158 case OBJ_END:
159 BUG("invalid json output state for emit(obj)");
160 }
161 generate(val);
162 if (output_state == TOP) out << std::endl;
163 }
164
165 void emit_tag(std::string_view tag) {
166 switch (output_state) {
167 case OBJ_START:
168 out << '{' << std::endl << ++indent;
169 break;
170 case OBJ_MID:
171 out << ',' << std::endl << indent;
172 break;
173 case TOP:
174 case VEC_START:
175 case VEC_MID:
176 case OBJ_AFTERTAG:
177 case OBJ_END:
178 BUG("invalid json output state for emit_tag");
179 }
180 out << '\"' << cstring(tag).escapeJson() << "\" : ";
181 output_state = OBJ_AFTERTAG;
182 }
183
184 template <typename T>
185 void emit(std::string_view tag, const T &val) {
186 emit_tag(tag);
187 generate(val);
188 output_state = OBJ_MID;
189 }
190
191 private:
192 template <typename T>
193 void generate(const safe_vector<T> &v) {
194 auto t = begin_vector();
195 for (auto &el : v) emit(el);
196 end_vector(t);
197 }
198
199 template <typename T>
200 void generate(const std::vector<T> &v) {
201 auto t = begin_vector();
202 for (auto &el : v) emit(el);
203 end_vector(t);
204 }
205
206 template <typename T, typename U>
207 void generate(const std::pair<T, U> &v) {
208 auto t = begin_object();
209 toJSON(v);
210 end_object(t);
211 }
212
213 public:
214 template <typename T, typename U>
215 void toJSON(const std::pair<T, U> &v) {
216 emit("first", v.first);
217 emit("second", v.second);
218 }
219
220 private:
221 template <typename T>
222 void generate(const std::optional<T> &v) {
223 auto t = begin_object();
224 emit("valid", !!v);
225 if (v) emit("value", *v);
226 end_object(t);
227 }
228
229 template <typename T>
230 void generate(const std::set<T> &v) {
231 auto t = begin_vector();
232 for (auto &el : v) emit(el);
233 end_vector(t);
234 }
235
236 template <typename T>
237 void generate(const ordered_set<T> &v) {
238 auto t = begin_vector();
239 for (auto &el : v) emit(el);
240 end_vector(t);
241 }
242
243 template <typename K, typename V>
244 void generate(const std::map<K, V> &v) {
245 auto t = begin_vector();
246 for (auto &el : v) emit(el);
247 end_vector(t);
248 }
249
250 template <typename K, typename V>
251 void generate(const ordered_map<K, V> &v) {
252 auto t = begin_vector();
253 for (auto &el : v) emit(el);
254 end_vector(t);
255 }
256
257 template <typename V>
258 void generate(const string_map<V> &v) {
259 auto t = begin_object();
260 for (auto &el : v) emit(el.first, el.second);
261 end_object(t);
262 }
263
264 template <class... Types>
265 void generate(const std::variant<Types...> &v) {
266 auto t = begin_object();
267 emit("variant_index", v.index());
268 std::visit([this](auto &value) { this->emit("value", value); }, v);
269 end_object(t);
270 }
271
272 void generate(bool v) { out << (v ? "true" : "false"); }
273 template <typename T>
274 std::enable_if_t<std::is_integral_v<T>> generate(T v) {
275 out << std::to_string(v);
276 }
277 void generate(double v) { out << std::to_string(v); }
278 template <typename T>
279 std::enable_if_t<std::is_same_v<T, big_int>> generate(const T &v) {
280 out << v;
281 }
282
283 void generate(cstring v) {
284 if (v) {
285 out << "\"" << v.escapeJson() << "\"";
286 } else {
287 out << "null";
288 }
289 }
290 template <typename T>
291 std::enable_if_t<std::is_same_v<T, LTBitMatrix> || std::is_enum_v<T>> generate(T v) {
292 out << "\"" << v << "\"";
293 }
294
295 void generate(const bitvec &v) { out << "\"" << v << "\""; }
296
297 void generate(const match_t &v) {
298 auto t = begin_object();
299 emit("word0", v.word0);
300 emit("word1", v.word1);
301 end_object(t);
302 }
303
304 template <typename T>
305 std::enable_if_t<has_toJSON<T>::value && !std::is_base_of_v<IR::INode, T>> generate(
306 const T &v) {
307 auto t = begin_object();
308 v.toJSON(*this);
309 end_object(t);
310 }
311
312 void generate(const IR::INode &v_) {
313 auto &v = *v_.getNode();
314 auto t = begin_object();
315 if (node_refs.find(v.id) != node_refs.end()) {
316 emit("Node_ID", v.id);
317 } else {
318 node_refs.insert(v.id);
319 v.toJSON(*this);
320 if (dumpSourceInfo) {
321 v.sourceInfoToJSON(*this);
322 }
323 }
324 end_object(t);
325 }
326
327 // This should more naturally be `generate(const T *v)`, but the extra `const &` is needed
328 // to avoid ambiguous overload failures between this and the array generate below
329 template <typename T>
330 void generate(const T *const &v) {
331 if (v)
332 generate(*v);
333 else
334 out << "null";
335 }
336
337 template <typename T, size_t N>
338 void generate(const T (&v)[N]) {
339 auto t = begin_vector();
340 for (auto &el : v) emit(el);
341 end_vector(t);
342 }
343};
344
345} // namespace P4
346
347#endif /* IR_JSON_GENERATOR_H_ */
Definition inode.h:42
Definition bitvec.h:120
Definition cstring.h:85
cstring escapeJson() const
Definition cstring.cpp:272
Definition indent.h:26
Definition ordered_map.h:32
Definition ordered_set.h:32
Definition safe_vector.h:18
Definition string_map.h:41
TODO: this is not really specific to BMV2, it should reside somewhere else.
Definition applyOptionsPragmas.cpp:13
Definition match.h:36