1134 lines
43 KiB
C++
1134 lines
43 KiB
C++
#include "schema_creator.h"
|
|
|
|
#include<set>
|
|
#include <utility>
|
|
|
|
namespace domain {
|
|
|
|
|
|
ComdelContext::ComdelContext(std::string name, bool inComponent, bool inConnection, bool inSingleAutomaticConnection, bool inBus)
|
|
: name(std::move(name)), inComponent(inComponent), inConnection(inConnection), inSingleAutomaticConnection(inSingleAutomaticConnection), inBus(inBus) {}
|
|
|
|
bool ComdelContext::doesAttributeExists(std::string name, Value::ValueType type) {
|
|
for (auto &attribute: attributes) {
|
|
if (attribute.getDefault().getType() == type && attribute.getName() == name) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ComdelContext::doesWireExists(std::string name) {
|
|
for (auto &w: wires) {
|
|
if (w == name) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
Component::ComponentType toType(ComponentNode::ComponentType type) {
|
|
if (type == ComponentNode::MEMORY) {
|
|
return Component::MEMORY;
|
|
} else if (type == ComponentNode::PROCESSOR) {
|
|
return Component::PROCESSOR;
|
|
}
|
|
return Component::OTHER;
|
|
}
|
|
|
|
Action::ActionType toType(ActionNode::ActionType type) {
|
|
if (type == ActionNode::ERROR) {
|
|
return Action::ERROR;
|
|
}
|
|
return Action::WARNING;
|
|
}
|
|
|
|
Wire::WireType toType(WireNode::WireType type) {
|
|
switch (type) {
|
|
case WireNode::R_WIRE:
|
|
return Wire::R_WIRE;
|
|
case WireNode::WIRE:
|
|
return Wire::WIRE_DEFAULT;
|
|
case WireNode::WIRED_AND:
|
|
return Wire::WIRED_AND;
|
|
case WireNode::WIRED_OR:
|
|
return Wire::WIRED_OR;
|
|
default:
|
|
return Wire::WIRE_DEFAULT;
|
|
}
|
|
}
|
|
|
|
Value::ValueType toType(ValueNode::ValueType type) {
|
|
switch (type) {
|
|
case ValueNode::BOOL:
|
|
return Value::BOOL;
|
|
case ValueNode::MEMORY:
|
|
return Value::MEMORY_REFERENCE;
|
|
case ValueNode::WIRE:
|
|
return Value::WIRE_REFERENCE;
|
|
case ValueNode::STRING:
|
|
return Value::STRING;
|
|
case ValueNode::INT:
|
|
return Value::INT;
|
|
case ValueNode::NIL:
|
|
return Value::NIL;
|
|
default:
|
|
return Value::UNDEFINED;
|
|
}
|
|
}
|
|
|
|
|
|
Value toType(ValueNode node, Value::ValueType type = Value::ValueType::UNDEFINED) {
|
|
if (type == Value::MEMORY_REFERENCE) {
|
|
if (node.is(ValueNode::NIL)) {
|
|
return Value::fromMemoryReference(nullopt);
|
|
} else {
|
|
return Value::fromMemoryReference(node.asIdentifier());
|
|
}
|
|
}
|
|
|
|
if (node.is(ValueNode::BOOL)) {
|
|
return Value::fromBool(node.asBool());
|
|
} else if (node.is(ValueNode::INT)) {
|
|
return Value::fromInt(node.asInt());
|
|
} else if (node.is(ValueNode::STRING)) {
|
|
return Value::fromString(node.asString());
|
|
} else if (node.is(ValueNode::NIL)) {
|
|
return Value::fromNull();
|
|
}
|
|
return Value::fromReference(node.asIdentifier(), Value::UNDEFINED);
|
|
}
|
|
|
|
|
|
Bus::BusType toType(BusNode::BusType type) {
|
|
if (type == BusNode::AUTOMATIC) {
|
|
return Bus::AUTOMATIC;
|
|
} else if (type == BusNode::SINGLE_AUTOMATIC) {
|
|
return Bus::SINGLE_AUTOMATIC;
|
|
}
|
|
return Bus::REGULAR;
|
|
}
|
|
|
|
|
|
Pin::PinType toType(PinNode::PinType type) {
|
|
if (type == PinNode::IN) {
|
|
return Pin::IN;
|
|
} else if (type == PinNode::OUT) {
|
|
return Pin::OUT;
|
|
}
|
|
return Pin::IN_OUT;
|
|
}
|
|
|
|
Popup::PopupType toType(PopupNode::PopupType type) {
|
|
if (type == PopupNode::AUTOMATIC) {
|
|
return Popup::AUTOMATIC;
|
|
}
|
|
return Popup::ON_DEMAND;
|
|
}
|
|
|
|
SchemaCreator::SchemaCreator(std::vector<FunctionValidator *> validators)
|
|
: validators(std::move(validators)) {}
|
|
|
|
std::optional<Library> SchemaCreator::loadLibrary(LibraryNode node) {
|
|
// library fields
|
|
if (!node.name) {
|
|
errors.emplace_back(node.span, "missing @name");
|
|
return nullopt;
|
|
} else {
|
|
name = node.name->asString();
|
|
}
|
|
|
|
if (node.componentHeader.has_value()) {
|
|
componentHeader = node.componentHeader->asString();
|
|
}
|
|
|
|
if (!node.componentDirectory) {
|
|
errors.emplace_back(node.span, "missing @componentDirectory");
|
|
return nullopt;
|
|
} else {
|
|
componentDirectory = node.componentDirectory->asString();
|
|
}
|
|
|
|
header = node.header ? node.header->asString() : "";
|
|
|
|
libraryInfo = node.libraryInfo ? node.libraryInfo->asString() : "";
|
|
|
|
for (auto &as: node.addressSpaces) {
|
|
addressSpaces.push_back(*loadAddressSpace(as));
|
|
}
|
|
|
|
for (auto &comp: node.components) {
|
|
auto component = loadComponent(comp);
|
|
if (component) {
|
|
components.push_back(*component);
|
|
}
|
|
}
|
|
|
|
for (auto &buse: node.buses) {
|
|
auto bus = loadBus(buse);
|
|
if (bus) {
|
|
buses.push_back(*bus);
|
|
}
|
|
}
|
|
|
|
for (auto &connection: node.connections) {
|
|
auto conn = loadConnection(connection);
|
|
if (conn) {
|
|
connections.push_back(*conn);
|
|
}
|
|
}
|
|
|
|
for (auto &message: node.messages) {
|
|
if (!message.value.is(ValueNode::STRING)) {
|
|
errors.emplace_back(message.value.span, "expected `string`");
|
|
} else {
|
|
messages[message.key.value] = message.value.asString();
|
|
}
|
|
}
|
|
|
|
if(errors.empty()) {
|
|
return Library(name, libraryInfo, header, componentDirectory, componentHeader, addressSpaces, components, buses,
|
|
connections, messages);
|
|
} else {
|
|
return nullopt;
|
|
}
|
|
|
|
}
|
|
|
|
std::optional<Bus> SchemaCreator::loadBus(BusNode node) {
|
|
std::string busName = node.name.value;
|
|
|
|
if (!node.instanceName) {
|
|
errors.emplace_back(node.span, "missing @instanceName");
|
|
return nullopt;
|
|
}
|
|
std::string instanceName = node.instanceName->value;
|
|
|
|
auto count = std::make_pair<int, int>(1, 1);
|
|
if (node.count) {
|
|
count = std::make_pair<int, int>(node.count->first.value, node.count->second.value);
|
|
}
|
|
if (count.first > count.second || count.first < 0) {
|
|
errors.emplace_back(node.count->span, "invalid @size");
|
|
return nullopt;
|
|
}
|
|
|
|
auto type = toType(node.type.value);
|
|
|
|
if (!node.tooltip && type == Bus::REGULAR) {
|
|
errors.emplace_back(node.span, "missing @tooltip");
|
|
return nullopt;
|
|
}
|
|
std::string tooltip = node.tooltip->asString();
|
|
|
|
if (!node.display && type == Bus::REGULAR) {
|
|
errors.emplace_back(node.span, "missing @display");
|
|
return nullopt;
|
|
}
|
|
if (node.display && (type == Bus::AUTOMATIC || type == Bus::SINGLE_AUTOMATIC)) {
|
|
errors.emplace_back(node.span, "automatic bus cannot have a @display");
|
|
return nullopt;
|
|
}
|
|
|
|
optional<ui::Bus> displayBus;
|
|
if (type == Bus::REGULAR) {
|
|
auto display = loadDisplay(*node.display);
|
|
if (!display) {
|
|
return nullopt;
|
|
}
|
|
if (display->getItems().size() != 1 || !display->getItems()[0].bus.has_value()) {
|
|
errors.emplace_back(node.span, "@display must contain only exactly one bus definition");
|
|
return nullopt;
|
|
}
|
|
displayBus = *display->getItems()[0].bus;
|
|
}
|
|
|
|
if (node.wires.empty()) {
|
|
errors.emplace_back(node.span, "missing @wires");
|
|
return nullopt;
|
|
}
|
|
std::vector<Wire> wires;
|
|
for (auto &_wire: node.wires) {
|
|
auto wire = loadWire(_wire);
|
|
if (wire) {
|
|
wires.push_back(*wire);
|
|
}
|
|
}
|
|
|
|
if (type == Bus::SINGLE_AUTOMATIC && wires.size() != 1) {
|
|
errors.emplace_back(node.span, "singleAutomatic bus must have exactly 1 wire defined");
|
|
return nullopt;
|
|
}
|
|
|
|
return Bus(busName, instanceName, tooltip, type, count, wires, displayBus);
|
|
}
|
|
|
|
|
|
std::optional<AddressSpace> SchemaCreator::loadAddressSpace(AddressSpaceNode node) {
|
|
return AddressSpace(node.name.value, node.range.first.value, node.range.second.value);
|
|
}
|
|
|
|
std::optional<Connection> SchemaCreator::loadConnection(ConnectionNode node) {
|
|
push(ComdelContext("connection", false, true, false, false));
|
|
|
|
std::string bus = node.bus.value;
|
|
auto busInstance = getBus(bus);
|
|
if (!busInstance) {
|
|
errors.emplace_back(node.span, "bus does not exist");
|
|
}
|
|
|
|
if (busInstance->getType() == Bus::SINGLE_AUTOMATIC) {
|
|
current().inSingleAutomaticConnection = true;
|
|
}
|
|
|
|
if (busInstance->getType() == Bus::REGULAR) {
|
|
ConnectionComponent first{node.first.component.value, node.first.pin.value};
|
|
|
|
auto componentInstance = getComponentPin(first.component, first.pin);
|
|
if (!componentInstance) {
|
|
errors.emplace_back(node.span, "pin does not exist");
|
|
}
|
|
|
|
if (node.second.has_value()) {
|
|
errors.emplace_back(node.span, "regular bus doesn't allow direct connections");
|
|
}
|
|
|
|
std::set<std::string> wireNames;
|
|
for (auto &wire: busInstance->getWires()) {
|
|
wireNames.insert(wire.getName());
|
|
current().wires.push_back(wire.getName());
|
|
}
|
|
|
|
std::vector<Attribute> attributes;
|
|
for (auto &attribute: node.attributes) {
|
|
auto attr = loadAttribute(attribute);
|
|
if (!attr) {
|
|
return nullopt;
|
|
}
|
|
attributes.push_back(*attr);
|
|
}
|
|
|
|
std::set<std::string> attributeNames;
|
|
for (auto attribute: attributes) {
|
|
attributeNames.insert(attribute.getName());
|
|
}
|
|
|
|
std::vector<Value> wires;
|
|
for (auto &firstWire: node.firstWires) {
|
|
if (firstWire.is(ValueNode::NIL)) {
|
|
wires.push_back(Value::fromNull());
|
|
} else if (firstWire.is(ValueNode::INT)) {
|
|
wires.push_back(Value::fromInt(firstWire.asInt()));
|
|
} else if (firstWire.is(ValueNode::IDENTIFIER)) {
|
|
if (attributeNames.count(firstWire.asIdentifier())) {
|
|
wires.push_back(Value::fromReference(firstWire.asIdentifier(), Value::ATTRIBUTE_REFERENCE));
|
|
} else if (wireNames.count(firstWire.asIdentifier())) {
|
|
wires.push_back(Value::fromReference(firstWire.asIdentifier(), Value::WIRE_REFERENCE));
|
|
} else {
|
|
errors.emplace_back(firstWire.span, "unknown identifier");
|
|
}
|
|
} else {
|
|
errors.emplace_back(firstWire.span, "unknown value type");
|
|
}
|
|
}
|
|
|
|
pop();
|
|
|
|
return Connection(first, nullopt, bus, attributes, wires, nullopt);
|
|
} else if (busInstance->getType() == Bus::AUTOMATIC || busInstance->getType() == Bus::SINGLE_AUTOMATIC) {
|
|
ConnectionComponent first{node.first.component.value, node.first.pin.value};
|
|
|
|
if (!node.second.has_value()) {
|
|
errors.emplace_back(node.span, "missing second component");
|
|
}
|
|
|
|
ConnectionComponent second{node.second->component.value, node.second->pin.value};
|
|
|
|
auto firstComponentInstance = getComponentPin(first.component, first.pin);
|
|
if (!firstComponentInstance) {
|
|
errors.emplace_back(node.span, "pin does not exist");
|
|
}
|
|
|
|
auto secondComponentInstance = getComponentPin(second.component, second.pin);
|
|
if (!secondComponentInstance) {
|
|
errors.emplace_back(node.span, "pin does not exist");
|
|
}
|
|
|
|
std::set<std::string> wireNames;
|
|
for (auto &wire: busInstance->getWires()) {
|
|
wireNames.insert(wire.getName());
|
|
current().wires.push_back(wire.getName());
|
|
}
|
|
|
|
if (node.attributes.size() != 2 && busInstance->getType() == Bus::SINGLE_AUTOMATIC) {
|
|
errors.emplace_back(node.span, "singleAutomatic must contain 2 attributes");
|
|
}
|
|
|
|
std::vector<Attribute> attributes;
|
|
for (auto &attribute: node.attributes) {
|
|
auto attr = loadAttribute(attribute);
|
|
if (!attr) {
|
|
return nullopt;
|
|
}
|
|
attributes.push_back(*attr);
|
|
}
|
|
|
|
std::set<std::string> attributeNames;
|
|
for (auto attribute: attributes) {
|
|
attributeNames.insert(attribute.getName());
|
|
}
|
|
|
|
std::vector<Value> firstWires;
|
|
for (auto &firstWire: node.firstWires) {
|
|
if (firstWire.is(ValueNode::NIL)) {
|
|
firstWires.push_back(Value::fromNull());
|
|
} else if (firstWire.is(ValueNode::INT)) {
|
|
firstWires.push_back(Value::fromInt(firstWire.asInt()));
|
|
} else if (firstWire.is(ValueNode::STRING) && busInstance->getType() == Bus::SINGLE_AUTOMATIC) {
|
|
firstWires.push_back(Value::fromString(firstWire.asString()));
|
|
} else if (firstWire.is(ValueNode::IDENTIFIER) && busInstance->getType() == Bus::AUTOMATIC) {
|
|
if (attributeNames.count(firstWire.asIdentifier())) {
|
|
firstWires.push_back(
|
|
Value::fromReference(firstWire.asIdentifier(), Value::ATTRIBUTE_REFERENCE));
|
|
} else if (wireNames.count(firstWire.asIdentifier())) {
|
|
firstWires.push_back(Value::fromReference(firstWire.asIdentifier(), Value::WIRE_REFERENCE));
|
|
} else {
|
|
errors.emplace_back(firstWire.span, "unknown identifier");
|
|
}
|
|
} else {
|
|
errors.emplace_back(firstWire.span, "unsupported value type");
|
|
}
|
|
}
|
|
|
|
|
|
std::vector<Value> secondWires;
|
|
for (auto &secondWire: *node.secondWires) {
|
|
if (secondWire.is(ValueNode::NIL)) {
|
|
secondWires.push_back(Value::fromNull());
|
|
} else if (secondWire.is(ValueNode::INT)) {
|
|
secondWires.push_back(Value::fromInt(secondWire.asInt()));
|
|
} else if (secondWire.is(ValueNode::STRING) && busInstance->getType() == Bus::SINGLE_AUTOMATIC) {
|
|
secondWires.push_back(Value::fromString(secondWire.asString()));
|
|
} else if (secondWire.is(ValueNode::IDENTIFIER) && busInstance->getType() == Bus::AUTOMATIC) {
|
|
if (attributeNames.count(secondWire.asIdentifier())) {
|
|
secondWires.push_back(
|
|
Value::fromReference(secondWire.asIdentifier(), Value::ATTRIBUTE_REFERENCE));
|
|
} else if (wireNames.count(secondWire.asIdentifier())) {
|
|
secondWires.push_back(Value::fromReference(secondWire.asIdentifier(), Value::WIRE_REFERENCE));
|
|
} else {
|
|
errors.emplace_back(secondWire.span, "unknown identifier");
|
|
}
|
|
} else {
|
|
errors.emplace_back(secondWire.span, "unsupported value type");
|
|
}
|
|
}
|
|
|
|
if (busInstance->getType() == Bus::SINGLE_AUTOMATIC && attributes.size() == 2) {
|
|
if (!attributes[0].getPopup().has_value()) {
|
|
errors.emplace_back(node.attributes[0].span, "@popup is required");
|
|
} else if (attributes[0].getDefault().getType() != Value::STRING) {
|
|
errors.emplace_back(node.attributes[0].span, "@attribute must be of type string");
|
|
} else {
|
|
domain::Popup popup = *attributes[0].getPopup();
|
|
popup.setEnumeration(createWireEnumeration(firstWires));
|
|
attributes[0].setPupup(popup);
|
|
}
|
|
|
|
if (!attributes[1].getPopup().has_value()) {
|
|
errors.emplace_back(node.attributes[1].span, "@popup is required");
|
|
} else if (attributes[1].getDefault().getType() != Value::STRING) {
|
|
errors.emplace_back(node.attributes[1].span, "@attribute must be of type string");
|
|
} else {
|
|
domain::Popup popup = *attributes[1].getPopup();
|
|
popup.setEnumeration(createWireEnumeration(secondWires));
|
|
attributes[1].setPupup(popup);
|
|
}
|
|
}
|
|
|
|
pop();
|
|
|
|
return Connection(first, second, bus, attributes, firstWires, secondWires);
|
|
}
|
|
pop();
|
|
errors.emplace_back(node.span, "unsupported connection type");
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Component> SchemaCreator::loadComponent(ComponentNode node) {
|
|
push(ComdelContext(node.name.value, true, false, false, false));
|
|
|
|
std::string componentName = node.name.value;
|
|
|
|
if (!node.tooltip) {
|
|
errors.emplace_back(node.span, "missing @tooltip");
|
|
pop();
|
|
return nullopt;
|
|
}
|
|
std::string tooltip = node.tooltip->asString();
|
|
|
|
if (!node.source) {
|
|
errors.emplace_back(node.span, "missing @source");
|
|
pop();
|
|
return nullopt;
|
|
}
|
|
std::string source = node.source->asString();
|
|
|
|
Component::ComponentType type = toType(node.type.value);
|
|
|
|
std::vector<Attribute> attributes;
|
|
for (auto &a: node.attributes) {
|
|
std::optional<Attribute> attribute = loadAttribute(a);
|
|
if (attribute) {
|
|
attributes.push_back(*attribute);
|
|
}
|
|
}
|
|
if (type == Component::PROCESSOR) {
|
|
attributes.push_back(*createMemoryAttribute());
|
|
}
|
|
|
|
context[context.size() - 1].attributes = attributes;
|
|
|
|
std::vector<Rule> rules;
|
|
for (auto &r: node.rules) {
|
|
std::optional<Rule> rule = loadRule(r);
|
|
if (rule) {
|
|
rules.push_back(*rule);
|
|
}
|
|
}
|
|
|
|
if (!node.instanceName) {
|
|
errors.emplace_back(node.span, "missing @instanceName");
|
|
pop();
|
|
return nullopt;
|
|
}
|
|
std::string instanceName = node.instanceName->value;
|
|
|
|
auto count = std::make_pair<int, int>(1, 1);
|
|
if (node.count) {
|
|
count = std::make_pair<int, int>(node.count->first.value, node.count->second.value);
|
|
}
|
|
if (count.first > count.second || count.first < 0) {
|
|
errors.emplace_back(node.count->first.span, "invalid @size");
|
|
pop();
|
|
return nullopt;
|
|
}
|
|
|
|
if (!node.display) {
|
|
errors.emplace_back(node.span, "missing @display");
|
|
pop();
|
|
return nullopt;
|
|
}
|
|
optional<Display> display = loadDisplay(*node.display);
|
|
if (!display) {
|
|
pop();
|
|
return nullopt;
|
|
}
|
|
|
|
std::vector<Pin> pins;
|
|
for (auto &p: node.pins) {
|
|
auto pin = loadPin(p);
|
|
if (!pin) {
|
|
pop();
|
|
return nullopt;
|
|
}
|
|
pins.push_back(*pin);
|
|
}
|
|
|
|
pop();
|
|
return Component(componentName, tooltip, source, type, rules, instanceName, count, *display, pins, attributes);
|
|
}
|
|
|
|
std::optional<Wire> SchemaCreator::loadWire(WireNode node) {
|
|
return Wire(
|
|
node.name.value,
|
|
toType(node.type.value),
|
|
node.size.value,
|
|
node.hidden,
|
|
node.hasTerminateWith,
|
|
toType(node.terminateWith)
|
|
);
|
|
}
|
|
|
|
optional<Pin> SchemaCreator::loadPin(PinNode node) {
|
|
std::string pinName = node.name.value;
|
|
Pin::PinType type = toType(node.type.value);
|
|
|
|
if (!node.tooltip) {
|
|
errors.emplace_back(node.span, "missing @tooltip");
|
|
return nullopt;
|
|
}
|
|
std::string tooltip = node.tooltip->asString();
|
|
|
|
if (!node.display) {
|
|
errors.emplace_back(node.span, "missing @display");
|
|
return nullopt;
|
|
}
|
|
std::vector<Attribute> attributes;
|
|
optional<Display> display = loadDisplay(*node.display);
|
|
if (!display) {
|
|
return nullopt;
|
|
}
|
|
if (display->getItems().size() != 1 || !display->getItems()[0].pin.has_value()) {
|
|
errors.emplace_back(node.span, "@display must contain only exactly one pin definition");
|
|
return nullopt;
|
|
}
|
|
ui::Pin displayPin = *display->getItems()[0].pin;
|
|
|
|
std::optional<std::string> connection = std::nullopt;
|
|
if (node.connection) {
|
|
connection = node.connection->asString();
|
|
}
|
|
|
|
std::optional<std::vector<Value>> wiresOpt = std::nullopt;
|
|
if (node.wires.has_value()) {
|
|
auto nodeWires = node.wires.value();
|
|
std::vector<Value> wires;
|
|
for (auto &nodeWire: nodeWires) {
|
|
if (nodeWire.is(ValueNode::NIL)) {
|
|
wires.push_back(Value::fromNull());
|
|
} else if (nodeWire.is(ValueNode::INT)) {
|
|
wires.push_back(Value::fromInt(nodeWire.asInt()));
|
|
} else {
|
|
errors.emplace_back(node.span, "unknown value type");
|
|
}
|
|
}
|
|
wiresOpt = wires;
|
|
}
|
|
|
|
return Pin(pinName, type, tooltip, connection, displayPin, wiresOpt);
|
|
}
|
|
|
|
Color readColor(std::vector<SourceError>& errors, DisplayItemNode& item, std::string property, Color _default) {
|
|
auto color = item.asColor(property, _default);
|
|
if(!color.has_value()) {
|
|
errors.emplace_back(item.span, "expected color");
|
|
return _default;
|
|
}
|
|
return *color;
|
|
}
|
|
|
|
std::string readString(std::vector<SourceError>& errors, DisplayItemNode& item, std::string property, std::string _default = "") {
|
|
auto value = item.asString(property, _default);
|
|
if(!value.has_value()) {
|
|
errors.emplace_back(item.span, "expected string");
|
|
return _default;
|
|
}
|
|
return *value;
|
|
}
|
|
|
|
long long int readInt(std::vector<SourceError>& errors, DisplayItemNode& item, std::string property, long long int _default = 0) {
|
|
auto value = item.asInt(property, _default);
|
|
if(!value.has_value()) {
|
|
errors.emplace_back(item.span, "expected int");
|
|
return _default;
|
|
}
|
|
return *value;
|
|
}
|
|
|
|
std::optional<Display> SchemaCreator::loadDisplay(DisplayNode node) {
|
|
std::vector<ui::Item> items;
|
|
for (auto &item: node.items) {
|
|
ui::Item displayItem;
|
|
std::string type = item.type.value;
|
|
|
|
auto fillColor = readColor(errors, item, "fillColor", Color(255, 255, 255, 255));
|
|
auto lineColor = readColor(errors, item, "lineColor", Color(0, 0, 0, 255));
|
|
|
|
if (type == "text") {
|
|
long long int x, y, w, h;
|
|
std::string text;
|
|
auto color = readColor(errors, item, "color", Color(0, 0, 0, 255));
|
|
x = readInt(errors, item, "x");
|
|
y = readInt(errors, item, "y");
|
|
w = readInt(errors, item, "w");
|
|
h = readInt(errors, item, "h");
|
|
text = readString(errors, item, "text");
|
|
displayItem.text = ui::Text(x, y, w, h, text, color);
|
|
} else if (type == "rect") {
|
|
long long int x, y, w, h;
|
|
x = readInt(errors, item, "x");
|
|
y = readInt(errors, item, "y");
|
|
w = readInt(errors, item, "w");
|
|
h = readInt(errors, item, "h");
|
|
displayItem.rect = ui::Rect(x, y, w, h, {lineColor, fillColor});
|
|
} else if (type == "line") {
|
|
long long int x1, y1, x2, y2;
|
|
x1 = readInt(errors, item, "x1");
|
|
y1 = readInt(errors, item, "y1");
|
|
x2 = readInt(errors, item, "x2");
|
|
y2 = readInt(errors, item, "y2");
|
|
displayItem.line = ui::Line(x1, y1, x2, y2, {lineColor, fillColor});
|
|
} else if (type == "pin") {
|
|
long long int x, y, w, h;
|
|
x = readInt(errors, item, "x");
|
|
y = readInt(errors, item, "y");
|
|
w = readInt(errors, item, "w");
|
|
h = readInt(errors, item, "h");
|
|
std::string _orientation = readString(errors, item, "orientation", "bottom");
|
|
std::string _pinType = readString(errors, item, "type", "out");
|
|
|
|
ui::PinOrientation orientation;
|
|
if (_orientation == "left") {
|
|
orientation = ui::PinOrientation::LEFT;
|
|
} else if (_orientation == "right") {
|
|
orientation = ui::PinOrientation::RIGHT;
|
|
} else if (_orientation == "top") {
|
|
orientation = ui::PinOrientation::TOP;
|
|
} else if (_orientation == "bottom") {
|
|
orientation = ui::PinOrientation::BOTTOM;
|
|
} else {
|
|
errors.emplace_back(item.span, "unknown pin orientation type '" + _orientation + "'");
|
|
}
|
|
|
|
ui::PinType pinType;
|
|
if (_pinType == "in") {
|
|
pinType = ui::PinType::IN;
|
|
} else if (_pinType == "out") {
|
|
pinType = ui::PinType::OUT;
|
|
} else if (_pinType == "in_out") {
|
|
pinType = ui::PinType::IN_OUT;
|
|
} else {
|
|
errors.emplace_back(item.span, "unknown pin type '" + _pinType + "'");
|
|
}
|
|
|
|
displayItem.pin = ui::Pin(x, y, w, h, orientation, pinType, {lineColor, fillColor});
|
|
} else if (type == "bus") {
|
|
long long int x, y, w, h;
|
|
x = readInt(errors, item, "x");
|
|
y = readInt(errors, item, "y");
|
|
w = readInt(errors, item, "w");
|
|
h = readInt(errors, item, "h");
|
|
std::string _orientation = readString(errors, item, "orientation", "bottom");
|
|
|
|
ui::BusOrientation orientation;
|
|
if (_orientation == "horizontal") {
|
|
orientation = ui::BusOrientation::HORIZONTAL;
|
|
} else if (_orientation == "vertical") {
|
|
orientation = ui::BusOrientation::VERTICAL;
|
|
} else {
|
|
errors.emplace_back(item.span, "unknown bus orientation type '" + _orientation + "'");
|
|
}
|
|
|
|
displayItem.bus = ui::Bus(x, y, w, h, orientation, {lineColor, fillColor});
|
|
} else {
|
|
errors.emplace_back(item.type.span, "unsupported display type");
|
|
}
|
|
items.push_back(displayItem);
|
|
}
|
|
return Display(items);
|
|
}
|
|
|
|
std::optional<Attribute> SchemaCreator::loadAttribute(AttributeNode node) {
|
|
std::string name = node.name.value;
|
|
pushAdditional(name);
|
|
Value value;
|
|
|
|
if (current().inComponent) {
|
|
if (node.type == ValueNode::INT) {
|
|
value = Value::fromInt(node.defaultValue->asInt());
|
|
} else if (node.type == ValueNode::BOOL) {
|
|
value = Value::fromBool(node.defaultValue->asBool());
|
|
} else if (node.type == ValueNode::STRING) {
|
|
value = Value::fromString(node.defaultValue->asString());
|
|
} else {
|
|
errors.emplace_back(node.name.span, "unsupported type");
|
|
}
|
|
}
|
|
if (current().inConnection && !current().inSingleAutomaticConnection) { // TODO remove identifier
|
|
if (node.type == ValueNode::WIRE || node.type == ValueNode::IDENTIFIER) {
|
|
if (current().doesWireExists(node.defaultValue->asIdentifier())) {
|
|
value = Value::fromReference(node.defaultValue->asIdentifier(), Value::WIRE_REFERENCE);
|
|
} else {
|
|
value = Value::fromReference("", Value::WIRE_REFERENCE);
|
|
errors.emplace_back(node.span, "unknown identifier");
|
|
}
|
|
} else {
|
|
errors.emplace_back(node.name.span, "unsupported type");
|
|
}
|
|
}
|
|
if (current().inSingleAutomaticConnection) {
|
|
if (node.type == ValueNode::STRING) {
|
|
value = Value::fromString(node.defaultValue->asString());
|
|
} else {
|
|
errors.emplace_back(node.name.span, "unsupported type");
|
|
}
|
|
}
|
|
|
|
current().attributes.emplace_back(name, value);
|
|
|
|
std::optional<Popup> popup;
|
|
if (node.popup) {
|
|
popup = loadPopup(*node.popup, name, toType(node.type));
|
|
}
|
|
|
|
pop();
|
|
return Attribute(name, value, popup);
|
|
}
|
|
|
|
std::optional<Popup> SchemaCreator::loadPopup(PopupNode node, std::string name, Value::ValueType type) {
|
|
auto popupType = toType(node.type.value().value);
|
|
|
|
pushAdditional(name);
|
|
|
|
current().attributes.clear();
|
|
current().attributes.emplace_back(name, Value::ofType(type));
|
|
|
|
if (!node.title) {
|
|
errors.emplace_back(node.span, "missing @title");
|
|
return nullopt;
|
|
}
|
|
std::string title = node.title->asString();
|
|
|
|
if (!node.text) {
|
|
errors.emplace_back(node.span, "missing @text");
|
|
return nullopt;
|
|
}
|
|
std::string text = node.text->asString();
|
|
|
|
std::vector<Rule> rules;
|
|
for (auto &r: node.rules) {
|
|
std::optional<Rule> rule = loadRule(r);
|
|
if (rule) {
|
|
rules.push_back(*rule);
|
|
}
|
|
}
|
|
|
|
std::vector<Enumeration> enumeration;
|
|
if (node.enumerated) {
|
|
for (auto &enumNode: node.enumeration) {
|
|
if (type == Value::INT || type == Value::STRING || type == Value::BOOL) {
|
|
auto value = toType(enumNode.value);
|
|
if (value.getType() == type) {
|
|
enumeration.emplace_back(enumNode.key.asString(), value);
|
|
} else {
|
|
errors.emplace_back(enumNode.span, "wrong type");
|
|
}
|
|
} else if (type == Value::WIRE_REFERENCE) {
|
|
auto value = toType(enumNode.value);
|
|
if (value.isType(Value::UNDEFINED)) {
|
|
if (current().doesWireExists(value.asReference())) {
|
|
value = Value::fromReference(value.asReference(), Value::WIRE_REFERENCE);
|
|
} else {
|
|
errors.emplace_back(enumNode.span, "unknown wire");
|
|
}
|
|
}
|
|
|
|
if (value.isType(Value::WIRE_REFERENCE) || value.isType(Value::INT) || value.isType(Value::NIL)) {
|
|
enumeration.emplace_back(enumNode.key.asString(), value);
|
|
} else {
|
|
errors.emplace_back(enumNode.span, "wrong type");
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (type == Value::WIRE_REFERENCE && !current().inConnection) {
|
|
errors.emplace_back(node.span, "@enumeration is required for attributes of type wire");
|
|
}
|
|
}
|
|
|
|
pop();
|
|
|
|
return Popup(title, text, popupType, rules, enumeration);
|
|
}
|
|
|
|
std::optional<Rule> SchemaCreator::loadRule(RuleNode node) {
|
|
std::vector<IfStatement> statements;
|
|
|
|
for (auto &stmt: node.statements) {
|
|
auto condition = loadCondition(stmt.condition);
|
|
if (condition) {
|
|
statements.emplace_back(*condition,
|
|
Action(toType(stmt.action.type.value), stmt.action.message.asString()));
|
|
} else {
|
|
return nullopt;
|
|
}
|
|
}
|
|
|
|
return Rule(statements);
|
|
}
|
|
|
|
std::optional<Condition> SchemaCreator::loadCondition(ConditionNode node) {
|
|
std::string function = node.functionName.value;
|
|
|
|
for (auto &validator: validators) {
|
|
if (validator->getName() == function) {
|
|
if (validator->getSignature().size() == node.params.size()) {
|
|
std::vector<Value> params;
|
|
for (uint j = 0; j < validator->getSignature().size(); j++) {
|
|
bool exists = false;
|
|
auto type = toType(node.params[j]);
|
|
if (type.getType() == Value::UNDEFINED) {
|
|
if (current().doesAttributeExists(type.asReference(), validator->getSignature()[j])) {
|
|
exists = true;
|
|
type = Value::fromReference(type.asReference(), Value::ATTRIBUTE_REFERENCE);
|
|
}
|
|
if (validator->getSignature()[j] == Value::ADDRESS_SPACE) {
|
|
if (hasAddressSpace(type.asReference())) {
|
|
exists = true;
|
|
type = Value::fromReference(type.asReference(), Value::ADDRESS_SPACE_REFERENCE);
|
|
}
|
|
}
|
|
|
|
if (!exists) {
|
|
errors.emplace_back(node.functionName.span, "unknown reference " + type.asReference());
|
|
}
|
|
}
|
|
params.push_back(type);
|
|
}
|
|
return Condition(function, params, node.negated);
|
|
} else {
|
|
errors.emplace_back(node.functionName.span, "wrong number of parameters");
|
|
}
|
|
}
|
|
}
|
|
|
|
errors.emplace_back(node.functionName.span, "unknown function name");
|
|
return nullopt;
|
|
}
|
|
|
|
|
|
Schema *SchemaCreator::loadSchema(SchemaNode node, Library &library) {
|
|
auto *schema = new Schema(library);
|
|
|
|
for (auto &instance: node.instances) {
|
|
if (library.hasComponent(instance.component.value)) {
|
|
schema->componentInstances.push_back(loadComponentInstance(instance, library));
|
|
}
|
|
if (library.hasBus(instance.component.value)) {
|
|
schema->busInstances.push_back(loadBusInstance(instance, library));
|
|
}
|
|
}
|
|
|
|
for (auto &conn: node.connections) {
|
|
auto firstComponent = schema->getComponentInstance(conn.first.instance.value);
|
|
if (firstComponent == nullptr) {
|
|
errors.emplace_back(conn.first.instance.span, "unknown component");
|
|
continue;
|
|
}
|
|
if (!firstComponent->component.hasPin(conn.first.pin.value)) {
|
|
errors.emplace_back(conn.first.pin.span, "unknown pin");
|
|
continue;
|
|
}
|
|
|
|
|
|
ComponentInstance *secondComponent = nullptr;
|
|
if (conn.second.has_value()) {
|
|
secondComponent = schema->getComponentInstance(conn.second->instance.value);
|
|
if (secondComponent == nullptr) {
|
|
errors.emplace_back(conn.second->instance.span, "unknown component");
|
|
continue;
|
|
}
|
|
if (!secondComponent->component.hasPin(conn.second->pin.value)) {
|
|
errors.emplace_back(conn.second->pin.span, "unknown pin");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
auto bus = schema->getBusInstance(conn.bus.value);
|
|
if (bus == nullptr) {
|
|
errors.emplace_back(conn.bus.span, "unknown bus");
|
|
continue;
|
|
}
|
|
|
|
std::optional<Connection> connection = std::nullopt;
|
|
if (secondComponent != nullptr) {
|
|
ConnectionComponent firstConn{firstComponent->component.getName(), conn.first.pin.value};
|
|
ConnectionComponent secondConn{secondComponent->component.getName(), conn.second->pin.value};
|
|
|
|
if (library.hasConnection(firstConn,
|
|
bus->bus.getName(),
|
|
secondConn)) {
|
|
connection = *library.getConnection(firstConn, bus->bus.getName(), secondConn);
|
|
} else {
|
|
errors.emplace_back(conn.span, "unknown connection");
|
|
continue;
|
|
}
|
|
} else {
|
|
ConnectionComponent firstConn{firstComponent->component.getName(), conn.first.pin.value};
|
|
|
|
if (library.hasConnection(firstConn,
|
|
bus->bus.getName())) {
|
|
connection = *library.getConnection(firstConn, bus->bus.getName());
|
|
} else {
|
|
errors.emplace_back(conn.span, "unknown connection");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
std::vector<InstanceAttribute> attributes;
|
|
for (auto &attr: conn.attributes) {
|
|
if (connection->hasAttribute(attr.name.value)) {
|
|
auto attribute = connection->getAttribute(attr.name.value);
|
|
auto value = toType(attr.value);
|
|
|
|
bool valueFound = false;
|
|
|
|
// all connection attributes must be of type enumeration
|
|
for (auto &en: attribute.getPopup()->getEnumeration()) {
|
|
if (en.getValue().equals(value)) {
|
|
valueFound = true;
|
|
break;
|
|
} else if(value.isType(Value::UNDEFINED)) {
|
|
if(en.getValue().isType(Value::WIRE_REFERENCE) && en.getValue().asReference() == value.asReference()) {
|
|
value = Value::fromReference(value.asReference(), Value::WIRE_REFERENCE);
|
|
valueFound = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!valueFound) {
|
|
errors.emplace_back(attr.span, "invalid value");
|
|
}
|
|
|
|
attributes.emplace_back(attribute.getName(), value, attribute);
|
|
} else {
|
|
errors.emplace_back(attr.name.span, "unknown attribute");
|
|
}
|
|
}
|
|
|
|
if (secondComponent == nullptr) {
|
|
schema->connections.push_back(
|
|
std::make_shared<BusConnectionInstance>(firstComponent, attributes, bus, *connection));
|
|
} else {
|
|
schema->connections.push_back(
|
|
std::make_shared<DirectConnectionInstance>(firstComponent, secondComponent, attributes, bus,
|
|
*connection));
|
|
}
|
|
}
|
|
|
|
if(errors.empty()) {
|
|
return schema;
|
|
} else {
|
|
delete schema;
|
|
return nullptr;
|
|
}
|
|
|
|
}
|
|
|
|
shared_ptr<ComponentInstance> SchemaCreator::loadComponentInstance(InstanceNode instance, Library &library) {
|
|
|
|
auto componentInstanceName = instance.name.value;
|
|
auto position = std::make_pair(instance.position->first.value, instance.position->second.value);
|
|
|
|
auto component = library.getComponent(instance.component.value);
|
|
|
|
// validate attributes
|
|
std::vector<InstanceAttribute> attributes;
|
|
for (auto &attr: instance.attributes) {
|
|
if (component.hasAttribute(attr.name.value, toType(attr.value.getType()))) {
|
|
auto attribute = component.getAttribute(attr.name.value);
|
|
attributes.emplace_back(attribute.getName(), toType(attr.value, attribute.getDefault().getType()),
|
|
attribute);
|
|
} else {
|
|
errors.emplace_back(SourceError(attr.name.span, "unknown attribute"));
|
|
}
|
|
}
|
|
|
|
if (attributes.size() < component.getAttributes().size()) {
|
|
for (auto &attr: component.getAttributes()) {
|
|
if (std::count_if(attributes.begin(), attributes.end(),
|
|
[&attr](InstanceAttribute &attribute) { return attr.getName() == attribute.name; }) ==
|
|
0) {
|
|
errors.emplace_back(SourceError(instance.span, "missing attribute '" + attr.getName() + "'"));
|
|
}
|
|
}
|
|
}
|
|
|
|
return std::make_shared<ComponentInstance>(componentInstanceName, attributes, position, component);
|
|
}
|
|
|
|
std::shared_ptr<BusInstance> SchemaCreator::loadBusInstance(InstanceNode instance, Library &library) {
|
|
|
|
auto busInstanceName = instance.name.value;
|
|
auto position = std::make_pair(instance.position->first.value, instance.position->second.value);
|
|
|
|
auto bus = library.getBus(instance.component.value);
|
|
|
|
long long size = 0;
|
|
if (instance.size) {
|
|
size = instance.size->value;
|
|
}
|
|
|
|
return std::make_shared<BusInstance>(busInstanceName, position, bus, static_cast<int>(size));
|
|
}
|
|
|
|
vector<Enumeration> SchemaCreator::createWireEnumeration(vector<Value> wireValues) {
|
|
vector<Enumeration> wires;
|
|
for (auto &wire: wireValues) {
|
|
if (wire.isType(Value::STRING)) {
|
|
wires.emplace_back(wire.asString(), wire);
|
|
}
|
|
}
|
|
return wires;
|
|
}
|
|
|
|
std::optional<Attribute> SchemaCreator::createMemoryAttribute() {
|
|
return Attribute("_memory", Value::fromMemoryReference(std::nullopt), createMemoryPopup());
|
|
}
|
|
|
|
std::optional<Popup> SchemaCreator::createMemoryPopup() {
|
|
return Popup("Postavi memoriju", "Postavi memoriju za izabrani procesor", Popup::AUTOMATIC, vector<Rule>{},
|
|
vector<Enumeration>{});
|
|
}
|
|
|
|
std::optional<Bus> SchemaCreator::getBus(std::string name) {
|
|
for (auto &bus: buses) {
|
|
if (bus.getName() == name) {
|
|
return bus;
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Pin> SchemaCreator::getComponentPin(std::string name, std::string pin) {
|
|
for (auto &c: components) {
|
|
if (c.getName() == name) {
|
|
for (auto &p: c.getPins()) {
|
|
if (p.getName() == pin) {
|
|
return p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nullopt;
|
|
}
|
|
|
|
bool SchemaCreator::hasAddressSpace(std::string name) {
|
|
for (auto &as: addressSpaces) {
|
|
if (as.getName() == name) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SchemaCreator::push(ComdelContext context) {
|
|
this->context.push_back(context);
|
|
}
|
|
|
|
void SchemaCreator::pushAdditional(std::string name) {
|
|
if (!this->context.empty()) {
|
|
this->context.push_back(current());
|
|
current().name = name;
|
|
} else {
|
|
ComdelContext con(name, false, false, false, false);
|
|
push(con);
|
|
}
|
|
}
|
|
|
|
ComdelContext &SchemaCreator::current() {
|
|
return this->context[this->context.size() - 1];
|
|
}
|
|
|
|
void SchemaCreator::pop() {
|
|
this->context.pop_back();
|
|
}
|
|
|
|
std::vector<SourceError> SchemaCreator::getErrors() {
|
|
return errors;
|
|
}
|
|
|
|
} // namespace domain
|