Compare commits

..

2 Commits

Author SHA1 Message Date
Borna Rajković f93a2afa63 Updated component and bus generation logic + bugfixes 2022-05-31 01:05:08 +02:00
Borna Rajković 86b8861533 Extracted schema and library management to Application 2022-05-30 23:11:06 +02:00
21 changed files with 528 additions and 236 deletions

View File

@ -37,5 +37,5 @@ add_executable(SchemeEditor
comdel/parser/comdellexer.cpp comdel/parser/comdellexer.cpp
main.cpp main.cpp
mainwindow.ui mainwindow.ui
comdel/domain/comdel_validator.cpp comdel/domain/comdel_validator.h comdel/display/attribute_dialog.cpp comdel/display/attribute_dialog.h comdel/display/name_dialog.cpp comdel/display/name_dialog.h comdel/domain/comdel_generator.cpp comdel/domain/comdel_generator.h comdel/display/library_list.cpp comdel/display/library_list.h) comdel/domain/comdel_validator.cpp comdel/domain/comdel_validator.h comdel/display/attribute_dialog.cpp comdel/display/attribute_dialog.h comdel/display/name_dialog.cpp comdel/display/name_dialog.h comdel/domain/comdel_generator.cpp comdel/domain/comdel_generator.h comdel/display/library_list.cpp comdel/display/library_list.h application.cpp application.h)
target_link_libraries(SchemeEditor Qt5::Core Qt5::Gui Qt5::Widgets) target_link_libraries(SchemeEditor Qt5::Core Qt5::Gui Qt5::Widgets)

199
application.cpp Normal file
View File

@ -0,0 +1,199 @@
//
// Created by bbr on 27.05.22..
//
#include "application.h"
#include "comdel/parser/parse_context.h"
#include "comdel/parser/parser_util.h"
#include "comdel/domain/schema_creator.h"
#include "comdel/domain/comdel_generator.h"
#include "comdel/domain/comdel_validator.h"
std::optional<domain::Library> Application::getLibrary() {
return library;
}
domain::Schema *Application::getSchema() {
return schema;
}
void Application::clear() {
if(schema != nullptr) {
delete schema;
schema = nullptr;
}
library = std::nullopt;
libraryPath = "";
}
bool Application::loadLibrary(std::string& filename, std::ostream &errorOutput) {
clear();
ParseContext parseContext;
auto libraryNode = load_library_from_file(&parseContext, filename.c_str(), errorOutput);
if(libraryNode) {
domain::SchemaCreator generator(validators);
library = generator.loadLibrary(*libraryNode);
for (auto& error : generator.getErrors()) {
parseContext.formatError(error, errorOutput, "ERROR: ");
}
if(library.has_value()) {
errorOutput<<"Failed creating library model"<<std::endl;
return false;
} else {
libraryPath = filename;
// on library load we create a new schema
schema = new domain::Schema();
}
} else {
errorOutput<<"Failed parsing library"<<std::endl;
return false;
}
return true;
}
bool Application::loadSchema(std::string &filename, std::ostream &errorOutput) {
clear();
ParseContext parseContext;
auto schemaNode = load_schema_from_file(&parseContext, filename.c_str(), errorOutput);
if(schemaNode) {
domain::SchemaCreator generator(validators);
library = generator.loadLibrary(*schemaNode->library);
libraryPath = schemaNode->source->asString();
for (auto& error : generator.getErrors()) {
parseContext.formatError(error, errorOutput, "ERROR: ");
}
if(library) {
schema = generator.loadSchema(*schemaNode, *library);
for (auto& error : generator.getErrors()) {
parseContext.formatError(error, errorOutput, "ERROR: ");
}
if(schema == nullptr) {
clear();
return false;
}
} else {
clear();
return false;
}
} else {
errorOutput<<"Failed parsing library"<<std::endl;
return false;
}
return true;
}
bool Application::generateSchema(std::ostringstream &output) {
if(schema == nullptr) {
return false;
}
domain::generate_schema(libraryPath, schema, output);
return true;
}
std::vector<domain::ValidationError> Application::validateSchema() {
if(schema == nullptr) {
return std::vector<domain::ValidationError>{domain::ValidationError(domain::Action::ERROR, "No schema loaded")};
}
domain::ComdelValidator validator{validators};
domain::ValidationContext context;
context.instance = nullptr;
context.attribute = nullptr;
context.addressSpaces = {};
for(auto &lib: library->getAddressSpaces()) {
context.addressSpaces.insert(std::make_pair(lib.getName(), lib));
}
auto errors = validator.validateSchema(*schema, context);
auto countValidation = validator.validateInstanceCount(*schema, *library, context);
errors.insert(errors.end(), countValidation.begin(), countValidation.end());
auto nameValidation = validator.validateInstanceNames(*schema, *library, context);
errors.insert(errors.end(), nameValidation.begin(), nameValidation.end());
auto pinValidation = validator.validatePinConnections(*schema, *library, context);
errors.insert(errors.end(), pinValidation.begin(), pinValidation.end());
return errors;
}
std::vector<domain::ValidationError> Application::generateComdel(std::ostringstream &output) {
auto errors = validateSchema();
if(Application::hasErrors(errors)) {
// as long as all validation errors are warning we continue with build
domain::generate_comdel(schema, library.value(), output);
}
return errors;
}
bool Application::hasErrors(std::vector<domain::ValidationError> errors) {
for(auto& err: errors) {
if(err.type == domain::Action::ERROR) {
return true;
}
}
return false;
}
// static instance of application
static Application *application = nullptr;
Application *Application::instance() {
if(application == nullptr) {
application = new Application();
}
return application;
}
std::shared_ptr<domain::ComponentInstance> Application::addComponent(domain::Component component, std::vector<domain::InstanceAttribute> attributes, int x, int y) {
std::set<std::string> names;
for(const auto& c: schema->componentInstances) {
names.insert(c->name);
}
std::string name = generateName(names, component.getInstanceName());
schema->componentInstances.push_back(std::make_shared<domain::ComponentInstance>(name, attributes, std::make_pair(x, y), component));
return schema->componentInstances.back();
}
std::shared_ptr<domain::BusInstance> Application::addBus(domain::Bus bus, int x, int y) {
std::set<std::string> names;
for(const auto& b: schema->busInstances) {
names.insert(b->name);
}
std::string name = generateName(names, bus.getInstanceName());
schema->busInstances.push_back(std::make_shared<domain::BusInstance>(name, std::make_pair(x, y), bus));
return schema->busInstances.back();
}
std::string Application::generateName(std::set<std::string>& names, std::string instanceName) {
if(names.find(instanceName) == names.end()) {
return instanceName;
}
char buffer[4];
for(int i=0; i<1000; i++) {
sprintf(buffer, "%03d", i);
auto name = instanceName + "_" + buffer;
if(names.find(name) == names.end()) {
return name;
}
}
// return default value as this should never happen
return instanceName;
}

48
application.h Normal file
View File

@ -0,0 +1,48 @@
//
// Created by bbr on 27.05.22..
//
#ifndef SCHEMEEDITOR_APPLICATION_H
#define SCHEMEEDITOR_APPLICATION_H
#include <set>
#include "comdel/domain/library.h"
#include "comdel/domain/schema.h"
#include "comdel/domain/comdel_validator.h"
class Application {
private:
std::string libraryPath;
std::optional<domain::Library> library = std::nullopt;
domain::Schema* schema = nullptr;
std::vector<domain::FunctionValidator*> validators = domain::getSupportedValidators();
std::string generateName(std::set<std::string>& names, std::string instanceName);
public:
std::optional<domain::Library> getLibrary();
domain::Schema* getSchema();
void clear();
bool loadLibrary(std::string& filename, std::ostream& errorOutput);
bool loadSchema(std::string& filename, std::ostream& errorOutput);
static Application* instance();
bool generateSchema(std::ostringstream &output);
std::vector<domain::ValidationError> validateSchema();
std::vector<domain::ValidationError> generateComdel(std::ostringstream &output);
std::shared_ptr<domain::ComponentInstance> addComponent(domain::Component component, std::vector<domain::InstanceAttribute> attributes, int x, int y);
std::shared_ptr<domain::BusInstance> addBus(domain::Bus bus, int x, int y);
static bool hasErrors(std::vector<domain::ValidationError> empty);
~Application() = default;
};
#endif //SCHEMEEDITOR_APPLICATION_H

View File

@ -5,6 +5,8 @@
#include "attribute_dialog.h" #include "attribute_dialog.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "application.h"
namespace display { namespace display {
void AttributeDialog::onUpdate() { void AttributeDialog::onUpdate() {
@ -15,7 +17,7 @@ namespace display {
domain::ValidationContext context; domain::ValidationContext context;
for (auto &addressSpace: MainWindow::getLibrary()->getAddressSpaces()) { for (auto &addressSpace: Application::instance()->getLibrary()->getAddressSpaces()) {
context.addressSpaces.insert(std::make_pair(addressSpace.getName(), addressSpace)); context.addressSpaces.insert(std::make_pair(addressSpace.getName(), addressSpace));
} }

View File

@ -2,17 +2,24 @@
#include "attribute_dialog.h" #include "attribute_dialog.h"
#include "name_dialog.h" #include "name_dialog.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "application.h"
#include <QMenu> #include <QMenu>
#include <QLine> #include <QLine>
#include <QGraphicsSceneContextMenuEvent> #include <QGraphicsSceneContextMenuEvent>
#include <set>
namespace display { namespace display {
void Component::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { void Component::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
QMenu menu; QMenu menu;
menu.addAction("Izmjeni ime", [this]() { menu.addAction("Izmjeni ime", [this]() {
auto dialog = new NameDialog(this->instance.get()); std::set<std::string> names;
for(const auto &component: Application::instance()->getSchema()->componentInstances) {
names.insert(component->name);
}
auto dialog = new NameDialog(this->instance.get(), names);
dialog->exec(); dialog->exec();
}); });
menu.addSeparator(); menu.addSeparator();
@ -24,7 +31,7 @@ namespace display {
[attr]() { [attr]() {
if (attr->value.getType() == domain::Value::MEMORY_REFERENCE) { if (attr->value.getType() == domain::Value::MEMORY_REFERENCE) {
auto dialog = new MemoryDialog(attr, auto dialog = new MemoryDialog(attr,
MainWindow::getSchema()->componentInstances); Application::instance()->getSchema()->componentInstances);
dialog->exec(); dialog->exec();
} else { } else {
auto dialog = new AttributeDialog(attr); auto dialog = new AttributeDialog(attr);
@ -92,7 +99,12 @@ namespace display {
void Bus::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { void Bus::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
QMenu menu; QMenu menu;
menu.addAction("Izmjeni ime", [this]() { menu.addAction("Izmjeni ime", [this]() {
auto dialog = new NameDialog(this->busInstance.get()); std::set<std::string> names;
for(const auto &component: Application::instance()->getSchema()->busInstances) {
names.insert(component->name);
}
auto dialog = new NameDialog(this->busInstance.get(), names);
dialog->exec(); dialog->exec();
}); });
menu.exec(event->screenPos()); menu.exec(event->screenPos());
@ -109,7 +121,7 @@ namespace display {
QVariant BusGroup::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) { QVariant BusGroup::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) {
if (change == ItemPositionChange && scene()) { if (change == ItemPositionChange && scene()) {
// value is the new position. // value is the new position.
QPointF newPos = value.toPointF(); QPoint newPos = value.toPointF().toPoint();
busInstance->position.first = newPos.x(); busInstance->position.first = newPos.x();
busInstance->position.second = newPos.y(); busInstance->position.second = newPos.y();
@ -135,7 +147,7 @@ namespace display {
QVariant ComponentGroup::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) { QVariant ComponentGroup::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) {
if (change == ItemPositionChange && scene()) { if (change == ItemPositionChange && scene()) {
// value is the new position. // value is the new position.
QPointF newPos = value.toPointF(); QPoint newPos = value.toPointF().toPoint();
componentInstance->position.first = newPos.x(); componentInstance->position.first = newPos.x();
componentInstance->position.second = newPos.y(); componentInstance->position.second = newPos.y();

View File

@ -2,37 +2,54 @@
// Created by bbr on 18. 04. 2022.. // Created by bbr on 18. 04. 2022..
// //
#include <set>
#include "name_dialog.h" #include "name_dialog.h"
display::NameDialog::NameDialog(domain::ComponentInstance *instance) : componentInstance(instance) { display::NameDialog::NameDialog(domain::ComponentInstance *instance, std::set<std::string>& names) : componentInstance(instance), usedNames(names) {
usedNames.erase(instance->name);
auto *layout = new QVBoxLayout(this); auto *layout = new QVBoxLayout(this);
layout->addWidget(new QLabel("Izmjeni ime", this)); layout->addWidget(new QLabel("Izmjeni ime", this));
edit = new QLineEdit(this); edit = new QLineEdit(this);
edit->insert(instance->name.c_str()); edit->insert(instance->name.c_str());
connect(edit, &QLineEdit::textChanged, this, &NameDialog::onNameUpdate);
layout->addWidget(edit); layout->addWidget(edit);
this->setWindowTitle("Izmjeni ime"); this->setWindowTitle("Izmjeni ime");
auto *button = new QPushButton("Ažuriraj", this); button = new QPushButton("Ažuriraj", this);
connect(button, &QPushButton::clicked, this, &NameDialog::onNameChange); connect(button, &QPushButton::clicked, this, &NameDialog::onNameChange);
layout->addWidget(button); layout->addWidget(button);
this->setLayout(layout); this->setLayout(layout);
} }
display::NameDialog::NameDialog(domain::BusInstance *instance): busInstance(instance) { display::NameDialog::NameDialog(domain::BusInstance *instance, std::set<std::string>& names): busInstance(instance), usedNames(names) {
usedNames.erase(instance->name);
auto *layout = new QVBoxLayout(this); auto *layout = new QVBoxLayout(this);
layout->addWidget(new QLabel("Izmjeni ime", this)); layout->addWidget(new QLabel("Izmjeni ime", this));
edit = new QLineEdit(this); edit = new QLineEdit(this);
edit->insert(instance->name.c_str()); edit->insert(instance->name.c_str());
connect(edit, &QLineEdit::textChanged, this, &NameDialog::onNameUpdate);
layout->addWidget(edit); layout->addWidget(edit);
this->setWindowTitle("Izmjeni ime"); this->setWindowTitle("Izmjeni ime");
auto *button = new QPushButton("Ažuriraj", this); button = new QPushButton("Ažuriraj", this);
connect(button, &QPushButton::clicked, this, &NameDialog::onNameChange); connect(button, &QPushButton::clicked, this, &NameDialog::onNameChange);
layout->addWidget(button); layout->addWidget(button);
this->setLayout(layout); this->setLayout(layout);
} }
void display::NameDialog::onNameUpdate(const QString &text) {
if(usedNames.find(text.toStdString()) == usedNames.end()) {
button->setDisabled(false);
} else {
button->setDisabled(true);
}
}
void display::NameDialog::onNameChange() { void display::NameDialog::onNameChange() {
if (componentInstance != nullptr) { if (componentInstance != nullptr) {
componentInstance->name = this->edit->text().toStdString(); componentInstance->name = this->edit->text().toStdString();
} else if (busInstance != nullptr) { } else if (busInstance != nullptr) {

View File

@ -7,24 +7,28 @@
#include <QLineEdit> #include <QLineEdit>
#include <QPushButton> #include <QPushButton>
#include <set>
#include <comdel/domain/instance.h> #include <comdel/domain/instance.h>
namespace display { namespace display {
class NameDialog : public QDialog { class NameDialog : public QDialog {
std::set<std::string> usedNames;
QLineEdit *edit = nullptr; QLineEdit *edit = nullptr;
domain::ComponentInstance *componentInstance = nullptr; domain::ComponentInstance *componentInstance = nullptr;
domain::BusInstance *busInstance = nullptr; domain::BusInstance *busInstance = nullptr;
QPushButton *button;
public: public:
NameDialog(domain::ComponentInstance *instance); NameDialog(domain::ComponentInstance *instance, std::set<std::string>& names);
NameDialog(domain::BusInstance *instance); NameDialog(domain::BusInstance *instance, std::set<std::string>& names);
public slots: public slots:
void onNameUpdate(const QString& text);
void onNameChange(); void onNameChange();
}; };

View File

@ -1,5 +1,7 @@
#include "component_display.h" #include "component_display.h"
#include "schema_display.h" #include "schema_display.h"
#include "application.h"
#include "attribute_dialog.h"
#include <QDrag> #include <QDrag>
#include <QDragEnterEvent> #include <QDragEnterEvent>
@ -10,13 +12,14 @@ namespace display {
Schema::Schema() { Schema::Schema() {
this->selectedBrush.setColor(QColor::fromRgb(20, 20, 125)); this->selectedBrush.setColor(QColor::fromRgb(20, 20, 125));
this->selectedPen.setColor(QColor::fromRgb(20, 20, 125)); this->selectedPen.setColor(QColor::fromRgb(20, 20, 125));
schema = nullptr;
library = std::nullopt;
this->setScene(&scene); this->setScene(&scene);
this->setAcceptDrops(true); this->setAcceptDrops(true);
} }
void Schema::setSchema(domain::Schema *_schema, domain::Library *_library) { void Schema::setSchema(domain::Schema *_schema, std::optional<domain::Library> _library) {
components.clear(); components.clear();
buses.clear(); buses.clear();
@ -85,15 +88,30 @@ namespace display {
auto attributes = std::vector<domain::InstanceAttribute>(); auto attributes = std::vector<domain::InstanceAttribute>();
for (auto attr: component.getAttributes()) { for (auto attr: component.getAttributes()) {
attributes.emplace_back(attr.getName(), attr.getDefault(), attr); domain::InstanceAttribute attribute(attr.getName(), attr.getDefault(), attr);
if(attr.getPopup().has_value() && attr.getPopup()->getType() == domain::Popup::AUTOMATIC) {
if(attr.getDefault().isType(domain::Value::MEMORY_REFERENCE)) {
auto dialog = new MemoryDialog(&attribute, schema->componentInstances);
if(dialog->exec() == QDialog::Rejected) {
// if any dialog isn't set, whole creation is rejected
event->ignore();
return;
}
} else {
auto dialog = new AttributeDialog(&attribute);
if(dialog->exec() == QDialog::Rejected) {
// if any dialog isn't set, whole creation is rejected
event->ignore();
return;
}
}
}
attributes.push_back(attribute);
} }
auto currentPos = this->mapToScene(event->pos()); auto currentPos = this->mapToScene(event->pos()).toPoint();
auto instance = std::make_shared<domain::ComponentInstance>(component.getInstanceName(), attributes, auto instance = Application::instance()->addComponent(component, attributes, currentPos.x(), currentPos.y());
std::make_pair(currentPos.x(), currentPos.y()),
component);
schema->componentInstances.push_back(instance);
auto group = new display::ComponentGroup(instance); auto group = new display::ComponentGroup(instance);
scene.addItem(group); scene.addItem(group);
@ -110,11 +128,9 @@ namespace display {
if (event->mimeData()->hasFormat("comdel/bus")) { if (event->mimeData()->hasFormat("comdel/bus")) {
auto bus = library->getBus(event->mimeData()->data("comdel/bus").toStdString()); auto bus = library->getBus(event->mimeData()->data("comdel/bus").toStdString());
auto currentPos = this->mapToScene(event->pos()); auto currentPos = this->mapToScene(event->pos()).toPoint();
auto instance = std::make_shared<domain::BusInstance>(bus.getName(), auto instance = Application::instance()->addBus(bus, currentPos.x(), currentPos.y());
std::make_pair(currentPos.x(), currentPos.y()), bus);
schema->busInstances.push_back(instance);
auto group = new display::BusGroup(instance); auto group = new display::BusGroup(instance);
scene.addItem(group); scene.addItem(group);
@ -216,6 +232,9 @@ namespace display {
for (auto bus: busInstances) { for (auto bus: busInstances) {
auto &group = buses[bus->name]; auto &group = buses[bus->name];
if(group == nullptr) {
continue;
}
auto rect = new QGraphicsRectItem(group->boundingRect()); auto rect = new QGraphicsRectItem(group->boundingRect());
rect->setPen(selectedPen); rect->setPen(selectedPen);
rect->setPos(group->scenePos()); rect->setPos(group->scenePos());

View File

@ -46,7 +46,7 @@ namespace display {
std::vector<BusConnection *> busConnections; std::vector<BusConnection *> busConnections;
std::vector<DirectConnection *> directConnections; std::vector<DirectConnection *> directConnections;
void setSchema(domain::Schema *schema, domain::Library *library); void setSchema(domain::Schema *schema, std::optional<domain::Library> library);
void updateConnections(); void updateConnections();
@ -65,8 +65,8 @@ namespace display {
private: private:
QGraphicsScene scene; QGraphicsScene scene;
domain::Schema *schema; domain::Schema *schema{};
domain::Library *library; std::optional<domain::Library> library;
std::vector<domain::BusInstance *> std::vector<domain::BusInstance *>
getAvailableConnectionBusses(domain::ComponentInstance *instance, domain::Pin &pin); getAvailableConnectionBusses(domain::ComponentInstance *instance, domain::Pin &pin);

View File

@ -10,6 +10,11 @@ namespace domain {
return name; return name;
} }
std::string Bus::getInstanceName() {
return instanceName;
}
int Wire::getWidth() { int Wire::getWidth() {
return width; return width;
} }
@ -30,9 +35,9 @@ namespace domain {
return type; return type;
} }
Bus::Bus(std::string name, std::string tooltip, BusType type, std::pair<int, int> count, std::vector<Wire> wires, Bus::Bus(std::string name, std::string instanceName, std::string tooltip, BusType type, std::pair<int, int> count, std::vector<Wire> wires,
std::optional<ui::Bus> displayBus) std::optional<ui::Bus> displayBus)
: name(name), tooltip(tooltip), type(type), count(count), wires(wires), displayBus(displayBus) {} : name(name), instanceName(instanceName), tooltip(tooltip), type(type), count(count), wires(wires), displayBus(displayBus) {}
std::string Bus::getName() { std::string Bus::getName() {
return name; return name;
@ -57,5 +62,4 @@ namespace domain {
std::optional<ui::Bus> Bus::getDisplayBus() { std::optional<ui::Bus> Bus::getDisplayBus() {
return displayBus; return displayBus;
} }
} // namespace domain } // namespace domain

View File

@ -54,6 +54,7 @@ namespace domain {
}; };
private: private:
std::string name; std::string name;
std::string instanceName;
std::string tooltip; std::string tooltip;
BusType type; BusType type;
@ -62,11 +63,13 @@ namespace domain {
std::vector<Wire> wires; std::vector<Wire> wires;
public: public:
Bus(std::string name, std::string tooltip, BusType type, std::pair<int, int> count, std::vector<Wire> wires, Bus(std::string name, std::string instanceName, std::string tooltip, BusType type, std::pair<int, int> count, std::vector<Wire> wires,
std::optional<ui::Bus> display = std::nullopt); std::optional<ui::Bus> display = std::nullopt);
std::string getName(); std::string getName();
std::string getInstanceName();
std::string getTooltip(); std::string getTooltip();
BusType getType(); BusType getType();

View File

@ -1,3 +1,4 @@
#include <set>
#include "comdel_validator.h" #include "comdel_validator.h"
#include "library.h" #include "library.h"
@ -38,11 +39,11 @@ namespace domain {
auto message = populateMessage( auto message = populateMessage(
"Not enough instances of component '{componentName}' required at least {min}, found {count}", "Not enough instances of component '{componentName}' required at least {min}, found {count}",
context); context);
errors.push_back(ValidationError{nullptr, nullptr, Action::ERROR, message}); errors.emplace_back(Action::ERROR, message);
} else if (count > comp.getCount().second) { } else if (count > comp.getCount().second) {
auto message = populateMessage( auto message = populateMessage(
"To many instances of component '{componentName}' allow at most {max}, found {count}", context); "To many instances of component '{componentName}' allow at most {max}, found {count}", context);
errors.push_back(ValidationError{nullptr, nullptr, Action::ERROR, message}); errors.emplace_back(Action::ERROR, message);
} }
} }
@ -64,11 +65,11 @@ namespace domain {
if (count < bus.getCount().first) { if (count < bus.getCount().first) {
auto message = populateMessage( auto message = populateMessage(
"Not enough instances of bus '{busName}' required at least {min}, found {count}", context); "Not enough instances of bus '{busName}' required at least {min}, found {count}", context);
errors.push_back(ValidationError{nullptr, nullptr, Action::ERROR, message}); errors.emplace_back(Action::ERROR, message);
} else if (count > bus.getCount().second) { } else if (count > bus.getCount().second) {
auto message = populateMessage( auto message = populateMessage(
"To many instances of bus '{busName}' allow at most {max}, found {count}", context); "To many instances of bus '{busName}' allow at most {max}, found {count}", context);
errors.push_back(ValidationError{nullptr, nullptr, Action::ERROR, message}); errors.emplace_back(Action::ERROR, message);
} }
} }
@ -86,7 +87,7 @@ namespace domain {
context.instance = inst.get(); context.instance = inst.get();
context.attributes["instanceName"] = Value::fromString(inst->name); context.attributes["instanceName"] = Value::fromString(inst->name);
auto message = populateMessage(pin.getConnection().getMessage(), context); auto message = populateMessage(pin.getConnection().getMessage(), context);
errors.push_back(ValidationError{nullptr, nullptr, Action::ERROR, message}); errors.emplace_back(Action::ERROR, message);
} }
} }
} }
@ -152,6 +153,24 @@ namespace domain {
return nullopt; return nullopt;
} }
std::vector<ValidationError>
ComdelValidator::validateInstanceNames(Schema &schema, Library &library, ValidationContext context) {
std::set<std::string> names;
std::vector<ValidationError> errors;
for(auto& component: schema.componentInstances) {
if(names.find(component->name) != names.end()) {
context.attributes["componentName"] = Value::fromString(component->name);
auto message = populateMessage(
"There are multiple component instances named '{componentName}'", context);
errors.emplace_back(Action::ERROR, message);
}
names.insert(component->name);
}
return errors;
}
std::string ComdelValidator::populateMessage(string source, ValidationContext context) { std::string ComdelValidator::populateMessage(string source, ValidationContext context) {
for (auto &[key, value]: context.attributes) { for (auto &[key, value]: context.attributes) {
source = replacePlaceholder(source, key, value); source = replacePlaceholder(source, key, value);
@ -202,5 +221,4 @@ namespace domain {
this->validators.insert(std::make_pair(validator->getName(), validator)); this->validators.insert(std::make_pair(validator->getName(), validator));
} }
} }
} }

View File

@ -12,6 +12,10 @@ namespace domain {
InstanceAttribute *attribute; InstanceAttribute *attribute;
Action::ActionType type; Action::ActionType type;
std::string message; std::string message;
ValidationError(Action::ActionType type, std::string message): instance(nullptr), attribute(nullptr), type(type), message(message) {}
ValidationError(ComponentInstance* instance, InstanceAttribute* attribute, Action::ActionType type, std::string message): instance(instance), attribute(attribute), type(type), message(message) {}
}; };
struct ValidationContext { struct ValidationContext {
@ -31,6 +35,8 @@ namespace domain {
std::optional<ValidationError> validateRule(Rule rule, ValidationContext context); std::optional<ValidationError> validateRule(Rule rule, ValidationContext context);
std::vector<ValidationError> validateInstanceNames(Schema &schema, Library &library, ValidationContext context);
std::vector<ValidationError> validateInstanceCount(Schema &schema, Library &library, ValidationContext context); std::vector<ValidationError> validateInstanceCount(Schema &schema, Library &library, ValidationContext context);
std::vector<ValidationError> std::vector<ValidationError>

View File

@ -167,8 +167,7 @@ namespace domain {
} }
for (auto &comp: node.components) { for (auto &comp: node.components) {
std::optional<Component> component; auto component = loadComponent(comp);
component = loadComponent(comp);
if (component) { if (component) {
components.push_back(*component); components.push_back(*component);
} }
@ -196,13 +195,24 @@ namespace domain {
} }
} }
return Library(name, libraryInfo, header, componentDirectory, componentHeader, addressSpaces, components, buses, if(errors.empty()) {
connections, messages); return Library(name, libraryInfo, header, componentDirectory, componentHeader, addressSpaces, components, buses,
connections, messages);
} else {
return nullopt;
}
} }
std::optional<Bus> SchemaCreator::loadBus(BusNode node) { std::optional<Bus> SchemaCreator::loadBus(BusNode node) {
std::string busName = node.name.value; 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); auto count = std::make_pair<int, int>(1, 1);
if (node.count) { if (node.count) {
count = std::make_pair<int, int>(node.count->first.value, node.count->second.value); count = std::make_pair<int, int>(node.count->first.value, node.count->second.value);
@ -259,7 +269,7 @@ namespace domain {
return nullopt; return nullopt;
} }
return Bus(busName, tooltip, type, count, wires, displayBus); return Bus(busName, instanceName, tooltip, type, count, wires, displayBus);
} }
@ -497,7 +507,7 @@ namespace domain {
pop(); pop();
return nullopt; return nullopt;
} }
std::string instanceName = node.instanceName->asString(); std::string instanceName = node.instanceName->value;
auto count = std::make_pair<int, int>(1, 1); auto count = std::make_pair<int, int>(1, 1);
if (node.count) { if (node.count) {
@ -950,7 +960,13 @@ namespace domain {
} }
} }
return schema; if(errors.empty()) {
return schema;
} else {
delete schema;
return nullptr;
}
} }
shared_ptr<ComponentInstance> SchemaCreator::loadComponentInstance(InstanceNode instance, Library &library) { shared_ptr<ComponentInstance> SchemaCreator::loadComponentInstance(InstanceNode instance, Library &library) {

View File

@ -309,7 +309,7 @@ struct ComponentNode : public AstNode {
std::optional<StringNode> source; std::optional<StringNode> source;
EnumNode<ComponentType> type; EnumNode<ComponentType> type;
std::vector<RuleNode> rules; std::vector<RuleNode> rules;
std::optional<StringNode> instanceName; std::optional<IdentifierNode> instanceName;
std::optional<CountNode> count; std::optional<CountNode> count;
std::optional<DisplayNode> display; std::optional<DisplayNode> display;
std::vector<PinNode> pins; std::vector<PinNode> pins;
@ -325,6 +325,7 @@ struct BusNode : public AstNode {
EnumNode<BusType> type; EnumNode<BusType> type;
IdentifierNode name; IdentifierNode name;
std::optional<IdentifierNode> instanceName;
std::optional<StringNode> tooltip; std::optional<StringNode> tooltip;
std::optional<CountNode> count; std::optional<CountNode> count;
std::optional<DisplayNode> display; std::optional<DisplayNode> display;

View File

@ -438,7 +438,7 @@ PResult<ComponentNode> ComdelParser::parseComponent() {
PResult<poly<AstNode>> err; PResult<poly<AstNode>> err;
if (check(TokenType::KW_INSTANCE_NAME)) { if (check(TokenType::KW_INSTANCE_NAME)) {
bump(); bump();
ASSIGN_OR_SET_ERR(component.instanceName, parseString()); ASSIGN_OR_SET_ERR(component.instanceName, parseIdentifier());
} else if (check(TokenType::KW_TOOLTIP)) { } else if (check(TokenType::KW_TOOLTIP)) {
bump(); bump();
ASSIGN_OR_SET_ERR(component.tooltip, parseString()); ASSIGN_OR_SET_ERR(component.tooltip, parseString());
@ -554,22 +554,30 @@ PResult<BusNode> ComdelParser::parseBus() {
PResult<poly<AstNode>> err; PResult<poly<AstNode>> err;
if (check(TokenType::KW_TOOLTIP)) { if (check(TokenType::KW_TOOLTIP)) {
bump(); bump();
ASSIGN_OR_RETURN_IF_ERR(bus.tooltip, parseString()); ASSIGN_OR_SET_ERR(bus.tooltip, parseString());
} else if (check(TokenType::KW_INSTANCE_NAME)) {
bump();
ASSIGN_OR_SET_ERR(bus.instanceName, parseIdentifier());
} else if (check(TokenType::KW_COUNT)) { } else if (check(TokenType::KW_COUNT)) {
ASSIGN_OR_RETURN_IF_ERR(bus.count, parseCount()); ASSIGN_OR_SET_ERR(bus.count, parseCount());
} else if (check(TokenType::KW_DISPLAY)) { } else if (check(TokenType::KW_DISPLAY)) {
ASSIGN_OR_RETURN_IF_ERR(bus.display, parseDisplay()); ASSIGN_OR_SET_ERR(bus.display, parseDisplay());
} else if (check(TokenType::KW_WIRES)) { } else if (check(TokenType::KW_WIRES)) {
bump(); bump();
RETURN_IF_NOT_TOKEN(TokenType::LBRACE); if(consume(TokenType::LBRACE)) {
while (check(TokenType::IDENTIFIER)) { while (check(TokenType::IDENTIFIER)) {
APPEND_OR_RETURN_IF_ERR(bus.wires, parseWire()); APPEND_OR_RETURN_IF_ERR(bus.wires, parseWire());
if (check(TokenType::COMMA)) { if (check(TokenType::COMMA)) {
RETURN_IF_NOT_TOKEN(TokenType::COMMA); RETURN_IF_NOT_TOKEN(TokenType::COMMA);
}
} }
} else {
err = unexpected();
}
if(!consume(TokenType::RBRACE)) {
err = unexpected();
} }
RETURN_IF_NOT_TOKEN(TokenType::RBRACE);
} else { } else {
err = unexpected(); err = unexpected();
bump(); bump();

View File

@ -7,7 +7,7 @@
component System component System
{ {
clock 100MHz; clock 100MHz;
//glavnaSabirnica //bus
wire<32> ADR; wire<32> ADR;
wire<32> DATA; wire<32> DATA;
wire READ; wire READ;
@ -23,31 +23,32 @@ component System
wire --BACK; wire --BACK;
//directRam
wire INT;
// components -------------------------------------------- // components --------------------------------------------
subcomponent Memorija memorija<false, 1, 65536, 8, 0>(ADR, DATA, READ, WRITE, SIZE, WAIT, INT); subcomponent Memorija mem<false, 1, 1024, 8, 1024>(ADR, DATA, READ, WRITE, SIZE, WAIT, 94534054858378, 0, null, null);
subcomponent FRISC procesor(ADR, DATA, READ, WRITE, SIZE, WAIT, INT0, INT1, INT2, INT3, --IACK, 1, *, INT) uses memorija; subcomponent FRISC procesor_002(ADR, DATA, READ, WRITE, SIZE, WAIT, INT0, INT1, INT2, INT3, --IACK, 1, *, 94534054858378, 0, null, null);
subcomponent FRISC procesor_001(ADR, DATA, READ, WRITE, SIZE, WAIT, INT0, INT1, INT2, INT3, --IACK, 1, *, 94534054858378, 0, null, null);
subcomponent FRISC procesor_000(ADR, DATA, READ, WRITE, SIZE, WAIT, INT0, INT1, INT2, INT3, --IACK, 1, *, 94534054858378, 0, null, null);
subcomponent FRISC procesor(ADR, DATA, READ, WRITE, SIZE, WAIT, INT0, INT1, INT2, INT3, --IACK, 1, *, 94534054858378, 0, null, null);
display { display {
component { x: -582; y: -296; ref: "procesor"; } component { x: 0; y: 0; ref: "procesor_002"; }
component { x: -446; y: -12; ref: "memorija"; } component { x: 0; y: 250; ref: "mem"; }
component { x: -89; y: 74; ref: "procesor_001"; }
component { x: -175; y: 195; ref: "procesor_000"; }
component { x: -195; y: 63; ref: "procesor"; }
// glavnaSabirnica bus // bus bus
rectangle { rectangle {
x: -581; y: -37; x: 0; y: 200;
w: 100; h: 20; w: 100; h: 20;
} }
// directRam bus line {x1:50; y1:116; x2:50; y2:210;}
line {x1:50; y1:234; x2:50; y2:210;}
line {x1:-39; y1:190; x2:50; y2:210;}
line {x1:-532; y1:-180; x2:-530; y2:-26;} line {x1:-145; y1:179; x2:50; y2:210;}
line {x1:-462; y1:16; x2:-466; y2:-246;} line {x1:-125; y1:311; x2:50; y2:210;}
line {x1:-396; y1:-28; x2:-530; y2:-26;}
} }
} }

View File

@ -18,7 +18,7 @@
} }
@component FRISC processor { @component FRISC processor {
@instanceName "procesor" @instanceName procesor
@tooltip "Procesor FRISC, mora postojati jedan" @tooltip "Procesor FRISC, mora postojati jedan"
@count (1, 1) @count (1, 1)
@source "FRISC.cdl" @source "FRISC.cdl"
@ -77,7 +77,7 @@
} }
@component Memorija memory { @component Memorija memory {
@instanceName "memorija" @instanceName memorija
@tooltip "Memorijska komponenta, mora postojati barem jedna" @tooltip "Memorijska komponenta, mora postojati barem jedna"
@count (1,1000) @count (1,1000)
@source "memory.cdl" @source "memory.cdl"
@ -219,7 +219,7 @@
} }
@component DMA { @component DMA {
@instanceName "dma" @instanceName dma
@tooltip "DMA-kontroler" @tooltip "DMA-kontroler"
@count (0,1000) @count (0,1000)
@source "dma.cdl" @source "dma.cdl"
@ -299,6 +299,7 @@
} }
@bus glavnaSabirnica regular { @bus glavnaSabirnica regular {
@instanceName glavnaSabirnica
@tooltip "sabirnica za spajanje FRISC a s memorijama i UI/jedinicama" @tooltip "sabirnica za spajanje FRISC a s memorijama i UI/jedinicama"
@count (1,1) @count (1,1)
@display { @display {
@ -334,6 +335,7 @@
} }
} }
@bus PIOSabirnica automatic { @bus PIOSabirnica automatic {
@instanceName PIOSabirnica
@count (0, 20) @count (0, 20)
@wires { @wires {
PIO_DATA<8>, PIO_DATA<8>,
@ -343,6 +345,7 @@
} }
@bus directRam automatic { @bus directRam automatic {
@instanceName directRam
@wires { @wires {
INT INT
} }

View File

@ -1,7 +1,7 @@
@source "frisc_library.csl" @source "frisc_library.csl"
@schema { @schema {
@instance proc FRISC { @instance procesor_002 FRISC {
@position (0, 0) @position (0, 0)
@attribute _memory null @attribute _memory null
} }
@ -15,11 +15,28 @@
@attribute pocetnaAdresa 1024 @attribute pocetnaAdresa 1024
} }
@instance procesor_001 FRISC {
@position (-89, 74)
@attribute _memory null
}
@instance procesor_000 FRISC {
@position (-175, 195)
@attribute _memory null
}
@instance procesor FRISC {
@position (-195, 63)
@attribute _memory null
}
@instance bus glavnaSabirnica { @instance bus glavnaSabirnica {
@position (0, 200) @position (0, 200)
@size 100 @size 100
} }
@connection (procesor_002.glavniPin, bus) {
}
@connection (mem.glavniPin, bus) { @connection (mem.glavniPin, bus) {
} }
} }

View File

@ -1,5 +1,6 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include "application.h"
#include <QFileDialog> #include <QFileDialog>
#include <QHBoxLayout> #include <QHBoxLayout>
@ -14,22 +15,13 @@
#include <fstream> #include <fstream>
#include <comdel/domain/comdel_generator.h> #include <comdel/domain/comdel_generator.h>
std::optional<domain::Library> MainWindow::library;
domain::Schema* MainWindow::schema;
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent) : QMainWindow(parent)
, ui(new Ui::MainWindow) , ui(new Ui::MainWindow)
{ {
schema = nullptr;
library = std::nullopt;
ui->setupUi(this); ui->setupUi(this);
setupUi(); setupUi();
// define allowed methods
validators = domain::getSupportedValidators();
} }
void MainWindow::setupUi() void MainWindow::setupUi()
@ -71,187 +63,123 @@ void MainWindow::setupUi()
void MainWindow::onLoadLibrary() { void MainWindow::onLoadLibrary() {
auto filename = QFileDialog::getOpenFileName(this, auto filename = QFileDialog::getOpenFileName(this,
tr("Open library"), "/home", tr("Comdel library (*.csl)")); tr("Open library"), "/home", tr("Comdel library (*.csl)"));
std::ostringstream buffer;
log->clear();
if(!filename.isEmpty()) { if(!filename.isEmpty()) {
librarySource = filename.toStdString(); std::ostringstream output;
clear(); clear();
ParseContext parseContext; auto librarySource = filename.toStdString();
auto libraryNode = load_library_from_file(&parseContext, filename.toStdString().c_str(), buffer);
if(libraryNode) {
domain::SchemaCreator generator(validators);
library = generator.loadLibrary(*libraryNode);
for (auto& error : generator.getErrors()) { auto instance = Application::instance();
parseContext.formatError(error, buffer, "ERROR: "); if(!instance->loadLibrary(librarySource, output)) {
} log->appendPlainText(QString::fromStdString(output.str()));
if(generator.getErrors().empty()) {
libraryDisplay->setLibrary(library);
// on library load we create a new schema
schema = new domain::Schema();
schemaDisplay->setSchema(schema, &(*library));
}
} else {
buffer<<"Failed loading library"<<std::endl;
} }
}
log->appendPlainText(QString::fromStdString(buffer.str())); libraryDisplay->setLibrary(instance->getLibrary());
schemaDisplay->setSchema(instance->getSchema(), instance->getLibrary());
}
} }
void MainWindow::onLoadSchema() { void MainWindow::onLoadSchema() {
auto filename = QFileDialog::getOpenFileName(this, auto filename = QFileDialog::getOpenFileName(this,
tr("Open schema"), "/home", tr("Comdel schema (*.csl)")); tr("Open schema"), "/home", tr("Comdel schema (*.csl)"));
std::ostringstream buffer;
log->clear();
if(!filename.isEmpty()) { if(!filename.isEmpty()) {
std::ostringstream output;
clear(); clear();
ParseContext parseContext; auto schemaSource = filename.toStdString();
auto schemaNode = load_schema_from_file(&parseContext, filename.toStdString().c_str(), buffer);
if(schemaNode) { auto instance = Application::instance();
domain::SchemaCreator generator(validators); if(!instance->loadSchema(schemaSource, output)) {
library = generator.loadLibrary(*schemaNode->library); log->appendPlainText(QString::fromStdString(output.str()));
librarySource = schemaNode->source->asString();
for (auto& error : generator.getErrors()) {
parseContext.formatError(error, buffer, "ERROR: ");
}
if(library) {
schema = generator.loadSchema(*schemaNode, *library);
for (auto& error : generator.getErrors()) {
parseContext.formatError(error, buffer, "ERROR: ");
delete schema;
schema = nullptr;
}
} else {
library = std::nullopt;
}
if(generator.getErrors().empty()) {
libraryDisplay->setLibrary(library);
schemaDisplay->setSchema(schema, &(*library));
}
} }
}
log->appendPlainText(QString::fromStdString(buffer.str())); libraryDisplay->setLibrary(instance->getLibrary());
schemaDisplay->setSchema(instance->getSchema(), instance->getLibrary());
}
} }
void MainWindow::onStoreScheme() { void MainWindow::onStoreScheme() {
if(schema == nullptr) {
return;
}
auto filename = QFileDialog::getSaveFileName(this, auto filename = QFileDialog::getSaveFileName(this,
tr("Save schema"), "/home", tr("Comdel schema (*.csl)")); tr("Save schema"), "/home", tr("Comdel schema (*.csl)"));
if(!filename.isEmpty()) {
log->clear();
std::ostringstream buffer; std::ostringstream output;
domain::generate_schema(librarySource, schema, buffer); if(Application::instance()->generateSchema(output)) {
std::ofstream out(filename.toStdString(), std::ios::out | std::ios::binary);
out<<output.str();
out.close();
log->appendPlainText("Successfully stored schema\n");
} else {
log->appendPlainText("Failed storing schema\n");
}
}
std::ofstream out(filename.toStdString(), std::ios::out | std::ios::binary);
out<<buffer.str();
out.close();
} }
void MainWindow::onGenerateComdel() { void MainWindow::onGenerateComdel() {
if(schema == nullptr) {
return;
}
auto filename = QFileDialog::getSaveFileName(this, auto filename = QFileDialog::getSaveFileName(this,
tr("Save schema"), "/home", tr("Comdel system (*.system)")); tr("Save schema"), "/home", tr("Comdel system (*.system)"));
if(!filename.isEmpty()) {
log->clear();
std::ostringstream buffer; std::ostringstream output;
domain::generate_comdel(schema, library.value(), buffer); auto validationErrors = Application::instance()->generateComdel(output);
std::ofstream out(filename.toStdString(), std::ios::out | std::ios::binary); std::ostringstream buff;
out<<buffer.str(); formatErrors(validationErrors, buff);
out.close(); log->appendPlainText(QString::fromStdString(buff.str()));
if(Application::hasErrors(validationErrors)) {
std::ofstream out(filename.toStdString(), std::ios::out | std::ios::binary);
out<<output.str();
out.close();
log->appendPlainText("Successfully generated comdel\n");
} else {
log->appendPlainText("Failed generating comdel\n");
}
}
} }
void MainWindow::onValidateSchema(bool /*toggled*/) { void MainWindow::onValidateSchema(bool /*toggled*/) {
if(schema == nullptr) {
return;
}
log->clear(); log->clear();
this->validationErrors.clear(); auto errors = Application::instance()->validateSchema();
domain::ComdelValidator validator{validators};
domain::ValidationContext context;
context.instance = nullptr;
context.attribute = nullptr;
context.addressSpaces = {};
for(auto &lib: library->getAddressSpaces()) {
context.addressSpaces.insert(std::make_pair(lib.getName(), lib));
}
auto errors = validator.validateSchema(*schema, context);
auto countValidation = validator.validateInstanceCount(*schema, *library, context);
errors.insert(errors.end(), countValidation.begin(), countValidation.end());
auto pinValidation = validator.validatePinConnections(*schema, *library, context);
errors.insert(errors.end(), pinValidation.begin(), pinValidation.end());
errors.erase(std::remove_if(
errors.begin(), errors.end(), [](const domain::ValidationError& error) { return error.type == domain::Action::WARNING;}),
errors.end());
std::ostringstream buff; std::ostringstream buff;
formatErrors(errors, buff);
for(auto err: errors) {
if(err.instance != nullptr) {
buff << err.instance->name;
}
if(err.attribute != nullptr) {
buff << "::" << err.attribute->name;
}
if(err.type == domain::Action::ERROR) {
buff << " [ERROR] ";
} else {
buff << " [WARNING] ";
}
buff << err.message << std::endl;
}
log->appendPlainText(QString::fromStdString(buff.str())); log->appendPlainText(QString::fromStdString(buff.str()));
validationErrors = errors;
} }
void MainWindow::formatErrors(std::vector<domain::ValidationError>& errors, std::ostream& output) {
for(auto& err: errors) {
if(err.instance != nullptr) {
output << err.instance->name;
}
if(err.attribute != nullptr) {
output << "::" << err.attribute->name;
}
if(err.type == domain::Action::ERROR) {
output << " [ERROR] ";
} else {
output << " [WARNING] ";
}
output << err.message << std::endl;
}
}
void MainWindow::clear() { void MainWindow::clear() {
validationErrors.clear(); log->clear();
if(schema != nullptr) { this->schemaDisplay->setSchema(nullptr, std::nullopt);
delete schema; this->libraryDisplay->setLibrary(std::nullopt);
schema = nullptr;
}
library = std::nullopt;
libraryDisplay->setLibrary(library);
schemaDisplay->setSchema(schema, nullptr);
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()

View File

@ -24,26 +24,11 @@ public:
MainWindow(QWidget *parent = nullptr); MainWindow(QWidget *parent = nullptr);
~MainWindow(); ~MainWindow();
display::Library *libraryDisplay;
display::Schema *schemaDisplay;
static std::optional<domain::Library> library;
static domain::Schema* schema;
std::vector<domain::FunctionValidator*> validators; std::vector<domain::FunctionValidator*> validators;
void setupUi(); void setupUi();
void clear(); void clear();
static domain::Schema* getSchema() {
return schema;
}
static std::optional<domain::Library> getLibrary() {
return library;
}
private slots: private slots:
void onLoadLibrary(); void onLoadLibrary();
void onLoadSchema(); void onLoadSchema();
@ -52,10 +37,11 @@ private slots:
void onGenerateComdel(); void onGenerateComdel();
private: private:
std::string librarySource; display::Library *libraryDisplay;
display::Schema *schemaDisplay;
Ui::MainWindow *ui; Ui::MainWindow *ui;
QPlainTextEdit *log; QPlainTextEdit *log;
std::vector<domain::ValidationError> validationErrors;
static void formatErrors(std::vector<domain::ValidationError>& errors, std::ostream& output);
}; };
#endif // MAIN_WINDOW_H #endif // MAIN_WINDOW_H