/* Copyright (C) 2021 MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #pragma once #include "conststring.h" namespace genericparser { using utils::ConstString; class Tokenizer { protected: const char* mStr; const char* mEnd; public: explicit Tokenizer(const char* str, size_t length) : mStr(str), mEnd(str + length) { } size_t length() const { return mEnd - mStr; } const char* ptr() const { return mStr; } bool isSpace() const { return mStr < mEnd && mStr[0] == ' '; } bool isDigit() const { return mStr < mEnd && mStr[0] >= '0' && mStr[0] <= '9'; } bool isChar(char chr) const { return mStr < mEnd && mStr[0] == chr; } bool isAnyCharOf(char chr0, char chr1) { return mStr < mEnd && (mStr[0] == chr0 || mStr[0] == chr1); } ConstString tokenSpaces() { if (!isSpace()) return ConstString(nullptr, 0); const char* start = mStr; for (; isSpace(); mStr++) { } return ConstString(start, mStr - start); } ConstString tokenDigits() { if (!isDigit()) return ConstString(nullptr, 0); const char* start = mStr; for (; isDigit(); mStr++) { } return ConstString(start, mStr - start); } ConstString tokenChar(char chr) { if (!isChar(chr)) return ConstString(nullptr, 0); return ConstString(mStr++, 1); } ConstString tokenAnyCharOf(char chr0, char chr1) { if (!isAnyCharOf(chr0, chr1)) return ConstString(nullptr, 0); return ConstString(mStr++, 1); } }; class Parser : public Tokenizer { protected: bool mSyntaxError; public: explicit Parser(const char* str, size_t length) : Tokenizer(str, length), mSyntaxError(false) { } explicit Parser(const std::string& str) : Parser(str.data(), str.length()) { } Parser& skipLeadingSpaces() { tokenSpaces(); return *this; } bool syntaxError() const { return mSyntaxError; } bool setSyntaxError() { mSyntaxError = true; return false; } const char* tokStart() const { return mStr; } const ConstString tokStartConstString() const { return ConstString(mStr, 0); } // A helper class template to set the parser syntax error // if A returned isNull() after parsing. template class SetSyntaxErrorOnNull : public A { public: SetSyntaxErrorOnNull(Parser* p) : A(p) { if (A::isNull()) p->setSyntaxError(); } }; // A helper class template for a rule in the form: := [ ] template class Opt : public A { public: explicit Opt(const A& rhs) : A(rhs) { } explicit Opt(Parser* p) : A(p) { if (A::isNull() && !p->syntaxError()) A::operator=(A::empty(p)); } }; // Letters in the class template names below mean: // U - unused - the result class does not have the source class inside // D - derive - the result class derives from the source class // M - member - the result class adds the source class as a member // M - mandatory - this part is required during parse time // O - optional - this part is optional during parse time // A helper class template for a rule in the form: := // i.e. both parts are mandatory at parse time // The value of is not important, and is created // only temporary on the stack. // Only the value of is important. // Example: // template class UD2MM : public B { public: explicit UD2MM(Parser* p) : B(A(p).isNull() ? B() : SetSyntaxErrorOnNull(p)) { } explicit UD2MM(const B& b) : B(b) { } explicit UD2MM() : B() { } bool isNull() const { return B::isNull(); } }; // A helper class template for a rule in the form: := // i.e. both parts are mandatory at parse time. template class DD2MM : public A, public B { public: // Sets syntax error if was not followed by explicit DD2MM(Parser* p) : A(p), B(A::isNull() ? B() : SetSyntaxErrorOnNull(p)) { } explicit DD2MM(const A& a, const B& b) : A(b), B(b) { } }; // A helper class template for a rule in the form: := [ ] // i.e. is mandatory, is optional at parse time. template class DD2MO : public A, public B { public: explicit DD2MO(Parser* p) : A(p), B(A::isNull() ? B() : B(p)) { } explicit DD2MO(const A& a, const B& b) : A(a), B(b) { } }; // A helper class template for a rule in the form: := [ ] // i.e. is mandatory, is optional at parse time. // The value of is not important and is not included // into the target class, e.g. // [ ] template class UD2MO : public B { public: explicit UD2MO(Parser* p) : B(A(p).isNull() ? B() : B(p)) { } explicit UD2MO(const B& b) : B(b) { } explicit UD2MO() : B() { } }; // A helper class template for a rule in the form: := [ ] // i.e. is mandatory, is optional at parse time. // The result class derives from "A". // The result class puts "B" as a member. template class DM2MO : public A { protected: B mB; public: explicit DM2MO(Parser* p) : A(p), mB(A::isNull() ? B() : B(p)) { } }; // A helper class template for a rule in the form: := [ ] // i.e. is optional, is mandatory at parse time. template class DD2OM : public Opt, public B { public: explicit DD2OM(Parser* p) : Opt(p), B(p) { if (B::isNull()) p->setSyntaxError(); } explicit DD2OM() : Opt(A()) { } explicit DD2OM(const A& a, const B& b) : Opt(a), B(b) { } }; // A helper class template for a rule in the form: := a | b template class Choice2 : public Container { public: explicit Choice2(const A& a) : Container(a) { } explicit Choice2(const B& b) : Container(b) { } explicit Choice2(Parser* p) : Container(A(p)) { if (Container::isNull() && !p->syntaxError()) *this = Choice2(B(p)); } }; }; } // namespace genericparser