#include "qmk_parser.h" qmk_parser::qmk_parser() { } qmk_parser::~qmk_parser() { } QString qmk_parser::parseFiles(QString mainLayout, QString revLayout, QString mainConfig, QString revConfig, QString keymapConfig, QString keymap, QString keycodes, bool ignoreUndefinedKeys) { m_isError = false; QVector allMacroList; QVector tempMacroList; QStringList macroInFile; int allMacroListSize; macroInFile = findMacro(deleteCommentsAndStrings(mainLayout)); foreach (QString macro, macroInFile) allMacroList.push_back(parseMacro(macro)); macroInFile = findMacro(deleteCommentsAndStrings(mainConfig)); foreach (QString macro, macroInFile) tempMacroList.push_back(parseMacro(macro)); allMacroListSize = allMacroList.size(); for (int i = (allMacroListSize - 1); i >= 0; i--) { foreach (QStringList tempMacro, tempMacroList) { if (tempMacro[0] == allMacroList[i][0]) { allMacroList.remove(i); break; } } } allMacroList.append(tempMacroList); tempMacroList.clear(); macroInFile = findMacro(deleteCommentsAndStrings(revLayout)); foreach (QString macro, macroInFile) tempMacroList.push_back(parseMacro(macro)); allMacroListSize = allMacroList.size(); for (int i = (allMacroListSize - 1); i >= 0; i--) { foreach (QStringList tempMacro, tempMacroList) { if (tempMacro[0] == allMacroList[i][0]) { allMacroList.remove(i); break; } } } allMacroList.append(tempMacroList); tempMacroList.clear(); macroInFile = findMacro(deleteCommentsAndStrings(revConfig)); foreach (QString macro, macroInFile) tempMacroList.push_back(parseMacro(macro)); allMacroListSize = allMacroList.size(); for (int i = (allMacroListSize - 1); i >= 0; i--) { foreach (QStringList tempMacro, tempMacroList) { if (tempMacro[0] == allMacroList[i][0]) { allMacroList.remove(i); break; } } } allMacroList.append(tempMacroList); tempMacroList.clear(); macroInFile = findMacro(deleteCommentsAndStrings(keymapConfig)); foreach (QString macro, macroInFile) tempMacroList.push_back(parseMacro(macro)); allMacroListSize = allMacroList.size(); for (int i = (allMacroListSize - 1); i >= 0; i--) { foreach (QStringList tempMacro, tempMacroList) { if (tempMacro[0] == allMacroList[i][0]) { allMacroList.remove(i); break; } } } allMacroList.append(tempMacroList); tempMacroList.clear(); keymap = deleteCommentsAndStrings(keymap); macroInFile = findMacro(keymap); foreach (QString macro, macroInFile) tempMacroList.push_back(parseMacro(macro)); allMacroListSize = allMacroList.size(); for (int i = (allMacroListSize - 1); i >= 0; i--) { foreach (QStringList tempMacro, tempMacroList) { if (tempMacro[0] == allMacroList[i][0]) { allMacroList.remove(i); break; } } } allMacroList.append(tempMacroList); tempMacroList.clear(); QString matrix = findExpression(keymap, "keymaps"); QString matrixWithMacro = setMacroToExpression(matrix, allMacroList); QString clearedKeycodes = deleteCommentsAndStrings(keycodes); QStringList macroInKeycodes = findMacro(clearedKeycodes); QVector macroKeycodesList; foreach (QString s, macroInKeycodes) macroKeycodesList.push_back(parseMacro(s)); matrixWithMacro = setMacroToExpression(matrixWithMacro, macroKeycodesList); int assignIndex = matrixWithMacro.indexOf("="); QString matrixVal = matrixWithMacro.mid(assignIndex + 1, matrixWithMacro.size() - assignIndex - 1); int leftBracketCnt = 0; for (int i = 0; i < matrixVal.size(); i++) { if (matrixVal[i] == "{") leftBracketCnt++; else if (matrixVal[i] == "}") break; } QVector>> matrix3; QVector> matrix2; QVector matrix1; if (leftBracketCnt == 1) matrix1 = parseOneDimArr(matrixVal); else if (leftBracketCnt == 2) matrix2 = parseTwoDimArr(matrixVal); else if (leftBracketCnt == 3) matrix3 = parseThreeDimArr(matrixVal); else return QString("Error - not correct dimensions in matrix data"); if (m_isError) return QString("Parsing keyboard matrix error (ullegal matrix in keymap file)"); QStringList keycodeEnums = findEnums(clearedKeycodes); QVector> enumValues; QVector> tempEnumValues; foreach (QString s, keycodeEnums) { if (!(getEnumName(s).contains("mouse", Qt::CaseInsensitive))) { tempEnumValues = getEnumValues(s); foreach (QVector enumValue, tempEnumValues) { if (!(enumValue[0].contains("mouse", Qt::CaseInsensitive))) enumValues.push_back(enumValue); } } } QVector fnEnumVal(2); fnEnumVal[0] = "MO(1)"; fnEnumVal[1] = "1"; enumValues.push_back(fnEnumVal); m_Rows.clear(); m_Columns.clear(); m_diodeDirection = 0; foreach (QStringList macro, allMacroList) { if (macro[0] == "MATRIX_ROW_PINS") m_Rows = parseOneDimArr(macro[1].right(macro[1].size() - macro[1].indexOf("{"))); else if (macro[0] == "MATRIX_COL_PINS") m_Columns = parseOneDimArr(macro[1].right(macro[1].size() - macro[1].indexOf("{"))); else if (macro[0] == "DIODE_DIRECTION") { if (macro[1] == "COL2ROW") m_diodeDirection = 1; else m_diodeDirection = 2; } } if (!matrix3.isEmpty()) { foreach (QVector> matrix2x, matrix3) matrix2.append(matrix2x); } if (!matrix2.isEmpty()) { foreach (QVector matrix1x, matrix2) matrix1.append(matrix1x); } if ((matrix1.size() != (m_Columns.size() * m_Rows.size())) && (matrix1.size() != (2 * m_Columns.size() * m_Rows.size()))) return QString("Error - matrix has not correct count of elements (maybe there is too many dimensions)"); m_Matrix.resize(m_Rows.size()); for (int i = 0; i < m_Rows.size(); i++) { m_Matrix[i].resize(m_Columns.size()); for (int j = 0; j < m_Columns.size(); j++) { m_Matrix[i][j] = -1; foreach (QVector value, enumValues) { if (matrix1[i * m_Columns.size() + j] == value[0]) { m_Matrix[i][j] = value[1].toInt(); break; } } if (m_Matrix[i][j] == -1) m_Matrix[i][j] = strToNum(matrix1[i * m_Columns.size() + j]); if (m_Matrix[i][j] == -1) { if (!ignoreUndefinedKeys) return QString("Error - undefined elements in matrix (%1)!").arg(matrix1[i * m_Columns.size() + j]); else m_Matrix[i][j] = 0; } } } if (matrix1.size() == (m_Columns.size() * m_Rows.size())) m_FnMatrix = m_Matrix; else { m_FnMatrix.resize(m_Rows.size()); for (int i = 0; i < m_Rows.size(); i++) { m_FnMatrix[i].resize(m_Columns.size()); for (int j = 0; j < m_Columns.size(); j++) { m_FnMatrix[i][j] = -1; foreach (QVector value, enumValues) { if (matrix1[m_Columns.size() * m_Rows.size() + i * m_Columns.size() + j] == value[0]) { m_FnMatrix[i][j] = value[1].toInt(); break; } } if (m_FnMatrix[i][j] == -1) { m_FnMatrix[i][j] = strToNum(matrix1[m_Columns.size() * m_Rows.size() + i * m_Columns.size() + j]); if (m_FnMatrix[i][j] == 1) // QMK FEATURE: CODE 0x01 IS TRANSPARENT m_FnMatrix[i][j] = 0; } if (m_FnMatrix[i][j] == -1) { if (!ignoreUndefinedKeys) return QString("Error - undefined elements in matrix (%1)!").arg(matrix1[m_Columns.size() * m_Rows.size() + i * m_Columns.size() + j]); else m_FnMatrix[i][j] = 0; } if (m_FnMatrix[i][j] == 0) m_FnMatrix[i][j] = m_Matrix[i][j]; } } } return QString(""); } QStringList qmk_parser::findMacro(QString src) { QStringList lines = src.split("\n"); QStringList macro; QString macroStr; bool macroFound = false; foreach (QString str, lines) { if (!macroFound) { if (str.contains("#define")) { macroFound = true; str = str.right(str.length() - 7); } } if (macroFound) { if (str.at(str.length() - 1) == "\\") macroStr += str.left(str.length() - 1); else { macroStr += str; macro.push_back(macroStr); macroFound = false; macroStr.clear(); } } } return macro; } QStringList qmk_parser::parseMacro(QString macro) { QString inputMacro; QStringList parsedMacro; inputMacro = macro.replace(QRegExp("^[ \t\v\r\n\f]+"), ""); inputMacro = inputMacro.replace(QRegExp("[ \t\v\r\n\f]+"), " "); int firstSpace = inputMacro.indexOf(QRegExp("[ \t\v\r\n\f]+")); QString macroName = inputMacro.left(firstSpace); if (macroName.contains("(")) { int leftBracketCnt = 0; int rightBracketCnt = 0; int argStart = inputMacro.indexOf("("); int argEnd = 0; for (int i = 0; i < inputMacro.size(); i++) { if (inputMacro[i] == "(") leftBracketCnt++; else if (inputMacro[i] == ")") { rightBracketCnt++; if (rightBracketCnt == leftBracketCnt) { argEnd = i; break; } } } if (argEnd <= argStart) { m_isError = true; return parsedMacro; } QString args = inputMacro.mid(argStart + 1, argEnd - argStart - 1); macroName = macroName.left(macroName.indexOf("(")); parsedMacro.append(macroName); args += "\n"; args.replace(QRegExp("^[ \t\v\r\n\f]+"), ""); args.replace(QRegExp("[ \t\v\r\n\f]+$"), ""); QStringList argsList = args.split(QRegExp("[, \t\v\r\n\f]+")); parsedMacro.append(argsList); QString body = inputMacro.right(inputMacro.size() - argEnd - 1); body += "\n"; body = body.replace(QRegExp("^[ \t\v\r\n\f]+"), ""); body = body.replace(QRegExp("[ \t\v\r\n\f]+$"), ""); parsedMacro.append(body); } else { parsedMacro.append(macroName); if (!((firstSpace < 1) || ((inputMacro.size() - firstSpace) < 0))) { QString body = inputMacro.right(inputMacro.size() - firstSpace); body += "\n"; body = body.replace(QRegExp("^[ \t\v\r\n\f]+"), ""); body = body.replace(QRegExp("[ \t\v\r\n\f]+$"), ""); parsedMacro.append(body); } } return parsedMacro; } QString qmk_parser::deleteCommentsAndStrings(QString src, bool deleteStrings) { QString srcNoComments(src); int i = 0; bool isOneLineComment = false; bool isMultiLineComment = false; bool isString = false; QVector> deleteAreas; QVector tempArea(2, 0); while (i < srcNoComments.size()) { if (isOneLineComment) { if ((srcNoComments[i-1] != "\\") && (srcNoComments[i] == "\n")) { tempArea[1] = i; deleteAreas.push_back(tempArea); isOneLineComment = false; } } else if (isMultiLineComment) { if ((srcNoComments[i-1] == "*") && (srcNoComments[i] == "/")) { tempArea[1] = i + 1; deleteAreas.push_back(tempArea); isMultiLineComment = false; } } else if (isString) { if ((srcNoComments[i-1] != "\\") && (srcNoComments[i] == "\"")) { tempArea[1] = i + 1; if (deleteStrings) deleteAreas.push_back(tempArea); isString = false; } } else { if (srcNoComments[i] == "\"") { tempArea[0] = i; isString = true; } else if (i < (srcNoComments.size() - 1)) { if ((srcNoComments[i] == "/") && (srcNoComments[i+1] == "/")) { tempArea[0] = i; isOneLineComment = true; } else if ((srcNoComments[i] == "/") && (srcNoComments[i+1] == "*")) { tempArea[0] = i; isMultiLineComment = true; } } } i++; } if (isOneLineComment) { tempArea[1] = srcNoComments.size(); deleteAreas.push_back(tempArea); } if (isMultiLineComment) m_isError = true; if (isString) m_isError = true; for (int i = (deleteAreas.size() - 1); i >= 0; i--) srcNoComments.replace(deleteAreas[i][0], deleteAreas[i][1] - deleteAreas[i][0], ""); // THIS PART IS SIMPLE #IF 0 DELETER. #IF 0 IS IN KEYCODE.H. WORKS UNCORRECT IN OTHER SITUATIONS!!! bool isIfZero = false; QStringList srcLines = srcNoComments.split("\n"); QString srcOut; for (int i = 0; i < srcLines.size(); i++) { if (srcLines[i].contains(QRegExp("^[\\s]*#if[\\s]*0"))) isIfZero = true; if ((!isIfZero) && (!srcLines[i].contains("#endif")) && (!srcLines[i].contains("#else")) && (!srcLines[i].contains("#if"))) srcOut += (srcLines[i] + "\n"); if (isIfZero) { if ((srcLines[i].contains("#endif")) || (srcLines[i].contains("#else"))) isIfZero = false; } } return srcOut; } QString qmk_parser::findExpression(QString src, QString expression) { QStringList srcList = src.split("\n"); QString fullExpression; foreach (QString str, srcList) { if (fullExpression.size() == 0) { if (str.contains(expression)) { fullExpression = fullExpression + str + "\n"; if (str.contains(";")) break; } } else { fullExpression = fullExpression + str + "\n"; if (str.contains(";")) break; } } fullExpression.replace(QRegExp("[ \t\v\r\n\f]+"), " "); fullExpression.replace(QRegExp("^[ \t\v\r\n\f]+"), ""); fullExpression.replace(QRegExp("[ \t\v\r\n\f]+$"), ""); return fullExpression; } QString qmk_parser::setMacroToExpression(QString expression, QVector macroList) { expression.replace("\\", ""); bool expressionHasMacro = true; while (expressionHasMacro) { expressionHasMacro = false; foreach (QStringList macro, macroList) { int macroIndex = expression.indexOf(macro[0]); if (macroIndex >= 0) { if (macroIndex > 0) { if (QString(expression[macroIndex-1]).contains(QRegExp("[A-Za-z0-9_]"))) macroIndex = -1; } if ((macroIndex + macro[0].size()) < expression.size()) { if (QString(expression[macroIndex + macro[0].size()]).contains(QRegExp("[A-Za-z0-9_]"))) macroIndex = -1; } } if (macroIndex >= 0) { if (macro.size() > 2) // MACRO WITH ARGUMENTS { int leftBracketCnt = 0; int rightBracketCnt = 0; int argStart = expression.indexOf("(", macroIndex); int argEnd = 0; for (int i = macroIndex; i < expression.size(); i++) { if (expression[i] == "(") leftBracketCnt++; else if (expression[i] == ")") { rightBracketCnt++; if (rightBracketCnt == leftBracketCnt) { argEnd = i; break; } } } if (argEnd <= argStart) { m_isError = true; return QString(""); } QString args = expression.mid(argStart + 1, argEnd - argStart - 1); args += "\n"; args.replace(QRegExp("^[ \t\v\r\n\f]+"), ""); args.replace(QRegExp("[ \t\v\r\n\f]+$"), ""); QStringList argsList = args.split(QRegExp("[, \t\v\r\n\f]+")); if (argsList.size() != (macro.size() - 2)) { m_isError = true; return QString(""); } QString replacedText = macro[macro.size() - 1]; for (int i = 0; i < argsList.size(); i++) replacedText.replace (macro[i + 1], argsList[i]); expression.replace(macroIndex, argEnd - macroIndex + 1, replacedText); } else if (macro.size() == 2) // MACRO WITHOUT ARGUMENTS expression.replace(macro[0], macro[1]); if (macro.size() >= 2) expressionHasMacro = true; } } } return expression; } QStringList qmk_parser::findEnums(QString src) { QStringList output; QStringList srcRows = src.split("\n"); QString currentEnum; foreach (QString s, srcRows) { if (currentEnum.isEmpty()) { if (s.contains("enum")) { currentEnum = s + "\n"; if (s.contains(";")) { output.push_back(currentEnum); currentEnum.clear(); } } } else { currentEnum = currentEnum + s + "\n"; if (s.contains(";")) { output.push_back(currentEnum); currentEnum.clear(); } } } return output; } QString qmk_parser::getEnumName(QString enumSrc) { if (!enumSrc.contains("{")) return QString(""); enumSrc = enumSrc.left(enumSrc.indexOf("{")); enumSrc = enumSrc.simplified(); QStringList enumNameList = enumSrc.split(QRegExp("\\s")); if (enumNameList.size() != 2) return QString(""); if (enumNameList[0] != "enum") return QString(""); return enumNameList[1]; } QVector> qmk_parser::getEnumValues(QString src) { QVector> output; QVector value(2); QVector leftBrackets; QVector rightBrackets; for (int i = 0; i < src.size(); i++) { if (src[i] == "{") leftBrackets.push_back(i); if (src[i] == "}") rightBrackets.push_back(i); } if ((leftBrackets.size() != 1) || (rightBrackets.size() != 1)) { m_isError = true; return output; } QString enumContent = src.mid(leftBrackets[0] + 1, rightBrackets[0] - leftBrackets[0] - 1); QStringList enumValues = enumContent.split(QRegExp("[\\s]*,[\\s]*")); int currentIndex = 0; foreach (QString s, enumValues) { if (s.contains("=")) { QStringList enumValueBody = s.split(QRegExp("[\\s]*=[\\s]*")); currentIndex = strToNum(enumValueBody[1].simplified()); if (currentIndex == -1) { m_isError = true; return output; } value[0] = enumValueBody[0].simplified(); value[1] = QString::number(currentIndex); currentIndex++; } else { value[0] = s.simplified(); value[1] = QString::number(currentIndex); currentIndex++; } output.push_back(value); } return output; } QVector qmk_parser::parseOneDimArr(QString arr) { arr.replace("{", ""); arr.replace("}", ""); arr.replace("\\", ""); QVector output; QStringList arrElements = arr.split(QRegExp("[\\s]*,[\\s]*")); foreach (QString str, arrElements) output.push_back(str.simplified()); return output; } QVector> qmk_parser::parseTwoDimArr(QString arr) { QVector> output; QVector leftBracketIndexes; QVector rightBracketIndexes; for (int i = 0; i < arr.size(); i++) { if (arr[i] == "{") leftBracketIndexes.push_back(i); else if (arr[i] == "}") rightBracketIndexes.push_back(i); } if (leftBracketIndexes.size() != rightBracketIndexes.size()) { m_isError = true; return output; } if ((leftBracketIndexes[1] < rightBracketIndexes[0]) && (leftBracketIndexes[leftBracketIndexes.size() - 1] < rightBracketIndexes[rightBracketIndexes.size() - 2])) { leftBracketIndexes.remove(0); rightBracketIndexes.remove(rightBracketIndexes.size() - 1); } for (int i = 0; i < (leftBracketIndexes.size() - 1); i++) { if (!((leftBracketIndexes[i] < rightBracketIndexes[i]) && (leftBracketIndexes[i + 1] > rightBracketIndexes[i]))) { m_isError = true; return output; } } output.resize(leftBracketIndexes.size()); QVector arrIndexes; for (int i = 0; i < leftBracketIndexes.size(); i++) { int startIndex = 0; if (i > 0) startIndex = rightBracketIndexes[i - 1] + 1; QString indexStr = arr.mid(startIndex, leftBracketIndexes[i] - startIndex); int index = 0; if ((indexStr.contains("[")) && (indexStr.contains("]"))) { indexStr = indexStr.mid(indexStr.indexOf("[") + 1, indexStr.indexOf("]") - indexStr.indexOf("[") - 1); indexStr = indexStr.simplified(); index = strToNum(indexStr); if (index < 0) { m_isError = true; return output; } arrIndexes.push_back(index); } QString oneDimentionStr = arr.mid(leftBracketIndexes[i], rightBracketIndexes[i] - leftBracketIndexes[i] + 1); if (!arrIndexes.isEmpty()) output[index] = parseOneDimArr(oneDimentionStr); else output[i] = parseOneDimArr(oneDimentionStr); } if ((arrIndexes.size() != 0) && (arrIndexes.size() != output.size())) { output.clear(); m_isError = true; return output; } return output; } QVector>> qmk_parser::parseThreeDimArr(QString arr) { QVector>> output; QVector leftBracketIndexes; QVector rightBracketIndexes; for (int i = 0; i < arr.size(); i++) { if (arr[i] == "{") leftBracketIndexes.push_back(i); else if (arr[i] == "}") rightBracketIndexes.push_back(i); } if (leftBracketIndexes.size() != rightBracketIndexes.size()) { m_isError = true; return output; } if ((leftBracketIndexes[2] < rightBracketIndexes[0]) && (leftBracketIndexes[leftBracketIndexes.size() - 1] < rightBracketIndexes[rightBracketIndexes.size() - 3])) { arr = arr.mid(leftBracketIndexes[0] + 1, rightBracketIndexes[rightBracketIndexes.size() - 1] - leftBracketIndexes[0] - 1); // leftBracketIndexes.remove(0); // rightBracketIndexes.remove(rightBracketIndexes.size() - 1); } // QVector leftBracket2dimIndexes; // QVector rightBracket2dimIndexes; leftBracketIndexes.clear(); rightBracketIndexes.clear(); int leftBracketCounter = 0; int rightBracketCounter = 0; for (int i = 0; i < arr.size(); i++) { if (arr[i] == "{") { if (leftBracketCounter == rightBracketCounter) leftBracketIndexes.push_back(i); leftBracketCounter++; } else if (arr[i] == "}") { rightBracketCounter++; if (leftBracketCounter == rightBracketCounter) rightBracketIndexes.push_back(i); } } if (leftBracketIndexes.size() != rightBracketIndexes.size()) { m_isError = true; return output; } output.resize(leftBracketIndexes.size()); QVector arrIndexes; for (int i = 0; i < leftBracketIndexes.size(); i++) { int startIndex = 0; if (i > 0) startIndex = rightBracketIndexes[i - 1] + 1; QString indexStr = arr.mid(startIndex, leftBracketIndexes[i] - startIndex); int index = 0; if ((indexStr.contains("[")) && (indexStr.contains("]"))) { indexStr = indexStr.mid(indexStr.indexOf("[") + 1, indexStr.indexOf("]") - indexStr.indexOf("[") - 1); indexStr = indexStr.simplified(); index = strToNum(indexStr); if (index < 0) { m_isError = true; return output; } arrIndexes.push_back(index); } QString oneDimentionStr = arr.mid(leftBracketIndexes[i], rightBracketIndexes[i] - leftBracketIndexes[i] + 1); if (!arrIndexes.isEmpty()) output[index] = parseTwoDimArr(oneDimentionStr); else output[i] = parseTwoDimArr(oneDimentionStr); } if ((arrIndexes.size() != 0) && (arrIndexes.size() != output.size())) { output.clear(); m_isError = true; return output; } return output; } int qmk_parser::strToNum(QString s) { bool success = false; int number; if ((s[0] != "0") || (s.size() == 1)) number = s.toInt(&success, 10); else { if ((s[1] == "x") || (s[1] == "X")) number = s.right(s.size() - 2).toInt(&success, 16); else if ((s[1] == "b") || (s[1] == "B")) number = s.right(s.size() - 2).toInt(&success, 2); else number = s.right(s.size() - 1).toInt(&success, 8); } if (!success) number = -1; // ERROR SIGN return number; } QVector qmk_parser::getColumns() { return m_Columns; } QVector qmk_parser::getRows() { return m_Rows; } QVector> qmk_parser::getMatrix() { return m_Matrix; } QVector> qmk_parser::getFnMatrix() { return m_FnMatrix; } int qmk_parser::getDiodeDirection() { return m_diodeDirection; }