diff --git a/parser.cpp b/parser.cpp index bbba302e..6ba4d087 100644 --- a/parser.cpp +++ b/parser.cpp @@ -11,6 +11,7 @@ http://mozilla.org/MPL/2.0/. #include "parser.h" #include "utilities.h" #include "Logs.h" +#include #include "scenenodegroups.h" @@ -24,259 +25,316 @@ http://mozilla.org/MPL/2.0/. // cParser -- generic class for parsing text data. // constructors -cParser::cParser( std::string const &Stream, buffertype const Type, std::string Path, bool const Loadtraction, std::vector Parameters, bool allowRandom ) : - mPath(Path), - LoadTraction( Loadtraction ), - allowRandomIncludes(allowRandom) { - // store to calculate sub-sequent includes from relative path - if( Type == buffertype::buffer_FILE ) { - mFile = Stream; - } - // reset pointers and attach proper type of buffer - switch (Type) { - case buffer_FILE: { - Path.append( Stream ); - mStream = std::make_shared( Path, std::ios_base::binary ); - // content of *.inc files is potentially grouped together - if( ( Stream.size() >= 4 ) - && ( ToLower( Stream.substr( Stream.size() - 4 ) ) == ".inc" ) ) { - mIncFile = true; - scene::Groups.create(); - } - break; - } - case buffer_TEXT: { - mStream = std::make_shared( Stream ); - break; - } - default: { - break; - } - } - // calculate stream size - if (mStream) - { - if( true == mStream->fail() ) { - ErrorLog( "Failed to open file \"" + Path + "\"" ); - } - else { - mSize = mStream->rdbuf()->pubseekoff( 0, std::ios_base::end ); - mStream->rdbuf()->pubseekoff( 0, std::ios_base::beg ); - mLine = 1; - } - } - // set parameter set if one was provided - if( false == Parameters.empty() ) { - parameters.swap( Parameters ); - } +cParser::cParser(std::string const &Stream, buffertype const Type, std::string Path, bool const Loadtraction, std::vector Parameters, bool allowRandom) + : mPath(Path), LoadTraction(Loadtraction), allowRandomIncludes(allowRandom) +{ + // store to calculate sub-sequent includes from relative path + if (Type == buffertype::buffer_FILE) + { + mFile = Stream; + } + // reset pointers and attach proper type of buffer + switch (Type) + { + case buffer_FILE: + { + Path.append(Stream); + mStream = std::make_shared(Path, std::ios_base::binary); + // content of *.inc files is potentially grouped together + if ((Stream.size() >= 4) && (ToLower(Stream.substr(Stream.size() - 4)) == ".inc")) + { + mIncFile = true; + scene::Groups.create(); + } + break; + } + case buffer_TEXT: + { + mStream = std::make_shared(Stream); + break; + } + default: + { + break; + } + } + // calculate stream size + if (mStream) + { + if (true == mStream->fail()) + { + ErrorLog("Failed to open file \"" + Path + "\""); + } + else + { + mSize = mStream->rdbuf()->pubseekoff(0, std::ios_base::end); + mStream->rdbuf()->pubseekoff(0, std::ios_base::beg); + mLine = 1; + } + } + // set parameter set if one was provided + if (false == Parameters.empty()) + { + parameters.swap(Parameters); + } } // destructor -cParser::~cParser() { +cParser::~cParser() +{ - if( true == mIncFile ) { - // wrap up the node group holding content of processed file - scene::Groups.close(); - } + if (true == mIncFile) + { + // wrap up the node group holding content of processed file + scene::Groups.close(); + } } -template <> -glm::vec3 -cParser::getToken( bool const ToLower, char const *Break ) { - // NOTE: this specialization ignores default arguments - getTokens( 3, false, "\n\r\t ,;[]" ); - glm::vec3 output; - *this - >> output.x - >> output.y - >> output.z; - return output; +template <> glm::vec3 cParser::getToken(bool const ToLower, char const *Break) +{ + // NOTE: this specialization ignores default arguments + getTokens(3, false, "\n\r\t ,;[]"); + glm::vec3 output; + *this >> output.x >> output.y >> output.z; + return output; }; -template<> -cParser& -cParser::operator>>( std::string &Right ) { +template <> cParser &cParser::operator>>(std::string &Right) +{ - if( true == this->tokens.empty() ) { return *this; } + if (true == this->tokens.empty()) + { + return *this; + } - Right = this->tokens.front(); - this->tokens.pop_front(); + Right = this->tokens.front(); + this->tokens.pop_front(); - return *this; + return *this; } -template<> -cParser& -cParser::operator>>( bool &Right ) { +template <> cParser &cParser::operator>>(bool &Right) +{ - if( true == this->tokens.empty() ) { return *this; } + if (true == this->tokens.empty()) + { + return *this; + } - Right = ( ( this->tokens.front() == "true" ) - || ( this->tokens.front() == "yes" ) - || ( this->tokens.front() == "1" ) ); - this->tokens.pop_front(); + Right = ((this->tokens.front() == "true") || (this->tokens.front() == "yes") || (this->tokens.front() == "1")); + this->tokens.pop_front(); - return *this; + return *this; } -template <> -bool -cParser::getToken( bool const ToLower, const char *Break ) { +template <> bool cParser::getToken(bool const ToLower, const char *Break) +{ - auto const token = getToken( true, Break ); - return ( ( token == "true" ) - || ( token == "yes" ) - || ( token == "1" ) ); + auto const token = getToken(true, Break); + return ((token == "true") || (token == "yes") || (token == "1")); } // methods -cParser & -cParser::autoclear( bool const Autoclear ) { +cParser &cParser::autoclear(bool const Autoclear) +{ - m_autoclear = Autoclear; - if( mIncludeParser ) { mIncludeParser->autoclear( Autoclear ); } + m_autoclear = Autoclear; + if (mIncludeParser) + { + mIncludeParser->autoclear(Autoclear); + } - return *this; + return *this; } bool cParser::getTokens(unsigned int Count, bool ToLower, const char *Break) { - if( true == m_autoclear ) { - // legacy parser behaviour - tokens.clear(); - } - /* - if (LoadTraction==true) - trtest="niemaproblema"; //wczytywać - else - trtest="x"; //nie wczytywać - */ -/* - int i; - this->str(""); - this->clear(); -*/ - for (unsigned int i = tokens.size(); i < Count; ++i) - { - std::string token = readToken(ToLower, Break); - if( true == token.empty() ) { - // no more tokens - break; - } - // collect parameters - tokens.emplace_back( token ); -/* - if (i == 0) - this->str(token); - else - { - std::string temp = this->str(); - temp.append("\n"); - temp.append(token); - this->str(temp); - } -*/ - } - if (tokens.size() < Count) - return false; - else - return true; + if (true == m_autoclear) + { + // legacy parser behaviour + tokens.clear(); + } + /* + if (LoadTraction==true) + trtest="niemaproblema"; //wczytywać + else + trtest="x"; //nie wczytywać + */ + /* + int i; + this->str(""); + this->clear(); + */ + for (unsigned int i = tokens.size(); i < Count; ++i) + { + std::string token = readToken(ToLower, Break); + if (true == token.empty()) + { + // no more tokens + break; + } + // collect parameters + tokens.emplace_back(token); + /* + if (i == 0) + this->str(token); + else + { + std::string temp = this->str(); + temp.append("\n"); + temp.append(token); + this->str(temp); + } + */ + } + if (tokens.size() < Count) + return false; + else + return true; } -std::string cParser::readToken( bool ToLower, const char *Break ) { +std::string cParser::readToken(bool ToLower, const char *Break) +{ - std::string token; - if( mIncludeParser ) { - // see if there's include parsing going on. clean up when it's done. - token = mIncludeParser->readToken( ToLower, Break ); - if( true == token.empty() ) { - mIncludeParser = nullptr; - } - } - if( true == token.empty() ) { - // get the token yourself if the delegation attempt failed - char c { 0 }; - do { - while( mStream->peek() != EOF && strchr( Break, c = mStream->get() ) == NULL ) { - if( ToLower ) - c = tolower( c ); - token += c; - if( findQuotes( token ) ) // do glue together words enclosed in quotes - continue; - if( skipComments && trimComments( token ) ) // don't glue together words separated with comment - break; - } - if( c == '\n' ) { - // update line counter - ++mLine; - } - } while( token == "" && mStream->peek() != EOF ); // double check in case of consecutive separators - } - // check the first token for potential presence of utf bom - if( mFirstToken ) { - mFirstToken = false; - if( token.rfind( "\xef\xbb\xbf", 0 ) == 0 ) { - token.erase( 0, 3 ); - } - if( true == token.empty() ) { - // potentially possible if our first token was standalone utf bom - token = readToken( ToLower, Break ); - } - } + std::string token; + token.clear(); + token.reserve(64); - if( false == parameters.empty() ) { - // if there's parameter list, check the token for potential parameters to replace - size_t pos; // początek podmienianego ciągu - while( ( pos = token.find( "(p" ) ) != std::string::npos ) { - // check if the token is a parameter which should be replaced with stored true value - auto const parameter{ token.substr( pos + 2, token.find( ")", pos ) - ( pos + 2 ) ) }; // numer parametru - token.erase( pos, token.find( ")", pos ) - pos + 1 ); // najpierw usunięcie "(pN)" - size_t nr = atoi( parameter.c_str() ) - 1; - if( nr < parameters.size() ) { - token.insert( pos, parameters.at( nr ) ); // wklejenie wartości parametru - if( ToLower ) - for( ; pos < parameters.at( nr ).size(); ++pos ) - token[ pos ] = tolower( token[ pos ] ); - } - else - token.insert( pos, "none" ); // zabezpieczenie przed brakiem parametru - } - } + if (mIncludeParser) + { + // see if there's include parsing going on. clean up when it's done. + token = mIncludeParser->readToken(ToLower, Break); + if (true == token.empty()) + { + mIncludeParser = nullptr; + } + } + if (true == token.empty()) + { + // get the token yourself if the delegation attempt failed + char c{0}; + do + { + prepareBreakTable(Break); - // launch child parser if include directive found. - // NOTE: parameter collecting uses default set of token separators. - if( expandIncludes && token == "include" ) { + int ic = 0; + while ((ic = mStream->get()) != EOF) + { + unsigned char uc = static_cast(ic); + char c = static_cast(uc); + + if (isBreak(uc)) + { + if (c == '\n') + ++mLine; + break; + } + + if (ToLower) + c = static_cast(std::tolower(uc)); + token.push_back(c); + + // Quotes: nie odpalaj findQuotes() na każdym znaku – tylko jak faktycznie trafiłeś " + if (c == '"') + { + token.pop_back(); // usuwamy " + token += readQuotes(); // doklejamy środek + continue; + } + + // Comments: trimComments() jest drogie -> odpalaj tylko jak może się zaczynać komentarz + if (skipComments && (c == '/' || c == '*') && trimComments(token)) + { + break; + } + } + + if (c == '\n') + { + // update line counter + ++mLine; + } + } while (token == "" && mStream->peek() != EOF); // double check in case of consecutive separators + } + // check the first token for potential presence of utf bom + if (mFirstToken) + { + mFirstToken = false; + if (token.rfind("\xef\xbb\xbf", 0) == 0) + { + token.erase(0, 3); + } + if (true == token.empty()) + { + // potentially possible if our first token was standalone utf bom + token = readToken(ToLower, Break); + } + } + + if (false == parameters.empty()) + { + // if there's parameter list, check the token for potential parameters to replace + size_t pos; // początek podmienianego ciągu + while ((pos = token.find("(p")) != std::string::npos) + { + // check if the token is a parameter which should be replaced with stored true value + auto const parameter{token.substr(pos + 2, token.find(")", pos) - (pos + 2))}; // numer parametru + token.erase(pos, token.find(")", pos) - pos + 1); // najpierw usunięcie "(pN)" + size_t nr = atoi(parameter.c_str()) - 1; + if (nr < parameters.size()) + { + token.insert(pos, parameters.at(nr)); // wklejenie wartości parametru + if (ToLower) + { + const auto &rep = parameters.at(nr); + for (size_t j = 0; j < rep.size(); ++j) + { + unsigned char uc = static_cast(token[pos + j]); + token[pos + j] = static_cast(std::tolower(uc)); + } + } + } + else + token.insert(pos, "none"); // zabezpieczenie przed brakiem parametru + } + } + + // launch child parser if include directive found. + // NOTE: parameter collecting uses default set of token separators. + if (expandIncludes && token == "include") + { std::string includefile = allowRandomIncludes ? deserialize_random_set(*this) : readToken(ToLower); // nazwa pliku replace_slashes(includefile); - if ((true == LoadTraction) || - ((false == contains(includefile, "tr/")) && (false == contains(includefile, "tra/")))) + if ((true == LoadTraction) || ((false == contains(includefile, "tr/")) && (false == contains(includefile, "tra/")))) { if (false == contains(includefile, "_ter.scm")) { - if (Global.ParserLogIncludes) { + if (Global.ParserLogIncludes) + { // WriteLog("including: " + includefile); } mIncludeParser = std::make_shared(includefile, buffer_FILE, mPath, LoadTraction, readParameters(*this)); mIncludeParser->allowRandomIncludes = allowRandomIncludes; mIncludeParser->autoclear(m_autoclear); - if (mIncludeParser->mSize <= 0) { + if (mIncludeParser->mSize <= 0) + { ErrorLog("Bad include: can't open file \"" + includefile + "\""); } } else { - if(true == Global.file_binary_terrain_state) + if (true == Global.file_binary_terrain_state) { WriteLog("SBT found, ignoring: " + includefile); readParameters(*this); } else { - if (Global.ParserLogIncludes) { + if (Global.ParserLogIncludes) + { WriteLog("including terrain: " + includefile); } - mIncludeParser = std::make_shared(includefile, buffer_FILE, mPath, - LoadTraction, readParameters(*this)); + mIncludeParser = std::make_shared(includefile, buffer_FILE, mPath, LoadTraction, readParameters(*this)); mIncludeParser->allowRandomIncludes = allowRandomIncludes; mIncludeParser->autoclear(m_autoclear); if (mIncludeParser->mSize <= 0) @@ -285,29 +343,31 @@ std::string cParser::readToken( bool ToLower, const char *Break ) { } } } - } - else { - while( token != "end" ) { - token = readToken( true ); // minimize risk of case mismatch on comparison - } - } - token = readToken(ToLower, Break); - } - else if( ( std::strcmp( Break, "\n\r" ) == 0 ) && ( token.compare( 0, 7, "include" ) == 0 ) ) { - // HACK: if the parser reads full lines we expect this line to contain entire include directive, to make parsing easier - cParser includeparser( token.substr( 7 ) ); - std::string includefile = allowRandomIncludes ? deserialize_random_set( includeparser ) : includeparser.readToken( ToLower ); // nazwa pliku - replace_slashes(includefile); - if ((true == LoadTraction) || - ((false == contains(includefile, "tr/")) && (false == contains(includefile, "tra/")))) + } + else + { + while (token != "end") + { + token = readToken(true); // minimize risk of case mismatch on comparison + } + } + token = readToken(ToLower, Break); + } + else if ((std::strcmp(Break, "\n\r") == 0) && (token.compare(0, 7, "include") == 0)) + { + // HACK: if the parser reads full lines we expect this line to contain entire include directive, to make parsing easier + cParser includeparser(token.substr(7)); + std::string includefile = allowRandomIncludes ? deserialize_random_set(includeparser) : includeparser.readToken(ToLower); // nazwa pliku + replace_slashes(includefile); + if ((true == LoadTraction) || ((false == contains(includefile, "tr/")) && (false == contains(includefile, "tra/")))) { if (false == contains(includefile, "_ter.scm")) { - if (Global.ParserLogIncludes) { + if (Global.ParserLogIncludes) + { // WriteLog("including: " + includefile); } - mIncludeParser = std::make_shared( - includefile, buffer_FILE, mPath, LoadTraction, readParameters(includeparser)); + mIncludeParser = std::make_shared(includefile, buffer_FILE, mPath, LoadTraction, readParameters(includeparser)); mIncludeParser->allowRandomIncludes = allowRandomIncludes; mIncludeParser->autoclear(m_autoclear); if (mIncludeParser->mSize <= 0) @@ -324,52 +384,57 @@ std::string cParser::readToken( bool ToLower, const char *Break ) { } else { - if (Global.ParserLogIncludes) { + if (Global.ParserLogIncludes) + { WriteLog("including terrain: " + includefile); } - mIncludeParser = - std::make_shared(includefile, buffer_FILE, mPath, LoadTraction, - readParameters(includeparser)); + mIncludeParser = std::make_shared(includefile, buffer_FILE, mPath, LoadTraction, readParameters(includeparser)); mIncludeParser->allowRandomIncludes = allowRandomIncludes; mIncludeParser->autoclear(m_autoclear); if (mIncludeParser->mSize <= 0) { ErrorLog("Bad include: can't open file \"" + includefile + "\""); } - - } + } } } - token = readToken( ToLower, Break ); - } - // all done - return token; + token = readToken(ToLower, Break); + } + // all done + return token; } -std::vector cParser::readParameters( cParser &Input ) { +std::vector cParser::readParameters(cParser &Input) +{ - std::vector includeparameters; - std::string parameter = Input.readToken( false ); // w parametrach nie zmniejszamy - while( ( parameter.empty() == false ) - && ( parameter != "end" ) ) { - includeparameters.emplace_back( parameter ); - parameter = Input.readToken( false ); - } - return includeparameters; + std::vector includeparameters; + std::string parameter = Input.readToken(false); // w parametrach nie zmniejszamy + while ((parameter.empty() == false) && (parameter != "end")) + { + includeparameters.emplace_back(parameter); + parameter = Input.readToken(false); + } + return includeparameters; } -std::string cParser::readQuotes(char const Quote) { // read the stream until specified char or stream end - std::string token = ""; - char c { 0 }; +std::string cParser::readQuotes(char const Quote) +{ // read the stream until specified char or stream end + std::string token; + token.reserve(64); + char c{0}; bool escaped = false; - while( mStream->peek() != EOF ) { // get all chars until the quote mark + while (mStream->peek() != EOF) + { // get all chars until the quote mark c = mStream->get(); - if (escaped) { + if (escaped) + { escaped = false; } - else { - if (c == '\\') { + else + { + if (c == '\\') + { escaped = true; continue; } @@ -380,122 +445,156 @@ std::string cParser::readQuotes(char const Quote) { // read the stream until spe if (c == '\n') ++mLine; // update line counter token += c; - } + } return token; } -void cParser::skipComment( std::string const &Endmark ) { // pobieranie znaków aż do znalezienia znacznika końca - std::string input = ""; - char c { 0 }; - auto const endmarksize = Endmark.size(); - while( mStream->peek() != EOF ) { - // o ile nie koniec pliku - c = mStream->get(); // pobranie znaku - if( c == '\n' ) { - // update line counter - ++mLine; - } - input += c; - if( input == Endmark ) // szukanie znacznika końca - break; - if( input.size() >= endmarksize ) { - // keep the read text short, to avoid pointless string re-allocations on longer comments - input = input.substr( 1 ); - } - } - return; +void cParser::skipComment(std::string const &Endmark) +{ + if (Endmark == "\n") + { + mStream->ignore(std::numeric_limits::max(), '\n'); + ++mLine; + return; + } + + // dla "*/" itp. – rolling window, bez substr() co znak + std::string window; + window.reserve(Endmark.size()); + + int ic = 0; + while ((ic = mStream->get()) != EOF) + { + char c = static_cast(ic); + if (c == '\n') + ++mLine; + + window.push_back(c); + if (window.size() > Endmark.size()) + window.erase(0, window.size() - Endmark.size()); + + if (window.size() == Endmark.size() && window == Endmark) + break; + } } -bool cParser::findQuotes( std::string &String ) { +bool cParser::findQuotes(std::string &String) +{ - if( String.back() == '\"' ) { + if (String.back() == '\"') + { - String.pop_back(); - String += readQuotes(); - return true; - } - return false; + String.pop_back(); + String += readQuotes(); + return true; + } + return false; } bool cParser::trimComments(std::string &String) { - for (auto const &comment : mComments) - { - if( String.size() < comment.first.size() ) { continue; } + for (auto const &comment : mComments) + { + if (String.size() < comment.first.size()) + { + continue; + } - if (String.compare( String.size() - comment.first.size(), comment.first.size(), comment.first ) == 0) - { - skipComment(comment.second); - String.resize(String.rfind(comment.first)); - return true; - } - } - return false; + if (String.compare(String.size() - comment.first.size(), comment.first.size(), comment.first) == 0) + { + skipComment(comment.second); + String.resize(String.rfind(comment.first)); + return true; + } + } + return false; } void cParser::injectString(const std::string &str) { - if (mIncludeParser) { + if (mIncludeParser) + { mIncludeParser->injectString(str); } - else { - mIncludeParser = std::make_shared( str, buffer_TEXT, "", LoadTraction, std::vector(), allowRandomIncludes ); - mIncludeParser->autoclear( m_autoclear ); + else + { + mIncludeParser = std::make_shared(str, buffer_TEXT, "", LoadTraction, std::vector(), allowRandomIncludes); + mIncludeParser->autoclear(m_autoclear); } } int cParser::getProgress() const { - return static_cast( mStream->rdbuf()->pubseekoff(0, std::ios_base::cur) * 100 / mSize ); + return static_cast(mStream->rdbuf()->pubseekoff(0, std::ios_base::cur) * 100 / mSize); } -int cParser::getFullProgress() const { +int cParser::getFullProgress() const +{ - int progress = getProgress(); - if( mIncludeParser ) return progress + ( ( 100 - progress )*( mIncludeParser->getProgress() ) / 100 ); - else return progress; + int progress = getProgress(); + if (mIncludeParser) + return progress + ((100 - progress) * (mIncludeParser->getProgress()) / 100); + else + return progress; } -std::size_t cParser::countTokens( std::string const &Stream, std::string Path ) { +std::size_t cParser::countTokens(std::string const &Stream, std::string Path) +{ - return cParser( Stream, buffer_FILE, Path ).count(); + return cParser(Stream, buffer_FILE, Path).count(); } -std::size_t cParser::count() { +std::size_t cParser::count() +{ - std::string token; - size_t count { 0 }; - do { - token = ""; - token = readToken( false ); - ++count; - } while( false == token.empty() ); + std::string token; + size_t count{0}; + do + { + token = ""; + token = readToken(false); + ++count; + } while (false == token.empty()); - return count - 1; + return count - 1; } -void cParser::addCommentStyle( std::string const &Commentstart, std::string const &Commentend ) { +void cParser::addCommentStyle(std::string const &Commentstart, std::string const &Commentend) +{ - mComments.insert( commentmap::value_type(Commentstart, Commentend) ); + mComments.insert(commentmap::value_type(Commentstart, Commentend)); } // returns name of currently open file, or empty string for text type stream -std::string -cParser::Name() const { +std::string cParser::Name() const +{ - if( mIncludeParser ) { return mIncludeParser->Name(); } - else { return mPath + mFile; } + if (mIncludeParser) + { + return mIncludeParser->Name(); + } + else + { + return mPath + mFile; + } } // returns number of currently processed line -std::size_t -cParser::Line() const { +std::size_t cParser::Line() const +{ - if( mIncludeParser ) { return mIncludeParser->Line(); } - else { return mLine; } + if (mIncludeParser) + { + return mIncludeParser->Line(); + } + else + { + return mLine; + } } -int cParser::LineMain() const { +int cParser::LineMain() const +{ return mIncludeParser ? -1 : mLine; } diff --git a/parser.h b/parser.h index d9d96670..39a0a83a 100644 --- a/parser.h +++ b/parser.h @@ -14,6 +14,9 @@ http://mozilla.org/MPL/2.0/. #include #include #include +#include +#include +#include ///////////////////////////////////////////////////////////////////////////////////////////////////// // cParser -- generic class for parsing text data, either from file or provided string @@ -21,134 +24,148 @@ http://mozilla.org/MPL/2.0/. class cParser //: public std::stringstream { public: - // parameters: - enum buffertype - { - buffer_FILE, - buffer_TEXT - }; - // constructors: - cParser(std::string const &Stream, buffertype const Type = buffer_TEXT, std::string Path = "", bool const Loadtraction = true, std::vector Parameters = std::vector(), bool allowRandom = false ); - // destructor: - virtual ~cParser(); - // methods: - template - cParser & - operator>>( Type_ &Right ); - template - Output_ - getToken( bool const ToLower = true, char const *Break = "\n\r\t ;" ) { - getTokens( 1, ToLower, Break ); - Output_ output; - *this >> output; - return output; }; - inline - void - ignoreToken() { - readToken(); }; - inline - bool - expectToken( std::string const &Value ) { - return readToken() == Value; }; - inline - bool - eof() { - return mStream->eof(); }; - inline - bool - ok() { - return ( !mStream->fail() ); }; - cParser & - autoclear( bool const Autoclear ); - inline - bool - autoclear() const { - return m_autoclear; } - bool - getTokens( unsigned int Count = 1, bool ToLower = true, char const *Break = "\n\r\t ;" ); - // returns next incoming token, if any, without removing it from the set - inline - std::string - peek() const { - return ( - false == tokens.empty() ? - tokens.front() : - "" ); } + // parameters: + enum buffertype + { + buffer_FILE, + buffer_TEXT + }; + // constructors: + cParser(std::string const &Stream, buffertype const Type = buffer_TEXT, std::string Path = "", bool const Loadtraction = true, std::vector Parameters = std::vector(), + bool allowRandom = false); + // destructor: + virtual ~cParser(); + // methods: + template cParser &operator>>(Type_ &Right); + template Output_ getToken(bool const ToLower = true, char const *Break = "\n\r\t ;") + { + getTokens(1, ToLower, Break); + Output_ output; + *this >> output; + return output; + }; + inline void ignoreToken() + { + readToken(); + }; + inline bool expectToken(std::string const &Value) + { + return readToken() == Value; + }; + inline bool eof() + { + return mStream->eof(); + }; + inline bool ok() + { + return (!mStream->fail()); + }; + cParser &autoclear(bool const Autoclear); + inline bool autoclear() const + { + return m_autoclear; + } + bool getTokens(unsigned int Count = 1, bool ToLower = true, char const *Break = "\n\r\t ;"); + // returns next incoming token, if any, without removing it from the set + inline std::string peek() const + { + return (false == tokens.empty() ? tokens.front() : ""); + } // inject string as internal include void injectString(const std::string &str); - // returns percentage of file processed so far - int getProgress() const; - int getFullProgress() const; - // - static std::size_t countTokens( std::string const &Stream, std::string Path = "" ); - // add custom definition of text which should be ignored when retrieving tokens - void addCommentStyle( std::string const &Commentstart, std::string const &Commentend ); - // returns name of currently open file, or empty string for text type stream - std::string Name() const; - // returns number of currently processed line - std::size_t Line() const; + // returns percentage of file processed so far + int getProgress() const; + int getFullProgress() const; + // + static std::size_t countTokens(std::string const &Stream, std::string Path = ""); + // add custom definition of text which should be ignored when retrieving tokens + void addCommentStyle(std::string const &Commentstart, std::string const &Commentend); + // returns name of currently open file, or empty string for text type stream + std::string Name() const; + // returns number of currently processed line + std::size_t Line() const; // returns number of currently processed line in main file, -1 if inside include int LineMain() const; bool expandIncludes = true; bool allowRandomIncludes = false; - bool skipComments = true; + bool skipComments = true; private: - // methods: - std::string readToken(bool ToLower = true, const char *Break = "\n\r\t ;"); - std::vector readParameters( cParser &Input ); - std::string readQuotes( char const Quote = '\"' ); - void skipComment( std::string const &Endmark ); - bool findQuotes( std::string &String ); - bool trimComments( std::string &String ); - std::size_t count(); - // members: - bool m_autoclear { true }; // unretrieved tokens are discarded when another read command is issued (legacy behaviour) - bool LoadTraction { true }; // load traction? - std::shared_ptr mStream; // relevant kind of buffer is attached on creation. - std::string mFile; // name of the open file, if any - std::string mPath; // path to open stream, for relative path lookups. - std::streamoff mSize { 0 }; // size of open stream, for progress report. - std::size_t mLine { 0 }; // currently processed line - bool mIncFile { false }; // the parser is processing an *.inc file - bool mFirstToken { true }; // processing first token in the current file; helper used when checking for utf bom - typedef std::map commentmap; - commentmap mComments { - commentmap::value_type( "/*", "*/" ), - commentmap::value_type( "//", "\n" ) }; - std::shared_ptr mIncludeParser; // child class to handle include directives. - std::vector parameters; // parameter list for included file. - std::deque tokens; + // methods: + std::string readToken(bool ToLower = true, const char *Break = "\n\r\t ;"); + std::vector readParameters(cParser &Input); + std::string readQuotes(char const Quote = '\"'); + void skipComment(std::string const &Endmark); + bool findQuotes(std::string &String); + bool trimComments(std::string &String); + std::size_t count(); + // members: + std::array mBreakTable{}; + const char* mBreakPtr{ nullptr }; + + inline void prepareBreakTable(const char* Break) { + if (Break == mBreakPtr) return; + mBreakPtr = Break; + mBreakTable.fill(0); + for (const unsigned char* p = reinterpret_cast(Break); *p; ++p) { + mBreakTable[*p] = 1; + } + } + inline bool isBreak(unsigned char c) const { return mBreakTable[c] != 0; } + + bool m_autoclear{true}; // unretrieved tokens are discarded when another read command is issued (legacy behaviour) + bool LoadTraction{true}; // load traction? + std::shared_ptr mStream; // relevant kind of buffer is attached on creation. + std::string mFile; // name of the open file, if any + std::string mPath; // path to open stream, for relative path lookups. + std::streamoff mSize{0}; // size of open stream, for progress report. + std::size_t mLine{0}; // currently processed line + bool mIncFile{false}; // the parser is processing an *.inc file + bool mFirstToken{true}; // processing first token in the current file; helper used when checking for utf bom + typedef std::map commentmap; + commentmap mComments{commentmap::value_type("/*", "*/"), commentmap::value_type("//", "\n")}; + std::shared_ptr mIncludeParser; // child class to handle include directives. + std::vector parameters; // parameter list for included file. + std::deque tokens; }; - -template <> -glm::vec3 -cParser::getToken( bool const ToLower, const char *Break ); - +template <> glm::vec3 cParser::getToken(bool const ToLower, const char *Break); template cParser& cParser::operator>>( Type_ &Right ) { - if( true == this->tokens.empty() ) { return *this; } + if (this->tokens.empty()) return *this; - std::stringstream converter( this->tokens.front() ); - converter >> Right; - this->tokens.pop_front(); + const std::string &s = this->tokens.front(); - return *this; + if constexpr (std::is_integral_v && !std::is_same_v) { + // szybkie i bez-locale + Type_ v{}; + auto res = std::from_chars(s.data(), s.data() + s.size(), v); + if (res.ec == std::errc{}) Right = v; + else Right = Type_{}; // albo zostaw bez zmian – jak wolisz + } + else if constexpr (std::is_floating_point_v) { + // std::from_chars dla float bywa różnie wspierane; strtod jest OK w praktyce MaSzyny + char *end = nullptr; + double v = std::strtod(s.c_str(), &end); + Right = static_cast(v); + } + else { + // fallback dla typów nie-liczbowych (np. jakieś własne) + std::stringstream converter(s); + converter >> Right; + } + + this->tokens.pop_front(); + return *this; } -template<> -cParser& -cParser::operator>>( std::string &Right ); -template<> -cParser& -cParser::operator>>( bool &Right ); +template <> cParser &cParser::operator>>(std::string &Right); -template<> -bool -cParser::getToken( bool const ToLower, const char *Break ); +template <> cParser &cParser::operator>>(bool &Right); + +template <> bool cParser::getToken(bool const ToLower, const char *Break);