You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
925 lines
31 KiB
925 lines
31 KiB
// 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);
|
|
}
|
|
}
|