/* * This file is part of the Colobot: Gold Edition source code * Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam * http://epsiteс.ch; http://colobot.info; http://github.com/colobot * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://gnu.org/licenses */ /////////////////////////////////////////////////// // expression of type Opérande1 + Opérande2 // Opérande1 > Opérande2 #include "CBot.h" #include // various constructors CBotTwoOpExpr::CBotTwoOpExpr() { m_leftop = m_rightop = NULL; // NULL to be able to delete without other name = "CBotTwoOpExpr"; // debug } CBotTwoOpExpr::~CBotTwoOpExpr() { delete m_leftop; delete m_rightop; } CBotLogicExpr::CBotLogicExpr() { m_condition = m_op1 = m_op2 = NULL; // NULL to be able to delete without other name = "CBotLogicExpr"; // debug } CBotLogicExpr::~CBotLogicExpr() { delete m_condition; delete m_op1; delete m_op2; } // type of operands accepted by operations #define ENTIER ((1<TokenStack(); // one end of stack please // search the intructions that may be suitable to the left of the operation CBotInstr* left = (*pOp == 0) ? CBotParExpr::Compile( p, pStk ) : // expression (...) left CBotTwoOpExpr::Compile( p, pStk, pOp ); // expression A * B left if (left == NULL) return pStack->Return(NULL, pStk); // if error, transmit // did we expected the operand? int TypeOp = p->GetType(); if ( IsInList( TypeOp, pOperations, typemasque ) ) { CBotTypResult type1, type2; type1 = pStk->GetTypResult(); // what kind of the first operand? if ( TypeOp == ID_LOGIC ) // special case provided for: ? op1: op2; { if ( !type1.Eq(CBotTypBoolean) ) { pStk->SetError( TX_BADTYPE, p); return pStack->Return(NULL, pStk); } CBotLogicExpr* inst = new CBotLogicExpr(); inst->m_condition = left; p = p->GetNext(); // skip the token of the operation inst->m_op1 = CBotExpression::Compile(p, pStk); CBotToken* pp = p; if ( inst->m_op1 == NULL || !IsOfType( p, ID_DOTS ) ) { pStk->SetError( TX_MISDOTS, p->GetStart()); delete inst; return pStack->Return(NULL, pStk); } type1 = pStk->GetTypResult(); inst->m_op2 = CBotExpression::Compile(p, pStk); if ( inst->m_op2 == NULL ) { pStk->SetError( TX_ENDOF, p->GetStart() ); delete inst; return pStack->Return(NULL, pStk); } type2 = pStk->GetTypResult(); if (!TypeCompatible(type1, type2)) { pStk->SetError( TX_BAD2TYPE, pp ); delete inst; return pStack->Return(NULL, pStk); } pStk->SetType(type1); // the greatest of 2 types return pStack->Return(inst, pStk); } CBotTwoOpExpr* inst = new CBotTwoOpExpr(); // element for operation inst->SetToken(p); // stores the operation p = p->GetNext(); // skip the token of the operation // looking statements that may be suitable for right if ( NULL != (inst->m_rightop = CBotTwoOpExpr::Compile( p, pStk, pOp )) ) // expression (...) right { // there is an second operand acceptable type2 = pStk->GetTypResult(); // what kind of results? // what kind of result? int TypeRes = MAX( type1.GetType(3), type2.GetType(3) ); if ( TypeOp == ID_ADD && type1.Eq(CBotTypString) ) { TypeRes = CBotTypString; type2 = type1; // any type convertible chain } else if ( TypeOp == ID_ADD && type2.Eq(CBotTypString) ) { TypeRes = CBotTypString; type1 = type2; // any type convertible chain } else if (!TypeOk( TypeRes, typemasque )) type1.SetType(99);// error of type switch ( TypeOp ) { case ID_LOG_OR: case ID_LOG_AND: case ID_TXT_OR: case ID_TXT_AND: case ID_EQ: case ID_NE: case ID_HI: case ID_LO: case ID_HS: case ID_LS: TypeRes = CBotTypBoolean; } if ( TypeCompatible (type1, type2, TypeOp ) ) // the results are compatible { // ok so, saves the operand in the object inst->m_leftop = left; // special for evaluation of the operations of the same level from left to right while ( IsInList( p->GetType(), pOperations, typemasque ) ) // same operation(s) follows? { TypeOp = p->GetType(); CBotTwoOpExpr* i = new CBotTwoOpExpr(); // element for operation i->SetToken(p); // stores the operation i->m_leftop = inst; // left operand type1 = TypeRes; p = p->GetNext(); // advance after i->m_rightop = CBotTwoOpExpr::Compile( p, pStk, pOp ); type2 = pStk->GetTypResult(); if ( !TypeCompatible (type1, type2, TypeOp) ) // the results are compatible { pStk->SetError(TX_BAD2TYPE, &i->m_token); delete i; return pStack->Return(NULL, pStk); } if ( TypeRes != CBotTypString ) TypeRes = MAX(type1.GetType(), type2.GetType()); inst = i; } CBotTypResult t(type1); t.SetType(TypeRes); // is a variable on the stack for the type of result pStk->SetVar(CBotVar::Create(static_cast(NULL), t)); // and returns the requested object return pStack->Return(inst, pStk); } pStk->SetError(TX_BAD2TYPE, &inst->m_token); } // in case of error, releases the elements delete left; delete inst; // and transmits the error to the stack return pStack->Return(NULL, pStk); } // if we are not dealing with an operation + or - // goes to that requested, the operand (left) found // instead of the object "addition" return pStack->Return(left, pStk); } bool IsNan(CBotVar* left, CBotVar* right, int* err = NULL) { if ( left ->GetInit() > IS_DEF || right->GetInit() > IS_DEF ) { if ( err != NULL ) *err = TX_OPNAN ; return true; } return false; } // performes the operation on two operands bool CBotTwoOpExpr::Execute(CBotStack* &pStack) { CBotStack* pStk1 = pStack->AddStack(this); // adds an item to the stack // or return in case of recovery // if ( pStk1 == EOX ) return true; // according to recovery, it may be in one of two states if ( pStk1->GetState() == 0 ) // first state, evaluates the left operand { if (!m_leftop->Execute(pStk1) ) return false; // interrupted here? // for OR and AND logic does not evaluate the second expression if not necessary if ( (GetTokenType() == ID_LOG_AND || GetTokenType() == ID_TXT_AND ) && pStk1->GetVal() == false ) { CBotVar* res = CBotVar::Create( static_cast(NULL), CBotTypBoolean); res->SetValInt(false); pStk1->SetVar(res); return pStack->Return(pStk1); // transmits the result } if ( (GetTokenType() == ID_LOG_OR||GetTokenType() == ID_TXT_OR) && pStk1->GetVal() == true ) { CBotVar* res = CBotVar::Create( static_cast(NULL), CBotTypBoolean); res->SetValInt(true); pStk1->SetVar(res); return pStack->Return(pStk1); // transmits the result } // passes to the next step pStk1->SetState(1); // ready for further } // requires a little more stack to avoid touching the result // of which is left on the stack, precisely CBotStack* pStk2 = pStk1->AddStack(); // adds an item to the stack // or return in case of recovery // 2e état, évalue l'opérande de droite if ( pStk2->GetState() == 0 ) { if ( !m_rightop->Execute(pStk2) ) return false; // interrupted here? pStk2->IncState(); } CBotTypResult type1 = pStk1->GetTypResult(); // what kind of results? CBotTypResult type2 = pStk2->GetTypResult(); CBotStack* pStk3 = pStk2->AddStack(this); // adds an item to the stack if ( pStk3->IfStep() ) return false; // shows the operation if step by step // creates a temporary variable to put the result // what kind of result? int TypeRes = MAX(type1.GetType(), type2.GetType()); if ( GetTokenType() == ID_ADD && type1.Eq(CBotTypString) ) { TypeRes = CBotTypString; } switch ( GetTokenType() ) { case ID_LOG_OR: case ID_LOG_AND: case ID_TXT_OR: case ID_TXT_AND: case ID_EQ: case ID_NE: case ID_HI: case ID_LO: case ID_HS: case ID_LS: TypeRes = CBotTypBoolean; break; case ID_DIV: TypeRes = MAX(TypeRes, CBotTypFloat); } // creates a variable for the result CBotVar* result = CBotVar::Create( static_cast(NULL), TypeRes); // creates a variable to perform the calculation in the appropriate type TypeRes = MAX(type1.GetType(), type2.GetType()); if ( GetTokenType() == ID_ADD && type1.Eq(CBotTypString) ) { TypeRes = CBotTypString; } CBotVar* temp; if ( TypeRes == CBotTypPointer ) TypeRes = CBotTypNullPointer; if ( TypeRes == CBotTypClass ) temp = CBotVar::Create( static_cast(NULL), CBotTypResult(CBotTypIntrinsic, type1.GetClass() ) ); else temp = CBotVar::Create( static_cast(NULL), TypeRes ); int err = 0; // is a operation according to request CBotVar* left = pStk1->GetVar(); CBotVar* right = pStk2->GetVar(); switch (GetTokenType()) { case ID_ADD: if ( !IsNan(left, right, &err) ) result->Add(left , right); // addition break; case ID_SUB: if ( !IsNan(left, right, &err) ) result->Sub(left , right); // substraction break; case ID_MUL: if ( !IsNan(left, right, &err) ) result->Mul(left , right); // multiplies break; case ID_POWER: if ( !IsNan(left, right, &err) ) result->Power(left , right); // power break; case ID_DIV: if ( !IsNan(left, right, &err) ) err = result->Div(left , right);// division break; case ID_MODULO: if ( !IsNan(left, right, &err) ) err = result->Modulo(left , right);// remainder of division break; case ID_LO: if ( !IsNan(left, right, &err) ) result->SetValInt(temp->Lo(left , right)); // lower break; case ID_HI: if ( !IsNan(left, right, &err) ) result->SetValInt(temp->Hi(left , right)); // top break; case ID_LS: if ( !IsNan(left, right, &err) ) result->SetValInt(temp->Ls(left , right)); // less than or equal break; case ID_HS: if ( !IsNan(left, right, &err) ) result->SetValInt(temp->Hs(left , right)); // greater than or equal break; case ID_EQ: if ( IsNan(left, right) ) result->SetValInt(left->GetInit() == right->GetInit()) ; else result->SetValInt(temp->Eq(left , right)); // equal break; case ID_NE: if ( IsNan(left, right) ) result->SetValInt(left ->GetInit() != right->GetInit()) ; else result->SetValInt(temp->Ne(left , right)); // different break; case ID_TXT_AND: case ID_LOG_AND: case ID_AND: if ( !IsNan(left, right, &err) ) result->And(left , right); // AND break; case ID_TXT_OR: case ID_LOG_OR: case ID_OR: if ( !IsNan(left, right, &err) ) result->Or(left , right); // OR break; case ID_XOR: if ( !IsNan(left, right, &err) ) result->XOr(left , right); // exclusive OR break; case ID_ASR: if ( !IsNan(left, right, &err) ) result->ASR(left , right); break; case ID_SR: if ( !IsNan(left, right, &err) ) result->SR(left , right); break; case ID_SL: if ( !IsNan(left, right, &err) ) result->SL(left , right); break; default: assert(0); } delete temp; pStk2->SetVar(result); // puts the result on the stack if ( err ) pStk2->SetError(err, &m_token); // and the possible error (division by zero) // pStk1->Return(pStk2); // releases the stack return pStack->Return(pStk2); // transmits the result } void CBotTwoOpExpr::RestoreState(CBotStack* &pStack, bool bMain) { if ( !bMain ) return; CBotStack* pStk1 = pStack->RestoreStack(this); // adds an item to the stack if ( pStk1 == NULL ) return; // according to recovery, it may be in one of two states if ( pStk1->GetState() == 0 ) // first state, evaluates the left operand { m_leftop->RestoreState(pStk1, bMain); // interrupted here! return; } CBotStack* pStk2 = pStk1->RestoreStack(); // adds an item to the stack if ( pStk2 == NULL ) return; // second state, evaluates the right operand if ( pStk2->GetState() == 0 ) { m_rightop->RestoreState(pStk2, bMain); // interrupted here! return; } } bool CBotLogicExpr::Execute(CBotStack* &pStack) { CBotStack* pStk1 = pStack->AddStack(this); // adds an item to the stack // or return in case of recovery // if ( pStk1 == EOX ) return true; if ( pStk1->GetState() == 0 ) { if ( !m_condition->Execute(pStk1) ) return false; if (!pStk1->SetState(1)) return false; } if ( pStk1->GetVal() == true ) { if ( !m_op1->Execute(pStk1) ) return false; } else { if ( !m_op2->Execute(pStk1) ) return false; } return pStack->Return(pStk1); // transmits the result } void CBotLogicExpr::RestoreState(CBotStack* &pStack, bool bMain) { if ( !bMain ) return; CBotStack* pStk1 = pStack->RestoreStack(this); // adds an item to the stack if ( pStk1 == NULL ) return; if ( pStk1->GetState() == 0 ) { m_condition->RestoreState(pStk1, bMain); return; } if ( pStk1->GetVal() == true ) { m_op1->RestoreState(pStk1, bMain); } else { m_op2->RestoreState(pStk1, bMain); } } #if 0 void t() { int x,y; 1>0 ? x = 0 : y = 0; } #endif #if 0 void t(bool t) { int x; x = 1 + t ? 1 : 3 + 4 * 2 ; t ? 0 : "test"; } #endif