Exemplo n.º 1
0
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;
}
Exemplo n.º 3
0
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
}
Exemplo n.º 5
0
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;
}