1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "tools/cddl/sema.h"
6
7 #include <string.h>
8 #include <unistd.h>
9
10 #include <cinttypes>
11 #include <cstdlib>
12 #include <iostream>
13 #include <map>
14 #include <memory>
15 #include <string>
16 #include <unordered_set>
17 #include <vector>
18
19 #include "absl/algorithm/container.h"
20 #include "absl/strings/numbers.h"
21 #include "absl/strings/string_view.h"
22 #include "absl/types/optional.h"
23 #include "tools/cddl/logging.h"
24
TypesWithId()25 std::vector<CppType*> CppSymbolTable::TypesWithId() {
26 if (!this->TypesWithId_.size()) {
27 for (const std::unique_ptr<CppType>& ptr : this->cpp_types) {
28 if (ptr->type_key == absl::nullopt) {
29 continue;
30 }
31 this->TypesWithId_.emplace_back(ptr.get());
32 }
33 }
34 return this->TypesWithId_;
35 }
36
CddlType()37 CddlType::CddlType()
38 : map(nullptr), op(CddlType::Op::kNone), constraint_type(nullptr) {}
~CddlType()39 CddlType::~CddlType() {
40 switch (which) {
41 case CddlType::Which::kDirectChoice:
42 direct_choice.std::vector<CddlType*>::~vector();
43 break;
44 case CddlType::Which::kValue:
45 value.std::string::~basic_string();
46 break;
47 case CddlType::Which::kId:
48 id.std::string::~basic_string();
49 break;
50 case CddlType::Which::kMap:
51 break;
52 case CddlType::Which::kArray:
53 break;
54 case CddlType::Which::kGroupChoice:
55 break;
56 case CddlType::Which::kGroupnameChoice:
57 break;
58 case CddlType::Which::kTaggedType:
59 tagged_type.~TaggedType();
60 break;
61 }
62 }
63
Entry()64 CddlGroup::Entry::Entry() : group(nullptr) {}
~Entry()65 CddlGroup::Entry::~Entry() {
66 switch (which) {
67 case CddlGroup::Entry::Which::kUninitialized:
68 break;
69 case CddlGroup::Entry::Which::kType:
70 type.~EntryType();
71 break;
72 case CddlGroup::Entry::Which::kGroup:
73 break;
74 }
75 }
76
CppType()77 CppType::CppType() : vector_type() {}
~CppType()78 CppType::~CppType() {
79 switch (which) {
80 case CppType::Which::kUninitialized:
81 break;
82 case CppType::Which::kUint64:
83 break;
84 case CppType::Which::kString:
85 break;
86 case CppType::Which::kBytes:
87 break;
88 case CppType::Which::kVector:
89 break;
90 case CppType::Which::kEnum:
91 enum_type.~Enum();
92 break;
93 case CppType::Which::kStruct:
94 struct_type.~Struct();
95 break;
96 case CppType::Which::kOptional:
97 break;
98 case CppType::Which::kDiscriminatedUnion:
99 discriminated_union.~DiscriminatedUnion();
100 break;
101 case CppType::Which::kTaggedType:
102 break;
103 }
104 }
105
InitVector()106 void CppType::InitVector() {
107 which = Which::kVector;
108 new (&vector_type) Vector();
109 }
110
InitEnum()111 void CppType::InitEnum() {
112 which = Which::kEnum;
113 new (&enum_type) Enum();
114 }
115
InitStruct()116 void CppType::InitStruct() {
117 which = Which::kStruct;
118 new (&struct_type) Struct();
119 }
120
InitDiscriminatedUnion()121 void CppType::InitDiscriminatedUnion() {
122 which = Which::kDiscriminatedUnion;
123 new (&discriminated_union) DiscriminatedUnion();
124 }
125
InitBytes()126 void CppType::InitBytes() {
127 which = Which::kBytes;
128 }
129
InitString(std::string * s,absl::string_view value)130 void InitString(std::string* s, absl::string_view value) {
131 new (s) std::string(value);
132 }
133
InitDirectChoice(std::vector<CddlType * > * direct_choice)134 void InitDirectChoice(std::vector<CddlType*>* direct_choice) {
135 new (direct_choice) std::vector<CddlType*>();
136 }
137
InitGroupEntry(CddlGroup::Entry::EntryType * entry)138 void InitGroupEntry(CddlGroup::Entry::EntryType* entry) {
139 new (entry) CddlGroup::Entry::EntryType();
140 }
141
AddCddlType(CddlSymbolTable * table,CddlType::Which which)142 CddlType* AddCddlType(CddlSymbolTable* table, CddlType::Which which) {
143 table->types.emplace_back(new CddlType);
144 CddlType* value = table->types.back().get();
145 value->which = which;
146 return value;
147 }
148
149 CddlType* AnalyzeType(CddlSymbolTable* table, const AstNode& type);
150 CddlGroup* AnalyzeGroup(CddlSymbolTable* table, const AstNode& group);
151
AnalyzeType2(CddlSymbolTable * table,const AstNode & type2)152 CddlType* AnalyzeType2(CddlSymbolTable* table, const AstNode& type2) {
153 const AstNode* node = type2.children;
154 if (node->type == AstNode::Type::kNumber ||
155 node->type == AstNode::Type::kText ||
156 node->type == AstNode::Type::kBytes) {
157 CddlType* value = AddCddlType(table, CddlType::Which::kValue);
158 InitString(&value->value, node->text);
159 return value;
160 } else if (node->type == AstNode::Type::kTypename) {
161 if (type2.text[0] == '~') {
162 dprintf(STDERR_FILENO, "We don't support the '~' operator.\n");
163 return nullptr;
164 }
165 CddlType* id = AddCddlType(table, CddlType::Which::kId);
166 InitString(&id->id, node->text);
167 return id;
168 } else if (node->type == AstNode::Type::kType) {
169 if (type2.text[0] == '#' && type2.text[1] == '6' && type2.text[2] == '.') {
170 CddlType* tagged_type = AddCddlType(table, CddlType::Which::kTaggedType);
171 tagged_type->tagged_type.tag_value =
172 atoll(type2.text.substr(3 /* #6. */).data());
173 tagged_type->tagged_type.type = AnalyzeType(table, *node);
174 return tagged_type;
175 }
176 dprintf(STDERR_FILENO, "Unknown type2 value, expected #6.[uint]\n");
177 } else if (node->type == AstNode::Type::kGroup) {
178 if (type2.text[0] == '{') {
179 CddlType* map = AddCddlType(table, CddlType::Which::kMap);
180 map->map = AnalyzeGroup(table, *node);
181 return map;
182 } else if (type2.text[0] == '[') {
183 CddlType* array = AddCddlType(table, CddlType::Which::kArray);
184 array->array = AnalyzeGroup(table, *node);
185 return array;
186 } else if (type2.text[0] == '&') {
187 // Represents a choice between options in this group (ie an enum), not a
188 // choice between groups (which is currently unsupported).
189 CddlType* group_choice =
190 AddCddlType(table, CddlType::Which::kGroupChoice);
191 group_choice->group_choice = AnalyzeGroup(table, *node);
192 return group_choice;
193 }
194 } else if (node->type == AstNode::Type::kGroupname) {
195 if (type2.text[0] == '&') {
196 CddlType* group_choice =
197 AddCddlType(table, CddlType::Which::kGroupnameChoice);
198 InitString(&group_choice->id, node->text);
199 return group_choice;
200 }
201 }
202 return nullptr;
203 }
204
AnalyzeRangeop(const AstNode & rangeop)205 CddlType::Op AnalyzeRangeop(const AstNode& rangeop) {
206 if (rangeop.text == "..") {
207 return CddlType::Op::kInclusiveRange;
208 } else if (rangeop.text == "...") {
209 return CddlType::Op::kExclusiveRange;
210 } else {
211 dprintf(STDERR_FILENO, "Unsupported '%s' range operator.\n",
212 rangeop.text.c_str());
213 return CddlType::Op::kNone;
214 }
215 }
216
AnalyzeCtlop(const AstNode & ctlop)217 CddlType::Op AnalyzeCtlop(const AstNode& ctlop) {
218 if (!ctlop.children) {
219 dprintf(STDERR_FILENO, "Missing id for control operator '%s'.\n",
220 ctlop.text.c_str());
221 return CddlType::Op::kNone;
222 }
223 const std::string& id = ctlop.children->text;
224 if (id == "size") {
225 return CddlType::Op::kSize;
226 } else if (id == "bits") {
227 return CddlType::Op::kBits;
228 } else if (id == "regexp") {
229 return CddlType::Op::kRegexp;
230 } else if (id == "cbor") {
231 return CddlType::Op::kCbor;
232 } else if (id == "cborseq") {
233 return CddlType::Op::kCborseq;
234 } else if (id == "within") {
235 return CddlType::Op::kWithin;
236 } else if (id == "and") {
237 return CddlType::Op::kAnd;
238 } else if (id == "lt") {
239 return CddlType::Op::kLess;
240 } else if (id == "le") {
241 return CddlType::Op::kLessOrEqual;
242 } else if (id == "gt") {
243 return CddlType::Op::kGreater;
244 } else if (id == "ge") {
245 return CddlType::Op::kGreaterOrEqual;
246 } else if (id == "eq") {
247 return CddlType::Op::kEqual;
248 } else if (id == "ne") {
249 return CddlType::Op::kNotEqual;
250 } else if (id == "default") {
251 return CddlType::Op::kDefault;
252 } else {
253 dprintf(STDERR_FILENO, "Unsupported '%s' control operator.\n",
254 ctlop.text.c_str());
255 return CddlType::Op::kNone;
256 }
257 }
258
259 // Produces CddlType by analyzing AST parsed from type1 rule
260 // ABNF rule: type1 = type2 [S (rangeop / ctlop) S type2]
AnalyzeType1(CddlSymbolTable * table,const AstNode & type1)261 CddlType* AnalyzeType1(CddlSymbolTable* table, const AstNode& type1) {
262 if (!type1.children) {
263 dprintf(STDERR_FILENO, "Missing type2 in type1 '%s'.\n",
264 type1.text.c_str());
265 return nullptr;
266 }
267 const AstNode& target_type = *type1.children;
268 CddlType* analyzed_type = AnalyzeType2(table, target_type);
269 if (!analyzed_type) {
270 dprintf(STDERR_FILENO, "Invalid type2 '%s' in type1 '%s'.\n",
271 target_type.text.c_str(), type1.text.c_str());
272 return nullptr;
273 }
274 if (!target_type.sibling) {
275 // No optional range or control operator, return type as-is
276 return analyzed_type;
277 }
278 const AstNode& operator_type = *target_type.sibling;
279 CddlType::Op op;
280 if (operator_type.type == AstNode::Type::kRangeop) {
281 op = AnalyzeRangeop(operator_type);
282 } else if (operator_type.type == AstNode::Type::kCtlop) {
283 op = AnalyzeCtlop(operator_type);
284 } else {
285 op = CddlType::Op::kNone;
286 }
287 if (op == CddlType::Op::kNone) {
288 dprintf(STDERR_FILENO,
289 "Unsupported or missing operator '%s' in type1 '%s'.\n",
290 operator_type.text.c_str(), type1.text.c_str());
291 return nullptr;
292 }
293 if (!operator_type.sibling) {
294 dprintf(STDERR_FILENO,
295 "Missing controller type for operator '%s' in type1 '%s'.\n",
296 operator_type.text.c_str(), type1.text.c_str());
297 return nullptr;
298 }
299 const AstNode& controller_type = *operator_type.sibling;
300 CddlType* constraint_type = AnalyzeType2(table, controller_type);
301 if (!constraint_type) {
302 dprintf(STDERR_FILENO,
303 "Invalid controller type '%s' for operator '%s' in type1 '%s'.\n",
304 controller_type.text.c_str(), operator_type.text.c_str(),
305 type1.text.c_str());
306 return nullptr;
307 }
308 analyzed_type->op = op;
309 analyzed_type->constraint_type = constraint_type;
310 return analyzed_type;
311 }
312
AnalyzeType(CddlSymbolTable * table,const AstNode & type)313 CddlType* AnalyzeType(CddlSymbolTable* table, const AstNode& type) {
314 const AstNode* type1 = type.children;
315 if (type1->sibling) {
316 // If the type we are looking at has a type choice, create a top-level
317 // choice object, with a vector containing all valid choices.
318 CddlType* type_choice = AddCddlType(table, CddlType::Which::kDirectChoice);
319 InitDirectChoice(&type_choice->direct_choice);
320 while (type1) {
321 type_choice->direct_choice.push_back(AnalyzeType1(table, *type1));
322 type1 = type1->sibling;
323 }
324 return type_choice;
325 } else {
326 // Else just return the single choice.
327 return AnalyzeType1(table, *type1);
328 }
329 }
330
331 bool AnalyzeGroupEntry(CddlSymbolTable* table,
332 const AstNode& group_entry,
333 CddlGroup::Entry* entry);
334
AnalyzeGroup(CddlSymbolTable * table,const AstNode & group)335 CddlGroup* AnalyzeGroup(CddlSymbolTable* table, const AstNode& group) {
336 // NOTE: |group.children| is a grpchoice, which we don't currently handle.
337 // Therefore, we assume it has no siblings and move on to immediately handling
338 // its grpent children.
339 const AstNode* node = group.children->children;
340 table->groups.emplace_back(new CddlGroup);
341 CddlGroup* group_def = table->groups.back().get();
342 while (node) {
343 group_def->entries.emplace_back(new CddlGroup::Entry);
344 AnalyzeGroupEntry(table, *node, group_def->entries.back().get());
345 node = node->sibling;
346 }
347 return group_def;
348 }
349
350 // Parses a string into an optional uint64_t, with the value being that
351 // represented by the string if it is present and nullopt if it cannot
352 // be parsed.
353 // TODO(rwkeane): Add support for hex and binary options.
ParseOptionalUint(const std::string & text)354 absl::optional<uint64_t> ParseOptionalUint(const std::string& text) {
355 if (text == "0") {
356 return 0;
357 }
358
359 uint64_t parsed = std::strtoul(text.c_str(), nullptr, 10);
360 if (!parsed) {
361 return absl::nullopt;
362 }
363 return parsed;
364 }
365
AnalyzeGroupEntry(CddlSymbolTable * table,const AstNode & group_entry,CddlGroup::Entry * entry)366 bool AnalyzeGroupEntry(CddlSymbolTable* table,
367 const AstNode& group_entry,
368 CddlGroup::Entry* entry) {
369 const AstNode* node = group_entry.children;
370
371 // If it's an occurance operator (so the entry is optional), mark it as such
372 // and proceed to the next the node.
373 if (node->type == AstNode::Type::kOccur) {
374 if (node->text == "?") {
375 entry->opt_occurrence_min = CddlGroup::Entry::kOccurrenceMinUnbounded;
376 entry->opt_occurrence_max = 1;
377 } else if (node->text == "+") {
378 entry->opt_occurrence_min = 1;
379 entry->opt_occurrence_max = CddlGroup::Entry::kOccurrenceMaxUnbounded;
380 } else {
381 auto index = node->text.find('*');
382 if (index == std::string::npos) {
383 return false;
384 }
385
386 int lower_bound = CddlGroup::Entry::kOccurrenceMinUnbounded;
387 std::string first_half = node->text.substr(0, index);
388 if ((first_half.length() != 1 || first_half.at(0) != '0') &&
389 first_half.length() != 0) {
390 lower_bound = std::atoi(first_half.c_str());
391 if (!lower_bound) {
392 return false;
393 }
394 }
395
396 int upper_bound = CddlGroup::Entry::kOccurrenceMaxUnbounded;
397 std::string second_half =
398 index >= node->text.length() ? "" : node->text.substr(index + 1);
399 if ((second_half.length() != 1 || second_half.at(0) != '0') &&
400 second_half.length() != 0) {
401 upper_bound = std::atoi(second_half.c_str());
402 if (!upper_bound) {
403 return false;
404 }
405 }
406
407 entry->opt_occurrence_min = lower_bound;
408 entry->opt_occurrence_max = upper_bound;
409 }
410 entry->occurrence_specified = true;
411 node = node->sibling;
412 } else {
413 entry->opt_occurrence_min = 1;
414 entry->opt_occurrence_max = 1;
415 entry->occurrence_specified = false;
416 }
417
418 // If it's a member key (key in a map), save it and go to next node.
419 if (node->type == AstNode::Type::kMemberKey) {
420 if (node->text[node->text.size() - 1] == '>')
421 return false;
422 entry->which = CddlGroup::Entry::Which::kType;
423 InitGroupEntry(&entry->type);
424 entry->type.opt_key = std::string(node->children->text);
425 entry->type.integer_key = ParseOptionalUint(node->integer_member_key_text);
426 node = node->sibling;
427 }
428
429 // If it's a type, process it as such.
430 if (node->type == AstNode::Type::kType) {
431 if (entry->which == CddlGroup::Entry::Which::kUninitialized) {
432 entry->which = CddlGroup::Entry::Which::kType;
433 InitGroupEntry(&entry->type);
434 }
435 entry->type.value = AnalyzeType(table, *node);
436 } else if (node->type == AstNode::Type::kGroupname) {
437 return false;
438 } else if (node->type == AstNode::Type::kGroup) {
439 entry->which = CddlGroup::Entry::Which::kGroup;
440 entry->group = AnalyzeGroup(table, *node);
441 }
442 return true;
443 }
444
BuildSymbolTable(const AstNode & rules)445 std::pair<bool, CddlSymbolTable> BuildSymbolTable(const AstNode& rules) {
446 std::pair<bool, CddlSymbolTable> result;
447 result.first = false;
448 auto& table = result.second;
449
450 // Parse over all rules iteratively.
451 for (const AstNode* rule = &rules; rule; rule = rule->sibling) {
452 AstNode* node = rule->children;
453
454 // Ensure that the node is either a type or group definition.
455 if (node->type != AstNode::Type::kTypename &&
456 node->type != AstNode::Type::kGroupname) {
457 Logger::Error("Error parsing node with text '%s'. Unexpected node type.",
458 node->text);
459 return result;
460 }
461 bool is_type = node->type == AstNode::Type::kTypename;
462 absl::string_view name = node->text;
463
464 // Ensure that the node is assignment.
465 node = node->sibling;
466 if (node->type != AstNode::Type::kAssign) {
467 Logger::Error("Error parsing node with text '%s'. Node type != kAssign.",
468 node->text);
469 return result;
470 }
471
472 // Process the definition.
473 node = node->sibling;
474 if (is_type) {
475 CddlType* type = AnalyzeType(&table, *node);
476 if (rule->type_key != absl::nullopt) {
477 auto parsed_type_key = ParseOptionalUint(rule->type_key.value());
478 if (parsed_type_key == absl::nullopt) {
479 return result;
480 }
481 type->type_key = parsed_type_key.value();
482 }
483 if (!type) {
484 Logger::Error(
485 "Error parsing node with text '%s'."
486 "Failed to analyze node type.",
487 node->text);
488 }
489 table.type_map.emplace(std::string(name), type);
490 } else {
491 table.groups.emplace_back(new CddlGroup);
492 CddlGroup* group = table.groups.back().get();
493 group->entries.emplace_back(new CddlGroup::Entry);
494 AnalyzeGroupEntry(&table, *node, group->entries.back().get());
495 table.group_map.emplace(std::string(name), group);
496 }
497 }
498
499 DumpSymbolTable(&result.second);
500
501 result.first = true;
502 return result;
503 }
504
505 // Fetches a C++ Type from all known definitons, or inserts a placeholder to be
506 // updated later if the type hasn't been defined yet.
GetCppType(CppSymbolTable * table,const std::string & name)507 CppType* GetCppType(CppSymbolTable* table, const std::string& name) {
508 if (name.empty()) {
509 table->cpp_types.emplace_back(new CppType);
510 return table->cpp_types.back().get();
511 }
512 auto entry = table->cpp_type_map.find(name);
513 if (entry != table->cpp_type_map.end())
514 return entry->second;
515 table->cpp_types.emplace_back(new CppType);
516 table->cpp_type_map.emplace(name, table->cpp_types.back().get());
517 return table->cpp_types.back().get();
518 }
519
520 bool IncludeGroupMembersInEnum(CppSymbolTable* table,
521 const CddlSymbolTable& cddl_table,
522 CppType* cpp_type,
523 const CddlGroup& group);
524
IncludeGroupMembersInSubEnum(CppSymbolTable * table,const CddlSymbolTable & cddl_table,CppType * cpp_type,const std::string & name)525 bool IncludeGroupMembersInSubEnum(CppSymbolTable* table,
526 const CddlSymbolTable& cddl_table,
527 CppType* cpp_type,
528 const std::string& name) {
529 auto group_entry = cddl_table.group_map.find(name);
530 if (group_entry == cddl_table.group_map.end()) {
531 return false;
532 }
533 if (group_entry->second->entries.size() != 1 ||
534 group_entry->second->entries[0]->which !=
535 CddlGroup::Entry::Which::kGroup) {
536 return false;
537 }
538 CppType* sub_enum = GetCppType(table, name);
539 if (sub_enum->which == CppType::Which::kUninitialized) {
540 sub_enum->InitEnum();
541 sub_enum->name = name;
542 if (!IncludeGroupMembersInEnum(table, cddl_table, sub_enum,
543 *group_entry->second->entries[0]->group)) {
544 return false;
545 }
546 }
547 cpp_type->enum_type.sub_members.push_back(sub_enum);
548 return true;
549 }
550
IncludeGroupMembersInEnum(CppSymbolTable * table,const CddlSymbolTable & cddl_table,CppType * cpp_type,const CddlGroup & group)551 bool IncludeGroupMembersInEnum(CppSymbolTable* table,
552 const CddlSymbolTable& cddl_table,
553 CppType* cpp_type,
554 const CddlGroup& group) {
555 for (const auto& x : group.entries) {
556 if (x->HasOccurrenceOperator() ||
557 x->which != CddlGroup::Entry::Which::kType) {
558 return false;
559 }
560 if (x->type.value->which == CddlType::Which::kValue &&
561 !x->type.opt_key.empty()) {
562 cpp_type->enum_type.members.emplace_back(
563 x->type.opt_key, atoi(x->type.value->value.c_str()));
564 } else if (x->type.value->which == CddlType::Which::kId) {
565 IncludeGroupMembersInSubEnum(table, cddl_table, cpp_type,
566 x->type.value->id);
567 } else {
568 return false;
569 }
570 }
571 return true;
572 }
573
574 CppType* MakeCppType(CppSymbolTable* table,
575 const CddlSymbolTable& cddl_table,
576 const std::string& name,
577 const CddlType& type);
578
AddMembersToStruct(CppSymbolTable * table,const CddlSymbolTable & cddl_table,CppType * cpp_type,const std::vector<std::unique_ptr<CddlGroup::Entry>> & entries)579 bool AddMembersToStruct(
580 CppSymbolTable* table,
581 const CddlSymbolTable& cddl_table,
582 CppType* cpp_type,
583 const std::vector<std::unique_ptr<CddlGroup::Entry>>& entries) {
584 for (const auto& x : entries) {
585 if (x->which == CddlGroup::Entry::Which::kType) {
586 if (x->type.opt_key.empty()) {
587 // If the represented node has no text (ie - it's code generated) then
588 // it must have an inner type that is based on the user input. If this
589 // one looks as expected, process it recursively.
590 if (x->type.value->which != CddlType::Which::kId ||
591 x->HasOccurrenceOperator()) {
592 return false;
593 }
594 auto group_entry = cddl_table.group_map.find(x->type.value->id);
595 if (group_entry == cddl_table.group_map.end())
596 return false;
597 if (group_entry->second->entries.size() != 1 ||
598 group_entry->second->entries[0]->which !=
599 CddlGroup::Entry::Which::kGroup) {
600 return false;
601 }
602 if (!AddMembersToStruct(
603 table, cddl_table, cpp_type,
604 group_entry->second->entries[0]->group->entries)) {
605 return false;
606 }
607 } else {
608 // Here it is a real type definition - so process it as such.
609 CppType* member_type =
610 MakeCppType(table, cddl_table,
611 cpp_type->name + std::string("_") + x->type.opt_key,
612 *x->type.value);
613 if (!member_type)
614 return false;
615 if (member_type->name.empty())
616 member_type->name = x->type.opt_key;
617 if (x->opt_occurrence_min ==
618 CddlGroup::Entry::kOccurrenceMinUnbounded &&
619 x->opt_occurrence_max == 1) {
620 // Create an "optional" type, with sub-type being the type that is
621 // optional. This corresponds with occurrence operator '?'.
622 table->cpp_types.emplace_back(new CppType);
623 CppType* optional_type = table->cpp_types.back().get();
624 optional_type->which = CppType::Which::kOptional;
625 optional_type->optional_type = member_type;
626 cpp_type->struct_type.members.emplace_back(
627 x->type.opt_key, x->type.integer_key, optional_type);
628 } else {
629 cpp_type->struct_type.members.emplace_back(
630 x->type.opt_key, x->type.integer_key, member_type);
631 }
632 }
633 } else {
634 // If it's not a type, it's a group so add its members recursuvely.
635 if (!AddMembersToStruct(table, cddl_table, cpp_type, x->group->entries))
636 return false;
637 }
638 }
639 return true;
640 }
641
MakeCppType(CppSymbolTable * table,const CddlSymbolTable & cddl_table,const std::string & name,const CddlType & type)642 CppType* MakeCppType(CppSymbolTable* table,
643 const CddlSymbolTable& cddl_table,
644 const std::string& name,
645 const CddlType& type) {
646 CppType* cpp_type = nullptr;
647 switch (type.which) {
648 case CddlType::Which::kId: {
649 if (type.id == "uint") {
650 cpp_type = GetCppType(table, name);
651 cpp_type->which = CppType::Which::kUint64;
652 } else if (type.id == "text") {
653 cpp_type = GetCppType(table, name);
654 cpp_type->which = CppType::Which::kString;
655 } else if (type.id == "bytes") {
656 cpp_type = GetCppType(table, name);
657 cpp_type->InitBytes();
658 if (type.op == CddlType::Op::kSize) {
659 size_t size = 0;
660 if (!absl::SimpleAtoi(type.constraint_type->value, &size)) {
661 return nullptr;
662 }
663 cpp_type->bytes_type.fixed_size = size;
664 }
665 } else {
666 cpp_type = GetCppType(table, type.id);
667 }
668 } break;
669 case CddlType::Which::kMap: {
670 cpp_type = GetCppType(table, name);
671 cpp_type->InitStruct();
672 cpp_type->struct_type.key_type = CppType::Struct::KeyType::kMap;
673 cpp_type->name = name;
674 if (!AddMembersToStruct(table, cddl_table, cpp_type, type.map->entries))
675 return nullptr;
676 } break;
677 case CddlType::Which::kArray: {
678 cpp_type = GetCppType(table, name);
679 if (type.array->entries.size() == 1 &&
680 type.array->entries[0]->HasOccurrenceOperator()) {
681 cpp_type->InitVector();
682 cpp_type->vector_type.min_length =
683 type.array->entries[0]->opt_occurrence_min;
684 cpp_type->vector_type.max_length =
685 type.array->entries[0]->opt_occurrence_max;
686 cpp_type->vector_type.element_type =
687 GetCppType(table, type.array->entries[0]->type.value->id);
688 } else {
689 cpp_type->InitStruct();
690 cpp_type->struct_type.key_type = CppType::Struct::KeyType::kArray;
691 cpp_type->name = name;
692 if (!AddMembersToStruct(table, cddl_table, cpp_type,
693 type.map->entries)) {
694 return nullptr;
695 }
696 }
697 } break;
698 case CddlType::Which::kGroupChoice: {
699 cpp_type = GetCppType(table, name);
700 cpp_type->InitEnum();
701 cpp_type->name = name;
702 if (!IncludeGroupMembersInEnum(table, cddl_table, cpp_type,
703 *type.group_choice)) {
704 return nullptr;
705 }
706 } break;
707 case CddlType::Which::kGroupnameChoice: {
708 cpp_type = GetCppType(table, name);
709 cpp_type->InitEnum();
710 cpp_type->name = name;
711 if (!IncludeGroupMembersInSubEnum(table, cddl_table, cpp_type, type.id)) {
712 return nullptr;
713 }
714 } break;
715 case CddlType::Which::kDirectChoice: {
716 cpp_type = GetCppType(table, name);
717 cpp_type->InitDiscriminatedUnion();
718 for (const auto* cddl_choice : type.direct_choice) {
719 CppType* member = MakeCppType(table, cddl_table, "", *cddl_choice);
720 if (!member)
721 return nullptr;
722 cpp_type->discriminated_union.members.push_back(member);
723 }
724 return cpp_type;
725 }
726 case CddlType::Which::kTaggedType: {
727 cpp_type = GetCppType(table, name);
728 cpp_type->which = CppType::Which::kTaggedType;
729 cpp_type->tagged_type.tag = type.tagged_type.tag_value;
730 cpp_type->tagged_type.real_type =
731 MakeCppType(table, cddl_table, "", *type.tagged_type.type);
732 } break;
733 default:
734 return nullptr;
735 }
736
737 cpp_type->type_key = type.type_key;
738 return cpp_type;
739 }
740
PrePopulateCppTypes(CppSymbolTable * table)741 void PrePopulateCppTypes(CppSymbolTable* table) {
742 std::vector<std::pair<std::string, CppType::Which>> default_types;
743 default_types.emplace_back("text", CppType::Which::kString);
744 default_types.emplace_back("tstr", CppType::Which::kString);
745 default_types.emplace_back("bstr", CppType::Which::kBytes);
746 default_types.emplace_back("bytes", CppType::Which::kBytes);
747 default_types.emplace_back("uint", CppType::Which::kUint64);
748
749 for (auto& pair : default_types) {
750 auto entry = table->cpp_type_map.find(pair.first);
751 if (entry != table->cpp_type_map.end())
752 continue;
753 table->cpp_types.emplace_back(new CppType);
754 auto* type = table->cpp_types.back().get();
755 type->name = pair.first;
756 type->which = pair.second;
757 table->cpp_type_map.emplace(pair.first, type);
758 }
759 }
760
BuildCppTypes(const CddlSymbolTable & cddl_table)761 std::pair<bool, CppSymbolTable> BuildCppTypes(
762 const CddlSymbolTable& cddl_table) {
763 std::pair<bool, CppSymbolTable> result;
764 result.first = false;
765 PrePopulateCppTypes(&result.second);
766 auto& table = result.second;
767 for (const auto& type_entry : cddl_table.type_map) {
768 if (!MakeCppType(&table, cddl_table, type_entry.first,
769 *type_entry.second)) {
770 return result;
771 }
772 }
773
774 result.first = true;
775 return result;
776 }
777
VerifyUniqueKeysInMember(std::unordered_set<std::string> * keys,const CppType::Struct::CppMember & member)778 bool VerifyUniqueKeysInMember(std::unordered_set<std::string>* keys,
779 const CppType::Struct::CppMember& member) {
780 return keys->insert(member.name).second &&
781 (!member.integer_key.has_value() ||
782 keys->insert(std::to_string(member.integer_key.value())).second);
783 }
784
HasUniqueKeys(const CppType & type)785 bool HasUniqueKeys(const CppType& type) {
786 std::unordered_set<std::string> keys;
787 return type.which != CppType::Which::kStruct ||
788 absl::c_all_of(type.struct_type.members,
789 [&keys](const CppType::Struct::CppMember& member) {
790 return VerifyUniqueKeysInMember(&keys, member);
791 });
792 }
793
IsUniqueEnumValue(std::vector<uint64_t> * values,uint64_t v)794 bool IsUniqueEnumValue(std::vector<uint64_t>* values, uint64_t v) {
795 auto it = std::lower_bound(values->begin(), values->end(), v);
796 if (it == values->end() || *it != v) {
797 values->insert(it, v);
798 return true;
799 }
800 return false;
801 }
802
HasUniqueEnumValues(std::vector<uint64_t> * values,const CppType & type)803 bool HasUniqueEnumValues(std::vector<uint64_t>* values, const CppType& type) {
804 return absl::c_all_of(type.enum_type.sub_members,
805 [values](CppType* sub_member) {
806 return HasUniqueEnumValues(values, *sub_member);
807 }) &&
808 absl::c_all_of(
809 type.enum_type.members,
810 [values](const std::pair<std::string, uint64_t>& member) {
811 return IsUniqueEnumValue(values, member.second);
812 });
813 }
814
HasUniqueEnumValues(const CppType & type)815 bool HasUniqueEnumValues(const CppType& type) {
816 std::vector<uint64_t> values;
817 return type.which != CppType::Which::kEnum ||
818 HasUniqueEnumValues(&values, type);
819 }
820
ValidateCppTypes(const CppSymbolTable & cpp_symbols)821 bool ValidateCppTypes(const CppSymbolTable& cpp_symbols) {
822 return absl::c_all_of(
823 cpp_symbols.cpp_types, [](const std::unique_ptr<CppType>& ptr) {
824 return HasUniqueKeys(*ptr) && HasUniqueEnumValues(*ptr);
825 });
826 }
827
DumpTypeKey(absl::optional<uint64_t> key)828 std::string DumpTypeKey(absl::optional<uint64_t> key) {
829 if (key != absl::nullopt) {
830 return " (type key=\"" + std::to_string(key.value()) + "\")";
831 }
832 return "";
833 }
834
DumpType(CddlType * type,int indent_level)835 void DumpType(CddlType* type, int indent_level) {
836 std::string output = "";
837 for (int i = 0; i <= indent_level; ++i)
838 output += "--";
839 switch (type->which) {
840 case CddlType::Which::kDirectChoice:
841 output = "kDirectChoice" + DumpTypeKey(type->type_key) + ": ";
842 Logger::Log(output);
843 for (auto& option : type->direct_choice)
844 DumpType(option, indent_level + 1);
845 break;
846 case CddlType::Which::kValue:
847 output += "kValue" + DumpTypeKey(type->type_key) + ": " + type->value;
848 Logger::Log(output);
849 break;
850 case CddlType::Which::kId:
851 output += "kId" + DumpTypeKey(type->type_key) + ": " + type->id;
852 Logger::Log(output);
853 break;
854 case CddlType::Which::kMap:
855 output += "kMap" + DumpTypeKey(type->type_key) + ": ";
856 Logger::Log(output);
857 DumpGroup(type->map, indent_level + 1);
858 break;
859 case CddlType::Which::kArray:
860 output += "kArray" + DumpTypeKey(type->type_key) + ": ";
861 Logger::Log(output);
862 DumpGroup(type->array, indent_level + 1);
863 break;
864 case CddlType::Which::kGroupChoice:
865 output += "kGroupChoice" + DumpTypeKey(type->type_key) + ": ";
866 Logger::Log(output);
867 DumpGroup(type->group_choice, indent_level + 1);
868 break;
869 case CddlType::Which::kGroupnameChoice:
870 output += "kGroupnameChoice" + DumpTypeKey(type->type_key) + ": ";
871 Logger::Log(output);
872 break;
873 case CddlType::Which::kTaggedType:
874 output += "kTaggedType" + DumpTypeKey(type->type_key) + ": " +
875 std::to_string(type->tagged_type.tag_value);
876 Logger::Log(output);
877 DumpType(type->tagged_type.type, indent_level + 1);
878 break;
879 }
880 }
881
DumpGroup(CddlGroup * group,int indent_level)882 void DumpGroup(CddlGroup* group, int indent_level) {
883 for (auto& entry : group->entries) {
884 std::string output = "";
885 for (int i = 0; i <= indent_level; ++i)
886 output += "--";
887 switch (entry->which) {
888 case CddlGroup::Entry::Which::kUninitialized:
889 break;
890 case CddlGroup::Entry::Which::kType:
891 output += "kType:";
892 if (entry->HasOccurrenceOperator()) {
893 output +=
894 "minOccurance: " + std::to_string(entry->opt_occurrence_min) +
895 " maxOccurance: " + std::to_string(entry->opt_occurrence_max);
896 }
897 if (!entry->type.opt_key.empty()) {
898 output += " " + entry->type.opt_key + "=>";
899 }
900 Logger::Log(output);
901 DumpType(entry->type.value, indent_level + 1);
902 break;
903 case CddlGroup::Entry::Which::kGroup:
904 if (entry->HasOccurrenceOperator())
905 output +=
906 "minOccurance: " + std::to_string(entry->opt_occurrence_min) +
907 " maxOccurance: " + std::to_string(entry->opt_occurrence_max);
908 Logger::Log(output);
909 DumpGroup(entry->group, indent_level + 1);
910 break;
911 }
912 }
913 }
914
DumpSymbolTable(CddlSymbolTable * table)915 void DumpSymbolTable(CddlSymbolTable* table) {
916 for (auto& entry : table->type_map) {
917 Logger::Log(entry.first);
918 DumpType(entry.second);
919 }
920 for (auto& entry : table->group_map) {
921 Logger::Log(entry.first);
922 DumpGroup(entry.second);
923 }
924 }
925