// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "tools/cddl/sema.h" #include #include #include #include #include #include #include #include #include #include #include "absl/algorithm/container.h" #include "absl/strings/numbers.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "tools/cddl/logging.h" std::vector CppSymbolTable::TypesWithId() { if (!this->TypesWithId_.size()) { for (const std::unique_ptr& ptr : this->cpp_types) { if (ptr->type_key == absl::nullopt) { continue; } this->TypesWithId_.emplace_back(ptr.get()); } } return this->TypesWithId_; } CddlType::CddlType() : map(nullptr), op(CddlType::Op::kNone), constraint_type(nullptr) {} CddlType::~CddlType() { switch (which) { case CddlType::Which::kDirectChoice: direct_choice.std::vector::~vector(); break; case CddlType::Which::kValue: value.std::string::~basic_string(); break; case CddlType::Which::kId: id.std::string::~basic_string(); break; case CddlType::Which::kMap: break; case CddlType::Which::kArray: break; case CddlType::Which::kGroupChoice: break; case CddlType::Which::kGroupnameChoice: break; case CddlType::Which::kTaggedType: tagged_type.~TaggedType(); break; } } CddlGroup::Entry::Entry() : group(nullptr) {} CddlGroup::Entry::~Entry() { switch (which) { case CddlGroup::Entry::Which::kUninitialized: break; case CddlGroup::Entry::Which::kType: type.~EntryType(); break; case CddlGroup::Entry::Which::kGroup: break; } } CppType::CppType() : vector_type() {} CppType::~CppType() { switch (which) { case CppType::Which::kUninitialized: break; case CppType::Which::kUint64: break; case CppType::Which::kString: break; case CppType::Which::kBytes: break; case CppType::Which::kVector: break; case CppType::Which::kEnum: enum_type.~Enum(); break; case CppType::Which::kStruct: struct_type.~Struct(); break; case CppType::Which::kOptional: break; case CppType::Which::kDiscriminatedUnion: discriminated_union.~DiscriminatedUnion(); break; case CppType::Which::kTaggedType: break; } } void CppType::InitVector() { which = Which::kVector; new (&vector_type) Vector(); } void CppType::InitEnum() { which = Which::kEnum; new (&enum_type) Enum(); } void CppType::InitStruct() { which = Which::kStruct; new (&struct_type) Struct(); } void CppType::InitDiscriminatedUnion() { which = Which::kDiscriminatedUnion; new (&discriminated_union) DiscriminatedUnion(); } void CppType::InitBytes() { which = Which::kBytes; } void InitString(std::string* s, absl::string_view value) { new (s) std::string(value); } void InitDirectChoice(std::vector* direct_choice) { new (direct_choice) std::vector(); } void InitGroupEntry(CddlGroup::Entry::EntryType* entry) { new (entry) CddlGroup::Entry::EntryType(); } CddlType* AddCddlType(CddlSymbolTable* table, CddlType::Which which) { table->types.emplace_back(new CddlType); CddlType* value = table->types.back().get(); value->which = which; return value; } CddlType* AnalyzeType(CddlSymbolTable* table, const AstNode& type); CddlGroup* AnalyzeGroup(CddlSymbolTable* table, const AstNode& group); CddlType* AnalyzeType2(CddlSymbolTable* table, const AstNode& type2) { const AstNode* node = type2.children; if (node->type == AstNode::Type::kNumber || node->type == AstNode::Type::kText || node->type == AstNode::Type::kBytes) { CddlType* value = AddCddlType(table, CddlType::Which::kValue); InitString(&value->value, node->text); return value; } else if (node->type == AstNode::Type::kTypename) { if (type2.text[0] == '~') { dprintf(STDERR_FILENO, "We don't support the '~' operator.\n"); return nullptr; } CddlType* id = AddCddlType(table, CddlType::Which::kId); InitString(&id->id, node->text); return id; } else if (node->type == AstNode::Type::kType) { if (type2.text[0] == '#' && type2.text[1] == '6' && type2.text[2] == '.') { CddlType* tagged_type = AddCddlType(table, CddlType::Which::kTaggedType); tagged_type->tagged_type.tag_value = atoll(type2.text.substr(3 /* #6. */).data()); tagged_type->tagged_type.type = AnalyzeType(table, *node); return tagged_type; } dprintf(STDERR_FILENO, "Unknown type2 value, expected #6.[uint]\n"); } else if (node->type == AstNode::Type::kGroup) { if (type2.text[0] == '{') { CddlType* map = AddCddlType(table, CddlType::Which::kMap); map->map = AnalyzeGroup(table, *node); return map; } else if (type2.text[0] == '[') { CddlType* array = AddCddlType(table, CddlType::Which::kArray); array->array = AnalyzeGroup(table, *node); return array; } else if (type2.text[0] == '&') { // Represents a choice between options in this group (ie an enum), not a // choice between groups (which is currently unsupported). CddlType* group_choice = AddCddlType(table, CddlType::Which::kGroupChoice); group_choice->group_choice = AnalyzeGroup(table, *node); return group_choice; } } else if (node->type == AstNode::Type::kGroupname) { if (type2.text[0] == '&') { CddlType* group_choice = AddCddlType(table, CddlType::Which::kGroupnameChoice); InitString(&group_choice->id, node->text); return group_choice; } } return nullptr; } CddlType::Op AnalyzeRangeop(const AstNode& rangeop) { if (rangeop.text == "..") { return CddlType::Op::kInclusiveRange; } else if (rangeop.text == "...") { return CddlType::Op::kExclusiveRange; } else { dprintf(STDERR_FILENO, "Unsupported '%s' range operator.\n", rangeop.text.c_str()); return CddlType::Op::kNone; } } CddlType::Op AnalyzeCtlop(const AstNode& ctlop) { if (!ctlop.children) { dprintf(STDERR_FILENO, "Missing id for control operator '%s'.\n", ctlop.text.c_str()); return CddlType::Op::kNone; } const std::string& id = ctlop.children->text; if (id == "size") { return CddlType::Op::kSize; } else if (id == "bits") { return CddlType::Op::kBits; } else if (id == "regexp") { return CddlType::Op::kRegexp; } else if (id == "cbor") { return CddlType::Op::kCbor; } else if (id == "cborseq") { return CddlType::Op::kCborseq; } else if (id == "within") { return CddlType::Op::kWithin; } else if (id == "and") { return CddlType::Op::kAnd; } else if (id == "lt") { return CddlType::Op::kLess; } else if (id == "le") { return CddlType::Op::kLessOrEqual; } else if (id == "gt") { return CddlType::Op::kGreater; } else if (id == "ge") { return CddlType::Op::kGreaterOrEqual; } else if (id == "eq") { return CddlType::Op::kEqual; } else if (id == "ne") { return CddlType::Op::kNotEqual; } else if (id == "default") { return CddlType::Op::kDefault; } else { dprintf(STDERR_FILENO, "Unsupported '%s' control operator.\n", ctlop.text.c_str()); return CddlType::Op::kNone; } } // Produces CddlType by analyzing AST parsed from type1 rule // ABNF rule: type1 = type2 [S (rangeop / ctlop) S type2] CddlType* AnalyzeType1(CddlSymbolTable* table, const AstNode& type1) { if (!type1.children) { dprintf(STDERR_FILENO, "Missing type2 in type1 '%s'.\n", type1.text.c_str()); return nullptr; } const AstNode& target_type = *type1.children; CddlType* analyzed_type = AnalyzeType2(table, target_type); if (!analyzed_type) { dprintf(STDERR_FILENO, "Invalid type2 '%s' in type1 '%s'.\n", target_type.text.c_str(), type1.text.c_str()); return nullptr; } if (!target_type.sibling) { // No optional range or control operator, return type as-is return analyzed_type; } const AstNode& operator_type = *target_type.sibling; CddlType::Op op; if (operator_type.type == AstNode::Type::kRangeop) { op = AnalyzeRangeop(operator_type); } else if (operator_type.type == AstNode::Type::kCtlop) { op = AnalyzeCtlop(operator_type); } else { op = CddlType::Op::kNone; } if (op == CddlType::Op::kNone) { dprintf(STDERR_FILENO, "Unsupported or missing operator '%s' in type1 '%s'.\n", operator_type.text.c_str(), type1.text.c_str()); return nullptr; } if (!operator_type.sibling) { dprintf(STDERR_FILENO, "Missing controller type for operator '%s' in type1 '%s'.\n", operator_type.text.c_str(), type1.text.c_str()); return nullptr; } const AstNode& controller_type = *operator_type.sibling; CddlType* constraint_type = AnalyzeType2(table, controller_type); if (!constraint_type) { dprintf(STDERR_FILENO, "Invalid controller type '%s' for operator '%s' in type1 '%s'.\n", controller_type.text.c_str(), operator_type.text.c_str(), type1.text.c_str()); return nullptr; } analyzed_type->op = op; analyzed_type->constraint_type = constraint_type; return analyzed_type; } CddlType* AnalyzeType(CddlSymbolTable* table, const AstNode& type) { const AstNode* type1 = type.children; if (type1->sibling) { // If the type we are looking at has a type choice, create a top-level // choice object, with a vector containing all valid choices. CddlType* type_choice = AddCddlType(table, CddlType::Which::kDirectChoice); InitDirectChoice(&type_choice->direct_choice); while (type1) { type_choice->direct_choice.push_back(AnalyzeType1(table, *type1)); type1 = type1->sibling; } return type_choice; } else { // Else just return the single choice. return AnalyzeType1(table, *type1); } } bool AnalyzeGroupEntry(CddlSymbolTable* table, const AstNode& group_entry, CddlGroup::Entry* entry); CddlGroup* AnalyzeGroup(CddlSymbolTable* table, const AstNode& group) { // NOTE: |group.children| is a grpchoice, which we don't currently handle. // Therefore, we assume it has no siblings and move on to immediately handling // its grpent children. const AstNode* node = group.children->children; table->groups.emplace_back(new CddlGroup); CddlGroup* group_def = table->groups.back().get(); while (node) { group_def->entries.emplace_back(new CddlGroup::Entry); AnalyzeGroupEntry(table, *node, group_def->entries.back().get()); node = node->sibling; } return group_def; } // Parses a string into an optional uint64_t, with the value being that // represented by the string if it is present and nullopt if it cannot // be parsed. // TODO(rwkeane): Add support for hex and binary options. absl::optional ParseOptionalUint(const std::string& text) { if (text == "0") { return 0; } uint64_t parsed = std::strtoul(text.c_str(), nullptr, 10); if (!parsed) { return absl::nullopt; } return parsed; } bool AnalyzeGroupEntry(CddlSymbolTable* table, const AstNode& group_entry, CddlGroup::Entry* entry) { const AstNode* node = group_entry.children; // If it's an occurance operator (so the entry is optional), mark it as such // and proceed to the next the node. if (node->type == AstNode::Type::kOccur) { if (node->text == "?") { entry->opt_occurrence_min = CddlGroup::Entry::kOccurrenceMinUnbounded; entry->opt_occurrence_max = 1; } else if (node->text == "+") { entry->opt_occurrence_min = 1; entry->opt_occurrence_max = CddlGroup::Entry::kOccurrenceMaxUnbounded; } else { auto index = node->text.find('*'); if (index == std::string::npos) { return false; } int lower_bound = CddlGroup::Entry::kOccurrenceMinUnbounded; std::string first_half = node->text.substr(0, index); if ((first_half.length() != 1 || first_half.at(0) != '0') && first_half.length() != 0) { lower_bound = std::atoi(first_half.c_str()); if (!lower_bound) { return false; } } int upper_bound = CddlGroup::Entry::kOccurrenceMaxUnbounded; std::string second_half = index >= node->text.length() ? "" : node->text.substr(index + 1); if ((second_half.length() != 1 || second_half.at(0) != '0') && second_half.length() != 0) { upper_bound = std::atoi(second_half.c_str()); if (!upper_bound) { return false; } } entry->opt_occurrence_min = lower_bound; entry->opt_occurrence_max = upper_bound; } entry->occurrence_specified = true; node = node->sibling; } else { entry->opt_occurrence_min = 1; entry->opt_occurrence_max = 1; entry->occurrence_specified = false; } // If it's a member key (key in a map), save it and go to next node. if (node->type == AstNode::Type::kMemberKey) { if (node->text[node->text.size() - 1] == '>') return false; entry->which = CddlGroup::Entry::Which::kType; InitGroupEntry(&entry->type); entry->type.opt_key = std::string(node->children->text); entry->type.integer_key = ParseOptionalUint(node->integer_member_key_text); node = node->sibling; } // If it's a type, process it as such. if (node->type == AstNode::Type::kType) { if (entry->which == CddlGroup::Entry::Which::kUninitialized) { entry->which = CddlGroup::Entry::Which::kType; InitGroupEntry(&entry->type); } entry->type.value = AnalyzeType(table, *node); } else if (node->type == AstNode::Type::kGroupname) { return false; } else if (node->type == AstNode::Type::kGroup) { entry->which = CddlGroup::Entry::Which::kGroup; entry->group = AnalyzeGroup(table, *node); } return true; } std::pair BuildSymbolTable(const AstNode& rules) { std::pair result; result.first = false; auto& table = result.second; // Parse over all rules iteratively. for (const AstNode* rule = &rules; rule; rule = rule->sibling) { AstNode* node = rule->children; // Ensure that the node is either a type or group definition. if (node->type != AstNode::Type::kTypename && node->type != AstNode::Type::kGroupname) { Logger::Error("Error parsing node with text '%s'. Unexpected node type.", node->text); return result; } bool is_type = node->type == AstNode::Type::kTypename; absl::string_view name = node->text; // Ensure that the node is assignment. node = node->sibling; if (node->type != AstNode::Type::kAssign) { Logger::Error("Error parsing node with text '%s'. Node type != kAssign.", node->text); return result; } // Process the definition. node = node->sibling; if (is_type) { CddlType* type = AnalyzeType(&table, *node); if (rule->type_key != absl::nullopt) { auto parsed_type_key = ParseOptionalUint(rule->type_key.value()); if (parsed_type_key == absl::nullopt) { return result; } type->type_key = parsed_type_key.value(); } if (!type) { Logger::Error( "Error parsing node with text '%s'." "Failed to analyze node type.", node->text); } table.type_map.emplace(std::string(name), type); } else { table.groups.emplace_back(new CddlGroup); CddlGroup* group = table.groups.back().get(); group->entries.emplace_back(new CddlGroup::Entry); AnalyzeGroupEntry(&table, *node, group->entries.back().get()); table.group_map.emplace(std::string(name), group); } } DumpSymbolTable(&result.second); result.first = true; return result; } // Fetches a C++ Type from all known definitons, or inserts a placeholder to be // updated later if the type hasn't been defined yet. CppType* GetCppType(CppSymbolTable* table, const std::string& name) { if (name.empty()) { table->cpp_types.emplace_back(new CppType); return table->cpp_types.back().get(); } auto entry = table->cpp_type_map.find(name); if (entry != table->cpp_type_map.end()) return entry->second; table->cpp_types.emplace_back(new CppType); table->cpp_type_map.emplace(name, table->cpp_types.back().get()); return table->cpp_types.back().get(); } bool IncludeGroupMembersInEnum(CppSymbolTable* table, const CddlSymbolTable& cddl_table, CppType* cpp_type, const CddlGroup& group); bool IncludeGroupMembersInSubEnum(CppSymbolTable* table, const CddlSymbolTable& cddl_table, CppType* cpp_type, const std::string& name) { auto group_entry = cddl_table.group_map.find(name); if (group_entry == cddl_table.group_map.end()) { return false; } if (group_entry->second->entries.size() != 1 || group_entry->second->entries[0]->which != CddlGroup::Entry::Which::kGroup) { return false; } CppType* sub_enum = GetCppType(table, name); if (sub_enum->which == CppType::Which::kUninitialized) { sub_enum->InitEnum(); sub_enum->name = name; if (!IncludeGroupMembersInEnum(table, cddl_table, sub_enum, *group_entry->second->entries[0]->group)) { return false; } } cpp_type->enum_type.sub_members.push_back(sub_enum); return true; } bool IncludeGroupMembersInEnum(CppSymbolTable* table, const CddlSymbolTable& cddl_table, CppType* cpp_type, const CddlGroup& group) { for (const auto& x : group.entries) { if (x->HasOccurrenceOperator() || x->which != CddlGroup::Entry::Which::kType) { return false; } if (x->type.value->which == CddlType::Which::kValue && !x->type.opt_key.empty()) { cpp_type->enum_type.members.emplace_back( x->type.opt_key, atoi(x->type.value->value.c_str())); } else if (x->type.value->which == CddlType::Which::kId) { IncludeGroupMembersInSubEnum(table, cddl_table, cpp_type, x->type.value->id); } else { return false; } } return true; } CppType* MakeCppType(CppSymbolTable* table, const CddlSymbolTable& cddl_table, const std::string& name, const CddlType& type); bool AddMembersToStruct( CppSymbolTable* table, const CddlSymbolTable& cddl_table, CppType* cpp_type, const std::vector>& entries) { for (const auto& x : entries) { if (x->which == CddlGroup::Entry::Which::kType) { if (x->type.opt_key.empty()) { // If the represented node has no text (ie - it's code generated) then // it must have an inner type that is based on the user input. If this // one looks as expected, process it recursively. if (x->type.value->which != CddlType::Which::kId || x->HasOccurrenceOperator()) { return false; } auto group_entry = cddl_table.group_map.find(x->type.value->id); if (group_entry == cddl_table.group_map.end()) return false; if (group_entry->second->entries.size() != 1 || group_entry->second->entries[0]->which != CddlGroup::Entry::Which::kGroup) { return false; } if (!AddMembersToStruct( table, cddl_table, cpp_type, group_entry->second->entries[0]->group->entries)) { return false; } } else { // Here it is a real type definition - so process it as such. CppType* member_type = MakeCppType(table, cddl_table, cpp_type->name + std::string("_") + x->type.opt_key, *x->type.value); if (!member_type) return false; if (member_type->name.empty()) member_type->name = x->type.opt_key; if (x->opt_occurrence_min == CddlGroup::Entry::kOccurrenceMinUnbounded && x->opt_occurrence_max == 1) { // Create an "optional" type, with sub-type being the type that is // optional. This corresponds with occurrence operator '?'. table->cpp_types.emplace_back(new CppType); CppType* optional_type = table->cpp_types.back().get(); optional_type->which = CppType::Which::kOptional; optional_type->optional_type = member_type; cpp_type->struct_type.members.emplace_back( x->type.opt_key, x->type.integer_key, optional_type); } else { cpp_type->struct_type.members.emplace_back( x->type.opt_key, x->type.integer_key, member_type); } } } else { // If it's not a type, it's a group so add its members recursuvely. if (!AddMembersToStruct(table, cddl_table, cpp_type, x->group->entries)) return false; } } return true; } CppType* MakeCppType(CppSymbolTable* table, const CddlSymbolTable& cddl_table, const std::string& name, const CddlType& type) { CppType* cpp_type = nullptr; switch (type.which) { case CddlType::Which::kId: { if (type.id == "uint") { cpp_type = GetCppType(table, name); cpp_type->which = CppType::Which::kUint64; } else if (type.id == "text") { cpp_type = GetCppType(table, name); cpp_type->which = CppType::Which::kString; } else if (type.id == "bytes") { cpp_type = GetCppType(table, name); cpp_type->InitBytes(); if (type.op == CddlType::Op::kSize) { size_t size = 0; if (!absl::SimpleAtoi(type.constraint_type->value, &size)) { return nullptr; } cpp_type->bytes_type.fixed_size = size; } } else { cpp_type = GetCppType(table, type.id); } } break; case CddlType::Which::kMap: { cpp_type = GetCppType(table, name); cpp_type->InitStruct(); cpp_type->struct_type.key_type = CppType::Struct::KeyType::kMap; cpp_type->name = name; if (!AddMembersToStruct(table, cddl_table, cpp_type, type.map->entries)) return nullptr; } break; case CddlType::Which::kArray: { cpp_type = GetCppType(table, name); if (type.array->entries.size() == 1 && type.array->entries[0]->HasOccurrenceOperator()) { cpp_type->InitVector(); cpp_type->vector_type.min_length = type.array->entries[0]->opt_occurrence_min; cpp_type->vector_type.max_length = type.array->entries[0]->opt_occurrence_max; cpp_type->vector_type.element_type = GetCppType(table, type.array->entries[0]->type.value->id); } else { cpp_type->InitStruct(); cpp_type->struct_type.key_type = CppType::Struct::KeyType::kArray; cpp_type->name = name; if (!AddMembersToStruct(table, cddl_table, cpp_type, type.map->entries)) { return nullptr; } } } break; case CddlType::Which::kGroupChoice: { cpp_type = GetCppType(table, name); cpp_type->InitEnum(); cpp_type->name = name; if (!IncludeGroupMembersInEnum(table, cddl_table, cpp_type, *type.group_choice)) { return nullptr; } } break; case CddlType::Which::kGroupnameChoice: { cpp_type = GetCppType(table, name); cpp_type->InitEnum(); cpp_type->name = name; if (!IncludeGroupMembersInSubEnum(table, cddl_table, cpp_type, type.id)) { return nullptr; } } break; case CddlType::Which::kDirectChoice: { cpp_type = GetCppType(table, name); cpp_type->InitDiscriminatedUnion(); for (const auto* cddl_choice : type.direct_choice) { CppType* member = MakeCppType(table, cddl_table, "", *cddl_choice); if (!member) return nullptr; cpp_type->discriminated_union.members.push_back(member); } return cpp_type; } break; case CddlType::Which::kTaggedType: { cpp_type = GetCppType(table, name); cpp_type->which = CppType::Which::kTaggedType; cpp_type->tagged_type.tag = type.tagged_type.tag_value; cpp_type->tagged_type.real_type = MakeCppType(table, cddl_table, "", *type.tagged_type.type); } break; default: return nullptr; } cpp_type->type_key = type.type_key; return cpp_type; } void PrePopulateCppTypes(CppSymbolTable* table) { std::vector> default_types; default_types.emplace_back("text", CppType::Which::kString); default_types.emplace_back("tstr", CppType::Which::kString); default_types.emplace_back("bstr", CppType::Which::kBytes); default_types.emplace_back("bytes", CppType::Which::kBytes); default_types.emplace_back("uint", CppType::Which::kUint64); for (auto& pair : default_types) { auto entry = table->cpp_type_map.find(pair.first); if (entry != table->cpp_type_map.end()) continue; table->cpp_types.emplace_back(new CppType); auto* type = table->cpp_types.back().get(); type->name = pair.first; type->which = pair.second; table->cpp_type_map.emplace(pair.first, type); } } std::pair BuildCppTypes( const CddlSymbolTable& cddl_table) { std::pair result; result.first = false; PrePopulateCppTypes(&result.second); auto& table = result.second; for (const auto& type_entry : cddl_table.type_map) { if (!MakeCppType(&table, cddl_table, type_entry.first, *type_entry.second)) { return result; } } result.first = true; return result; } bool VerifyUniqueKeysInMember(std::unordered_set* keys, const CppType::Struct::CppMember& member) { return keys->insert(member.name).second && (!member.integer_key.has_value() || keys->insert(std::to_string(member.integer_key.value())).second); } bool HasUniqueKeys(const CppType& type) { std::unordered_set keys; return type.which != CppType::Which::kStruct || absl::c_all_of(type.struct_type.members, [&keys](const CppType::Struct::CppMember& member) { return VerifyUniqueKeysInMember(&keys, member); }); } bool IsUniqueEnumValue(std::vector* values, uint64_t v) { auto it = std::lower_bound(values->begin(), values->end(), v); if (it == values->end() || *it != v) { values->insert(it, v); return true; } return false; } bool HasUniqueEnumValues(std::vector* values, const CppType& type) { return absl::c_all_of(type.enum_type.sub_members, [values](CppType* sub_member) { return HasUniqueEnumValues(values, *sub_member); }) && absl::c_all_of( type.enum_type.members, [values](const std::pair& member) { return IsUniqueEnumValue(values, member.second); }); } bool HasUniqueEnumValues(const CppType& type) { std::vector values; return type.which != CppType::Which::kEnum || HasUniqueEnumValues(&values, type); } bool ValidateCppTypes(const CppSymbolTable& cpp_symbols) { return absl::c_all_of( cpp_symbols.cpp_types, [](const std::unique_ptr& ptr) { return HasUniqueKeys(*ptr) && HasUniqueEnumValues(*ptr); }); } std::string DumpTypeKey(absl::optional key) { if (key != absl::nullopt) { return " (type key=\"" + std::to_string(key.value()) + "\")"; } return ""; } void DumpType(CddlType* type, int indent_level) { std::string output = ""; for (int i = 0; i <= indent_level; ++i) output += "--"; switch (type->which) { case CddlType::Which::kDirectChoice: output = "kDirectChoice" + DumpTypeKey(type->type_key) + ": "; Logger::Log(output); for (auto& option : type->direct_choice) DumpType(option, indent_level + 1); break; case CddlType::Which::kValue: output += "kValue" + DumpTypeKey(type->type_key) + ": " + type->value; Logger::Log(output); break; case CddlType::Which::kId: output += "kId" + DumpTypeKey(type->type_key) + ": " + type->id; Logger::Log(output); break; case CddlType::Which::kMap: output += "kMap" + DumpTypeKey(type->type_key) + ": "; Logger::Log(output); DumpGroup(type->map, indent_level + 1); break; case CddlType::Which::kArray: output += "kArray" + DumpTypeKey(type->type_key) + ": "; Logger::Log(output); DumpGroup(type->array, indent_level + 1); break; case CddlType::Which::kGroupChoice: output += "kGroupChoice" + DumpTypeKey(type->type_key) + ": "; Logger::Log(output); DumpGroup(type->group_choice, indent_level + 1); break; case CddlType::Which::kGroupnameChoice: output += "kGroupnameChoice" + DumpTypeKey(type->type_key) + ": "; Logger::Log(output); break; case CddlType::Which::kTaggedType: output += "kTaggedType" + DumpTypeKey(type->type_key) + ": " + std::to_string(type->tagged_type.tag_value); Logger::Log(output); DumpType(type->tagged_type.type, indent_level + 1); break; } } void DumpGroup(CddlGroup* group, int indent_level) { for (auto& entry : group->entries) { std::string output = ""; for (int i = 0; i <= indent_level; ++i) output += "--"; switch (entry->which) { case CddlGroup::Entry::Which::kUninitialized: break; case CddlGroup::Entry::Which::kType: output += "kType:"; if (entry->HasOccurrenceOperator()) { output += "minOccurance: " + std::to_string(entry->opt_occurrence_min) + " maxOccurance: " + std::to_string(entry->opt_occurrence_max); } if (!entry->type.opt_key.empty()) { output += " " + entry->type.opt_key + "=>"; } Logger::Log(output); DumpType(entry->type.value, indent_level + 1); break; case CddlGroup::Entry::Which::kGroup: if (entry->HasOccurrenceOperator()) output += "minOccurance: " + std::to_string(entry->opt_occurrence_min) + " maxOccurance: " + std::to_string(entry->opt_occurrence_max); Logger::Log(output); DumpGroup(entry->group, indent_level + 1); break; } } } void DumpSymbolTable(CddlSymbolTable* table) { for (auto& entry : table->type_map) { Logger::Log(entry.first); DumpType(entry.second); } for (auto& entry : table->group_map) { Logger::Log(entry.first); DumpGroup(entry.second); } }