From 86b886153378d1f7d55030a6c88829a67fe1facb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20Rajkovi=C4=87?= Date: Mon, 30 May 2022 23:11:06 +0200 Subject: [PATCH] Extracted schema and library management to Application --- CMakeLists.txt | 2 +- application.cpp | 160 +++++++++++++ application.h | 41 ++++ comdel/display/attribute_dialog.cpp | 4 +- comdel/display/component_display.cpp | 3 +- comdel/display/schema_display.cpp | 8 +- comdel/display/schema_display.h | 6 +- comdel/domain/comdel_validator.cpp | 30 ++- comdel/domain/comdel_validator.h | 6 + examples/simplified FRISC model/comdel.system | 26 ++- mainwindow.cpp | 210 ++++++------------ mainwindow.h | 20 +- 12 files changed, 334 insertions(+), 182 deletions(-) create mode 100644 application.cpp create mode 100644 application.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 306e068..7ec6016 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,5 +37,5 @@ add_executable(SchemeEditor comdel/parser/comdellexer.cpp main.cpp 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) diff --git a/application.cpp b/application.cpp new file mode 100644 index 0000000..22fa292 --- /dev/null +++ b/application.cpp @@ -0,0 +1,160 @@ +// +// 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 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()) { + libraryPath = filename; + // on library load we create a new schema + schema = new domain::Schema(); + } else { + errorOutput<<"Failed creating library model"<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"< Application::validateSchema() { + if(schema == nullptr) { + return std::vector{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 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 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; +} diff --git a/application.h b/application.h new file mode 100644 index 0000000..2a4fd56 --- /dev/null +++ b/application.h @@ -0,0 +1,41 @@ +// +// Created by bbr on 27.05.22.. +// + +#ifndef SCHEMEEDITOR_APPLICATION_H +#define SCHEMEEDITOR_APPLICATION_H + + +#include "comdel/domain/library.h" +#include "comdel/domain/schema.h" +#include "comdel/domain/comdel_validator.h" + + +class Application { +private: + std::string libraryPath; + std::optional library = std::nullopt; + domain::Schema* schema = nullptr; +public: + std::optional getLibrary(); + domain::Schema* getSchema(); + std::vector validators = domain::getSupportedValidators(); + + 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 validateSchema(); + std::vector generateComdel(std::ostringstream &output); + + static bool hasErrors(std::vector empty); + + ~Application() = default; +}; + + +#endif //SCHEMEEDITOR_APPLICATION_H diff --git a/comdel/display/attribute_dialog.cpp b/comdel/display/attribute_dialog.cpp index 4d12b75..35ebad1 100644 --- a/comdel/display/attribute_dialog.cpp +++ b/comdel/display/attribute_dialog.cpp @@ -5,6 +5,8 @@ #include "attribute_dialog.h" #include "mainwindow.h" +#include "application.h" + namespace display { void AttributeDialog::onUpdate() { @@ -15,7 +17,7 @@ namespace display { 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)); } diff --git a/comdel/display/component_display.cpp b/comdel/display/component_display.cpp index 4995a4d..cb72069 100644 --- a/comdel/display/component_display.cpp +++ b/comdel/display/component_display.cpp @@ -2,6 +2,7 @@ #include "attribute_dialog.h" #include "name_dialog.h" #include "mainwindow.h" +#include "application.h" #include #include @@ -24,7 +25,7 @@ namespace display { [attr]() { if (attr->value.getType() == domain::Value::MEMORY_REFERENCE) { auto dialog = new MemoryDialog(attr, - MainWindow::getSchema()->componentInstances); + Application::instance()->getSchema()->componentInstances); dialog->exec(); } else { auto dialog = new AttributeDialog(attr); diff --git a/comdel/display/schema_display.cpp b/comdel/display/schema_display.cpp index ce2214a..fe2c171 100644 --- a/comdel/display/schema_display.cpp +++ b/comdel/display/schema_display.cpp @@ -10,13 +10,14 @@ namespace display { Schema::Schema() { this->selectedBrush.setColor(QColor::fromRgb(20, 20, 125)); this->selectedPen.setColor(QColor::fromRgb(20, 20, 125)); - + schema = nullptr; + library = std::nullopt; this->setScene(&scene); this->setAcceptDrops(true); } - void Schema::setSchema(domain::Schema *_schema, domain::Library *_library) { + void Schema::setSchema(domain::Schema *_schema, std::optional _library) { components.clear(); buses.clear(); @@ -216,6 +217,9 @@ namespace display { for (auto bus: busInstances) { auto &group = buses[bus->name]; + if(group == nullptr) { + continue; + } auto rect = new QGraphicsRectItem(group->boundingRect()); rect->setPen(selectedPen); rect->setPos(group->scenePos()); diff --git a/comdel/display/schema_display.h b/comdel/display/schema_display.h index 6593435..9d05e72 100644 --- a/comdel/display/schema_display.h +++ b/comdel/display/schema_display.h @@ -46,7 +46,7 @@ namespace display { std::vector busConnections; std::vector directConnections; - void setSchema(domain::Schema *schema, domain::Library *library); + void setSchema(domain::Schema *schema, std::optional library); void updateConnections(); @@ -65,8 +65,8 @@ namespace display { private: QGraphicsScene scene; - domain::Schema *schema; - domain::Library *library; + domain::Schema *schema{}; + std::optional library; std::vector getAvailableConnectionBusses(domain::ComponentInstance *instance, domain::Pin &pin); diff --git a/comdel/domain/comdel_validator.cpp b/comdel/domain/comdel_validator.cpp index d4ba64e..be35322 100644 --- a/comdel/domain/comdel_validator.cpp +++ b/comdel/domain/comdel_validator.cpp @@ -1,3 +1,4 @@ +#include #include "comdel_validator.h" #include "library.h" @@ -38,11 +39,11 @@ namespace domain { auto message = populateMessage( "Not enough instances of component '{componentName}' 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 > comp.getCount().second) { auto message = populateMessage( "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) { auto message = populateMessage( "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) { auto message = populateMessage( "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.attributes["instanceName"] = Value::fromString(inst->name); 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; } + std::vector + ComdelValidator::validateInstanceNames(Schema &schema, Library &library, ValidationContext context) { + std::set names; + std::vector 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) { for (auto &[key, value]: context.attributes) { source = replacePlaceholder(source, key, value); @@ -202,5 +221,4 @@ namespace domain { this->validators.insert(std::make_pair(validator->getName(), validator)); } } - } \ No newline at end of file diff --git a/comdel/domain/comdel_validator.h b/comdel/domain/comdel_validator.h index f5d9fb5..30f5b6f 100644 --- a/comdel/domain/comdel_validator.h +++ b/comdel/domain/comdel_validator.h @@ -12,6 +12,10 @@ namespace domain { InstanceAttribute *attribute; Action::ActionType type; 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 { @@ -31,6 +35,8 @@ namespace domain { std::optional validateRule(Rule rule, ValidationContext context); + std::vector validateInstanceNames(Schema &schema, Library &library, ValidationContext context); + std::vector validateInstanceCount(Schema &schema, Library &library, ValidationContext context); std::vector diff --git a/examples/simplified FRISC model/comdel.system b/examples/simplified FRISC model/comdel.system index 5a8c258..2ded265 100644 --- a/examples/simplified FRISC model/comdel.system +++ b/examples/simplified FRISC model/comdel.system @@ -7,7 +7,7 @@ component System { clock 100MHz; - //glavnaSabirnica + //bus wire<32> ADR; wire<32> DATA; wire READ; @@ -28,17 +28,21 @@ component System // components -------------------------------------------- - subcomponent Memorija memorija(ADR, DATA, READ, WRITE, SIZE, WAIT, INT); - subcomponent FRISC procesor(ADR, DATA, READ, WRITE, SIZE, WAIT, INT0, INT1, INT2, INT3, --IACK, 1, *, INT) uses memorija; + subcomponent Memorija mem(ADR, DATA, READ, WRITE, SIZE, WAIT, INT); + subcomponent FRISC proc(ADR, DATA, READ, WRITE, SIZE, WAIT, INT0, INT1, INT2, INT3, --IACK, 1, *, INT); + subcomponent FRISC procesor(ADR, DATA, READ, WRITE, SIZE, WAIT, INT0, INT1, INT2, INT3, --IACK, 1, *, 93852075053817, 0, null, null); + subcomponent FRISC procesor(ADR, DATA, READ, WRITE, SIZE, WAIT, INT0, INT1, INT2, INT3, --IACK, 1, *, 93852075053817, 0, null, null); display { - component { x: -582; y: -296; ref: "procesor"; } - component { x: -446; y: -12; ref: "memorija"; } + component { x: 0; y: 0; ref: "proc"; } + component { x: 0; y: 250; ref: "mem"; } + component { x: -185; y: 9; ref: "procesor"; } + component { x: -181; y: 194; ref: "procesor"; } - // glavnaSabirnica bus + // bus bus rectangle { - x: -581; y: -37; + x: 0; y: 200; w: 100; h: 20; } @@ -46,8 +50,10 @@ component System // directRam bus - line {x1:-532; y1:-180; x2:-530; y2:-26;} - line {x1:-462; y1:16; x2:-466; y2:-246;} - line {x1:-396; y1:-28; x2:-530; y2:-26;} + line {x1:50; y1:116; x2:50; y2:210;} + line {x1:50; y1:234; x2:50; y2:210;} + line {x1:-16; y1:278; x2:116; y2:50;} + line {x1:-135; y1:125; x2:50; y2:210;} + line {x1:-131; y1:310; x2:50; y2:210;} } } \ No newline at end of file diff --git a/mainwindow.cpp b/mainwindow.cpp index 77241a3..8ca3948 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,5 +1,6 @@ #include "mainwindow.h" #include "ui_mainwindow.h" +#include "application.h" #include #include @@ -14,22 +15,13 @@ #include #include -std::optional MainWindow::library; -domain::Schema* MainWindow::schema; - MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { - schema = nullptr; - library = std::nullopt; - ui->setupUi(this); setupUi(); - - // define allowed methods - validators = domain::getSupportedValidators(); } void MainWindow::setupUi() @@ -71,187 +63,123 @@ void MainWindow::setupUi() void MainWindow::onLoadLibrary() { auto filename = QFileDialog::getOpenFileName(this, tr("Open library"), "/home", tr("Comdel library (*.csl)")); - - std::ostringstream buffer; - - log->clear(); - if(!filename.isEmpty()) { - librarySource = filename.toStdString(); - + std::ostringstream output; clear(); - ParseContext parseContext; - auto libraryNode = load_library_from_file(&parseContext, filename.toStdString().c_str(), buffer); - if(libraryNode) { - domain::SchemaCreator generator(validators); - library = generator.loadLibrary(*libraryNode); + auto librarySource = filename.toStdString(); - for (auto& error : generator.getErrors()) { - parseContext.formatError(error, buffer, "ERROR: "); - } - - 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"<loadLibrary(librarySource, output)) { + log->appendPlainText(QString::fromStdString(output.str())); } - } - log->appendPlainText(QString::fromStdString(buffer.str())); + libraryDisplay->setLibrary(instance->getLibrary()); + schemaDisplay->setSchema(instance->getSchema(), instance->getLibrary()); + } } void MainWindow::onLoadSchema() { auto filename = QFileDialog::getOpenFileName(this, tr("Open schema"), "/home", tr("Comdel schema (*.csl)")); - - std::ostringstream buffer; - - log->clear(); - if(!filename.isEmpty()) { + std::ostringstream output; clear(); - ParseContext parseContext; - auto schemaNode = load_schema_from_file(&parseContext, filename.toStdString().c_str(), buffer); + auto schemaSource = filename.toStdString(); - if(schemaNode) { - domain::SchemaCreator generator(validators); - library = generator.loadLibrary(*schemaNode->library); - - 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)); - } + auto instance = Application::instance(); + if(!instance->loadSchema(schemaSource, output)) { + log->appendPlainText(QString::fromStdString(output.str())); } - } - log->appendPlainText(QString::fromStdString(buffer.str())); + libraryDisplay->setLibrary(instance->getLibrary()); + schemaDisplay->setSchema(instance->getSchema(), instance->getLibrary()); + } } void MainWindow::onStoreScheme() { - if(schema == nullptr) { - return; - } - auto filename = QFileDialog::getSaveFileName(this, 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<appendPlainText("Successfully stored schema\n"); + } else { + log->appendPlainText("Failed storing schema\n"); + } + } - std::ofstream out(filename.toStdString(), std::ios::out | std::ios::binary); - out<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); - out<appendPlainText(QString::fromStdString(buff.str())); + + if(Application::hasErrors(validationErrors)) { + std::ofstream out(filename.toStdString(), std::ios::out | std::ios::binary); + out<appendPlainText("Successfully generated comdel\n"); + } else { + log->appendPlainText("Failed generating comdel\n"); + } + } } void MainWindow::onValidateSchema(bool /*toggled*/) { - if(schema == nullptr) { - return; - } log->clear(); - this->validationErrors.clear(); - - 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()); + auto errors = Application::instance()->validateSchema(); std::ostringstream 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; - } + formatErrors(errors, buff); log->appendPlainText(QString::fromStdString(buff.str())); - validationErrors = errors; } +void MainWindow::formatErrors(std::vector& 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() { - validationErrors.clear(); - if(schema != nullptr) { - delete schema; - schema = nullptr; - } - library = std::nullopt; - - libraryDisplay->setLibrary(library); - schemaDisplay->setSchema(schema, nullptr); + log->clear(); + this->schemaDisplay->setSchema(nullptr, std::nullopt); + this->libraryDisplay->setLibrary(std::nullopt); } MainWindow::~MainWindow() diff --git a/mainwindow.h b/mainwindow.h index 270a661..44eba88 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -24,26 +24,11 @@ public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); - display::Library *libraryDisplay; - display::Schema *schemaDisplay; - - static std::optional library; - static domain::Schema* schema; - std::vector validators; void setupUi(); void clear(); - static domain::Schema* getSchema() { - return schema; - } - - static std::optional getLibrary() { - return library; - } - - private slots: void onLoadLibrary(); void onLoadSchema(); @@ -52,10 +37,11 @@ private slots: void onGenerateComdel(); private: - std::string librarySource; + display::Library *libraryDisplay; + display::Schema *schemaDisplay; Ui::MainWindow *ui; QPlainTextEdit *log; - std::vector validationErrors; + static void formatErrors(std::vector& errors, std::ostream& output); }; #endif // MAIN_WINDOW_H