326 lines
9.8 KiB
C++
326 lines
9.8 KiB
C++
/*
|
|
* This file is part of the Colobot: Gold Edition source code
|
|
* Copyright (C) 2001-2018, Daniel Roux, EPSITEC SA & TerranovaTeam
|
|
* http://epsitec.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
|
|
*/
|
|
|
|
#include "CBot/CBotInstr/CBotExpression.h"
|
|
|
|
#include "CBot/CBotInstr/CBotInstrUtils.h"
|
|
|
|
#include "CBot/CBotInstr/CBotTwoOpExpr.h"
|
|
|
|
#include "CBot/CBotStack.h"
|
|
#include "CBot/CBotCStack.h"
|
|
|
|
#include "CBot/CBotVar/CBotVar.h"
|
|
|
|
#include <cassert>
|
|
|
|
namespace CBot
|
|
{
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
CBotExpression::CBotExpression()
|
|
{
|
|
m_leftop = nullptr;
|
|
m_rightop = nullptr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotExpression::~CBotExpression()
|
|
{
|
|
delete m_leftop;
|
|
delete m_rightop;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotInstr* CBotExpression::Compile(CBotToken* &p, CBotCStack* pStack)
|
|
{
|
|
CBotToken* pp = p;
|
|
|
|
CBotExpression* inst = new CBotExpression();
|
|
|
|
inst->m_leftop = CBotLeftExpr::Compile(p, pStack);
|
|
|
|
inst->SetToken(p);
|
|
int OpType = p->GetType();
|
|
|
|
if ( pStack->IsOk() &&
|
|
IsOfTypeList(p, ID_ASS, ID_ASSADD, ID_ASSSUB, ID_ASSMUL, ID_ASSDIV, ID_ASSMODULO,
|
|
ID_ASSAND, ID_ASSXOR, ID_ASSOR,
|
|
ID_ASSSL , ID_ASSSR, ID_ASSASR, 0 ))
|
|
{
|
|
if (inst->m_leftop == nullptr)
|
|
{
|
|
pStack->SetError(CBotErrBadLeft, p->GetEnd());
|
|
delete inst;
|
|
return nullptr;
|
|
}
|
|
|
|
if ( p->GetType() == ID_SEP )
|
|
{
|
|
pStack->SetError(CBotErrNoExpression, p);
|
|
delete inst;
|
|
return nullptr;
|
|
}
|
|
|
|
inst->m_rightop = CBotExpression::Compile(p, pStack);
|
|
if (inst->m_rightop == nullptr)
|
|
{
|
|
delete inst;
|
|
return nullptr;
|
|
}
|
|
|
|
CBotTypResult type1 = pStack->GetTypResult();
|
|
|
|
// get the variable assigned to mark
|
|
CBotVar* var = nullptr;
|
|
inst->m_leftop->ExecuteVar(var, pStack);
|
|
if (var == nullptr)
|
|
{
|
|
delete inst;
|
|
return nullptr;
|
|
}
|
|
|
|
if (OpType != ID_ASS && !var->IsDefined())
|
|
{
|
|
pStack->SetError(CBotErrNotInit, pp);
|
|
delete inst;
|
|
return nullptr;
|
|
}
|
|
|
|
CBotTypResult type2 = var->GetTypResult();
|
|
|
|
// what types are acceptable?
|
|
switch (OpType)
|
|
{
|
|
case ID_ASS:
|
|
// if (type2 == CBotTypClass) type2 = -1;
|
|
if ((type1.Eq(CBotTypPointer) && type2.Eq(CBotTypPointer)) ||
|
|
(type1.Eq(CBotTypClass) && type2.Eq(CBotTypClass) ) )
|
|
{
|
|
/* CBotClass* c1 = type1.GetClass();
|
|
CBotClass* c2 = type2.GetClass();
|
|
if (!c1->IsChildOf(c2)) type2.SetType(-1);
|
|
//- if (!type1.Eq(CBotTypClass)) var->SetPointer(pStack->GetVar()->GetPointer());*/
|
|
var->SetInit(CBotVar::InitType::IS_POINTER);
|
|
}
|
|
else
|
|
var->SetInit(CBotVar::InitType::DEF);
|
|
|
|
break;
|
|
case ID_ASSADD:
|
|
if (type2.Eq(CBotTypBoolean) ||
|
|
type2.GetType() > CBotTypString ) type2.SetType(-1); // numbers and strings
|
|
break;
|
|
case ID_ASSSUB:
|
|
case ID_ASSMUL:
|
|
case ID_ASSDIV:
|
|
case ID_ASSMODULO:
|
|
if (type2.GetType() >= CBotTypBoolean) type2.SetType(-1); // numbers only
|
|
break;
|
|
}
|
|
|
|
if (!TypeCompatible(type1, type2, OpType))
|
|
{
|
|
pStack->SetError(CBotErrBadType1, &inst->m_token);
|
|
delete inst;
|
|
return nullptr;
|
|
}
|
|
|
|
return inst; // compatible type?
|
|
}
|
|
|
|
delete inst;
|
|
int start, end;
|
|
CBotError error = pStack->GetError(start, end);
|
|
|
|
p = pp; // returns to the top
|
|
pStack->SetError(CBotNoErr,0); // forget the error
|
|
|
|
CBotInstr* i = CBotTwoOpExpr::Compile(p, pStack); // tries without assignment
|
|
if (i != nullptr && error == CBotErrPrivate && p->GetType() == ID_ASS)
|
|
pStack->ResetError(error, start, end);
|
|
return i;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool CBotExpression::Execute(CBotStack* &pj)
|
|
{
|
|
CBotStack* pile = pj->AddStack(this);
|
|
|
|
// CBotToken* pToken = m_leftop->GetToken();
|
|
|
|
CBotVar* pVar = nullptr;
|
|
|
|
CBotStack* pile1 = pile;
|
|
|
|
CBotVar::InitType initKind = CBotVar::InitType::DEF;
|
|
CBotVar* result = nullptr;
|
|
|
|
// must be done before any indexes (stack can be changed)
|
|
if (!m_leftop->ExecuteVar(pVar, pile, nullptr, false)) return false; // variable before accessing the value on the right
|
|
|
|
if ( pile1->GetState()==0)
|
|
{
|
|
pile1->SetCopyVar(pVar); // keeps the copy on the stack (if interrupted)
|
|
pile1->IncState();
|
|
}
|
|
|
|
CBotStack* pile2 = pile->AddStack();
|
|
|
|
if ( pile2->GetState()==0)
|
|
{
|
|
if (m_rightop && !m_rightop->Execute(pile2)) return false; // initial value // interrupted?
|
|
if (m_rightop)
|
|
{
|
|
CBotVar* var = pile1->GetVar();
|
|
CBotVar* value = pile2->GetVar();
|
|
if (var->GetType() == CBotTypString && value->GetType() != CBotTypString)
|
|
{
|
|
CBotVar* newVal = CBotVar::Create("", var->GetTypResult());
|
|
value->Update(pj->GetUserPtr());
|
|
newVal->SetValString(value->GetValString());
|
|
pile2->SetVar(newVal);
|
|
}
|
|
}
|
|
pile2->IncState();
|
|
}
|
|
|
|
if (pile1->GetState() == 1)
|
|
{
|
|
if (m_token.GetType() != ID_ASS)
|
|
{
|
|
pVar = pile1->GetVar(); // recovers if interrupted
|
|
initKind = pVar->GetInit();
|
|
if (initKind == CBotVar::InitType::IS_NAN)
|
|
{
|
|
pile2->SetError(CBotErrNan, m_leftop->GetToken());
|
|
return pj->Return(pile2);
|
|
}
|
|
result = CBotVar::Create("", pVar->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC));
|
|
}
|
|
|
|
switch (m_token.GetType())
|
|
{
|
|
case ID_ASS:
|
|
break;
|
|
case ID_ASSADD:
|
|
result->Add(pile1->GetVar(), pile2->GetVar());
|
|
pile2->SetVar(result);
|
|
break;
|
|
case ID_ASSSUB:
|
|
result->Sub(pile1->GetVar(), pile2->GetVar());
|
|
pile2->SetVar(result);
|
|
break;
|
|
case ID_ASSMUL:
|
|
result->Mul(pile1->GetVar(), pile2->GetVar());
|
|
pile2->SetVar(result);
|
|
break;
|
|
case ID_ASSDIV:
|
|
if (initKind != CBotVar::InitType::UNDEF &&
|
|
result->Div(pile1->GetVar(), pile2->GetVar()))
|
|
pile2->SetError(CBotErrZeroDiv, &m_token);
|
|
pile2->SetVar(result);
|
|
break;
|
|
case ID_ASSMODULO:
|
|
if (initKind != CBotVar::InitType::UNDEF &&
|
|
result->Modulo(pile1->GetVar(), pile2->GetVar()))
|
|
pile2->SetError(CBotErrZeroDiv, &m_token);
|
|
pile2->SetVar(result);
|
|
break;
|
|
case ID_ASSAND:
|
|
result->And(pile1->GetVar(), pile2->GetVar());
|
|
pile2->SetVar(result);
|
|
break;
|
|
case ID_ASSXOR:
|
|
result->XOr(pile1->GetVar(), pile2->GetVar());
|
|
pile2->SetVar(result);
|
|
break;
|
|
case ID_ASSOR:
|
|
result->Or(pile1->GetVar(), pile2->GetVar());
|
|
pile2->SetVar(result);
|
|
break;
|
|
case ID_ASSSL:
|
|
result->SL(pile1->GetVar(), pile2->GetVar());
|
|
pile2->SetVar(result);
|
|
break;
|
|
case ID_ASSSR:
|
|
result->SR(pile1->GetVar(), pile2->GetVar());
|
|
pile2->SetVar(result);
|
|
break;
|
|
case ID_ASSASR:
|
|
result->ASR(pile1->GetVar(), pile2->GetVar());
|
|
pile2->SetVar(result);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
if (initKind == CBotVar::InitType::UNDEF)
|
|
pile2->SetError(CBotErrNotInit, m_leftop->GetToken());
|
|
|
|
pile1->IncState();
|
|
}
|
|
|
|
if (!m_leftop->Execute( pile2, pile1 ))
|
|
return false;
|
|
|
|
return pj->Return(pile2);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void CBotExpression::RestoreState(CBotStack* &pj, bool bMain)
|
|
{
|
|
if (bMain)
|
|
{
|
|
// CBotToken* pToken = m_leftop->GetToken();
|
|
// CBotVar* pVar = nullptr;
|
|
|
|
CBotStack* pile = pj->RestoreStack(this);
|
|
if (pile == nullptr) return;
|
|
|
|
CBotStack* pile1 = pile;
|
|
|
|
if ( pile1->GetState()==0)
|
|
{
|
|
m_leftop->RestoreStateVar(pile, true);
|
|
return;
|
|
}
|
|
|
|
m_leftop->RestoreStateVar(pile, false);
|
|
|
|
CBotStack* pile2 = pile->RestoreStack();
|
|
if (pile2 == nullptr) return;
|
|
|
|
if ( pile2->GetState()==0)
|
|
{
|
|
if (m_rightop) m_rightop->RestoreState(pile2, bMain);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::map<std::string, CBotInstr*> CBotExpression::GetDebugLinks()
|
|
{
|
|
auto links = CBotInstr::GetDebugLinks();
|
|
links["m_leftop"] = m_leftop;
|
|
links["m_rightop"] = m_rightop;
|
|
return links;
|
|
}
|
|
|
|
} // namespace CBot
|