KviKvsTreeNode * KviKvsParser::parseComment() { KVSP_ASSERT((KVSP_curCharUnicode == '#') || (KVSP_curCharUnicode == '/')); switch(KVSP_curCharUnicode) { case '#': // bash style skipToNextLine(); break; case '/': KVSP_skipChar; switch(KVSP_curCharUnicode) { case '/': // c++ style skipToNextLine(); break; case '*': { const QChar * pBegin = KVSP_curCharPointer; // c style, multiline KVSP_skipChar; for(;;) { switch(KVSP_curCharUnicode) { case 0: warning(pBegin,__tr2qs_ctx("Unterminated c-style multiline comment","kvs")); error(KVSP_curCharPointer,__tr2qs_ctx("Unexpected end of script in multiline comment","kvs")); return 0; break; case '*': while(KVSP_curCharUnicode == '*') KVSP_skipChar; if(KVSP_curCharUnicode == '/') { KVSP_skipChar; return 0; } break; } KVSP_skipChar; } } break; default: error(KVSP_curCharPointer,__tr2qs_ctx("Unexpected character '%q' (unicode %x) after a slash (is it a typo or a malformed comment begin?)","kvs"),KVSP_curCharPointer,KVSP_curCharUnicode); return 0; break; } break; default: // shouldn't be here :/ KVSP_ASSERT(false); break; } return 0; }
KviKvsTreeNodeExpression * KviKvsParser::parseExpressionOperand(char terminator) { switch(KVSP_curCharUnicode) { case 0: case '\r': case '\n': error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of script in expression", "kvs")); return nullptr; break; case '(': KVSP_skipChar; skipSpaces(); return parseExpression(')'); // sub expression break; case '-': { KVSP_skipChar; skipSpaces(); KviKvsTreeNodeExpression * d = parseExpressionOperand(terminator); if(!d) return nullptr; return new KviKvsTreeNodeExpressionUnaryOperatorNegate(d->location(), d); } break; case '!': { KVSP_skipChar; skipSpaces(); KviKvsTreeNodeExpression * d = parseExpressionOperand(terminator); if(!d) return nullptr; return new KviKvsTreeNodeExpressionUnaryOperatorLogicalNot(d->location(), d); } break; case '~': { KVSP_skipChar; skipSpaces(); KviKvsTreeNodeExpression * d = parseExpressionOperand(terminator); if(!d) return nullptr; return new KviKvsTreeNodeExpressionUnaryOperatorBitwiseNot(d->location(), d); } break; default: // anything else at this point is an operand core return parseExpressionOperandCore(terminator); break; } // not reached KVSP_ASSERT(false); return nullptr; }
KviKvsTreeNodeCommand * KviKvsParser::parseCommand() { KVSP_ASSERT(KVSP_curCharIsLetter || (KVSP_curCharUnicode == '_')); const QChar * pIdentifier = KVSP_curCharPointer; while(KVSP_curCharIsLetterOrNumber || (KVSP_curCharUnicode == '_'))KVSP_skipChar; int iIdentifierLen = KVSP_curCharPointer - pIdentifier; const QChar * pSecondPart = 0; int iSecondPartLen = 0; bool bHasNamespaceSoMustBeAlias = false; if(KVSP_curCharUnicode == '.') { // a module command KVSP_skipChar; pSecondPart = KVSP_curCharPointer; if(!KVSP_curCharIsLetter) { warning(KVSP_curCharPointer - 1,__tr2qs_ctx("Stray dot ('.') character or invalid following module command name","kvs")); error(KVSP_curCharPointer,__tr2qs_ctx("Syntax error: malformed module command identifier","kvs")); return 0; } KVSP_skipChar; while(KVSP_curCharIsLetterOrNumber || (KVSP_curCharUnicode == '_'))KVSP_skipChar; iSecondPartLen = KVSP_curCharPointer - pSecondPart; } else while(KVSP_curCharUnicode == ':') { // an alias with namespace(s) ? // here we allow the syntax of the form // <namespace>::{<namespace>::}<alias_name> bHasNamespaceSoMustBeAlias = true; KVSP_skipChar; if(KVSP_curCharUnicode == ':') { KVSP_skipChar; if(!KVSP_curCharIsLetter) { warning(KVSP_curCharPointer - 1,__tr2qs_ctx("Stray '::' sequence or invalid following alias name","kvs")); error(KVSP_curCharPointer,__tr2qs_ctx("Syntax error: malformed alias identifier","kvs")); return 0; } KVSP_skipChar; while(KVSP_curCharIsLetterOrNumber || (KVSP_curCharUnicode == '_'))KVSP_skipChar; } else { warning(KVSP_curCharPointer - 1,__tr2qs_ctx("Stray ':' character: did you mean '...<namespace>::<alias_name>' ?","kvs")); error(KVSP_curCharPointer,__tr2qs_ctx("Syntax error: malformed (alias?) command identifier","kvs")); return 0; } iIdentifierLen = KVSP_curCharPointer - pIdentifier; } QString szIdentifier(pIdentifier,iIdentifierLen); skipSpaces(); KviKvsTreeNodeSwitchList * sw = 0; KviKvsTreeNodeData * pRebindData; if(KVSP_curCharUnicode == '-') { // extract the switch sw = parseCommandSwitchList(); if(!sw) { if(error()) return 0; // else it might be a negative number or something that does not seem // to be a switch anyway pRebindData = 0; } else { pRebindData = sw->getStandardRebindingSwitch(); } } else { pRebindData = 0; } KviKvsTreeNodeCommand * cmd=0; if(!bHasNamespaceSoMustBeAlias) { // We're lookin' for interpreters like perl and python // perl.begin and python.begin have a *really* half special // parsing routine if((iIdentifierLen == 4) || (iIdentifierLen == 6)) { if(pIdentifier->toLower().unicode() == 'p') { // Set the interpreter name QString szInterpreter; if(KviQString::equalCI(szIdentifier,"perl")) { szInterpreter = "perl"; } else if(KviQString::equalCI(szIdentifier,"python")) { szInterpreter = "python"; } if((szInterpreter == "perl") || (szInterpreter == "python")) { if(pSecondPart) { QString szSecondPart(pSecondPart,iSecondPartLen); if(KviQString::equalCI(szSecondPart,"begin")) { if(szInterpreter == "perl") { // yep, that's perl.begin cmd = parseSpecialCommandPerlBegin(); } else if(szInterpreter == "python") { // yep, that's python.begin cmd = parseSpecialCommandPythonBegin(); } if(!cmd) { // might be an error, but might be not... // it is an error only if error() returns true // but since the caller will take care of it // we just return 0 if(sw)delete sw; if(pRebindData)delete pRebindData; return 0; } cmd->setLocation(pIdentifier); if(sw) { cmd->setSwitchList(sw); // cmd becomes child of the rebinding switch if(pRebindData)return new KviKvsTreeNodeRebindingSwitch(pRebindData->location(),pRebindData,cmd); } return cmd; } } } } } if(!pSecondPart) { // is this a special command ? // Here theoretically we could also lookup special commands composed of two parts but we actually don't need it. // Looking up only the first part if there is a second part, instead, // is dangerous since it may generate infinite loops (help.open vs help) KviKvsSpecialCommandParsingRoutine * ccpr = KviKvsKernel::instance()->findSpecialCommandParsingRoutine(szIdentifier); if(ccpr) { cmd = (this->*(ccpr->proc))(); if(!cmd) { // might be an error, but might be not... // it is an error only if error() returns true // but since the caller will take care of it // we just return 0 if(sw)delete sw; if(pRebindData)delete pRebindData; return 0; } cmd->setLocation(pIdentifier); if(sw) { cmd->setSwitchList(sw); // cmd becomes child of the rebinding switch if(pRebindData)return new KviKvsTreeNodeRebindingSwitch(pRebindData->location(),pRebindData,cmd); } return cmd; } } // is it a callback command ? if(KVSP_curCharUnicode == '(') { // core callback command // module callback command KviKvsTreeNodeDataList * dl = parseCommaSeparatedParameterList(); if(!dl) { if(sw)delete sw; if(pRebindData)delete pRebindData; return 0; } if(!skipSpacesAndNewlines()) { if(sw)delete sw; if(pRebindData)delete pRebindData; delete dl; return 0; } const QChar * pClbkBegin = KVSP_curCharPointer; KviKvsTreeNodeInstruction * ins = parseInstruction(); if(!ins) { if(error()) { if(sw)delete sw; if(pRebindData)delete pRebindData; return 0; } // actually we need empty callbacks (for alias() at least) // the single command implementations should take care of checking it /*else { warning(pIdentifier,__tr2qs_ctx("Callback command called with an empty callback instruction","kvs")); error(KVSP_curCharPointer,__tr2qs_ctx("Callback commands must have a callback instruction","kvs")); if(sw)delete sw; delete dl; return 0; }*/ } else { delete ins; // in fact we don't need it, it will be reparsed the first time it is called // Q: Couldn't we actually use the already parsed tree ? // A: No: the tree must be reparsed in a new parser context // since we're keeping track of global and local variables... // The locals of this context are NOT the same as the locals // of the other context. } QString szCallbackName = szIdentifier; szCallbackName += " callback"; QString szBlock(pClbkBegin,KVSP_curCharPointer - pClbkBegin); KviCommandFormatter::bufferFromBlock(szBlock); KviKvsScript * clbk = new KviKvsScript(szCallbackName,szBlock); if(pSecondPart) { cmd = new KviKvsTreeNodeModuleCallbackCommand(pIdentifier,szIdentifier,QString(pSecondPart,iSecondPartLen),dl,clbk); } else { KviKvsCoreCallbackCommandExecRoutine * r = KviKvsKernel::instance()->findCoreCallbackCommandExecRoutine(szIdentifier); if(r) { cmd = new KviKvsTreeNodeCoreCallbackCommand(pIdentifier,szIdentifier,dl,r,clbk); } else { error(KVSP_curCharPointer,__tr2qs_ctx("Unknown callback command \"%Q\"","kvs"),&szIdentifier); if(sw)delete sw; if(pRebindData)delete pRebindData; delete dl; delete clbk; return 0; } } if(sw) { cmd->setSwitchList(sw); // cmd becomes child of the rebinding switch if(pRebindData)return new KviKvsTreeNodeRebindingSwitch(pRebindData->location(),pRebindData,cmd); } return cmd; } } // must be core simple command, module simple command or alias KviKvsTreeNodeDataList * pl = parseCommandParameterList(); if(!pl) { if(sw)delete sw; if(pRebindData)delete pRebindData; return 0; // this MUST be an error } if(bHasNamespaceSoMustBeAlias) { // alias for sure, bind at runtime cmd = new KviKvsTreeNodeAliasSimpleCommand(pIdentifier,szIdentifier,pl); } else { if(pSecondPart) { cmd = new KviKvsTreeNodeModuleSimpleCommand(pIdentifier,szIdentifier,QString(pSecondPart,iSecondPartLen),pl); } else { KviKvsCoreSimpleCommandExecRoutine * r = KviKvsKernel::instance()->findCoreSimpleCommandExecRoutine(szIdentifier); if(r) { cmd = new KviKvsTreeNodeCoreSimpleCommand(pIdentifier,szIdentifier,pl,r); } else { // must be an alias in root namespace, bind at runtime cmd = new KviKvsTreeNodeAliasSimpleCommand(pIdentifier,szIdentifier,pl); } } } if(sw) { cmd->setSwitchList(sw); // cmd becomes child of the rebinding switch if(pRebindData)return new KviKvsTreeNodeRebindingSwitch(pRebindData->location(),pRebindData,cmd); } return cmd; }
KviKvsTreeNodeExpression * KviKvsParser::parseExpression(char terminator) { // we're inside the expression now skipSpaces(); if(KVSP_curCharUnicode == terminator) { // empty expression // constant 0 ? KVSP_skipChar; return new KviKvsTreeNodeExpressionConstantOperand(KVSP_curCharPointer, new KviKvsVariant((kvs_int_t)0)); } KviKvsTreeNodeExpression * left = parseExpressionOperand(terminator); if(!left) return nullptr; QString sz; left->contextDescription(sz); skipSpaces(); if(KVSP_curCharUnicode == terminator) { KVSP_skipChar; return left; } // not a terminator... must be an operator (or an error, eventually) KviKvsTreeNodeExpression * curTopOperator = parseExpressionBinaryOperator(); if(!curTopOperator) { delete left; return nullptr; // error } curTopOperator->contextDescription(sz); curTopOperator->setLeft(left); // ok.. parse the right side // Now curTopOperator has the left subtree (one node) set // and it points to the TOP (=ROOT) node // Evaluate the rest KviKvsTreeNodeExpression * operand; KviKvsTreeNodeExpression * incompleteOperator = curTopOperator; KviKvsTreeNodeExpression * auxOperator; for(;;) { skipSpaces(); operand = parseExpressionOperand(terminator); if(!operand) { delete curTopOperator; return nullptr; } operand->contextDescription(sz); skipSpaces(); if(KVSP_curCharUnicode == terminator) { KVSP_skipChar; incompleteOperator->setRight(operand); return curTopOperator; } auxOperator = parseExpressionBinaryOperator(); if(!auxOperator) { delete curTopOperator; delete operand; return nullptr; } auxOperator->contextDescription(sz); //now compare operators... if(incompleteOperator->precedence() > auxOperator->precedence()) { // This in fact means that incomplete has LOWER precedence than // aux and thus aux should be done first. incompleteOperator->setRight(auxOperator); auxOperator->setLeft(operand); } else { // incomplete has GREATER precedence than aux and thus aux should be done first incompleteOperator->setRight(operand); //right tree complete // go up until we find an operator with lower precedence than auxOperator (>=) KviKvsTreeNodeExpression * tempOperator = incompleteOperator->parentWithPrecedenceLowerThan(auxOperator->precedence()); if(tempOperator == nullptr) { auxOperator->setLeft(curTopOperator); curTopOperator = auxOperator; } else { KVSP_ASSERT(tempOperator->right()); auxOperator->setLeft(tempOperator->right()); tempOperator->setRight(auxOperator); } } incompleteOperator = auxOperator; KVSP_ASSERT(incompleteOperator->right() == nullptr); } KVSP_ASSERT(false); return nullptr; //newer here }
KviKvsTreeNodeData * KviKvsParser::parseDollar(bool bInObjScope) { KVSP_ASSERT(KVSP_curCharUnicode == '$'); const QChar * pDollarBegin = KVSP_curCharPointer; const QChar * pBegin = KVSP_curCharPointer; KVSP_skipChar; if(!KVSP_curCharIsFunctionStart) { if(KVSP_curCharUnicode == 0)warning(KVSP_curCharPointer,__tr2qs_ctx("Unexpected end of script after '$' function call prefix","kvs")); else warning(KVSP_curCharPointer,__tr2qs_ctx("Unexpected character %q (unicode %x) after '$' function call prefix","kvs"),KVSP_curCharPointer,KVSP_curCharUnicode); error(KVSP_curCharPointer,__tr2qs_ctx("Syntax error after '$' function call prefix. If you want to use a plain '$' in the code you need to escape it","kvs")); return 0; } if(KVSP_curCharUnicode == '(') { // expression eval if(bInObjScope) { error(KVSP_curCharPointer,__tr2qs_ctx("Invalid expression evaluation in object scope","kvs")); return 0; } KVSP_skipChar; skipSpaces(); return parseExpression(')'); } if(KVSP_curCharUnicode == '{') { // command block eval <--- senseless ??? if(bInObjScope) { error(KVSP_curCharPointer,__tr2qs_ctx("Invalid command evaluation in object scope","kvs")); return 0; } KviKvsTreeNodeInstruction * i = parseInstructionBlock(); if(!i) { if(error())return 0; // trigger an error anyway: this is abused syntax :D error(KVSP_curCharPointer,__tr2qs_ctx("Empty instruction block for command evaluation","kvs")); return 0; } return new KviKvsTreeNodeCommandEvaluation(pDollarBegin,i); } if(KVSP_curCharUnicode == '#') { // parameter count if(bInObjScope) { error(KVSP_curCharPointer,__tr2qs_ctx("Parameter identifiers are forbidden in object scope (after the '->' operator)","kvs")); return 0; } KVSP_skipChar; return new KviKvsTreeNodeParameterCount(pDollarBegin); } if(KVSP_curCharIsNumber) { // this is a parameter identifier // $1-4 $1- $3 if(bInObjScope) { error(KVSP_curCharPointer,__tr2qs_ctx("Parameter identifiers are forbidden in object scope (after the '->' operator)","kvs")); return 0; } pBegin = KVSP_curCharPointer; while(KVSP_curCharIsNumber) KVSP_skipChar; QString szNum1(pBegin,KVSP_curCharPointer - pBegin); bool bOk; int iNum1 = szNum1.toInt(&bOk); if(!bOk)qDebug("Ops... a non-number made by numbers ?"); if(KVSP_curCharUnicode != '-') { // end return new KviKvsTreeNodeSingleParameterIdentifier(pDollarBegin,iNum1); } // dash... make sure it's not $N->$something KVSP_skipChar; if(KVSP_curCharUnicode == '>') { // object scope operator in fact... // go back to the - and return a single parameter identifier KVSP_backChar; return new KviKvsTreeNodeSingleParameterIdentifier(pDollarBegin,iNum1); } if(!KVSP_curCharIsNumber) { // from iNum1 to the end return new KviKvsTreeNodeMultipleParameterIdentifier(pDollarBegin,iNum1,-1); } pBegin = KVSP_curCharPointer; while(KVSP_curCharIsNumber) KVSP_skipChar; QString szNum2(pBegin,KVSP_curCharPointer - pBegin); int iNum2 = szNum2.toInt(&bOk); if(!bOk)qDebug("Ops... a non-number made by numbers (2) ?"); if(iNum1 < iNum2)return new KviKvsTreeNodeMultipleParameterIdentifier(pDollarBegin,iNum1,iNum2); else { warning(pBegin,__tr2qs_ctx("Ending index of a multiple parameter identifier is lower or equal to the starting index. This will evaluate to a single parameter identifier.","kvs")); return new KviKvsTreeNodeSingleParameterIdentifier(pDollarBegin,iNum1); } } pBegin = KVSP_curCharPointer; //KVSP_skipChar; bool bHasNamespaceNotInObjScopeSoMustBeAlias = false; // ;D if(KVSP_curCharUnicode == '$') { if(bInObjScope) { error(KVSP_curCharPointer,__tr2qs_ctx("Syntax error: invalid $$ ($this) function call in object scope","kvs")); return 0; } // handle $$ KVSP_skipChar; } else { while((KVSP_curCharIsLetterOrNumber) || (KVSP_curCharUnicode == '_'))KVSP_skipChar; if(!bInObjScope) { while(KVSP_curCharUnicode == ':') { // check for namespaces // here we allow the syntax of the form // <namespace>::{<namespace>::}<alias_name> bHasNamespaceNotInObjScopeSoMustBeAlias = true; // ;D KVSP_skipChar; if(KVSP_curCharUnicode == ':') { KVSP_skipChar; if(!KVSP_curCharIsLetter) { warning(KVSP_curCharPointer - 1,__tr2qs_ctx("Stray '::' sequence or invalid following alias name","kvs")); error(KVSP_curCharPointer,__tr2qs_ctx("Syntax error: malformed alias function call identifier","kvs")); return 0; } KVSP_skipChar; while(KVSP_curCharIsLetterOrNumber || (KVSP_curCharUnicode == '_'))KVSP_skipChar; } else { warning(KVSP_curCharPointer - 1,__tr2qs_ctx("Stray ':' character: did you mean '...<namespace>::<alias_name>' ?","kvs")); error(KVSP_curCharPointer,__tr2qs_ctx("Syntax error: malformed (alias?) function call identifier","kvs")); return 0; } } } } QString szIdentifier1(pBegin,KVSP_curCharPointer - pBegin); const QChar * pId2 = 0; int iId2Len = 0; bool bModuleFunctionCall = false; if(!bHasNamespaceNotInObjScopeSoMustBeAlias) { if(!bInObjScope) { if(KVSP_curCharUnicode == '.') { KVSP_skipChar; if(KVSP_curCharIsLetter) { pId2 = KVSP_curCharPointer; while((KVSP_curCharIsLetterOrNumber) || (KVSP_curCharUnicode == '_')) KVSP_skipChar; iId2Len = KVSP_curCharPointer - pId2; bModuleFunctionCall = true; } else { KVSP_backChar; } } } else { // object scope, check for "class name" namespace // the class name namespace has the format "<namespace>::<namespace>::..::<classname> // so the last :: is the delimiter of the function name const QChar * pOriginalEndOfId1 = KVSP_curCharPointer; const QChar * pEndOfId1 = pOriginalEndOfId1; while(KVSP_curCharUnicode == ':') { // base class function call ? KVSP_skipChar; if(KVSP_curCharUnicode == ':') { KVSP_skipChar; if(KVSP_curCharIsLetter) { pId2 = KVSP_curCharPointer; while((KVSP_curCharIsLetterOrNumber) || (KVSP_curCharUnicode == '_')) KVSP_skipChar; iId2Len = KVSP_curCharPointer - pId2; } else { KVSP_setCurCharPointer(pOriginalEndOfId1); pId2 = 0; iId2Len = 0; break; } } else { KVSP_setCurCharPointer(pOriginalEndOfId1); pId2 = 0; iId2Len = 0; break; } } if(pId2) { // yes, that's fine: reset it szIdentifier1.setUnicode(pBegin,pEndOfId1 - pBegin); } } } KviKvsTreeNodeDataList * l; if(KVSP_curCharUnicode != '(') { // no parameters passed //KVSP_setCurCharPointer(pBegin); // will get an empty data list l = new KviKvsTreeNodeDataList(pDollarBegin); } else { // check for the special syntax () KVSP_skipChar; if(KVSP_curCharUnicode == ')') { // $call(), assume no parameters passed l = new KviKvsTreeNodeDataList(pDollarBegin); KVSP_skipChar; } else { KVSP_backChar; l = parseCommaSeparatedParameterList(); if(!l)return 0; // error } } if(bHasNamespaceNotInObjScopeSoMustBeAlias) { // namespace::alias function call return new KviKvsTreeNodeAliasFunctionCall(pDollarBegin,szIdentifier1,l); } else if(bModuleFunctionCall) { // module function call return new KviKvsTreeNodeModuleFunctionCall(pDollarBegin,szIdentifier1,QString(pId2,iId2Len),l); } else { if(bInObjScope) { // object function call (our parent will be a scope operator) if(pId2) { // base class object function call return new KviKvsTreeNodeBaseObjectFunctionCall(pDollarBegin,szIdentifier1,QString(pId2,iId2Len),l); } else { // plain object function call return new KviKvsTreeNodeThisObjectFunctionCall(pDollarBegin,szIdentifier1,l); } } else { // core or alias function call KviKvsCoreFunctionExecRoutine * r = KviKvsKernel::instance()->findCoreFunctionExecRoutine(szIdentifier1); if(r) { // core function call return new KviKvsTreeNodeCoreFunctionCall(pDollarBegin,szIdentifier1,r,l); } else { // root namespace alias function call return new KviKvsTreeNodeAliasFunctionCall(pDollarBegin,szIdentifier1,l); } } } // not reached KVSP_ASSERT(false); return 0; }