blob: 2ecb162a9f4c9ea1183a0068e8db7aa8dc72eff9 [file] [log] [blame]
// 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 <string.h>
#include <unistd.h>
#include <cinttypes>
#include <cstdlib>
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <unordered_set>
#include <vector>
#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<CppType*> CppSymbolTable::TypesWithId() {
if (!this->TypesWithId_.size()) {
for (const std::unique_ptr<CppType>& 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<CddlType*>::~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<CddlType*>* direct_choice) {
new (direct_choice) std::vector<CddlType*>();
}
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<uint64_t> 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<bool, CddlSymbolTable> BuildSymbolTable(const AstNode& rules) {
std::pair<bool, CddlSymbolTable> 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<std::unique_ptr<CddlGroup::Entry>>& 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<std::pair<std::string, CppType::Which>> 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<bool, CppSymbolTable> BuildCppTypes(
const CddlSymbolTable& cddl_table) {
std::pair<bool, CppSymbolTable> 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<std::string>* 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<std::string> 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<uint64_t>* 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<uint64_t>* 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<std::string, uint64_t>& member) {
return IsUniqueEnumValue(values, member.second);
});
}
bool HasUniqueEnumValues(const CppType& type) {
std::vector<uint64_t> 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<CppType>& ptr) {
return HasUniqueKeys(*ptr) && HasUniqueEnumValues(*ptr);
});
}
std::string DumpTypeKey(absl::optional<uint64_t> 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);
}
}