diff --git a/src/Lib/Script/FrontEnd/Lexer.cc b/src/Lib/Script/FrontEnd/Lexer.cc index 56da511c81a42f445e9f07574b3903a0e3ca3e95..89593f2af36b8a09f1e5d0732ed50d7842bdcd87 100644 --- a/src/Lib/Script/FrontEnd/Lexer.cc +++ b/src/Lib/Script/FrontEnd/Lexer.cc @@ -83,17 +83,31 @@ tokenizeFile(const char *file, TokenList *tokens, std::string *storage) throw std::runtime_error("Found an empty file!"); } + auto cantBeFollowedById = [&fileContent, &loc](const std::string &type) -> void { + bool nextIsIdChar; + if (fileContent.chopNextIdChar(&nextIsIdChar); nextIsIdChar) { + throw std::runtime_error(loc.toString() + ": Invalid " + type + + " found, directly followed by an identifier..."); + } + }; + for (;;) { global_continue: + /* Find a color literal? */ + fileContent = StrV::trimL(fileContent, &trimmedAmount); + loc = Location::shift(loc, trimmedAmount); + if (fileContent.tryChopColorLiteral(&qualifiedName, &trimmedAmount)) { + cantBeFollowedById("color literal"); + tokens->push_back(Token::fromColorLit(loc, qualifiedName)); + loc = Location::shift(loc, trimmedAmount); + continue; + } + /* Find a floating? */ fileContent = StrV::trimL(fileContent, &trimmedAmount); loc = Location::shift(loc, trimmedAmount); if (fileContent.tryChopFloating(&floating, &trimmedAmount)) { - if (fileContent.chopNextIdChar(&ok); ok) { - throw std::runtime_error(loc.toString() + - ": Invalid floating point found, directly " - "followed by an identifier..."); - } + cantBeFollowedById("floating point"); tokens->push_back(Token::fromFloating(loc, floating)); loc = Location::shift(loc, trimmedAmount); continue; @@ -103,10 +117,7 @@ tokenizeFile(const char *file, TokenList *tokens, std::string *storage) fileContent = StrV::trimL(fileContent, &trimmedAmount); loc = Location::shift(loc, trimmedAmount); if (fileContent.tryChopInteger(&integer, &trimmedAmount)) { - if (fileContent.chopNextIdChar(&ok); ok) { - throw std::runtime_error(loc.toString() + ": Invalid integer found, directly " - "followed by an identifier..."); - } + cantBeFollowedById("integer"); tokens->push_back(Token::fromInteger(loc, integer)); loc = Location::shift(loc, trimmedAmount); continue; @@ -130,10 +141,7 @@ tokenizeFile(const char *file, TokenList *tokens, std::string *storage) fileContent = StrV::trimL(fileContent, &trimmedAmount); loc = Location::shift(loc, trimmedAmount); if (fileContent.tryChopEscapedString(&qualifiedName, &trimmedAmount)) { - if (fileContent.chopNextIdChar(&ok); ok) { - throw std::runtime_error(loc.toString() + "Invalid string literal, directly " - "followed by an identifier..."); - } + cantBeFollowedById("string literal"); tokens->push_back(Token::fromStringLit(loc, qualifiedName)); loc = Location::shift(loc, trimmedAmount); continue; diff --git a/src/Lib/Script/FrontEnd/StrV.cc b/src/Lib/Script/FrontEnd/StrV.cc index 5c24d413e6a7ee329f145e1fb999eaaa073c98b7..60bfa036428751f444b81540c8e02d9f290c2afe 100644 --- a/src/Lib/Script/FrontEnd/StrV.cc +++ b/src/Lib/Script/FrontEnd/StrV.cc @@ -296,6 +296,52 @@ StrV::tryChopEscapedString(StrV *ret) noexcept return StrV::tryChopEscapedString(ret, &amount); } +bool +StrV::tryChopColorLiteral(StrV *ret) noexcept +{ + int amount = 0; + return tryChopColorLiteral(ret, &amount); +} + +bool +StrV::tryChopColorLiteral(StrV *ret, int *amount) noexcept +{ + // Valid colors example: + // #314159 + // #31415900 + // #314159AA + // The amount should be 7 or 9 (`# + rgb` or `# + rgba`) + + *amount = 0; + StrV sv = StrV::copy(*this); + + if ((sv.count == 0) || (sv.data[0] != '#')) + return false; + + *amount += 1; + sv.chopLeft(1); + + while (sv.count && (*amount <= 8)) { + if (const char c = sv.data[0]; + (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { + *amount += 1; + sv.chopLeft(1); + } else { + break; + } + } + + if (*amount != 9 && *amount != 7) { + *amount = 0; + return false; + } + + ret->data = data; + ret->count = *amount; + *this = sv; + return true; +} + bool StrV::tryChopEscapedString(StrV *ret, int *amount) noexcept { diff --git a/src/Lib/Script/FrontEnd/StrV.hh b/src/Lib/Script/FrontEnd/StrV.hh index 061012c69be56ccefe1ec0d68412e1da2e8e2759..cb6ccad40fbddaf5f03f8cab143617029a0344a9 100644 --- a/src/Lib/Script/FrontEnd/StrV.hh +++ b/src/Lib/Script/FrontEnd/StrV.hh @@ -40,10 +40,12 @@ struct StrV final { bool tryChopInteger(int *) noexcept; bool tryChopFloating(double *) noexcept; bool tryChopEscapedString(StrV *) noexcept; + bool tryChopColorLiteral(StrV *) noexcept; bool tryChopInteger(int *, int *) noexcept; bool tryChopFloating(double *, int *) noexcept; bool tryChopEscapedString(StrV *, int *) noexcept; + bool tryChopColorLiteral(StrV *, int *) noexcept; /* Find substrings or characters in a string view. Returns 1 if found, 0 * otherwise. For the index_of function the index of the character is placed diff --git a/src/Lib/Script/FrontEnd/Token.cc b/src/Lib/Script/FrontEnd/Token.cc index 71f6f53b4c8eec2d55351f70685cf3c0e2c5ec27..992950cddbb8da5ade4e44eb2ffc6e0438a5619b 100644 --- a/src/Lib/Script/FrontEnd/Token.cc +++ b/src/Lib/Script/FrontEnd/Token.cc @@ -15,6 +15,7 @@ toString(::Vivy::Script::Token::Type t) noexcept case ::Vivy::Script::Token::FLOATING: return std::string("FLOATING"); case ::Vivy::Script::Token::QNAME: return std::string("QNAME"); case ::Vivy::Script::Token::STRINGLIT: return std::string("STRINGLIT"); + case ::Vivy::Script::Token::COLORLIT: return std::string("COLORLIT"); } assert(0 && "Invalid Type was passed"); return ""; @@ -26,12 +27,24 @@ Token::isStringLit() const noexcept return (unionType == STRINGLIT); } +bool +Token::isColorLit() const noexcept +{ + return (unionType == COLORLIT); +} + void Token::assertTypeStringLit() const { assertType(STRINGLIT); } +void +Token::assertTypeColorLit() const +{ + assertType(COLORLIT); +} + bool Token::isSimple(StrV sv) const noexcept { @@ -99,6 +112,7 @@ Token::toString() const noexcept switch (unionType) { case SIMPLE: case STRINGLIT: + case COLORLIT: case QNAME: ret += value.str.toStdString(); break; case INTEGER: ret += std::to_string(value.integer); break; @@ -173,6 +187,13 @@ Token::asStringLit() const return value.str; } +StrV +Token::asColorLit() const +{ + ensureUnionType(COLORLIT); + return value.str; +} + Token Token::fromStringLit(::Vivy::Script::Location loc, StrV v) noexcept { @@ -182,6 +203,15 @@ Token::fromStringLit(::Vivy::Script::Location loc, StrV v) noexcept return ret; } +Token +Token::fromColorLit(::Vivy::Script::Location loc, StrV v) noexcept +{ + Token ret(loc); + ret.value.str = v; + ret.unionType = COLORLIT; + return ret; +} + Token Token::fromInteger(::Vivy::Script::Location loc, int v) noexcept { diff --git a/src/Lib/Script/FrontEnd/Token.hh b/src/Lib/Script/FrontEnd/Token.hh index e41e7dac993bfc9c5b606831c94412d1e4823ea2..4344986f1e5a575c0f81150ea260f735453274b7 100644 --- a/src/Lib/Script/FrontEnd/Token.hh +++ b/src/Lib/Script/FrontEnd/Token.hh @@ -14,6 +14,7 @@ public: INTEGER, FLOATING, STRINGLIT, + COLORLIT, QNAME, }; @@ -39,10 +40,12 @@ public: double asFloating() const; StrV asQName() const; StrV asStringLit() const; + StrV asColorLit() const; std::string toString() const noexcept; bool isQName() const noexcept; + bool isColorLit() const noexcept; bool isStringLit() const noexcept; bool isSimple(StrV) const noexcept; bool isSimple(const std::initializer_list<StrV> &) const noexcept; @@ -50,9 +53,11 @@ public: /* Throws an exception if the Token is not of the correct Type */ void assertType(const Type) const; void assertTypeStringLit() const; + void assertTypeColorLit() const; void assertTypeSimple(const StrV) const; void assertTypeSimple(const std::initializer_list<StrV> &) const; + static Token fromColorLit(::Vivy::Script::Location, StrV) noexcept; static Token fromStringLit(::Vivy::Script::Location, StrV) noexcept; static Token fromInteger(::Vivy::Script::Location, int) noexcept; static Token fromFloating(::Vivy::Script::Location, double) noexcept;