ExpType CExpression::TypeCheck() { int step = 0; if (ExpTree) delete ExpTree; ExpTree = DoTree(step); Evaluate(&constantValue); return constantValue.Type(); }
void CExpression::ParseTokenStream() { // Make the tree int step = 0; pExpressionOwner = NULL; ExpTree = DoTree(step); ExpTree = PostSortTree(ExpTree); // Determine if the overall expression is constant, collapsing constant parts along // the way. isConstant = ExpTree->Constant(); // If constant, get the constant value. if (isConstant) { CollapseConstantTree(ExpTree); ExpTree->Evaluate(&constantValue); } }
ExpPart* CExpression::DoBrackets(int& step) { ExpPart* brackets; Depth++; // Step over left bracket step++; brackets = DoTree(step); // brackets = PostSortTree(brackets); Depth--; // Step over right bracket step++; return brackets; }
// Do Tree: convert token stream to expression tree ExpPart* CExpression::DoTree(int& step, bool unaryMode) { ExpPart* LeftPart = NULL; ExpPart* Brackets = NULL; bool Bracketing; // Brackets override the operator precedentation // Too lazy to scope each case label lol ExpInteger* pInt; ExpFloat* pFloat; ExpString* pStr; ExpIdent* pIdent; ExpAdd* pAdd; ExpSubtract* pSub; ExpMultiply* pMult; ExpDivide* pDiv; ExpMod* pMod; ExpPower* pPow; ExpDot* pDot; ExpSin* pSin; ExpCos* pCos; ExpTan* pTan; ExpSqrt* pSqrt; ExpFuncInt* pFuncInt; ExpFuncFloat* pFuncFloat; ExpFuncStr* pFuncStr; ExpEqual* pEqual; ExpNotEqual* pNotEqual; ExpLess* pLess; ExpLessEqual* pLessEqual; ExpGreater* pGreater; ExpGreaterEqual* pGreaterEqual; ExpAnd* pAnd; ExpOr* pOr; ExpConditional* pConditional; ExpAsin* pAsin; ExpAcos* pAcos; ExpAtan* pAtan; ExpAbs* pAbs; ExpExp* pExp; ExpLn* pLn; ExpLog10* pLog10; ExpCeil* pCeil; ExpRound* pRound; ExpRandom* pRandom; ExpLen* pLen; ExpArray* pArray; ExpAt* pAt; ExpVariableName* pVariableName; bool dotIdentifier; while (step < toks.size()) { const Token& curTok = toks[step]; if (curTok.t == T_NULL) break; Bracketing = false; switch (curTok.t) { ////////////////////// // Operands // // Operands are simply kept to one side until we find an // operator to do something with. case T_INTEGER: pInt = NEW_UNARY_EXP(ExpInteger, pInt); pInt->value = curTok.iValue; // Return on close bracket or comma CHECK_TERMINATE(); break; case T_FLOAT: pFloat = NEW_UNARY_EXP(ExpFloat, pFloat); pFloat->value = curTok.fValue; // Return on close bracket or comma CHECK_TERMINATE(); break; case T_STRINGLITERAL: pStr = NEW_UNARY_EXP(ExpString, pStr); pStr->value = curTok.str; // Return on close bracket or comma CHECK_TERMINATE(); break; case T_VARIABLENAME: { pVariableName = NEW_UNARY_EXP(ExpVariableName, pVariableName); CString name = curTok.str; name.MakeLower(); // Lookup the private variable index if (pExpressionOwner != NULL) pVariableName->index = find_index(pExpressionOwner->privateVars.begin(), pExpressionOwner->privateVars.end(), name); else { vector<CString>::iterator it = pRuntime->globalNames.begin(); vector<CString>::iterator end = pRuntime->globalNames.end(); for ( ; it != end; it++) { CString curName = *it; curName.MakeLower(); if (curName == name) { pVariableName->index = it - pRuntime->globalNames.begin(); break; } } } pVariableName->pOwnerType = pExpressionOwner; // Return on close bracket or comma CHECK_TERMINATE(); break; } case T_LEFTCURLY: pArray = NEW_UNARY_EXP(ExpArray, pArray); // NEW_UNARY_EXP increments step, but we want to put it back, because the loop // below expects to start on { step--; // Loop until all elements collected while (toks[step].t != T_RIGHTCURLY && step < toks.size()) { step++; // Step over { or , // Obtain element ExpPart* element = DoTree(step); // Add to elements pArray->expElements.push_back(element); } // Step over } step++; // Resize the evaluation buffer pArray->arr.resize(pArray->expElements.size()); CHECK_TERMINATE(); break; ////////////////////// // Operators case T_ADD: pAdd = NEW_BINARY_EXP(ExpAdd, pAdd); // If child operator is lower precedence, this node must swap places with the child if (HasPrecedence(pAdd, pAdd->r)) return SwapTree(pAdd); return pAdd; case T_SUBTRACT: pSub = NEW_BINARY_EXP(ExpSubtract, pSub); if (HasPrecedence(pSub, pSub->r)) return SwapTree(pSub); return pSub; case T_MULTIPLY: pMult = NEW_BINARY_EXP(ExpMultiply, pMult); if (HasPrecedence(pMult, pMult->r)) return SwapTree(pMult); return pMult; case T_DIVIDE: pDiv = NEW_BINARY_EXP(ExpDivide, pDiv); if (HasPrecedence(pDiv, pDiv->r) || SamePrecedence(pDiv, pDiv->r)) return SwapTree(pDiv); // Force left-to-right evaluation /* if (SamePrecedence(pDiv, pDiv->r)) SwapRightToLeft(pDiv); */ return pDiv; case T_MOD: pMod = NEW_BINARY_EXP(ExpMod, pMod); if (HasPrecedence(pMod, pMod->r)) return SwapTree(pMod); // Force left-to-right evaluation if (SamePrecedence(pMod, pMod->r)) SwapRightToLeft(pMod); return pMod; case T_POWER: pPow = NEW_BINARY_EXP(ExpPower, pPow); if (HasPrecedence(pPow, pPow->r)) return SwapTree(pPow); // Force left-to-right evaluation if (SamePrecedence(pPow, pPow->r)) SwapRightToLeft(pPow); return pPow; case T_AT: pAt = NEW_BINARY_EXP(ExpAt, pAt); if (HasPrecedence(pAt, pAt->r)) return SwapTree(pAt); // Force left-to-right evaluation if (SamePrecedence(pAt, pAt->r)) SwapRightToLeft(pAt); return pAt; case T_EQUAL: pEqual = NEW_BINARY_EXP(ExpEqual, pEqual); if (HasPrecedence(pEqual, pEqual->r)) return SwapTree(pEqual); return pEqual; case T_NOTEQUAL: pNotEqual = NEW_BINARY_EXP(ExpNotEqual, pNotEqual); if (HasPrecedence(pNotEqual, pNotEqual->r)) return SwapTree(pNotEqual); return pNotEqual; case T_LESS: pLess = NEW_BINARY_EXP(ExpLess, pLess); if (HasPrecedence(pLess, pLess->r)) return SwapTree(pLess); return pLess; case T_LESSEQUAL: pLessEqual = NEW_BINARY_EXP(ExpLessEqual, pLessEqual); if (HasPrecedence(pLessEqual, pLessEqual->r)) return SwapTree(pLessEqual); return pLessEqual; case T_GREATER: pGreater = NEW_BINARY_EXP(ExpGreater, pGreater); if (HasPrecedence(pGreater, pGreater->r)) return SwapTree(pGreater); return pGreater; case T_GREATEREQUAL: pGreaterEqual = NEW_BINARY_EXP(ExpGreaterEqual, pGreaterEqual); if (HasPrecedence(pGreaterEqual, pGreaterEqual->r)) return SwapTree(pGreaterEqual); return pGreaterEqual; case T_AND: pAnd = NEW_BINARY_EXP(ExpAnd, pAnd); if (HasPrecedence(pAnd, pAnd->r)) return SwapTree(pAnd); return pAnd; case T_OR: pOr = NEW_BINARY_EXP(ExpOr, pOr); if (HasPrecedence(pOr, pOr->r)) return SwapTree(pOr); return pOr; case T_CONDITIONAL: // a ? b : c pConditional = new ExpConditional; pConditional->t = T_CONDITIONAL; pConditional->pCExp = this; pConditional->bracketdepth = Depth; pConditional->a = LeftPart; // Get condition part (a) step++; // Get TRUE part (b) /*if (toks[step].t == T_LEFTBRACKET) pConditional->b = DoBrackets(step); else*/ pConditional->b = DoTree(step); pConditional->b = PostSortTree(pConditional->b); // Step over colon step++; // Get FALSE part (c) /*if (toks[step].t == T_LEFTBRACKET) pConditional->c = DoBrackets(step); else*/ pConditional->c = DoTree(step); pConditional->c = PostSortTree(pConditional->c); // (never has precedence) return pConditional; // Identifier, eg. call without a dot: Object Object(5) MouseX case T_IDENTIFIER: { // Idents cause variability isConstant = false; pIdent = NEW_UNARY_EXP(ExpIdent, pIdent); pIdent->ident = curTok.str; pIdent->pRuntime = pRuntime; pIdent->pSystemObject = &(pRuntime->system); pIdent->numParams = 0; pIdent->paramsEnd = pIdent->parameters; for (int x = 0; x < CRUNTIME_MAX_PARAMS; x++) pIdent->expParamList[x].eType = EXPTYPE_NULL; // This will store the correct OID for this name, else -1 and load an expression routine LookupIdentifier(curTok.str, pIdent); // Check for OID caller match pIdent->isCallerOid = (pIdent->oid == this->oidCaller); // True if dot precedes this identifier (bit of a hack, but hey) // We can't check for unary mode, because unary operators use it and it would break "sin Array(1)" dotIdentifier = false; if (step > 1) if (toks[step-2].t == T_DOT) dotIdentifier = true; // Save expression owner for variable name translation. Don't change the owner type if // this identifier is an expression name. if (!dotIdentifier) { if (pIdent->oid == -1) pExpressionOwner = NULL; else pExpressionOwner = pRuntime->objects[pIdent->oid]; } // Cursor is currently on token after identifier (NEW_UNARY_EXP increments). // Check for left bracket & parameters if not part of a dot identifier (ie the x of object.x) if (step < toks.size() && !dotIdentifier) if (toks[step].t == T_LEFTBRACKET) { // While we're not on the closing bracket while (toks[step].t != T_RIGHTBRACKET && step < toks.size()) { // Step over left bracket/comma step++; // Obtain this parameter's tree, closing on the comma ExpPart* param = DoTree(step); param = PostSortTree(param); pIdent->parameters[pIdent->numParams++] = param; pIdent->paramsEnd = pIdent->parameters + pIdent->numParams; } // Step over right bracket step++; } // Now we have params parsed, but if this isn't a system expression, there isn't yet a routine stored for it. // This means it is the default return format, eg. Array(5). Use the Default Return Value address. if (pIdent->oid > -1) { pIdent->pType = pRuntime->pOid(pIdent->oid); pIdent->pTypeInstances = &(pIdent->pType->instances); pIdent->routine = &CRunObject::ReturnDefaultValue; } else pIdent->pType = NULL; pIdent->hasParameters = pIdent->numParams > 0; // Return on close bracket or comma CHECK_TERMINATE(); // Return if function call (followed by lbracket) if (toks[step].t == T_LEFTBRACKET) return LeftPart; break; } case T_DOT: // Plugin returns cause variability isConstant = false; pDot = new ExpDot; pDot->t = T_DOT; // Object name part pDot->l = (ExpIdent*)LeftPart; pDot->pCExp = this; pDot->pRuntime = pRuntime; pDot->bracketdepth = Depth; pDot->oid = ((ExpIdent*)(pDot->l))->oid; pDot->pType = pRuntime->objects[pDot->oid]; pDot->pTypeInstances = &(pDot->pType->instances); pDot->unnamedExp = false; pDot->numParams = 0; pDot->paramsEnd = pDot->parameters; // Save owner type for variable name translation pExpressionOwner = pDot->pType; for (int x = 0; x < CRUNTIME_MAX_PARAMS; x++) pDot->expParamList[x].eType = EXPTYPE_NULL; step++; // Check for OID match pDot->isCallerOid = (pDot->oid == this->oidCaller); // Brackets not allowed after dot. Obtain expression name part. pDot->r = (ExpIdent*)DoTree(step, true); // Now obtain a list of the parameters. We are pointing after the expression // name part, i.e. after: obj.exp [->] ( params ... // So if parameters exist, this is a left bracket. if (step < toks.size()) if (toks[step].t == T_LEFTBRACKET) { // While we're not on the closing bracket while (toks[step].t != T_RIGHTBRACKET && step < toks.size()) { // Step over left bracket/comma step++; // Obtain this parameter's tree, closing on the comma ExpPart* param = DoTree(step); param = PostSortTree(param); pDot->parameters[pDot->numParams++] = param; pDot->paramsEnd = pDot->parameters + pDot->numParams; } // Step over right bracket step++; } // Has parameters? pDot->hasParameters = pDot->numParams > 0; // Dot will have precedence over most operators if (HasPrecedence(pDot, pDot->r)) { ExpPart* newParent = SwapTree(pDot); // Look up the plugin expression routine LookupPluginExpression(((ExpIdent*)(pDot->l))->oid, ((ExpIdent*)(pDot->r))->ident, pDot); return newParent; } // Look up the plugin expression routine LookupPluginExpression(((ExpIdent*)(pDot->l))->oid, ((ExpIdent*)(pDot->r))->ident, pDot); //return pDot; LeftPart = pDot; CHECK_TERMINATE(); break; ////////////////////// // Unary operators case T_SIN: pSin = NEW_UNARY_OPERATOR(ExpSin, pSin); // Return on close bracket CHECK_TERMINATE(); break; case T_COS: pCos = NEW_UNARY_OPERATOR(ExpCos, pCos); // Return on close bracket CHECK_TERMINATE(); break; case T_TAN: pTan = NEW_UNARY_OPERATOR(ExpTan, pTan); // Return on close bracket CHECK_TERMINATE(); break; case T_SQRT: pSqrt = NEW_UNARY_OPERATOR(ExpSqrt, pSqrt); // Return on close bracket /* if (step >= toks.size()) return LeftPart; if (toks[step].t == T_RIGHTBRACKET) return LeftPart; */ CHECK_TERMINATE(); break; case T_FUNC_INT: pFuncInt = NEW_UNARY_OPERATOR(ExpFuncInt, pFuncInt); // Return on close bracket CHECK_TERMINATE(); break; case T_FUNC_FLOAT: pFuncFloat = NEW_UNARY_OPERATOR(ExpFuncFloat, pFuncFloat); // Return on close bracket CHECK_TERMINATE(); break; case T_FUNC_STR: pFuncStr = NEW_UNARY_OPERATOR(ExpFuncStr, pFuncStr); // Return on close bracket CHECK_TERMINATE(); break; case T_ASIN: pAsin = NEW_UNARY_OPERATOR(ExpAsin, pAsin); // Return on close bracket CHECK_TERMINATE(); break; case T_ACOS: pAcos = NEW_UNARY_OPERATOR(ExpAcos, pAcos); // Return on close bracket CHECK_TERMINATE(); break; case T_ATAN: pAtan = NEW_UNARY_OPERATOR(ExpAtan, pAtan); // Return on close bracket CHECK_TERMINATE(); break; case T_ABS: pAbs = NEW_UNARY_OPERATOR(ExpAbs, pAbs); // Return on close bracket CHECK_TERMINATE(); break; case T_EXP: pExp = NEW_UNARY_OPERATOR(ExpExp, pExp); // Return on close bracket CHECK_TERMINATE(); break; case T_LN: pLn = NEW_UNARY_OPERATOR(ExpLn, pLn); // Return on close bracket CHECK_TERMINATE(); break; case T_LOG10: pLog10 = NEW_UNARY_OPERATOR(ExpLog10, pLog10); // Return on close bracket CHECK_TERMINATE(); break; case T_CEIL: pCeil = NEW_UNARY_OPERATOR(ExpCeil, pCeil); // Return on close bracket CHECK_TERMINATE(); break; case T_ROUND: pRound = NEW_UNARY_OPERATOR(ExpRound, pRound); // Return on close bracket CHECK_TERMINATE(); break; case T_RANDOM: pRandom = NEW_UNARY_OPERATOR(ExpRandom, pRandom); // Return on close bracket CHECK_TERMINATE(); break; case T_LEN: pLen = NEW_UNARY_OPERATOR(ExpLen, pLen); // Return on close bracket CHECK_TERMINATE(); break; case T_LEFTBRACKET: // THIS WILL BE CALLED ALWAYS NOW!!! //step++; //TOTIGS: Everywhere else you call DoBracket you dont add 1 to the step... // resulting in like 2 brackets omfg LeftPart = DoBrackets(step); if (unaryMode) return LeftPart; CHECK_TERMINATE() break; case T_NULL: step++; break; default: throw runtime_error("Syntax error in an expression. Please report this bug."); }//switch }//while return LeftPart; }
// Tokenise a user's typed expression string void CExpression::ParseUserString(const char* exp, int* pchpos, bool editorMode) { toks.clear(); whitespaceCount = 0; // Loop every char for (int i = 0; exp[i] != NULL; i++) { if (pchpos) *pchpos = i; char NextCh = exp[i+1]; char CurCh = exp[i]; char PrevCh = exp[(i == 0 ? 0 : i-1)]; char strbuf[128]; // Check this char switch (exp[i]) { case '+': AppendToken(T_ADD, "+"); break; case '-': // If previous token is not operand, use as negative sign // Fix 25/4/07: random(4) - random(5) interprets '-' as integer: explicit check for right bracket if (toks.size() == 0 || ((!TokenFitsRule(toks.back().t, T_ANY_VALUE) || toks.back().t == T_LEFTBRACKET) && toks.back().t != T_RIGHTBRACKET)) { i += ConsumeDigits(strbuf, &(exp[i])); // Contains a dot: float if (StrContains(strbuf, '.')) AppendToken(T_FLOAT, strbuf); else // Else integer AppendToken(T_INTEGER, strbuf); } else AppendToken(T_SUBTRACT, "-"); break; case '*': AppendToken(T_MULTIPLY, "*"); break; case '/': AppendToken(T_DIVIDE, "/"); break; case '^': AppendToken(T_POWER, "^"); break; case '%': AppendToken(T_MOD, "%"); break; case '(': AppendToken(T_LEFTBRACKET, "("); break; case ')': AppendToken(T_RIGHTBRACKET, ")"); break; case '{': AppendToken(T_LEFTCURLY, "{"); break; case '}': AppendToken(T_RIGHTCURLY, "}"); break; case '@': AppendToken(T_AT, "@"); break; case ',': AppendToken(T_COMMA, ","); break; case '.': AppendToken(T_DOT, "."); break; case '"': i += AppendString(&(exp[i]), editorMode); break; case '\'': i += AppendString(&(exp[i]), editorMode, true); break; case '=': AppendToken(T_EQUAL, "="); break; case '<': if (NextCh == '=') { AppendToken(T_LESSEQUAL, "<="); i++; } else if (NextCh == '>') { AppendToken(T_NOTEQUAL, "<>"); i++; } else AppendToken(T_LESS, "<"); break; // Alternative not equal operator != <> case '!': if (NextCh == '=') { AppendToken(T_NOTEQUAL, "!="); i++; } else NotifyError("Syntax error: '!'"); break; case '&': AppendToken(T_AND, "&"); break; case '|': AppendToken(T_OR, "|"); break; case '>': if (NextCh == '=') { AppendToken(T_GREATEREQUAL, ">="); i++; } else AppendToken(T_GREATER, ">"); break; case '?': AppendToken(T_CONDITIONAL, "?"); break; case ':': AppendToken(T_COLON, ":"); break; default: // Parse numbers and idents if (IsChAlphaNumeric(CurCh)) i += ConsumeAppendIdentifier(strbuf, &(exp[i])); // Skip over whitespace else if (IsWhitespace(CurCh)) { // In editor mode add a T_WHITESPACE token if (editorMode) { Token t; t.length = 0; t.t = T_WHITESPACE; while (IsWhitespace(exp[i])) { t.str += exp[i]; i++; } // We want the next for loop iteration to see the next char i--; toks.push_back(t); } // Add to last token length if (toks.size() > 0) toks[toks.size() - 1].length++; } // Else unknown character else { string error = "Unknown character '"; error += CurCh; error += "'"; throw runtime_error(error.c_str()); } }//switch }//for // Only perform preprocessing when not in edittime mode if (!editorMode) PreProcessTokStream(); /////////////////////////// // Final step: parse to tree // Determine if the overall expression is constant, collapsing constant parts along // the way. // Runtime only #ifdef RUNTIME // Make the tree int step = 0; ExpTree = DoTree(step); isConstant = ExpTree->Constant(); // If constant, get the constant value. if (isConstant) { CollapseConstantTree(ExpTree); ExpTree->Evaluate(&constantValue); } #endif }