aboutsummaryrefslogtreecommitdiff
path: root/src/VersionParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VersionParser.cpp')
-rw-r--r--src/VersionParser.cpp362
1 files changed, 362 insertions, 0 deletions
diff --git a/src/VersionParser.cpp b/src/VersionParser.cpp
new file mode 100644
index 0000000..7b28cef
--- /dev/null
+++ b/src/VersionParser.cpp
@@ -0,0 +1,362 @@
+#include "../include/VersionParser.hpp"
+#include "../include/StringView.hpp"
+
+namespace sibs
+{
+ static int stringToIntNoVerify(const StringView &str)
+ {
+ int result = 0;
+ if(str.size > 0)
+ result += (str[0] - '0');
+
+ for(int i = 1; i < (int)str.size; ++i)
+ {
+ int num = str[i] - '0';
+ result += (10 * ((int)str.size - i) * num);
+ }
+ return result;
+ }
+
+ static bool isNum(char c)
+ {
+ return c >= '0' && c <= '9';
+ }
+
+ static bool isAlpha(char c)
+ {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+ }
+
+ Result<PackageVersion> parsePackageVersion(const StringView &versionStr, int *size)
+ {
+ PackageVersion result = { 0, 0, 0 };
+ int *versionPtr = &result.major;
+ int digitStart = 0;
+ int i = 0;
+
+ for(; i < (int)versionStr.size; ++i)
+ {
+ char c = versionStr[i];
+ if(isNum(c))
+ {
+ if(digitStart == -1)
+ digitStart = i;
+ }
+ else
+ {
+ int length = i - digitStart;
+ if(digitStart == -1 || length == 0)
+ return Result<PackageVersion>::Err("Package version string is in invalid format. Expected to be in format xxx.xxx.xxx where x is a number");
+
+ *versionPtr = stringToIntNoVerify({ versionStr.data + digitStart, (usize)(i - digitStart) });
+ bool endOfVersions = versionPtr == &result.patch;
+ ++versionPtr;
+ digitStart = -1;
+
+ if(c != '.' || endOfVersions)
+ break;
+ }
+ }
+
+ if(i == 0)
+ return Result<PackageVersion>::Err("version can't be empty");
+
+ if(digitStart != -1)
+ {
+ *versionPtr = stringToIntNoVerify({ versionStr.data + digitStart, (usize)(i - digitStart) });
+ ++versionPtr;
+ }
+
+ if(size)
+ *size = i;
+
+ if(versionPtr == &result.major)
+ return Result<PackageVersion>::Err("version can't be empty");
+
+ return Result<PackageVersion>::Ok(result);
+ }
+
+ VersionTokenizer::VersionTokenizer() :
+ start(nullptr),
+ code(nullptr),
+ size(0),
+ index(0)
+ {
+
+ }
+
+ VersionTokenizer::VersionTokenizer(const char *_start, const usize _size) :
+ start(_start),
+ code(_start),
+ size(_size),
+ index(0)
+ {
+
+ }
+
+ VersionTokenizer::VersionTokenizer(const VersionTokenizer &other)
+ {
+ start = other.start;
+ code = other.code;
+ size = other.size;
+ index = other.index;
+ }
+
+ VersionToken VersionTokenizer::next()
+ {
+ while(index < size)
+ {
+ char c = code[index];
+ if(c == ' ' || c == '\t' || c == '\n' || c == '\r')
+ ++index;
+ else
+ break;
+ }
+
+ if(index >= size)
+ return VersionToken::END_OF_FILE;
+
+ char c = code[index];
+ if(isNum(c))
+ {
+ int versionStrSize = 0;
+ identifier.data = code + index;
+ Result<PackageVersion> packageVersion = parsePackageVersion({ code + index, (usize)(size - index) }, &versionStrSize);
+ identifier.size = versionStrSize;
+ index += versionStrSize;
+ if(!packageVersion)
+ {
+ errMsg = packageVersion.getErrMsg();
+ return VersionToken::INVALID;
+ }
+ version = packageVersion.unwrap();
+ return VersionToken::VERSION_NUMBER;
+ }
+ else if(isAlpha(c))
+ {
+ usize identifierStart = index;
+ ++index;
+ while(index < size && isAlpha(code[index]))
+ {
+ ++index;
+ }
+ usize identifierEnd = index;
+ usize identifierLength = identifierEnd - identifierStart;
+
+ if(identifierLength == 3 && strncmp(code + identifierStart, "and", 3) == 0)
+ {
+ return VersionToken::AND;
+ }
+ else
+ {
+ errMsg = "Invalid identifier ";
+ errMsg += std::string(code + identifierStart, identifierLength);
+ return VersionToken::INVALID;
+ }
+ }
+ else if(c == '<')
+ {
+ ++index;
+ if(index < size && code[index] == '=')
+ {
+ ++index;
+ operation = VersionOperation::LESS_EQUAL;
+ return VersionToken::OPERATION;
+ }
+ operation = VersionOperation::LESS;
+ return VersionToken::OPERATION;
+ }
+ else if(c == '=')
+ {
+ ++index;
+ operation = VersionOperation::EQUAL;
+ return VersionToken::OPERATION;
+ }
+ else if(c == '>')
+ {
+ ++index;
+ if(index < size && code[index] == '=')
+ {
+ ++index;
+ operation = VersionOperation::GREATER_EQUAL;
+ return VersionToken::OPERATION;
+ }
+ operation = VersionOperation::GREATER;
+ return VersionToken::OPERATION;
+ }
+ else
+ {
+ errMsg = "Unexpected character ";
+ errMsg += c;
+ return VersionToken::INVALID;
+ }
+ }
+
+ Result<PackageVersionRange> VersionParser::parse(const char *code, const usize size)
+ {
+ versionRange = PackageVersionRange();
+ tokenizer = VersionTokenizer(code, size);
+ VersionToken token = parseStart();
+
+ if(token == VersionToken::END_OF_FILE)
+ {
+ if(!versionRange.startDefined)
+ return Result<PackageVersionRange>::Err("version can't be empty");
+ return Result<PackageVersionRange>::Ok(versionRange);
+ }
+ else if(token == VersionToken::INVALID)
+ return Result<PackageVersionRange>::Err(tokenizer.errMsg);
+ else
+ {
+ std::string errMsg = "Unexpected token '";
+ switch(token)
+ {
+ case VersionToken::NONE:
+ {
+ errMsg += "<none>";
+ break;
+ }
+ case VersionToken::OPERATION:
+ {
+ errMsg += "operation ";
+ errMsg += asString(tokenizer.operation);
+ break;
+ }
+ case VersionToken::AND:
+ {
+ errMsg += "and";
+ break;
+ }
+ case VersionToken::VERSION_NUMBER:
+ {
+ errMsg += "version ";
+ errMsg += std::string(tokenizer.identifier.data, tokenizer.identifier.size);
+ break;
+ }
+ default:
+ break;
+ }
+ errMsg += "'";
+ return Result<PackageVersionRange>::Err(errMsg);
+ }
+ }
+
+ VersionToken VersionParser::parseStart()
+ {
+ VersionToken token = tokenizer.next();
+ if(token == VersionToken::VERSION_NUMBER)
+ {
+ versionRange.startOperation = VersionOperation::GREATER_EQUAL;
+ versionRange.start = tokenizer.version;
+ versionRange.startDefined = true;
+ token = tokenizer.next();
+ if(token == VersionToken::AND)
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Unexpected end version when start version does not have operation defined";
+ }
+ }
+ else if(token == VersionToken::OPERATION)
+ {
+ versionRange.startOperation = tokenizer.operation;
+ token = tokenizer.next();
+ if(token == VersionToken::VERSION_NUMBER)
+ {
+ versionRange.start = tokenizer.version;
+ versionRange.startDefined = true;
+ switch(versionRange.startOperation)
+ {
+ case VersionOperation::LESS:
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Unexpected version end when start version is expected to less than ";
+ tokenizer.errMsg += versionRange.start.toString();
+ return token;
+ }
+ case VersionOperation::LESS_EQUAL:
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Unexpected version end when start version is expected to be less or equal to ";
+ tokenizer.errMsg += versionRange.start.toString();
+ return token;
+ }
+ case VersionOperation::EQUAL:
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Unexpected version end when start version is expected to be exactly ";
+ tokenizer.errMsg += versionRange.start.toString();
+ return token;
+ }
+ default:
+ break;
+ }
+
+ token = tokenizer.next();
+ if(token == VersionToken::AND)
+ {
+ return parseEnd();
+ }
+ }
+ else if(token == VersionToken::INVALID)
+ return token;
+ else
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Expected version after operation";
+ }
+ }
+ return token;
+ }
+
+ VersionToken VersionParser::parseEnd()
+ {
+ VersionToken token = tokenizer.next();
+ if(token == VersionToken::OPERATION)
+ {
+ versionRange.endOperation = tokenizer.operation;
+ switch(versionRange.endOperation)
+ {
+ case VersionOperation::EQUAL:
+ case VersionOperation::GREATER:
+ case VersionOperation::GREATER_EQUAL:
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "End version can only have operations < and <=";
+ return token;
+ }
+ default:
+ break;
+ }
+
+ token = tokenizer.next();
+ if(token == VersionToken::VERSION_NUMBER)
+ {
+ versionRange.end = tokenizer.version;
+ versionRange.endDefined = true;
+ if(versionRange.end <= versionRange.start)
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Expected version end to be greater than ";
+ tokenizer.errMsg += versionRange.start.toString();
+ tokenizer.errMsg += ", was ";
+ tokenizer.errMsg += versionRange.end.toString();
+ return token;
+ }
+ token = tokenizer.next();
+ }
+ else if(token == VersionToken::INVALID)
+ return token;
+ else
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Expected version after operation";
+ }
+ }
+ else
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Expected end version to have operation defined";
+ }
+ return token;
+ }
+}