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