/* * Author: Pawel Jablonski * E-mail: pj@xirx.net * WWW: xirx.net * GIT: git.xirx.net * * License: You can use this code however you like * but leave information about the original author. * Code is free for non-commercial and commercial use. */ #include "parser.h" /** * Constructor of the parser * * @param dataList Data list */ Parser::Parser(DataList &dataList) : dataList(dataList) { } /** * Parse a file * * @param path Path to the file * * @return Status of parsing the file */ bool Parser::parseFile(const QString &path) { QTextStream out(stdout); QList fileList; QList mapList; fileList.append(QFileInfo(path).absoluteFilePath()); for(int i = 0; i < fileList.size(); i++) { QString filePath = fileList.at(i); QFile file(filePath); QStringList lineList; if(!file.open(QIODevice::ReadOnly)) { out << QString("ERROR: Cannot open file: \"%1\"\n").arg(filePath); return(false); } while(!file.atEnd()) { lineList.append(file.readLine().trimmed()); } file.close(); QSharedPointer data(new Data(filePath)); QSharedPointer codeSection(new CodeSection("")); for(int j = 0; j < lineList.size(); j++) { // Trimm line QString line = lineList.at(j); int offset = line.indexOf(QRegularExpression("(?= 0) { line = line.left(offset).trimmed(); } if(line.length() <= 0) { continue; } // Find label offset = line.indexOf(QRegularExpression("\\s+")); QString label; if(offset > 0) { label = line.left(offset).trimmed(); } else { label = line; } line = line.mid(label.length()).trimmed(); // Find external or global bool isExternal = false; bool isGlobal = false; if(!label.compare("external", Qt::CaseInsensitive)) { if(this->dataList.getMode() == DataList::Mode::Rom) { isExternal = true; } else { out << QString("ERROR: Special keyword \"external\" is not allowed in APP mode, found at line %1 in file: \"%2\"\n").arg(j + 1).arg(filePath); return(false); } } if(!label.compare("global", Qt::CaseInsensitive)) { isGlobal = true; } if(isExternal || isGlobal) { offset = line.indexOf(QRegularExpression("\\s+")); if(offset > 0) { label = line.left(offset).trimmed(); } else { label = line; } line = line.mid(label.length()).trimmed(); } // Find code section if((label.left(1) == ".") && (label.right(1) == ":")) { if(label.mid(1, (label.length() - 2)).contains(QRegularExpression("^([0-9a-zA-Z_-]+)$"))) { if(!data->addCodeSection(codeSection)) { out << QString("ERROR: Reuse of section name: \"%1\", found at line %2 in file: \"%3\"\n").arg(codeSection->getName()).arg(j + 1).arg(filePath); return(false); } codeSection = QSharedPointer(new CodeSection(label.left(label.length() - 1))); if(isExternal) { if(!this->dataList.addExternalCodeSection(codeSection)) { out << QString("ERROR: Reuse of external section name: \"%1\", found at line %2 in file: \"%3\"\n").arg(codeSection->getName()).arg(j + 1).arg(filePath); return(false); } } else if(isGlobal) { if(!this->dataList.addGlobalCodeSection(codeSection)) { out << QString("ERROR: Reuse of global section name: \"%1\", found at line %2 in file: \"%3\"\n").arg(codeSection->getName()).arg(j + 1).arg(filePath); return(false); } } if(line.length() == 0) { continue; } offset = line.indexOf(QRegularExpression("\\s+")); if(offset > 0) { label = line.left(offset).trimmed(); } else { label = line; } line = line.mid(label.length()).trimmed(); } else { out << QString("ERROR: Syntax error at line %1 in file: \"%2\"\n").arg(j + 1).arg(filePath); return(false); } } // Parse include if(!label.compare("include", Qt::CaseInsensitive)) { QString tPath; if(QFileInfo(line).isAbsolute()) { tPath = line; } else { tPath = QFileInfo(QFileInfo(filePath).absolutePath().append("/").append(line)).absoluteFilePath(); } bool exists = false; for(int j = 0; j < this->dataList.size(); j++) { if(this->dataList.at(j)->getFileName() == tPath) { exists = true; break; } } if(!exists) { for(const QString &fileFor : fileList) { if(fileFor == tPath) { exists = true; break; } } } if(!exists) { fileList.append(tPath); } continue; } // Parse map if(!label.compare("map", Qt::CaseInsensitive)) { if(this->dataList.getMode() != DataList::Mode::App) { out << QString("ERROR: Special keyword \"map\" is not allowed in ROM mode, found at line %1 in file: \"%2\"\n").arg(j + 1).arg(filePath); return(false); } QString tPath; if(QFileInfo(line).isAbsolute()) { tPath = line; } else { tPath = QFileInfo(QFileInfo(filePath).absolutePath().append("/").append(line)).absoluteFilePath(); } bool exists = false; for(int k = 0; k < this->dataList.size(); k++) { if(this->dataList.at(k)->getFileName() == tPath) { exists = true; break; } } if(!exists) { for(const QString &mapFor : mapList) { if(mapFor == tPath) { exists = true; break; } } } if(!exists) { mapList.append(tPath); } QFile file(tPath); QStringList lineList; if(!file.open(QIODevice::ReadOnly)) { out << QString("ERROR: Cannot open file: \"%1\"\n").arg(tPath); return(false); } while(!file.atEnd()) { lineList.append(file.readLine().trimmed()); } file.close(); for(int j = 0; j < lineList.size(); j++) { // Trimm line QString line = lineList.at(j); int offset = line.indexOf(QRegularExpression("(?= 0) { line = line.left(offset).trimmed(); } if(line.length() <= 0) { continue; } QStringList lineItems = line.split(QRegularExpression("\\s+")); if(!this->parseMap(j, tPath, lineItems)) { return(false); } } continue; } // Parse ram if(!label.compare("var", Qt::CaseInsensitive)) { QSharedPointer ramItem = this->parseRam(j, line); if(!ramItem.isNull()) { if(!data->addRamItem(ramItem)) { out << QString("ERROR: Reuse of name: \"%1\", found at line %2 in file: \"%3\"\n").arg(ramItem->getName()).arg(j + 1).arg(filePath); return(false); } if(isExternal) { if(!this->dataList.addExternalRamItem(ramItem)) { out << QString("ERROR: Reuse of external name: \"%1\", found at line %2 in file: \"%3\"\n").arg(ramItem->getName()).arg(j + 1).arg(filePath); return(false); } } else if(isGlobal) { if(!this->dataList.addGlobalRamItem(ramItem)) { out << QString("ERROR: Reuse of global name: \"%1\", found at line %2 in file: \"%3\"\n").arg(ramItem->getName()).arg(j + 1).arg(filePath); return(false); } } } else { out << QString("ERROR: Syntax error at line %1 in file: \"%2\"\n").arg(j + 1).arg(filePath); return(false); } continue; } // Parse flash if(!label.compare("const", Qt::CaseInsensitive)) { QSharedPointer flashItem = this->parseFlash(j, line); if(!flashItem.isNull()) { if(!data->addFlashItem(flashItem)) { out << QString("ERROR: Reuse of name: \"%1\", found at line %2 in file: \"%3\"\n").arg(flashItem->getName()).arg(j + 1).arg(filePath); return(false); } if(isExternal) { if(!this->dataList.addExternalFlashItem(flashItem)) { out << QString("ERROR: Reuse of external name: \"%1\", found at line %2 in file: \"%3\"\n").arg(flashItem->getName()).arg(j + 1).arg(filePath); return(false); } } else if(isGlobal) { if(!this->dataList.addGlobalFlashItem(flashItem)) { out << QString("ERROR: Reuse of global name: \"%1\", found at line %2 in file: \"%3\"\n").arg(flashItem->getName()).arg(j + 1).arg(filePath); return(false); } } } else { out << QString("ERROR: Syntax error at line %1 in file: \"%2\"\n").arg(j + 1).arg(filePath); return(false); } continue; } // Parse code QSharedPointer codeItem = this->parseCode(j, label, line); if(!codeItem.isNull()) { codeSection->addCodeItem(codeItem); } else { out << QString("ERROR: Syntax error at line %1 in file: \"%2\"\n").arg(j + 1).arg(filePath); return(false); } } if(!data->addCodeSection(codeSection)) { out << QString("ERROR: Reuse of section name: \"%1\", found in file: \"%2\"\n").arg(codeSection->getName(), filePath); return(false); } this->dataList.append(data); } return(true); } /** * Parse a map * * @param i Line number * @param mapPath Path to the map file * @param lineItems List of the line items * * @return Status of parsing the map */ bool Parser::parseMap(int i, const QString &mapPath, const QStringList &lineItems) { QTextStream out(stdout); QString name; unsigned int address; QString tAddress; if(lineItems.size() == 3) { name = lineItems.at(0); tAddress = lineItems.at(2); if((name.left(1) == ".") && (lineItems.at(1) == "=")) { if(name.mid(1).contains(QRegularExpression("^([0-9a-zA-Z_-]+)$"))) { if(tAddress.contains(QRegularExpression("^(0x[0-9abcdefABCDEF]+)$"))) { bool tOk; address = tAddress.midRef(2).toUInt(&tOk, 16); if(tOk) { if(address < Data::MEMORY_SIZE) { QSharedPointer codeSection(new CodeSection(name)); codeSection->setAddress(address); if(!this->dataList.addExternalCodeSection(codeSection)) { out << QString("ERROR: Reuse of external section name: \"%1\", found at line %2 in file: \"%3\"\n").arg(name).arg(i + 1).arg(mapPath); return(false); } return(true); } } } } } } else if(lineItems.size() == 5) { name = lineItems.at(2); tAddress = lineItems.at(4); if((!lineItems.at(0).compare("external", Qt::CaseInsensitive)) && (lineItems.at(3) == "=")) { if(name.contains(QRegularExpression("^([0-9a-zA-Z_-]+)$"))) { if(tAddress.contains(QRegularExpression("^(0x[0-9abcdefABCDEF]+)$"))) { bool tOk; address = tAddress.midRef(2).toUInt(&tOk, 16); if(tOk) { if(address < Data::MEMORY_SIZE) { if(!lineItems.at(1).compare("var", Qt::CaseInsensitive)) { QSharedPointer ramItem(new RamItem(0, name, "")); ramItem->setAddress(address); if(!this->dataList.addExternalRamItem(ramItem)) { out << QString("ERROR: Reuse of external section name: \"%1\", found at line %2 in file: \"%3\"\n").arg(name).arg(i + 1).arg(mapPath); return(false); } } else if(!lineItems.at(1).compare("const", Qt::CaseInsensitive)) { QSharedPointer flashItem(new FlashItem(0, name, QList())); flashItem->setAddress(address); if(!this->dataList.addExternalFlashItem(flashItem)) { out << QString("ERROR: Reuse of external section name: \"%1\", found at line %2 in file: \"%3\"\n").arg(name).arg(i + 1).arg(mapPath); return(false); } } return(true); } } } } } } out << QString("ERROR: Syntax error at line %1 in file: \"%2\"\n").arg(i + 1).arg(mapPath); return(false); } /** * Parse a variable line * * @param i Line number * @param line Line raw data * * @return RAM Item object */ QSharedPointer Parser::parseRam(int i, QString &line) { QSharedPointer ramItem(nullptr); bool parseError = false; QString name; QString arraySize; if(line.length() == 0) { parseError = true; } if(line.contains(QRegularExpression("^([a-zA-Z0-9_-]+)$"))) { name = line.trimmed(); } else if(line.contains(QRegularExpression(R"(^([a-zA-Z0-9_-]+)\[([0-9]+)\]$)"))) { int offset = line.indexOf("["); name = line.left(offset).trimmed(); arraySize = line.mid((offset + 1), (line.length() - offset - 2)); } else { parseError = true; } if(!parseError) { ramItem.reset(new RamItem((i + 1), name, arraySize)); } return(ramItem); } /** * Parse a const line * * @param i Line number * @param line Line raw data * * @return Flash Item object */ QSharedPointer Parser::parseFlash(int i, QString &line) { QSharedPointer flashItem(nullptr); bool parseError = false; QString name; QStringList valueList; int offset = line.indexOf(QRegularExpression("\\s+")); if(offset > 0) { name = line.left(offset).trimmed(); line = line.mid(name.length()).trimmed(); if(line.length() > 0) { valueList = line.split(QRegularExpression(R"((? Parser::parseCode(int i, const QString &label, QString &line) { QSharedPointer codeItem(nullptr); bool parseError = false; QStringList paramList; if(line.length() > 0) { paramList = line.split(QRegularExpression("(?