// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "log_query.h" #include using namespace std; using namespace NLMISC; bool ConversionTable[LGS::TSupportedParamType::nb_enum_items][LGS::TSupportedParamType::nb_enum_items] = { // the type of the log param-> // spt_uint32, spt_uint64, spt_sint32, spt_float, spt_string, spt_entityId, spt_sheetId, spt_itemId, /* v The type of the query param v*/ /*spt_uint32*/ { true, true, true, true, true, false, true, false }, /*spt_uint64*/ { true, true, true, true, true, true, false, true }, /*spt_sint32*/ { true, true, true, true, true, false, false, false }, /*spt_float*/ { true, true, true, true, true, false, false, false }, /*spt_string*/ { true, true, true, true, true, true, true, true }, /*spt_entityId*/{ false, false, false, false, true, true, false, false }, /*spt_sheetId*/ { false, false, false, false, true, false, true, false }, /*spt_itemId*/ { false, false, false, false, true, false, false, true }, }; CQueryParser::CQueryParser(const TLogDefinitions &logDefs) : _LogDefs(logDefs) { // init the keywords table _Keywords.insert(std::make_pair(std::string("or"), tt_or)); _Keywords.insert(std::make_pair(std::string("and"), tt_and)); _Keywords.insert(std::make_pair(std::string("like"), tt_like)); _Keywords.insert(std::make_pair(std::string("full_context"), tt_full_context)); _Keywords.insert(std::make_pair(std::string("output_prefix"), tt_output_prefix)); _Keywords.insert(std::make_pair(std::string("yesterday"), tt_yesterday)); _Keywords.insert(std::make_pair(std::string("secs"), tt_secs)); _Keywords.insert(std::make_pair(std::string("mins"), tt_mins)); _Keywords.insert(std::make_pair(std::string("hours"), tt_hours)); _Keywords.insert(std::make_pair(std::string("days"), tt_days)); _Keywords.insert(std::make_pair(std::string("weeks"), tt_weeks)); _Keywords.insert(std::make_pair(std::string("months"), tt_months)); _Keywords.insert(std::make_pair(std::string("years"), tt_years)); } LGS::TSupportedParamType CQueryParser::parseParamType(const std::string &typeName) { if (typeName == "uint32") return LGS::TSupportedParamType::spt_uint32; else if (typeName == "uint64") return LGS::TSupportedParamType::spt_uint64; else if (typeName == "sint32") return LGS::TSupportedParamType::spt_sint32; else if (typeName == "float") return LGS::TSupportedParamType::spt_float; else if (typeName == "string") return LGS::TSupportedParamType::spt_string; else if (typeName == "entityId") return LGS::TSupportedParamType::spt_entityId; else if (typeName == "sheetId") return LGS::TSupportedParamType::spt_sheetId; else if (typeName == "itemId") return LGS::TSupportedParamType::spt_itemId; else return LGS::TSupportedParamType::invalid_val; } CQueryParser::TParserResult CQueryParser::parseQuery(const std::string &queryStr, bool parseOnlyOption) { // Query are formated as follow // query : options expr; // options: (options)*; // option : 'full-context' // | ?? // ; // expr : andExpr ('or' andExpr)? // ; // andExpr: atom ('and' atom)?; // atom : '(' expr ')' // | predicate; // operator : '<' // | '<=' // | '>' // | '>=' // | '=' // | '==' // | '!=' // | 'like'; // predicate : (ID | ('<' TYPE '>')) operator constant; // constant : STRING // | INT // | LONG // | FLOAT // | DATE // | entityId // | itemId // | sint; // date : DATE // | 'yesterday' // | INT 'days' // | INT 'weeks' // | INT 'months' // | INT 'years'; // longInt : INT ('l'|'L'); // sint : '-' INT; // entityId : '(' INT ':' INT ':' INT ':' INT ')'; // itemId : '[' INT ':' INT ':' INT ']'; // ID : ('a'-'z' | 'A'-'Z' | '_')('0'-'9'|'a'-'z' | 'A'-'Z' | '_')*; // STRING : '\"' (~'\"')* '\"'; // INT : ('0x')? ('0'-'9')+ // LONG : INT ('l'|'L'); // FLOAT : INT '.' INT; // DATE : INT '-' INT '-' INT (INT ':' INT)?; // TYPE : '{' ID '}' try { TParserResult pr; iterator first = queryStr.begin(); parseOptions(pr, first, queryStr.end()); if (parseOnlyOption) { return pr; } auto_ptr rootNode(parseExpr(first, queryStr.end())); // make sure we have consumed all the stream iterator rew = first; TToken tok = getNextToken(first, queryStr.end()); if (tok.TokenType != tt_EOF) throw EInvalidQuery(tok.It, "Not all the query content have been read"); pr.QueryTree = rootNode; return pr; } catch (const EInvalidQuery &iq) { nlwarning("Error will parsing query near char %u : %s", iq.It - queryStr.begin(), iq.ErrorStr); throw iq; } } void CQueryParser::parseOptions(CQueryParser::TParserResult &parserResult, CQueryParser::iterator &it, CQueryParser::iterator end) { // read options until the last one while (parseOption(parserResult, it, end)); } bool CQueryParser::parseOption(CQueryParser::TParserResult &parserResult, CQueryParser::iterator &it, CQueryParser::iterator end) { iterator rew = it; TToken tok = getNextToken(it, end); switch (tok.TokenType) { case tt_full_context: parserResult.FullContext = true; return true; case tt_output_prefix: { // read a output prefix tok = getNextToken(it, end); if (tok.TokenType != tt_EQUAL) throw EInvalidQuery(tok.It, "Output prefix option must be followed by an equal sign '='"); tok = getNextToken(it, end); if (tok.TokenType != tt_ID && tok.TokenType != tt_STRING) throw EInvalidQuery(tok.It, "Output prefix option must be followed by a the prefix value after the '=' sign"); // ok, store the prefix if (tok.TokenType == tt_STRING) parserResult.OutputPrefix = tok.Text.substr(1, tok.Text.size()-1); else parserResult.OutputPrefix = tok.Text; return true; } } // not an option it = rew; return false; } TQueryNode *CQueryParser::buildPredicate(const LGS::TParamValue &refVal, const TToken &operatorType, const TToken &leftValue, CQueryParser::iterator &it, CQueryParser::iterator end) { TQueryNode *node = NULL; if (leftValue.TokenType == tt_ID) { switch (operatorType.TokenType) { case tt_LESS: node = new TPredicateNode(refVal, leftValue.Text, _LogDefs); break; case tt_LESS_EQUAL: node = new TPredicateNode(refVal, leftValue.Text, _LogDefs); break; case tt_GREATER: node = new TPredicateNode(refVal, leftValue.Text, _LogDefs); break; case tt_GREATER_EQUAL: node = new TPredicateNode(refVal, leftValue.Text, _LogDefs); break; case tt_EQUAL: node = new TPredicateNode(refVal, leftValue.Text, _LogDefs); break; case tt_NOT_EQUAL: node = new TPredicateNode(refVal, leftValue.Text, _LogDefs); break; case tt_like: node = new TPredicateNode(refVal, leftValue.Text, _LogDefs); break; default: throw EInvalidQuery(operatorType.It, "Invalid operator for predicate"); } if (!node->init()) { nlwarning("No log match a parameter named %s with a predicate of type %s", leftValue.Text.c_str(), refVal.getType().toString().c_str()); } } else if (leftValue.TokenType == tt_TYPE) { LGS::TSupportedParamType type = parseParamType(leftValue.Text); if (type == LGS::TSupportedParamType::invalid_val) throw EInvalidQuery(leftValue.It, "Invalid type name"); switch (operatorType.TokenType) { case tt_LESS: node = new TTypePredicateNode(refVal, type, _LogDefs); break; case tt_LESS_EQUAL: node = new TTypePredicateNode(refVal, type, _LogDefs); break; case tt_GREATER: node = new TTypePredicateNode(refVal, type, _LogDefs); break; case tt_GREATER_EQUAL: node = new TTypePredicateNode(refVal, type, _LogDefs); break; case tt_EQUAL: node = new TTypePredicateNode(refVal, type, _LogDefs); break; case tt_NOT_EQUAL: node = new TTypePredicateNode(refVal, type, _LogDefs); break; case tt_like: node = new TTypePredicateNode(refVal, type, _LogDefs); break; default: throw EInvalidQuery(operatorType.It, "Invalid operator for predicate"); } if (!node->init()) { nlwarning("No log match a parameter of type %s with a predicate of type %s", leftValue.Text.c_str(), refVal.getType().toString().c_str()); } } return node; } sint32 CQueryParser::parseSint(CQueryParser::iterator &it, CQueryParser::iterator end) { iterator rew = it; skipWS(it, end); iterator start = it; TToken tok = getNextToken(it, end); if (tok.TokenType != tt_DASH) throw EInvalidQuery(rew, "Invalid start character for sint, must be '-'"); rew = it; tok = getNextToken(it, end); if (tok.TokenType != tt_INT) throw EInvalidQuery(rew, "Invalid content for sint, must be an int value"); sint32 val; NLMISC::fromString(string(start, it), val); return val; } std::string CQueryParser::parseItemId(CQueryParser::iterator &it, CQueryParser::iterator end) { iterator rew = it; it = skipWS(it, end); iterator start = it; TToken tok = getNextToken(it, end); if (tok.TokenType != tt_OPEN_BRACKET) throw EInvalidQuery(tok.It, "Invalid start character for item id, must be '['"); rew = it; tok = getNextToken(it, end); if (tok.TokenType != tt_INT) throw EInvalidQuery(tok.It, "Invalid first element for item id, must be an int value"); rew = it; tok = getNextToken(it, end); if (tok.TokenType != tt_COLON) throw EInvalidQuery(rew, "Invalid separator for item id, must be a ':' char"); rew = it; tok = getNextToken(it, end); if (tok.TokenType != tt_INT) throw EInvalidQuery(tok.It, "Invalid second element for item id, must be an int value"); rew = it; tok = getNextToken(it, end); if (tok.TokenType != tt_COLON) throw EInvalidQuery(tok.It, "Invalid separator for item id, must be a ':' char"); rew = it; tok = getNextToken(it, end); if (tok.TokenType != tt_INT) throw EInvalidQuery(tok.It, "Invalid third element for item id, must be an int value"); tok = getNextToken(it, end); if (tok.TokenType != tt_CLOSE_BRACKET) throw EInvalidQuery(tok.It, "Invalid end character for item id, must be ']'"); return string(start, it); } std::string CQueryParser::parseEntityId(CQueryParser::iterator &it, CQueryParser::iterator end) { iterator rew = it; it = skipWS(it, end); iterator start = it; TToken tok = getNextToken(it, end); if (tok.TokenType != tt_OPEN_PAR) throw EInvalidQuery(tok.It, "Invalid start character for entity id, must be '('"); rew = it; tok = getNextToken(it, end); if (tok.TokenType != tt_INT) throw EInvalidQuery(tok.It, "Invalid first element for entity id, must be an hexa value"); rew = it; tok = getNextToken(it, end); if (tok.TokenType != tt_COLON) throw EInvalidQuery(tok.It, "Invalid separator for entity id, must be a ':' char"); rew = it; tok = getNextToken(it, end); if (tok.TokenType != tt_INT && tok.TokenType != tt_NAKED_HEXA && !(tok.TokenType == tt_ID && isNakedHexa(tok.Text))) throw EInvalidQuery(tok.It, "Invalid second element for entity id, must be an int or naked hexa value"); rew = it; tok = getNextToken(it, end); if (tok.TokenType != tt_COLON) throw EInvalidQuery(tok.It, "Invalid separator for entity id, must be a ':' char"); rew = it; tok = getNextToken(it, end); if (tok.TokenType != tt_INT && tok.TokenType != tt_NAKED_HEXA && !(tok.TokenType == tt_ID && isNakedHexa(tok.Text))) throw EInvalidQuery(tok.It, "Invalid third element for entity id, must be an int or naked hexa value"); rew = it; tok = getNextToken(it, end); if (tok.TokenType != tt_COLON) throw EInvalidQuery(tok.It, "Invalid separator for entity id, must be a ':' char"); rew = it; tok = getNextToken(it, end); if (tok.TokenType != tt_INT && tok.TokenType != tt_NAKED_HEXA && !(tok.TokenType == tt_ID && isNakedHexa(tok.Text))) throw EInvalidQuery(tok.It, "Invalid fourth element for entity id, must be an int or naked hexa value"); rew = it; tok = getNextToken(it, end); if (tok.TokenType != tt_CLOSE_PAR) throw EInvalidQuery(tok.It, "Invalid end character for entity id, must be ')'"); return string(start, it); } bool CQueryParser::isNakedHexa(const std::string &text) { for (uint i=0; i= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') )) return false; } return true; } LGS::TParamValue CQueryParser::parseConstant(CQueryParser::iterator &it, CQueryParser::iterator end) { iterator rew = it; TToken constantTok = getNextToken(it, end); switch (constantTok.TokenType) { case tt_INT: { uint32 num; NLMISC::fromString(constantTok.Text, num); rew = it; TToken dateTag = getNextToken(it, end); switch (dateTag.TokenType) { case tt_secs: num = CTime::getSecondsSince1970()-num; break; case tt_mins: num = CTime::getSecondsSince1970()-num*60; break; case tt_hours: num = CTime::getSecondsSince1970()-num*(60*60); break; case tt_days: num = CTime::getSecondsSince1970()-num*(60*60*24); break; case tt_weeks: num = CTime::getSecondsSince1970()-num*(60*60*24*7); break; case tt_months: num = CTime::getSecondsSince1970()-num*(60*60*24*30); break; case tt_years: num = CTime::getSecondsSince1970()-num*(60*60*24*365); break; default: // rewind the last token it = rew; } return LGS::TParamValue(num); } case tt_STRING: return LGS::TParamValue(constantTok.Text.substr(1, constantTok.Text.size()-1)); case tt_FLOAT: return LGS::TParamValue(float(atof(constantTok.Text.c_str()))); case tt_LONG: return LGS::TParamValue(uint64(atol(constantTok.Text.c_str()))); case tt_DATE: { struct tm date; memset(&date, 0, sizeof(date)); int nbParam = sscanf(constantTok.Text.c_str(), "%i-%i-%i %i:%i:%i", &date.tm_year, &date.tm_mon, &date.tm_mday, &date.tm_hour, &date.tm_min, &date.tm_sec); // adjust the year offset date.tm_year -= 1900; // adjust month date.tm_mon -= 1; // let the CRunt time compute the daylight saving offset date.tm_isdst = -1; time_t t = mktime(&date); return LGS::TParamValue(uint32(t)); } case tt_OPEN_PAR: { it = rew; std::string eids = parseEntityId(it, end); CEntityId eid(eids); return LGS::TParamValue(eid); } case tt_OPEN_BRACKET: { it = rew; std::string itemIdStr = parseItemId(it, end); INVENTORIES::TItemId itemId(itemIdStr); return LGS::TParamValue(itemId); } case tt_DASH: { it = rew; sint32 i = parseSint(it, end); return LGS::TParamValue(i); } case tt_ID: { return LGS::TParamValue(CSheetId(constantTok.Text)); } case tt_yesterday: { uint32 now = CTime::getSecondsSince1970(); // return date of yesterday return LGS::TParamValue(now - 60*60*24); } default: throw EInvalidQuery(constantTok.It, "Invalid constant value on right of operator for predicate"); } return LGS::TParamValue(); } TQueryNode* CQueryParser::parsePredicate(CQueryParser::iterator &it, CQueryParser::iterator end) { iterator rew = it; TToken idTok = getNextToken(it, end); if (idTok.TokenType == tt_OPEN_BRACE) { // try to read a type idTok = getNextToken(it, end); if (idTok.TokenType != tt_ID) throw EInvalidQuery(idTok.It, "Param type predicate must follow the form '{type}', invalid type identifier"); TToken tok = getNextToken(it, end); if (tok.TokenType != tt_CLOSE_BRACE) throw EInvalidQuery(tok.It, "Param type predicate must follow the form '{type}', invalid close char (not a closing brace '}')"); idTok.TokenType = tt_TYPE; } if (idTok.TokenType != tt_ID && idTok.TokenType != tt_TYPE) throw EInvalidQuery(idTok.It, "Invalid left parameter for predicate"); TToken opTok = getNextToken(it, end); switch (opTok.TokenType) { case tt_LESS: case tt_LESS_EQUAL: case tt_GREATER: case tt_GREATER_EQUAL: case tt_EQUAL: case tt_NOT_EQUAL: case tt_like: { LGS::TParamValue refVal = parseConstant(it, end); return buildPredicate(refVal, opTok, idTok, it, end); } default: throw EInvalidQuery(opTok.It, "Invalid operator for predicate"); } return NULL; } TQueryNode* CQueryParser::parseAtom(CQueryParser::iterator &it, CQueryParser::iterator end) { iterator rew = it; TQueryNode *node = NULL; TToken tok = getNextToken(it, end); if (tok.TokenType == tt_OPEN_PAR) { node = parseExpr(it, end); // must have a close token tok = getNextToken(it, end); if (tok.TokenType != tt_CLOSE_PAR) throw EInvalidQuery(tok.It, "Missing closing parenthesis"); } else { it = rew; node = parsePredicate(it, end); } return node; } TQueryNode *CQueryParser::parseAndExpr(CQueryParser::iterator &it, CQueryParser::iterator end) { TQueryNode *node = parseAtom(it, end); iterator rew = it; TToken tok = getNextToken(it, end); while (tok.TokenType != tt_EOF) { if (tok.TokenType == tt_and) { TQueryNode *left = node; // create a 'or' root node node = new TCombineNode; // parse another expression TQueryNode *right = parseAtom(it, end); node->LeftNode = left; node->RightNode = right; rew = it; // parse an optional 'and' tok = getNextToken(it, end); } else { it = rew; break; } } return node; } TQueryNode *CQueryParser::parseExpr(CQueryParser::iterator &it, CQueryParser::iterator end) { TQueryNode *node = parseAndExpr(it, end); iterator rew = it; TToken tok = getNextToken(it, end); while (tok.TokenType != tt_EOF) { if (tok.TokenType == tt_or) { TQueryNode *left = node; // create a 'or' root node node = new TCombineNode; // parse another expression TQueryNode *right = parseAndExpr(it, end); node->LeftNode = left; node->RightNode = right; rew = it; // parse an optional 'or' tok = getNextToken(it, end); } else { it = rew; break; } } return node; } // parse an ID bool CQueryParser::parseID(CQueryParser::iterator &it, CQueryParser::iterator end) { iterator rew = it; if (it != end && (*it>='a' && *it<='z' || *it>='A' && *it<='Z' || *it == '_')) { ++it; while (it != end && (*it>='a' && *it<='z' || *it>='A' && *it<='Z' || *it == '_' || *it>='0' && *it<='9' || *it == '.')) ++it; return true; } it = rew; return false; } // parse an STRING bool CQueryParser::parseSTRING(CQueryParser::iterator &it, CQueryParser::iterator end) { iterator rew = it; if (it != end && *it == '"') { ++it; char c = *it; while (it != end && *it != '"') ++it; if (it != end && *it == '"') { ++it; return true; } } it = rew; return false; } // Parse a date bool CQueryParser::parseDATE(CQueryParser::iterator &it, CQueryParser::iterator end) { iterator rew = it; // year is already parsed // if (!parseINT(it, end)) // goto failed; if (getNextToken(it, end).TokenType != tt_DASH) goto failed; // month if (!parseINT(it, end)) goto failed; if (getNextToken(it, end).TokenType != tt_DASH) goto failed; // day if (!parseINT(it, end)) goto failed; // optional hour rew = it; it = skipWS(it, end); if (parseINT(it, end)) { if (getNextToken(it, end).TokenType != tt_COLON) goto noHour; if (!parseINT(it, end)) goto noHour; // optional sec rew = it; if (getNextToken(it, end).TokenType != tt_COLON) goto noHour; if (!parseINT(it, end)) goto noHour; } else { noHour: // no hour, rewind it = rew; } // ok, the date if correctly parsed return true; failed: it = rew; return false; } // parse an FLOAT bool CQueryParser::parseFLOAT(CQueryParser::iterator &it, CQueryParser::iterator end) { iterator rew = it; if (getNextToken(it, end).TokenType != tt_DOT) goto failed; if (!parseINT(it, end)) goto failed; return true; failed: it = rew; return false; } // Parse a long int (64 bits) bool CQueryParser::parseLONG_INT(CQueryParser::iterator &it, CQueryParser::iterator end) { iterator rew = it; TToken tok = getNextToken(it, end); if (tok.TokenType != tt_ID || (tok.Text != "l" && tok.Text != "L")) goto failed; // ok, this is a long int return true; failed: it= rew; return false; } /// Parse a naked hexa (ie. without the '0x' prefix) bool CQueryParser::parseNAKED_HEXA(iterator &it, iterator end) { iterator rew = it; while (it != end && ((*it >= 'a' && *it <= 'f') || (*it >= 'A' && *it <= 'F'))) ++it; if (it == rew) // no character consumed, not a naked hexa goto failed; // the iterator have advanced, check the next character if (it != end && ((*it >= 'a' && *it <= 'z') || (*it >= 'A' && *it <= 'Z') || (*it == '_'))) { // not a naked hexa goto failed; } // ok, this is a naked hexa return true; failed: it= rew; return false; } // parse an INT bool CQueryParser::parseINT(CQueryParser::iterator &it, CQueryParser::iterator end) { iterator rew = it; if (it != end && *it >='0' && *it <='9') { ++it; // check for 'hex' constant if (it != end && (*it == 'x' || *it == 'X')) { ++it; if (it == end || !(*it >= '0' && *it<='9' || *it >= 'a' && *it<='f' || *it >= 'A' && *it<='F')) { // need at least one hexdigit after the 0x prefix it = rew; return false; } // read the hex digit while (it != end && ( (*it >='0' && *it <= '9') || (*it >='a' && *it <= 'f') || (*it >='A' && *it <= 'F'))) ++it; } else { // read decimal digits while (it != end && *it >='0' && *it <= '9') ++it; } return true; } it = rew; return false; } // The lexer CQueryParser::TToken CQueryParser::getNextToken(CQueryParser::iterator &it, CQueryParser::iterator end) { TToken ret; iterator rew = it; it = skipWS(it, end); ret.It = it; if (it == end) { ret.TokenType = tt_EOF; ret.Text = ""; return ret; } iterator first = it; char c = *it; if (c>='a' && c<='z' || c>='A' && c<='Z' || c == '_') { // try to read an ID if (parseID(it, end)) { ret.TokenType = tt_ID; ret.Text = std::string(first, it); } } else if (c == '"') { // try to read a string if (parseSTRING(it, end)) { ret.TokenType = tt_STRING; ret.Text = std::string(first, it-1); } } else if (c >= '0' && c <= '9') { if (parseINT(it, end)) { // try to read a long int if (parseLONG_INT(it, end)) { ret.TokenType = tt_LONG; ret.Text = string(first, it); } // try to read a date else if (parseDATE(it, end)) { ret.TokenType = tt_DATE; ret.Text = std::string(first, it); } // try to read a float else if (parseFLOAT(it, end)) { ret.TokenType = tt_FLOAT; ret.Text = std::string(first, it); } // try to read a naked hexa else if (parseNAKED_HEXA(it, end)) { ret.TokenType = tt_NAKED_HEXA; ret.Text = std::string(first, it); } else /*if (parseINT(it, end))*/ { // just a simple int ret.TokenType = tt_INT; ret.Text = std::string(first, it); } } } else if (c == '<') { // read less or less equal ++it; if (it != end && *it == '=') { // consume the '=' ++it; ret.TokenType = tt_LESS_EQUAL; ret.Text = "<="; } else { ret.TokenType = tt_LESS; ret.Text = "<"; } } else if (c == '>') { // read greater or greater equal ++it; if (it != end && *it == '=') { // consume the '=' ++it; ret.TokenType = tt_GREATER_EQUAL; ret.Text = ">="; } else { ret.TokenType = tt_GREATER; ret.Text = ">"; } } else if (c == '=') { // try to read '==' ++it; if (it != end && *it == '=') { // we support '==' and '=', so consume the secondary '=' if any ++it; } // equal ret.TokenType = tt_EQUAL; ret.Text = "=="; } else if (c == '!') { // try to read '!=' ++it; if (it != end && *it == '=') { ++it; // not equal ret.TokenType = tt_NOT_EQUAL; ret.Text = "!="; } else { // nothing recognised --it; // no valid alternative found ! throw EInvalidQuery(it, "Invalid char following '!', need an '=' sign"); } } else if (c == '(') { ++it; ret.TokenType = tt_OPEN_PAR; ret.Text = "("; } else if (c == ')') { ++it; ret.TokenType = tt_CLOSE_PAR; ret.Text = ")"; } else if (c == '[') { ++it; ret.TokenType = tt_OPEN_BRACKET; ret.Text = "["; } else if (c == ']') { ++it; ret.TokenType = tt_CLOSE_BRACKET; ret.Text = "]"; } else if (c == '{') { ++it; ret.TokenType = tt_OPEN_BRACE; ret.Text = "{"; } else if (c == '}') { ++it; ret.TokenType = tt_CLOSE_BRACE; ret.Text = "}"; } else if (c == '-') { ++it; ret.TokenType = tt_DASH; ret.Text = "-"; } else if (c == ':') { ++it; ret.TokenType = tt_COLON; ret.Text = ":"; } else if (c == '.') { ++it; ret.TokenType = tt_DOT; ret.Text = "."; } else { // no valid alternative found ! throw EInvalidQuery(it, "Can not found a valid lexer token"); } // check for keyword if (_Keywords.find(ret.Text) != _Keywords.end()) { // change the token type ret.TokenType = _Keywords[ret.Text]; } return ret; } CQueryParser::iterator CQueryParser::skipWS(CQueryParser::iterator it, CQueryParser::iterator end) { while (it != end) { switch (*it) { case ' ': case '\n': case '\r': case '\t': // this is a white space // break the switch and advance to next character break; default: // not a white space, return the iterator return it; } ++it; } // no more character to read, return the iterator (should be end) return it; } CQueryParser *createQueryParser(const TLogDefinitions &logDefs) { return new CQueryParser(logDefs); }