/* * 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 */ /////////////////////////////////////////////////////////////////////// // compilation of various instructions // compile all routines as static // and return an object according to what was found as instruction // compiler principle: // compile the routines return an object of the class corresponding to the operation found // this is always a subclass of CBotInstr. // (CBotInstr objects are never used directly) // compiles if the routine returns NULL is that the statement is false // or misunderstood. // the error is then on the stack CBotCStack :: Isok () is false #include "CBot.h" #include CBotInstr::CBotInstr() { name = "CBotInstr"; m_next = NULL; m_next2b = NULL; m_next3 = NULL; m_next3b = NULL; } CBotInstr::~CBotInstr() { delete m_next; delete m_next2b; delete m_next3; delete m_next3b; } // counter of nested loops, // to determine the break and continue valid // list of labels used int CBotInstr::m_LoopLvl = 0; CBotStringArray CBotInstr::m_labelLvl = CBotStringArray(); // adds a level with a label void CBotInstr::IncLvl(CBotString& label) { m_labelLvl.SetSize(m_LoopLvl+1); m_labelLvl[m_LoopLvl] = label; m_LoopLvl++; } // adds a level (switch statement) void CBotInstr::IncLvl() { m_labelLvl.SetSize(m_LoopLvl+1); m_labelLvl[m_LoopLvl] = "#SWITCH"; m_LoopLvl++; } // free a level void CBotInstr::DecLvl() { m_LoopLvl--; m_labelLvl[m_LoopLvl].Empty(); } // control validity of break and continue bool CBotInstr::ChkLvl(const CBotString& label, int type) { int i = m_LoopLvl; while (--i>=0) { if ( type == ID_CONTINUE && m_labelLvl[i] == "#SWITCH") continue; if (label.IsEmpty()) return true; if (m_labelLvl[i] == label) return true; } return false; } bool CBotInstr::IsOfClass(CBotString n) { return name == n; } //////////////////////////////////////////////////////////////////////////// // database management class CBotInstr // set the token corresponding to the instruction void CBotInstr::SetToken(CBotToken* p) { m_token = *p; } // return the type of the token assicated with the instruction int CBotInstr::GetTokenType() { return m_token.GetType(); } // return associated token CBotToken* CBotInstr::GetToken() { return &m_token; } // adds the statement following the other void CBotInstr::AddNext(CBotInstr* n) { CBotInstr* p = this; while (p->m_next != NULL) p = p->m_next; p->m_next = n; } void CBotInstr::AddNext3(CBotInstr* n) { CBotInstr* p = this; while (p->m_next3 != NULL) p = p->m_next3; p->m_next3 = n; } void CBotInstr::AddNext3b(CBotInstr* n) { CBotInstr* p = this; while (p->m_next3b != NULL) p = p->m_next3b; p->m_next3b = n; } // returns next statement CBotInstr* CBotInstr::GetNext() { return m_next; } CBotInstr* CBotInstr::GetNext3() { return m_next3; } CBotInstr* CBotInstr::GetNext3b() { return m_next3b; } /////////////////////////////////////////////////////////////////////////// // compile an instruction which can be // while, do, try, throw, if, for, switch, break, continue, return // int, float, boolean, string, // declaration of an instance of a class // arbitrary expression CBotInstr* CBotInstr::Compile(CBotToken* &p, CBotCStack* pStack) { CBotToken* pp = p; if (p == NULL) return NULL; int type = p->GetType(); // what is the next token // is it a lable? if (IsOfType(pp, TokenTypVar) && IsOfType(pp, ID_DOTS)) { type = pp->GetType(); // these instructions accept only lable if (!IsOfTypeList(pp, ID_WHILE, ID_FOR, ID_DO, 0)) { pStack->SetError(TX_LABEL, pp->GetStart()); return NULL; } } // call routine corresponding to the compilation token found switch (type) { case ID_WHILE: return CBotWhile::Compile(p, pStack); case ID_FOR: return CBotFor::Compile(p, pStack); case ID_DO: return CBotDo::Compile(p, pStack); case ID_BREAK: case ID_CONTINUE: return CBotBreak::Compile(p, pStack); case ID_SWITCH: return CBotSwitch::Compile(p, pStack); case ID_TRY: return CBotTry::Compile(p, pStack); case ID_THROW: return CBotThrow::Compile(p, pStack); case ID_DEBUGDD: return CBotStartDebugDD::Compile(p, pStack); case ID_INT: return CBotInt::Compile(p, pStack); case ID_FLOAT: return CBotFloat::Compile(p, pStack); case ID_STRING: return CBotIString::Compile(p, pStack); case ID_BOOLEAN: case ID_BOOL: return CBotBoolean::Compile(p, pStack); case ID_IF: return CBotIf::Compile(p, pStack); case ID_RETURN: return CBotReturn::Compile(p, pStack); case ID_ELSE: pStack->SetStartError(p->GetStart()); pStack->SetError(TX_ELSEWITHOUTIF, p->GetEnd()); return NULL; case ID_CASE: pStack->SetStartError(p->GetStart()); pStack->SetError(TX_OUTCASE, p->GetEnd()); return NULL; } pStack->SetStartError(p->GetStart()); // should not be a reserved word DefineNum if (p->GetType() == TokenTypDef) { pStack->SetError(TX_RESERVED, p); return NULL; } // this might be an instance of class definnition CBotToken* ppp = p; if (IsOfType(ppp, TokenTypVar)) { if (CBotClass::Find(p) != NULL) { // yes, compiles the declaration of the instance return CBotClassInst::Compile(p, pStack); } } // this can be an arythmetic instruction CBotInstr* inst = CBotExpression::Compile(p, pStack); if (IsOfType(p, ID_SEP)) { return inst; } pStack->SetError(TX_ENDOF, p->GetStart()); delete inst; return NULL; } bool CBotInstr::Execute(CBotStack* &pj) { CBotString ClassManquante = name; assert(0); // should never go through this routine // but use the routines of the subclasses return false; } bool CBotInstr::Execute(CBotStack* &pj, CBotVar* pVar) { if (!Execute(pj)) return false; pVar->SetVal(pj->GetVar()); return true; } void CBotInstr::RestoreState(CBotStack* &pj, bool bMain) { CBotString ClassManquante = name; assert(0); // should never go through this routine // but use the routines of the subclasses } bool CBotInstr::ExecuteVar(CBotVar* &pVar, CBotCStack* &pile) { assert(0); // dad do not know, see the girls return false; } bool CBotInstr::ExecuteVar(CBotVar* &pVar, CBotStack* &pile, CBotToken* prevToken, bool bStep, bool bExtend) { assert(0); // dad do not know, see the girls return false; } void CBotInstr::RestoreStateVar(CBotStack* &pile, bool bMain) { assert(0); // dad do not know, see the girls } // this routine is defined only for the subclass CBotCase // this allows to make the call on all instructions CompCase // to see if it's a case to the desired value. bool CBotInstr::CompCase(CBotStack* &pj, int val) { return false; } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // compiles a statement block " { i ; i ; } " // this class have no constructor because there is never an instance of this // class // the object returned by Compile is usually of type CBotListInstr CBotInstr* CBotBlock::Compile(CBotToken* &p, CBotCStack* pStack, bool bLocal) { pStack->SetStartError(p->GetStart()); if (IsOfType(p, ID_OPBLK)) { CBotInstr* inst = CBotListInstr::Compile(p, pStack, bLocal); if (IsOfType(p, ID_CLBLK)) { return inst; } pStack->SetError(TX_CLOSEBLK, p->GetStart()); // missing parenthesis delete inst; return NULL; } pStack->SetError(TX_OPENBLK, p->GetStart()); return NULL; } CBotInstr* CBotBlock::CompileBlkOrInst(CBotToken* &p, CBotCStack* pStack, bool bLocal) { // is this a new block if (p->GetType() == ID_OPBLK) return CBotBlock::Compile(p, pStack); // otherwise, look for a single statement instead // to handle the case with local definition instruction (*) CBotCStack* pStk = pStack->TokenStack(p, bLocal); return pStack->Return( CBotInstr::Compile(p, pStk), // a single instruction pStk); } // (*) is the case in the following statement // if (1 == 1) int x = 0; // where the variable x is known only in the block following the if ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // compiles a list of instructions separated by semicolons CBotListInstr::CBotListInstr() { m_Instr = NULL; name = "CBotListInstr"; } CBotListInstr::~CBotListInstr() { delete m_Instr; } CBotInstr* CBotListInstr::Compile(CBotToken* &p, CBotCStack* pStack, bool bLocal) { CBotCStack* pStk = pStack->TokenStack(p, bLocal); // variables are local CBotListInstr* inst = new CBotListInstr(); while (true) { if (p == NULL) break; if (IsOfType(p, ID_SEP)) continue; // empty statement ignored if (p->GetType() == ID_CLBLK) break; if (IsOfType(p, 0)) { pStack->SetError(TX_CLOSEBLK, p->GetStart()); delete inst; return pStack->Return(NULL, pStk); } CBotInstr* i = CBotBlock::CompileBlkOrInst(p, pStk); // compiles next if (!pStk->IsOk()) { delete inst; return pStack->Return(NULL, pStk); } if (inst->m_Instr == NULL) inst->m_Instr = i; else inst->m_Instr->AddNext(i); // added a result } return pStack->Return(inst, pStk); } // executes a set of instructions bool CBotListInstr::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this, true); //needed for SetState() if (pile->StackOver() ) return pj->Return( pile); CBotInstr* p = m_Instr; // the first expression int state = pile->GetState(); while (state-->0) p = p->GetNext(); // returns to the interrupted operation if (p != NULL) while (true) { if (!p->Execute(pile)) return false; p = p->GetNext(); if (p == NULL) break; if (!pile->IncState()) ;//return false; // ready for next } return pj->Return(pile); } void CBotListInstr::RestoreState(CBotStack* &pj, bool bMain) { if (!bMain) return; CBotStack* pile = pj->RestoreStack(this); if (pile == NULL) return; CBotInstr* p = m_Instr; // the first expression int state = pile->GetState(); while ( p != NULL && state-- > 0) { p->RestoreState(pile, false); p = p->GetNext(); // returns to the interrupted operation } if (p != NULL) p->RestoreState(pile, true); } ////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // compilation of an element to the left of an assignment CBotLeftExprVar::CBotLeftExprVar() { name = "CBotLeftExprVar"; m_typevar = -1; m_nIdent = 0; } CBotLeftExprVar::~CBotLeftExprVar() { } CBotInstr* CBotLeftExprVar::Compile(CBotToken* &p, CBotCStack* pStack) { // verifies that the token is a variable name if (p->GetType() != TokenTypVar) { pStack->SetError( TX_NOVAR, p->GetStart()); return NULL; } CBotLeftExprVar* inst = new CBotLeftExprVar(); inst->SetToken(p); p = p->GetNext(); return inst; } // creates a variable and assigns the result to the stack bool CBotLeftExprVar::Execute(CBotStack* &pj) { CBotVar* var1; CBotVar* var2; var1 = CBotVar::Create(m_token.GetString(), m_typevar); var1->SetUniqNum(m_nIdent); // with the unique identifier pj->AddVar(var1); // place it on the stack var2 = pj->GetVar(); // result on the stack if (var2) var1->SetVal(var2); // do the assignment return true; } void CBotLeftExprVar::RestoreState(CBotStack* &pj, bool bMain) { CBotVar* var1; var1 = pj->FindVar(m_token.GetString()); if (var1 == NULL) assert(0); var1->SetUniqNum(m_nIdent); // with the unique identifier } ////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // defining an array of any type // int a[12]; // point x[]; CBotInstArray::CBotInstArray() { m_var = NULL; m_listass = NULL; name = "CBotInstArray"; } CBotInstArray::~CBotInstArray() { delete m_var; delete m_listass; } CBotInstr* CBotInstArray::Compile(CBotToken* &p, CBotCStack* pStack, CBotTypResult type) { CBotCStack* pStk = pStack->TokenStack(p); CBotInstArray* inst = new CBotInstArray(); CBotToken* vartoken = p; inst->SetToken(vartoken); // determinse the expression is valid for the item on the left side if (NULL != (inst->m_var = CBotLeftExprVar::Compile( p, pStk ))) { if (pStk->CheckVarLocal(vartoken)) // redefinition of the variable? { pStk->SetError(TX_REDEFVAR, vartoken); goto error; } CBotInstr* i; while (IsOfType(p, ID_OPBRK)) { if (p->GetType() != ID_CLBRK) i = CBotExpression::Compile(p, pStk); // expression for the value else i = new CBotEmpty(); // if no special formula inst->AddNext3b(i); // construct a list type = CBotTypResult(CBotTypArrayPointer, type); if (!pStk->IsOk() || !IsOfType(p, ID_CLBRK )) { pStk->SetError(TX_CLBRK, p->GetStart()); goto error; } } CBotVar* var = CBotVar::Create(vartoken, type); // create an instance inst->m_typevar = type; var->SetUniqNum( (static_cast(inst->m_var))->m_nIdent = CBotVar::NextUniqNum()); pStack->AddVar(var); // place it on the stack if (IsOfType(p, ID_ASS)) // with an assignment { inst->m_listass = CBotListArray::Compile(p, pStk, type.GetTypElem()); } if (pStk->IsOk()) return pStack->Return(inst, pStk); } error: delete inst; return pStack->Return(NULL, pStk); } // executes the definition of an array bool CBotInstArray::Execute(CBotStack* &pj) { CBotStack* pile1 = pj->AddStack(this); CBotStack* pile = pile1; if (pile1->GetState() == 0) { // seek the maximum dimension of the table CBotInstr* p = GetNext3b(); // the different formulas int nb = 0; while (p != NULL) { pile = pile->AddStack(); // little room to work nb++; if (pile->GetState() == 0) { if (!p->Execute(pile)) return false; // size calculation //interrupted? pile->IncState(); } p = p->GetNext3b(); } p = GetNext3b(); pile = pile1; // returns to the stack int n = 0; int max[100]; while (p != NULL) { pile = pile->AddStack(); CBotVar* v = pile->GetVar(); // result max[n] = v->GetValInt(); // value if (max[n]>MAXARRAYSIZE) { pile->SetError(TX_OUTARRAY, &m_token); return pj->Return (pile); } n++; p = p->GetNext3b(); } while (n<100) max[n++] = 0; m_typevar.SetArray(max); // store the limitations // create simply a NULL pointer CBotVar* var = CBotVar::Create(m_var->GetToken(), m_typevar); var->SetPointer(NULL); var->SetUniqNum((static_cast(m_var))->m_nIdent); pj->AddVar(var); #if STACKMEM pile1->AddStack()->Delete(); #else delete pile1->AddStack(); // need more indices #endif pile1->IncState(); } if (pile1->GetState() == 1) { if (m_listass != NULL) // there is the assignment for this table { CBotVar* pVar = pj->FindVar((static_cast(m_var))->m_nIdent); if (!m_listass->Execute(pile1, pVar)) return false; } pile1->IncState(); } if (pile1->IfStep()) return false; if ( m_next2b && !m_next2b->Execute(pile1 )) return false; return pj->Return(pile1); } void CBotInstArray::RestoreState(CBotStack* &pj, bool bMain) { CBotStack* pile1 = pj; CBotVar* var = pj->FindVar(m_var->GetToken()->GetString()); if (var != NULL) var->SetUniqNum((static_cast(m_var))->m_nIdent); if (bMain) { pile1 = pj->RestoreStack(this); CBotStack* pile = pile1; if (pile == NULL) return; if (pile1->GetState() == 0) { // seek the maximum dimension of the table CBotInstr* p = GetNext3b(); while (p != NULL) { pile = pile->RestoreStack(); if (pile == NULL) return; if (pile->GetState() == 0) { p->RestoreState(pile, bMain); return; } p = p->GetNext3b(); } } if (pile1->GetState() == 1 && m_listass != NULL) { m_listass->RestoreState(pile1, bMain); } } if (m_next2b ) m_next2b->RestoreState( pile1, bMain); } // special case for empty indexes bool CBotEmpty :: Execute(CBotStack* &pj) { CBotVar* pVar = CBotVar::Create("", CBotTypInt); pVar->SetValInt(-1); pj->SetVar(pVar); return true; } void CBotEmpty :: RestoreState(CBotStack* &pj, bool bMain) { } ////////////////////////////////////////////////////////////////////////////////////// // defining a list table initialization // int [ ] a [ ] = (( 1, 2, 3 ) , ( 3, 2, 1 )) ; CBotListArray::CBotListArray() { m_expr = NULL; name = "CBotListArray"; } CBotListArray::~CBotListArray() { delete m_expr; } CBotInstr* CBotListArray::Compile(CBotToken* &p, CBotCStack* pStack, CBotTypResult type) { CBotCStack* pStk = pStack->TokenStack(p); CBotToken* pp = p; if (IsOfType( p, ID_NULL )) { CBotInstr* inst = new CBotExprNull (); inst->SetToken(pp); return pStack->Return(inst, pStk); // ok with empty element } CBotListArray* inst = new CBotListArray(); if (IsOfType( p, ID_OPENPAR )) { // each element takes the one after the other if (type.Eq( CBotTypArrayPointer )) { type = type.GetTypElem(); pStk->SetStartError(p->GetStart()); if (NULL == ( inst->m_expr = CBotListArray::Compile( p, pStk, type ) )) { goto error; } while (IsOfType( p, ID_COMMA )) // other elements? { pStk->SetStartError(p->GetStart()); CBotInstr* i = CBotListArray::Compile(p, pStk, type); if (NULL == i) { goto error; } inst->m_expr->AddNext3(i); } } else { pStk->SetStartError(p->GetStart()); if (NULL == ( inst->m_expr = CBotTwoOpExpr::Compile( p, pStk ))) { goto error; } CBotVar* pv = pStk->GetVar(); // result of the expression if (pv == NULL || !TypesCompatibles( type, pv->GetTypResult())) // compatible type? { pStk->SetError(TX_BADTYPE, p->GetStart()); goto error; } while (IsOfType( p, ID_COMMA )) // other elements? { pStk->SetStartError(p->GetStart()); CBotInstr* i = CBotTwoOpExpr::Compile(p, pStk) ; if (NULL == i) { goto error; } CBotVar* pv = pStk->GetVar(); // result of the expression if (pv == NULL || !TypesCompatibles( type, pv->GetTypResult())) // compatible type? { pStk->SetError(TX_BADTYPE, p->GetStart()); goto error; } inst->m_expr->AddNext3(i); } } if (!IsOfType(p, ID_CLOSEPAR) ) { pStk->SetError(TX_CLOSEPAR, p->GetStart()); goto error; } return pStack->Return(inst, pStk); } error: delete inst; return pStack->Return(NULL, pStk); } // executes the definition of an array bool CBotListArray::Execute(CBotStack* &pj, CBotVar* pVar) { CBotStack* pile1 = pj->AddStack(); CBotVar* pVar2; CBotInstr* p = m_expr; int n = 0; for (; p != NULL ; n++, p = p->GetNext3()) { if (pile1->GetState() > n) continue; pVar2 = pVar->GetItem(n, true); if (!p->Execute(pile1, pVar2)) return false; // evaluate expression pile1->IncState(); } return pj->Return(pile1); } void CBotListArray::RestoreState(CBotStack* &pj, bool bMain) { if (bMain) { CBotStack* pile = pj->RestoreStack(this); if (pile == NULL) return; CBotInstr* p = m_expr; int state = pile->GetState(); while(state-- > 0) p = p->GetNext3() ; p->RestoreState(pile, bMain); // size calculation //interrupted! } } ////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // definition of an integer variable // int a, b = 12; CBotInt::CBotInt() { m_next = NULL; // for multiple definitions m_var = m_expr = NULL; name = "CBotInt"; } CBotInt::~CBotInt() { delete m_var; delete m_expr; } CBotInstr* CBotInstr::CompileArray(CBotToken* &p, CBotCStack* pStack, CBotTypResult type, bool first) { if (IsOfType(p, ID_OPBRK)) { if (!IsOfType(p, ID_CLBRK)) { pStack->SetError(TX_CLBRK, p->GetStart()); return NULL; } CBotInstr* inst = CompileArray(p, pStack, CBotTypResult(CBotTypArrayPointer, type), false); if (inst != NULL || !pStack->IsOk()) return inst; } // compiles an array declaration if (first) return NULL ; CBotInstr* inst = CBotInstArray::Compile(p, pStack, type); if (inst == NULL) return NULL; if (IsOfType(p, ID_COMMA)) // several definitions { if (NULL != ( inst->m_next2b = CBotInstArray::CompileArray(p, pStack, type, false))) // compiles next one { return inst; } delete inst; return NULL; } if (IsOfType(p, ID_SEP)) // end of instruction { return inst; } delete inst; pStack->SetError(TX_ENDOF, p->GetStart()); return NULL; } CBotInstr* CBotInt::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, bool noskip) { CBotToken* pp = cont ? NULL : p; // no repetition of the token "int" if (!cont && !IsOfType(p, ID_INT)) return NULL; CBotInt* inst = static_cast(CompileArray(p, pStack, CBotTypInt)); if (inst != NULL || !pStack->IsOk()) return inst; CBotCStack* pStk = pStack->TokenStack(pp); inst = new CBotInt(); inst->m_expr = NULL; CBotToken* vartoken = p; inst->SetToken(vartoken); // determines the expression is valid for the item on the left side if (NULL != (inst->m_var = CBotLeftExprVar::Compile( p, pStk ))) { (static_cast(inst->m_var))->m_typevar = CBotTypInt; if (pStk->CheckVarLocal(vartoken)) // redefinition of the variable { pStk->SetError(TX_REDEFVAR, vartoken); goto error; } if (IsOfType(p, ID_OPBRK)) { delete inst; // type is not CBotInt p = vartoken; // returns the variable name // compiles an array declaration CBotInstr* inst2 = CBotInstArray::Compile(p, pStk, CBotTypInt); if (!pStk->IsOk() ) { pStk->SetError(TX_CLBRK, p->GetStart()); goto error; } if (IsOfType(p, ID_COMMA)) // several definition chained { if (NULL != ( inst2->m_next2b = CBotInt::Compile(p, pStk, true, noskip))) // compile the next one { return pStack->Return(inst2, pStk); } } inst = static_cast(inst2); goto suite; // no assignment, variable already created } if (IsOfType(p, ID_ASS)) // with an assignment? { if (NULL == ( inst->m_expr = CBotTwoOpExpr::Compile( p, pStk ))) { goto error; } if (pStk->GetType() >= CBotTypBoolean) // compatible type ? { pStk->SetError(TX_BADTYPE, p->GetStart()); goto error; } } { CBotVar* var = CBotVar::Create(vartoken, CBotTypInt);// create the variable (evaluated after the assignment) var->SetInit(inst->m_expr != NULL); // if initialized with assignment var->SetUniqNum( //set it with a unique number (static_cast(inst->m_var))->m_nIdent = CBotVar::NextUniqNum()); pStack->AddVar(var); // place it on the stack } if (IsOfType(p, ID_COMMA)) // chained several definitions { if (NULL != ( inst->m_next2b = CBotInt::Compile(p, pStk, true, noskip))) // compile next one { return pStack->Return(inst, pStk); } } suite: if (noskip || IsOfType(p, ID_SEP)) // instruction is completed { return pStack->Return(inst, pStk); } pStk->SetError(TX_ENDOF, p->GetStart()); } error: delete inst; return pStack->Return(NULL, pStk); } // execute the definition of the integer variable bool CBotInt::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this); // essential for SetState() if ( pile->GetState()==0) { if (m_expr && !m_expr->Execute(pile)) return false; // initial value // interrupted? m_var->Execute(pile); // creates and assign the result if (!pile->SetState(1)) return false; } if (pile->IfStep()) return false; if ( m_next2b && !m_next2b->Execute(pile)) return false; // other(s) definition(s) return pj->Return(pile); // forward below } void CBotInt::RestoreState(CBotStack* &pj, bool bMain) { CBotStack* pile = pj; if (bMain) { pile = pj->RestoreStack(this); if (pile == NULL) return; if ( pile->GetState()==0) { if (m_expr) m_expr->RestoreState(pile, bMain); // initial value // interrupted? return; } } m_var->RestoreState(pile, bMain); if (m_next2b) m_next2b->RestoreState(pile, bMain); // other(s) definition(s) } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // defining a boolean variable // int a, b = false; CBotBoolean::CBotBoolean() { m_var = m_expr = NULL; name = "CBotBoolean"; } CBotBoolean::~CBotBoolean() { delete m_var; delete m_expr; } CBotInstr* CBotBoolean::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, bool noskip) { CBotToken* pp = cont ? NULL : p; if (!cont && !IsOfType(p, ID_BOOLEAN, ID_BOOL)) return NULL; CBotBoolean* inst = static_cast(CompileArray(p, pStack, CBotTypBoolean)); if (inst != NULL || !pStack->IsOk()) return inst; CBotCStack* pStk = pStack->TokenStack(pp); inst = new CBotBoolean(); inst->m_expr = NULL; CBotToken* vartoken = p; inst->SetToken(vartoken); CBotVar* var = NULL; if (NULL != (inst->m_var = CBotLeftExprVar::Compile( p, pStk ))) { (static_cast(inst->m_var))->m_typevar = CBotTypBoolean; if (pStk->CheckVarLocal(vartoken)) // redefinition of the variable { pStk->SetError(TX_REDEFVAR, vartoken); goto error; } if (IsOfType(p, ID_OPBRK)) { delete inst; // type is not CBotInt p = vartoken; // resutns to the variable name // compiles an array declaration inst = static_cast(CBotInstArray::Compile(p, pStk, CBotTypBoolean)); if (!pStk->IsOk() ) { pStk->SetError(TX_CLBRK, p->GetStart()); goto error; } goto suite; // no assignment, variable already created } if (IsOfType(p, ID_ASS)) { if (NULL == ( inst->m_expr = CBotTwoOpExpr::Compile( p, pStk ))) { goto error; } if (!pStk->GetTypResult().Eq(CBotTypBoolean)) { pStk->SetError(TX_BADTYPE, p->GetStart()); goto error; } } var = CBotVar::Create(vartoken, CBotTypBoolean);// create the variable (evaluated after the assignment) var->SetInit(inst->m_expr != NULL); var->SetUniqNum( (static_cast(inst->m_var))->m_nIdent = CBotVar::NextUniqNum()); pStack->AddVar(var); suite: if (IsOfType(p, ID_COMMA)) { if (NULL != ( inst->m_next2b = CBotBoolean::Compile(p, pStk, true, noskip))) { return pStack->Return(inst, pStk); } } if (noskip || IsOfType(p, ID_SEP)) { return pStack->Return(inst, pStk); } pStk->SetError(TX_ENDOF, p->GetStart()); } error: delete inst; return pStack->Return(NULL, pStk); } // executes a boolean variable definition bool CBotBoolean::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this);//essential for SetState() if ( pile->GetState()==0) { if (m_expr && !m_expr->Execute(pile)) return false; m_var->Execute(pile); if (!pile->SetState(1)) return false; } if (pile->IfStep()) return false; if ( m_next2b && !m_next2b->Execute(pile)) return false; return pj->Return(pile); } void CBotBoolean::RestoreState(CBotStack* &pj, bool bMain) { CBotStack* pile = pj; if (bMain) { pile = pj->RestoreStack(this); if (pile == NULL) return; if ( pile->GetState()==0) { if (m_expr) m_expr->RestoreState(pile, bMain); // initial value interrupted? return; } } m_var->RestoreState(pile, bMain); if (m_next2b) m_next2b->RestoreState(pile, bMain); // other(s) definition(s) } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // definition of a real/float variable // int a, b = 12.4; CBotFloat::CBotFloat() { m_var = m_expr = NULL; name = "CBotFloat"; } CBotFloat::~CBotFloat() { delete m_var; delete m_expr; } CBotInstr* CBotFloat::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, bool noskip) { CBotToken* pp = cont ? NULL : p; if (!cont && !IsOfType(p, ID_FLOAT)) return NULL; CBotFloat* inst = static_cast(CompileArray(p, pStack, CBotTypFloat)); if (inst != NULL || !pStack->IsOk()) return inst; CBotCStack* pStk = pStack->TokenStack(pp); inst = new CBotFloat(); inst->m_expr = NULL; CBotToken* vartoken = p; CBotVar* var = NULL; inst->SetToken(vartoken); if (NULL != (inst->m_var = CBotLeftExprVar::Compile( p, pStk ))) { (static_cast(inst->m_var))->m_typevar = CBotTypFloat; if (pStk->CheckVarLocal(vartoken)) // redefinition of a variable { pStk->SetStartError(vartoken->GetStart()); pStk->SetError(TX_REDEFVAR, vartoken->GetEnd()); goto error; } if (IsOfType(p, ID_OPBRK)) { delete inst; p = vartoken; inst = static_cast(CBotInstArray::Compile(p, pStk, CBotTypFloat)); if (!pStk->IsOk() ) { pStk->SetError(TX_CLBRK, p->GetStart()); goto error; } goto suite; // no assignment, variable already created } if (IsOfType(p, ID_ASS)) { if (NULL == ( inst->m_expr = CBotTwoOpExpr::Compile( p, pStk ))) { goto error; } if (pStk->GetType() >= CBotTypBoolean) { pStk->SetError(TX_BADTYPE, p->GetStart()); goto error; } } var = CBotVar::Create(vartoken, CBotTypFloat); var->SetInit(inst->m_expr != NULL); var->SetUniqNum( (static_cast(inst->m_var))->m_nIdent = CBotVar::NextUniqNum()); pStack->AddVar(var); suite: if (IsOfType(p, ID_COMMA)) { if (NULL != ( inst->m_next2b = CBotFloat::Compile(p, pStk, true, noskip))) { return pStack->Return(inst, pStk); } } if (noskip || IsOfType(p, ID_SEP)) { return pStack->Return(inst, pStk); } pStk->SetError(TX_ENDOF, p->GetStart()); } error: delete inst; return pStack->Return(NULL, pStk); } // executes the definition of a real variable bool CBotFloat::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this); if ( pile->GetState()==0) { if (m_expr && !m_expr->Execute(pile)) return false; m_var->Execute(pile); if (!pile->SetState(1)) return false; } if (pile->IfStep()) return false; if ( m_next2b && !m_next2b->Execute(pile)) return false; return pj->Return(pile); } void CBotFloat::RestoreState(CBotStack* &pj, bool bMain) { CBotStack* pile = pj; if (bMain) { pile = pj->RestoreStack(this); if (pile == NULL) return; if ( pile->GetState()==0) { if (m_expr) m_expr->RestoreState(pile, bMain); return; } } m_var->RestoreState(pile, bMain); if (m_next2b) m_next2b->RestoreState(pile, bMain); } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // define a string variable // int a, b = "salut"; CBotIString::CBotIString() { m_var = m_expr = NULL; name = "CBotIString"; } CBotIString::~CBotIString() { delete m_var; delete m_expr; } CBotInstr* CBotIString::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, bool noskip) { CBotToken* pp = cont ? NULL : p; if (!cont && !IsOfType(p, ID_STRING)) return NULL; CBotIString* inst = static_cast(CompileArray(p, pStack, CBotTypString)); if (inst != NULL || !pStack->IsOk()) return inst; CBotCStack* pStk = pStack->TokenStack(pp); inst = new CBotIString(); inst->m_expr = NULL; CBotToken* vartoken = p; inst->SetToken(vartoken); if (NULL != (inst->m_var = CBotLeftExprVar::Compile( p, pStk ))) { (static_cast(inst->m_var))->m_typevar = CBotTypString; if (pStk->CheckVarLocal(vartoken)) { pStk->SetStartError(vartoken->GetStart()); pStk->SetError(TX_REDEFVAR, vartoken->GetEnd()); goto error; } if (IsOfType(p, ID_ASS)) { if (NULL == ( inst->m_expr = CBotTwoOpExpr::Compile( p, pStk ))) { goto error; } /* if (!pStk->GetTypResult().Eq(CBotTypString)) // type compatible ? { pStk->SetError(TX_BADTYPE, p->GetStart()); goto error; }*/ } CBotVar* var = CBotVar::Create(vartoken, CBotTypString); var->SetInit(inst->m_expr != NULL); var->SetUniqNum( (static_cast(inst->m_var))->m_nIdent = CBotVar::NextUniqNum()); pStack->AddVar(var); if (IsOfType(p, ID_COMMA)) { if (NULL != ( inst->m_next2b = CBotIString::Compile(p, pStk, true, noskip))) { return pStack->Return(inst, pStk); } } if (noskip || IsOfType(p, ID_SEP)) { return pStack->Return(inst, pStk); } pStk->SetError(TX_ENDOF, p->GetStart()); } error: delete inst; return pStack->Return(NULL, pStk); } // executes the definition of the string variable bool CBotIString::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this); if ( pile->GetState()==0) { if (m_expr && !m_expr->Execute(pile)) return false; m_var->Execute(pile); if (!pile->SetState(1)) return false; } if (pile->IfStep()) return false; if ( m_next2b && !m_next2b->Execute(pile)) return false; return pj->Return(pile); } void CBotIString::RestoreState(CBotStack* &pj, bool bMain) { CBotStack* pile = pj; if (bMain) { pile = pj->RestoreStack(this); if (pile == NULL) return; if ( pile->GetState()==0) { if (m_expr) m_expr->RestoreState(pile, bMain); return; } } m_var->RestoreState(pile, bMain); if (m_next2b) m_next2b->RestoreState(pile, bMain); } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // compiles a statement such as " x = 123 " ou " z * 5 + 4 " // with or without assignment CBotExpression::CBotExpression() { m_leftop = NULL; m_rightop = NULL; name = "CBotExpression"; } 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 == NULL) { pStack->SetError(TX_BADLEFT, p->GetEnd()); delete inst; return NULL; } inst->m_rightop = CBotExpression::Compile(p, pStack); if (inst->m_rightop == NULL) { delete inst; return NULL; } CBotTypResult type1 = pStack->GetTypResult(); // get the variable assigned to mark CBotVar* var = NULL; inst->m_leftop->ExecuteVar(var, pStack); if (var == NULL) { delete inst; return NULL; } if (OpType != ID_ASS && var->GetInit() != IS_DEF) { pStack->SetError(TX_NOTINIT, pp); delete inst; return NULL; } 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(2); } else var->SetInit(true); break; case ID_ASSADD: if (type2.Eq(CBotTypBoolean) || type2.Eq(CBotTypPointer) ) type2 = -1; // numbers and strings break; case ID_ASSSUB: case ID_ASSMUL: case ID_ASSDIV: case ID_ASSMODULO: if (type2.GetType() >= CBotTypBoolean) type2 = -1; // numbers only break; } if (!TypeCompatible(type1, type2, OpType)) { pStack->SetError(TX_BADTYPE, &inst->m_token); delete inst; return NULL; } return inst; // compatible type? } delete inst; int start, end, error = pStack->GetError(start, end); p = pp; // returns to the top pStack->SetError(0,0); // forget the error CBotInstr* i = CBotTwoOpExpr::Compile(p, pStack); // tries without assignment if (i != NULL && error == TX_PRIVATE && p->GetType() == ID_ASS) pStack->ResetError(error, start, end); return i; } // executes an expression with assignment bool CBotExpression::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this); // CBotToken* pToken = m_leftop->GetToken(); CBotVar* pVar = NULL; CBotStack* pile1 = pile; bool IsInit = true; CBotVar* result = NULL; // must be done before any indexes (stack can be changed) if (!m_leftop->ExecuteVar(pVar, pile, NULL, 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? pile2->IncState(); } if (pile1->GetState() == 1) { if (m_token.GetType() != ID_ASS) { pVar = pile1->GetVar(); // recovers if interrupted IsInit = pVar->GetInit(); if (IsInit == IS_NAN) { pile2->SetError(TX_OPNAN, m_leftop->GetToken()); return pj->Return(pile2); } result = CBotVar::Create("", pVar->GetTypResult(2)); } 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 (IsInit && result->Div(pile1->GetVar(), pile2->GetVar())) pile2->SetError(TX_DIVZERO, &m_token); pile2->SetVar(result); break; case ID_ASSMODULO: if (IsInit && result->Modulo(pile1->GetVar(), pile2->GetVar())) pile2->SetError(TX_DIVZERO, &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 (!IsInit) pile2->SetError(TX_NOTINIT, 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 = NULL; CBotStack* pile = pj->RestoreStack(this); if (pile == NULL) 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 == NULL) return; if ( pile2->GetState()==0) { if (m_rightop) m_rightop->RestoreState(pile2, bMain); return; } } } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // compile a statement such as "(condition)" // the condition must be Boolean // this class has no constructor, because there is never an instance of this class // the object returned by Compile is usually type CBotExpression CBotInstr* CBotCondition::Compile(CBotToken* &p, CBotCStack* pStack) { pStack->SetStartError(p->GetStart()); if (IsOfType(p, ID_OPENPAR)) { CBotInstr* inst = CBotBoolExpr::Compile(p, pStack); if (NULL != inst) { if (IsOfType(p, ID_CLOSEPAR)) { return inst; } pStack->SetError(TX_CLOSEPAR, p->GetStart()); // missing parenthesis } delete inst; } pStack->SetError(TX_OPENPAR, p->GetStart()); // missing parenthesis return NULL; } ////////////////////////////////////////////////////////////////////////////////////// // compile a statement such as "(condition)" // the condition must be Boolean // // this class has no constructor, because there is never an instance of this // class // the object returned by Compile is usually type CBotExpression // CBotInstr* CBotBoolExpr::Compile(CBotToken* &p, CBotCStack* pStack) { pStack->SetStartError(p->GetStart()); CBotInstr* inst = CBotTwoOpExpr::Compile(p, pStack); if (NULL != inst) { if (pStack->GetTypResult().Eq(CBotTypBoolean)) { return inst; } pStack->SetError(TX_NOTBOOL, p->GetStart()); // is not a boolean } delete inst; return NULL; } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // compile either: // instruction in parentheses (...) // a unary expression (negative, not) // variable name // variables pre and post-incremented or decremented // a given number DefineNum // a constant // procedure call // new statement // // this class has no constructor, because there is never an instance of this class // the object returned by Compile is the class corresponding to the instruction CBotInstr* CBotParExpr::Compile(CBotToken* &p, CBotCStack* pStack) { CBotCStack* pStk = pStack->TokenStack(); pStk->SetStartError(p->GetStart()); // is it an expression in parentheses? if (IsOfType(p, ID_OPENPAR)) { CBotInstr* inst = CBotExpression::Compile(p, pStk); if (NULL != inst) { if (IsOfType(p, ID_CLOSEPAR)) { return pStack->Return(inst, pStk); } pStk->SetError(TX_CLOSEPAR, p->GetStart()); } delete inst; return pStack->Return(NULL, pStk); } // is this a unary operation? CBotInstr* inst = CBotExprUnaire::Compile(p, pStk); if (inst != NULL || !pStk->IsOk()) return pStack->Return(inst, pStk); // is it a variable name? if (p->GetType() == TokenTypVar) { // this may be a method call without the "this." before inst = CBotExprVar::CompileMethode(p, pStk); if (inst != NULL) return pStack->Return(inst, pStk); // is it a procedure call? inst = CBotInstrCall::Compile(p, pStk); if (inst != NULL || !pStk->IsOk()) return pStack->Return(inst, pStk); CBotToken* pvar = p; // no, it an "ordinaty" variable inst = CBotExprVar::Compile(p, pStk); CBotToken* pp = p; // post incremented or decremented? if (IsOfType(p, ID_INC, ID_DEC)) { if (pStk->GetType() >= CBotTypBoolean) { pStk->SetError(TX_BADTYPE, pp); delete inst; return pStack->Return(NULL, pStk); } // recompile the variable for read-only delete inst; p = pvar; inst = CBotExprVar::Compile(p, pStk, PR_READ); p = p->GetNext(); CBotPostIncExpr* i = new CBotPostIncExpr(); i->SetToken(pp); i->m_Instr = inst; // associated statement return pStack->Return(i, pStk); } return pStack->Return(inst, pStk); } // pre increpemted or pre decremented? CBotToken* pp = p; if (IsOfType(p, ID_INC, ID_DEC)) { CBotPreIncExpr* i = new CBotPreIncExpr(); i->SetToken(pp); if (p->GetType() == TokenTypVar) { if (NULL != (i->m_Instr = CBotExprVar::Compile(p, pStk, PR_READ))) { if (pStk->GetType() >= CBotTypBoolean) { pStk->SetError(TX_BADTYPE, pp); delete inst; return pStack->Return(NULL, pStk); } return pStack->Return(i, pStk); } delete i; return pStack->Return(NULL, pStk); } } // is it a number or DefineNum? if (p->GetType() == TokenTypNum || p->GetType() == TokenTypDef ) { CBotInstr* inst = CBotExprNum::Compile(p, pStk); return pStack->Return(inst, pStk); } // is this a chaine? if (p->GetType() == TokenTypString) { CBotInstr* inst = CBotExprAlpha::Compile(p, pStk); return pStack->Return(inst, pStk); } // is a "true" or "false" if (p->GetType() == ID_TRUE || p->GetType() == ID_FALSE ) { CBotInstr* inst = CBotExprBool::Compile(p, pStk); return pStack->Return(inst, pStk); } // is an object to be created with new if (p->GetType() == ID_NEW) { CBotInstr* inst = CBotNew::Compile(p, pStk); return pStack->Return(inst, pStk); } // is a null pointer if (IsOfType(p, ID_NULL)) { CBotInstr* inst = new CBotExprNull (); inst->SetToken(pp); CBotVar* var = CBotVar::Create("", CBotTypNullPointer); pStk->SetVar(var); return pStack->Return(inst, pStk); } // is a number nan if (IsOfType(p, ID_NAN)) { CBotInstr* inst = new CBotExprNan (); inst->SetToken(pp); CBotVar* var = CBotVar::Create("", CBotTypInt); var->SetInit(IS_NAN); pStk->SetVar(var); return pStack->Return(inst, pStk); } return pStack->Return(NULL, pStk); } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // Management of pre-and post increment / decrement // There is no routine Compiles, the object is created directly // Compiles in CBotParExpr :: CBotPostIncExpr::CBotPostIncExpr() { m_Instr = NULL; name = "CBotPostIncExpr"; } CBotPostIncExpr::~CBotPostIncExpr() { delete m_Instr; } CBotPreIncExpr::CBotPreIncExpr() { m_Instr = NULL; name = "CBotPreIncExpr"; } CBotPreIncExpr::~CBotPreIncExpr() { delete m_Instr; } bool CBotPostIncExpr::Execute(CBotStack* &pj) { CBotStack* pile1 = pj->AddStack(this); CBotStack* pile2 = pile1; CBotVar* var1 = NULL; // retrieves the variable fields and indexes according if (!(static_cast(m_Instr))->ExecuteVar(var1, pile2, NULL, true)) return false; pile1->SetState(1); pile1->SetCopyVar(var1); // places the result (before incrementation); CBotStack* pile3 = pile2->AddStack(this); if (pile3->IfStep()) return false; if (var1->GetInit() == IS_NAN) { pile1->SetError(TX_OPNAN, &m_token); } if (var1->GetInit() != IS_DEF) { pile1->SetError(TX_NOTINIT, &m_token); } if (GetTokenType() == ID_INC) var1->Inc(); else var1->Dec(); return pj->Return(pile1); // operation done, result on pile2 } void CBotPostIncExpr::RestoreState(CBotStack* &pj, bool bMain) { if (!bMain) return; CBotStack* pile1 = pj->RestoreStack(this); if (pile1 == NULL) return; (static_cast(m_Instr))->RestoreStateVar(pile1, bMain); if (pile1 != NULL) pile1->RestoreStack(this); } bool CBotPreIncExpr::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this); if (pile->IfStep()) return false; CBotVar* var1; if (pile->GetState() == 0) { CBotStack* pile2 = pile; // retrieves the variable fields and indexes according // pile2 is modified on return if (!(static_cast(m_Instr))->ExecuteVar(var1, pile2, NULL, true)) return false; if (var1->GetInit() == IS_NAN) { pile->SetError(TX_OPNAN, &m_token); return pj->Return(pile); // operation performed } if (var1->GetInit() != IS_DEF) { pile->SetError(TX_NOTINIT, &m_token); return pj->Return(pile); // operation performed } if (GetTokenType() == ID_INC) var1->Inc(); else var1->Dec(); // ((CBotVarInt*)var1)->m_val pile->IncState(); } if (!m_Instr->Execute(pile)) return false; return pj->Return(pile); // operation performed } void CBotPreIncExpr::RestoreState(CBotStack* &pj, bool bMain) { if (!bMain) return; CBotStack* pile = pj->RestoreStack(this); if (pile == NULL) return; if (pile->GetState() == 0) { return; } m_Instr->RestoreState(pile, bMain); } ////////////////////////////////////////////////////////////////////////////////////// // compile an unary expression // + // - // not // ! // ~ CBotExprUnaire::CBotExprUnaire() { m_Expr = NULL; name = "CBotExprUnaire"; } CBotExprUnaire::~CBotExprUnaire() { delete m_Expr; } CBotInstr* CBotExprUnaire::Compile(CBotToken* &p, CBotCStack* pStack) { int op = p->GetType(); CBotToken* pp = p; if (!IsOfTypeList( p, ID_ADD, ID_SUB, ID_LOG_NOT, ID_TXT_NOT, ID_NOT, 0 )) return NULL; CBotCStack* pStk = pStack->TokenStack(pp); CBotExprUnaire* inst = new CBotExprUnaire(); inst->SetToken(pp); if (NULL != (inst->m_Expr = CBotParExpr::Compile( p, pStk ))) { if (op == ID_ADD && pStk->GetType() < CBotTypBoolean) // only with the number return pStack->Return(inst, pStk); if (op == ID_SUB && pStk->GetType() < CBotTypBoolean) // only with the numer return pStack->Return(inst, pStk); if (op == ID_NOT && pStk->GetType() < CBotTypFloat) // only with an integer return pStack->Return(inst, pStk); if (op == ID_LOG_NOT && pStk->GetTypResult().Eq(CBotTypBoolean))// only with boolean return pStack->Return(inst, pStk); if (op == ID_TXT_NOT && pStk->GetTypResult().Eq(CBotTypBoolean))// only with boolean return pStack->Return(inst, pStk); pStk->SetError(TX_BADTYPE, &inst->m_token); } delete inst; return pStack->Return(NULL, pStk); } // executes unary expression bool CBotExprUnaire::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this); if (pile->GetState() == 0) { if (!m_Expr->Execute(pile)) return false; // interrupted ? pile->IncState(); } CBotStack* pile2 = pile->AddStack(); if (pile2->IfStep()) return false; CBotVar* var = pile->GetVar(); // get the result on the stack switch (GetTokenType()) { case ID_ADD: break; case ID_SUB: var->Neg(); // change the sign break; case ID_NOT: case ID_LOG_NOT: case ID_TXT_NOT: var->Not(); break; } return pj->Return(pile); // forwards below } void CBotExprUnaire::RestoreState(CBotStack* &pj, bool bMain) { if (!bMain) return; CBotStack* pile = pj->RestoreStack(this); if ( pile == NULL) return; if (pile->GetState() == 0) { m_Expr->RestoreState(pile, bMain); // interrupted here! return; } } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // index management for arrays // array [ expression ] CBotIndexExpr::CBotIndexExpr() { m_expr = NULL; name = "CBotIndexExpr"; } CBotIndexExpr::~CBotIndexExpr() { delete m_expr; } // finds a field from the instance at compile time bool CBotIndexExpr::ExecuteVar(CBotVar* &pVar, CBotCStack* &pile) { if (pVar->GetType(1) != CBotTypArrayPointer) assert(0); pVar = (static_cast(pVar))->GetItem(0, false); // at compile time makes the element [0] if (pVar == NULL) { pile->SetError(TX_OUTARRAY, m_token.GetEnd()); return false; } if (m_next3 != NULL) return m_next3->ExecuteVar(pVar, pile); return true; } // attention, changes the pointer to the stack intentionally // place the index calculated on the additional stack bool CBotIndexExpr::ExecuteVar(CBotVar* &pVar, CBotStack* &pile, CBotToken* prevToken, bool bStep, bool bExtend) { CBotStack* pj = pile; if (pVar->GetType(1) != CBotTypArrayPointer) assert(0); pile = pile->AddStack(); if (pile->GetState() == 0) { if (!m_expr->Execute(pile)) return false; pile->IncState(); } // handles array CBotVar* p = pile->GetVar(); // result on the stack if (p == NULL || p->GetType() > CBotTypDouble) { pile->SetError(TX_BADINDEX, prevToken); return pj->Return(pile); } int n = p->GetValInt(); // position in the table pVar = (static_cast(pVar))->GetItem(n, bExtend); if (pVar == NULL) { pile->SetError(TX_OUTARRAY, prevToken); return pj->Return(pile); } pVar->Maj(pile->GetPUser(), true); if ( m_next3 != NULL && !m_next3->ExecuteVar(pVar, pile, prevToken, bStep, bExtend) ) return false; // does not release the stack // to avoid recalculation of the index twice where appropriate return true; } void CBotIndexExpr::RestoreStateVar(CBotStack* &pile, bool bMain) { pile = pile->RestoreStack(); if (pile == NULL) return; if (bMain && pile->GetState() == 0) { m_expr->RestoreState(pile, true); return; } if (m_next3) m_next3->RestoreStateVar(pile, bMain); } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // field management in an instance (dot operator) // toto.x CBotFieldExpr::CBotFieldExpr() { name = "CBotFieldExpr"; m_nIdent = 0; } CBotFieldExpr::~CBotFieldExpr() { } void CBotFieldExpr::SetUniqNum(int num) { m_nIdent = num; } // find a field from the instance at compile bool CBotFieldExpr::ExecuteVar(CBotVar* &pVar, CBotCStack* &pile) { if (pVar->GetType(1) != CBotTypPointer) assert(0); pVar = pVar->GetItemRef(m_nIdent); if (pVar == NULL) { pile->SetError(TX_NOITEM, &m_token); return false; } if ( m_next3 != NULL && !m_next3->ExecuteVar(pVar, pile) ) return false; return true; } bool CBotFieldExpr::ExecuteVar(CBotVar* &pVar, CBotStack* &pile, CBotToken* prevToken, bool bStep, bool bExtend) { CBotStack* pj = pile; pile = pile->AddStack(this); // changes in output stack if (pile == EOX) return true; if (pVar->GetType(1) != CBotTypPointer) assert(0); CBotVarClass* pItem = pVar->GetPointer(); if (pItem == NULL) { pile->SetError(TX_NULLPT, prevToken); return pj->Return(pile); } if (pItem->GetUserPtr() == OBJECTDELETED) { pile->SetError(TX_DELETEDPT, prevToken); return pj->Return(pile); } if (bStep && pile->IfStep()) return false; pVar = pVar->GetItemRef(m_nIdent); if (pVar == NULL) { pile->SetError(TX_NOITEM, &m_token); return pj->Return(pile); } if (pVar->IsStatic()) { // for a static variable, takes it in the class itself CBotClass* pClass = pItem->GetClass(); pVar = pClass->GetItem(m_token.GetString()); } // request the update of the element, if applicable pVar->Maj(pile->GetPUser(), true); if ( m_next3 != NULL && !m_next3->ExecuteVar(pVar, pile, &m_token, bStep, bExtend) ) return false; // does not release the stack // to maintain the state SetState () corresponding to step return true; } void CBotFieldExpr::RestoreStateVar(CBotStack* &pj, bool bMain) { pj = pj->RestoreStack(this); if (pj == NULL) return; if (m_next3 != NULL) m_next3->RestoreStateVar(pj, bMain); } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // compile a left operand for an assignment CBotLeftExpr::CBotLeftExpr() { name = "CBotLeftExpr"; m_nIdent = 0; } CBotLeftExpr::~CBotLeftExpr() { } // compiles an expression for a left-operand (left of an assignment) // this can be // toto // toto[ 3 ] // toto.x // toto.pos.x // toto[2].pos.x // toto[1].pos[2].x // toto[1][2][3] CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack) { CBotCStack* pStk = pStack->TokenStack(); pStk->SetStartError(p->GetStart()); // is it a variable name? if (p->GetType() == TokenTypVar) { CBotLeftExpr* inst = new CBotLeftExpr(); // creates the object inst->SetToken(p); CBotVar* var; if (NULL != (var = pStk->FindVar(p))) // seek if known variable { inst->m_nIdent = var->GetUniqNum(); if (inst->m_nIdent > 0 && inst->m_nIdent < 9000) { if ( var->IsPrivate(PR_READ) && !pStk->GetBotCall()->m_bCompileClass) { pStk->SetError(TX_PRIVATE, p); goto err; } // this is an element of the current class // adds the equivalent of this. before CBotToken pthis("this"); inst->SetToken(&pthis); inst->m_nIdent = -2; // indent for this CBotFieldExpr* i = new CBotFieldExpr(); // new element i->SetToken(p); // keeps the name of the token inst->AddNext3(i); // add after var = pStk->FindVar(pthis); var = var->GetItem(p->GetString()); i->SetUniqNum(var->GetUniqNum()); } p = p->GetNext(); // next token while (true) { if (var->GetType() == CBotTypArrayPointer) { if (IsOfType( p, ID_OPBRK )) { CBotIndexExpr* i = new CBotIndexExpr(); i->m_expr = CBotExpression::Compile(p, pStk); inst->AddNext3(i); // add to the chain var = (static_cast(var))->GetItem(0,true); // gets the component [0] if (i->m_expr == NULL) { pStk->SetError(TX_BADINDEX, p->GetStart()); goto err; } if (!pStk->IsOk() || !IsOfType( p, ID_CLBRK )) { pStk->SetError(TX_CLBRK, p->GetStart()); goto err; } continue; } } if (var->GetType(1) == CBotTypPointer) // for classes { if (IsOfType(p, ID_DOT)) { CBotToken* pp = p; CBotFieldExpr* i = new CBotFieldExpr(); // new element i->SetToken(pp); // keeps the name of the token inst->AddNext3(i); // adds after if (p->GetType() == TokenTypVar) // must be a name { var = var->GetItem(p->GetString()); // get item correspondent if (var != NULL) { if ( var->IsPrivate(PR_READ) && !pStk->GetBotCall()->m_bCompileClass) { pStk->SetError(TX_PRIVATE, pp); goto err; } i->SetUniqNum(var->GetUniqNum()); p = p->GetNext(); // skips the name continue; } pStk->SetError(TX_NOITEM, p); } pStk->SetError(TX_DOT, p->GetStart()); goto err; } } break; } if (pStk->IsOk()) return static_cast (pStack->Return(inst, pStk)); } pStk->SetError(TX_UNDEFVAR, p); err: delete inst; return static_cast ( pStack->Return(NULL, pStk)); } return static_cast ( pStack->Return(NULL, pStk)); } // runs, is a variable and assigns the result to the stack bool CBotLeftExpr::Execute(CBotStack* &pj, CBotStack* array) { CBotStack* pile = pj->AddStack(); CBotVar* var1 = NULL; CBotVar* var2 = NULL; // fetch a variable (not copy) if (!ExecuteVar(var1, array, NULL, false)) return false; if (pile->IfStep()) return false; if (var1) { var2 = pj->GetVar(); // result on the input stack if (var2) { CBotTypResult t1 = var1->GetTypResult(); CBotTypResult t2 = var2->GetTypResult(); if (t2.Eq(CBotTypPointer)) { CBotClass* c1 = t1.GetClass(); CBotClass* c2 = t2.GetClass(); if ( !c2->IsChildOf(c1)) { CBotToken* pt = &m_token; pile->SetError(TX_BADTYPE, pt); return pj->Return(pile); // operation performed } } var1->SetVal(var2); // do assignment } pile->SetCopyVar(var1); // replace the stack with the copy of the variable // (for name) } return pj->Return(pile); // operation performed } // fetch a variable during compilation bool CBotLeftExpr::ExecuteVar(CBotVar* &pVar, CBotCStack* &pile) { pVar = pile->FindVar(m_token); if (pVar == NULL) return false; if ( m_next3 != NULL && !m_next3->ExecuteVar(pVar, pile) ) return false; return true; } // fetch the variable at runtume bool CBotLeftExpr::ExecuteVar(CBotVar* &pVar, CBotStack* &pile, CBotToken* prevToken, bool bStep) { pile = pile->AddStack(this); pVar = pile->FindVar(m_nIdent); if (pVar == NULL) { #ifdef _DEBUG assert(0); #endif pile->SetError(2, &m_token); return false; } if (bStep && m_next3 == NULL && pile->IfStep()) return false; if ( m_next3 != NULL && !m_next3->ExecuteVar(pVar, pile, &m_token, bStep, true) ) return false; return true; } void CBotLeftExpr::RestoreStateVar(CBotStack* &pile, bool bMain) { pile = pile->RestoreStack(this); if (pile == NULL) return; if (m_next3 != NULL) m_next3->RestoreStateVar(pile, bMain); } ////////////////////////////////////////////////////////////////////////////////////////// // converts a string into integer // may be of the form 0xabc123 long GetNumInt(const char* p) { long num = 0; while (*p >= '0' && *p <= '9') { num = num * 10 + *p - '0'; p++; } if (*p == 'x' || *p == 'X') { while (*++p != 0) { if (*p >= '0' && *p <= '9') { num = num * 16 + *p - '0'; continue; } if (*p >= 'A' && *p <= 'F') { num = num * 16 + *p - 'A' + 10; continue; } if (*p >= 'a' && *p <= 'f') { num = num * 16 + *p - 'a' + 10; continue; } break; } } return num; } // converts a string into a float number extern float GetNumFloat(const char* p) { double num = 0; double div = 10; bool bNeg = false; if (*p == '-') { bNeg = true; p++; } while (*p >= '0' && *p <= '9') { num = num * 10. + (*p - '0'); p++; } if (*p == '.') { p++; while (*p >= '0' && *p <= '9') { num = num + (*p - '0') / div; div = div * 10; p++; } } int exp = 0; if (*p == 'e' || *p == 'E') { char neg = 0; p++; if (*p == '-' || *p == '+') neg = *p++; while (*p >= '0' && *p <= '9') { exp = exp * 10 + (*p - '0'); p++; } if (neg == '-') exp = -exp; } while (exp > 0) { num *= 10.0; exp--; } while (exp < 0) { num /= 10.0; exp++; } if (bNeg) num = -num; return static_cast(num); } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // compiles a token representing a number CBotExprNum::CBotExprNum() { name = "CBotExprNum"; } CBotExprNum::~CBotExprNum() { } CBotInstr* CBotExprNum::Compile(CBotToken* &p, CBotCStack* pStack) { CBotCStack* pStk = pStack->TokenStack(); CBotExprNum* inst = new CBotExprNum(); inst->SetToken(p); CBotString s = p->GetString(); inst->m_numtype = CBotTypInt; if (p->GetType() == TokenTypDef) { inst->m_valint = p->GetIdKey(); } else { if (s.Find('.') >= 0 || ( s.Find('x') < 0 && ( s.Find('e') >= 0 || s.Find('E') >= 0 ) )) { inst->m_numtype = CBotTypFloat; inst->m_valfloat = GetNumFloat(s); } else { inst->m_valint = GetNumInt(s); } } if (pStk->NextToken(p)) { CBotVar* var = CBotVar::Create(static_cast(NULL), inst->m_numtype); pStk->SetVar(var); return pStack->Return(inst, pStk); } delete inst; return pStack->Return(NULL, pStk); } // execute, returns the corresponding number bool CBotExprNum::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this); if (pile->IfStep()) return false; CBotVar* var = CBotVar::Create(static_cast(NULL), m_numtype); CBotString nombre ; if (m_token.GetType() == TokenTypDef) { nombre = m_token.GetString(); } switch (m_numtype) { case CBotTypShort: case CBotTypInt: var->SetValInt(m_valint, nombre); break; case CBotTypFloat: var->SetValFloat(m_valfloat); break; } pile->SetVar(var); // place on the stack return pj->Return(pile); // it's ok } void CBotExprNum::RestoreState(CBotStack* &pj, bool bMain) { if (bMain) pj->RestoreStack(this); } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // compile a token representing a string CBotExprAlpha::CBotExprAlpha() { name = "CBotExprAlpha"; } CBotExprAlpha::~CBotExprAlpha() { } CBotInstr* CBotExprAlpha::Compile(CBotToken* &p, CBotCStack* pStack) { CBotCStack* pStk = pStack->TokenStack(); CBotExprAlpha* inst = new CBotExprAlpha(); inst->SetToken(p); p = p->GetNext(); CBotVar* var = CBotVar::Create(static_cast(NULL), CBotTypString); pStk->SetVar(var); return pStack->Return(inst, pStk); } // execute, returns the corresponding string bool CBotExprAlpha::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this); if (pile->IfStep()) return false; CBotVar* var = CBotVar::Create(static_cast(NULL), CBotTypString); CBotString chaine = m_token.GetString(); chaine = chaine.Mid(1, chaine.GetLength()-2); // removes the quotes var->SetValString(chaine); // value of the number pile->SetVar(var); // put on the stack return pj->Return(pile); } void CBotExprAlpha::RestoreState(CBotStack* &pj, bool bMain) { if (bMain) pj->RestoreStack(this); } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // compile a token representing true or false CBotExprBool::CBotExprBool() { name = "CBotExprBool"; } CBotExprBool::~CBotExprBool() { } CBotInstr* CBotExprBool::Compile(CBotToken* &p, CBotCStack* pStack) { CBotCStack* pStk = pStack->TokenStack(); CBotExprBool* inst = NULL; if ( p->GetType() == ID_TRUE || p->GetType() == ID_FALSE ) { inst = new CBotExprBool(); inst->SetToken(p); // stores the operation false or true p = p->GetNext(); CBotVar* var = CBotVar::Create(static_cast(NULL), CBotTypBoolean); pStk->SetVar(var); } return pStack->Return(inst, pStk); } // executes, returns true or false bool CBotExprBool::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this); if (pile->IfStep()) return false; CBotVar* var = CBotVar::Create(static_cast(NULL), CBotTypBoolean); if (GetTokenType() == ID_TRUE) var->SetValInt(1); else var->SetValInt(0); pile->SetVar(var); // put on the stack return pj->Return(pile); // forwards below } void CBotExprBool::RestoreState(CBotStack* &pj, bool bMain) { if (bMain) pj->RestoreStack(this); } ////////////////////////////////////////////////////////////////////////////////////////// // management of the operand "null" CBotExprNull::CBotExprNull() { name = "CBotExprNull"; } CBotExprNull::~CBotExprNull() { } // executes, returns an empty pointer bool CBotExprNull::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this); if (pile->IfStep()) return false; CBotVar* var = CBotVar::Create(static_cast(NULL), CBotTypNullPointer); var->SetInit(true); // null pointer valid pile->SetVar(var); // place on the stack return pj->Return(pile); // forwards below } void CBotExprNull::RestoreState(CBotStack* &pj, bool bMain) { if (bMain) pj->RestoreStack(this); } ////////////////////////////////////////////////////////////////////////////////////////// // management of the operand "nan" CBotExprNan::CBotExprNan() { name = "CBotExprNan"; } CBotExprNan::~CBotExprNan() { } // executes, returns null pointer bool CBotExprNan::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this); if (pile->IfStep()) return false; CBotVar* var = CBotVar::Create(static_cast(NULL), CBotTypInt); var->SetInit(IS_NAN); // nan pile->SetVar(var); // put on the stack return pj->Return(pile); // forward below } void CBotExprNan::RestoreState(CBotStack* &pj, bool bMain) { if (bMain) pj->RestoreStack(this); } ////////////////////////////////////////////////////////////////////////////////////// // compile a variable name // check that it is known on the stack // and it has been initialized CBotExprVar::CBotExprVar() { name = "CBotExprVar"; m_nIdent = 0; } CBotExprVar::~CBotExprVar() { } CBotInstr* CBotExprVar::Compile(CBotToken* &p, CBotCStack* pStack, int privat) { // CBotToken* pDebut = p; CBotCStack* pStk = pStack->TokenStack(); pStk->SetStartError(p->GetStart()); // is it a variable? if (p->GetType() == TokenTypVar) { CBotInstr* inst = new CBotExprVar(); // create the object inst->SetToken(p); CBotVar* var; if (NULL != (var = pStk->FindVar(p))) // seek if known variable { int ident = var->GetUniqNum(); (static_cast(inst))->m_nIdent = ident; // identifies variable by its number if (ident > 0 && ident < 9000) { if ( var->IsPrivate(privat) && !pStk->GetBotCall()->m_bCompileClass) { pStk->SetError(TX_PRIVATE, p); goto err; } // This is an element of the current class // ads the equivalent of this. before CBotToken token("this"); inst->SetToken(&token); (static_cast(inst))->m_nIdent = -2; // identificator for this CBotFieldExpr* i = new CBotFieldExpr(); // new element i->SetToken(p); // keeps the name of the token i->SetUniqNum(ident); inst->AddNext3(i); // added after } p = p->GetNext(); // next token while (true) { if (var->GetType() == CBotTypArrayPointer) { if (IsOfType( p, ID_OPBRK )) // check if there is an aindex { CBotIndexExpr* i = new CBotIndexExpr(); i->m_expr = CBotExpression::Compile(p, pStk); // compile the formula inst->AddNext3(i); // add to the chain var = (static_cast(var))->GetItem(0,true); // gets the component [0] if (i->m_expr == NULL) { pStk->SetError(TX_BADINDEX, p->GetStart()); goto err; } if (!pStk->IsOk() || !IsOfType( p, ID_CLBRK )) { pStk->SetError(TX_CLBRK, p->GetStart()); goto err; } continue; } } if (var->GetType(1) == CBotTypPointer) // for classes { if (IsOfType(p, ID_DOT)) { CBotToken* pp = p; if (p->GetType() == TokenTypVar) // must be a name { if (p->GetNext()->GetType() == ID_OPENPAR) // a method call? { CBotInstr* i = CBotInstrMethode::Compile(p, pStk, var); if (!pStk->IsOk()) goto err; inst->AddNext3(i); // added after return pStack->Return(inst, pStk); } else { CBotFieldExpr* i = new CBotFieldExpr(); // new element i->SetToken(pp); // keeps the name of the token inst->AddNext3(i); // add after var = var->GetItem(p->GetString()); // get item correspondent if (var != NULL) { i->SetUniqNum(var->GetUniqNum()); if ( var->IsPrivate() && !pStk->GetBotCall()->m_bCompileClass) { pStk->SetError(TX_PRIVATE, pp); goto err; } } } if (var != NULL) { p = p->GetNext(); // skips the name continue; } pStk->SetError(TX_NOITEM, p); goto err; } pStk->SetError(TX_DOT, p->GetStart()); goto err; } } break; } pStk->SetCopyVar(var); // place the copy of the variable on the stack (for type) if (pStk->IsOk()) return pStack->Return(inst, pStk); } pStk->SetError(TX_UNDEFVAR, p); err: delete inst; return pStack->Return(NULL, pStk); } return pStack->Return(NULL, pStk); } CBotInstr* CBotExprVar::CompileMethode(CBotToken* &p, CBotCStack* pStack) { CBotToken* pp = p; CBotCStack* pStk = pStack->TokenStack(); pStk->SetStartError(pp->GetStart()); // is it a variable ? if (pp->GetType() == TokenTypVar) { CBotToken pthis("this"); CBotVar* var = pStk->FindVar(pthis); if (var == 0) return pStack->Return(NULL, pStk); CBotInstr* inst = new CBotExprVar(); // this is an element of the current class // adds the equivalent of this. before inst->SetToken(&pthis); (static_cast(inst))->m_nIdent = -2; // ident for this CBotToken* pp = p; if (pp->GetType() == TokenTypVar) { if (pp->GetNext()->GetType() == ID_OPENPAR) // a method call? { CBotInstr* i = CBotInstrMethode::Compile(pp, pStk, var); if (pStk->IsOk()) { inst->AddNext3(i); // add after p = pp; // previous instruction return pStack->Return(inst, pStk); } pStk->SetError(0,0); // the error is not adressed here } } delete inst; } return pStack->Return(NULL, pStk); } // execute, making the value of a variable bool CBotExprVar::Execute(CBotStack* &pj) { CBotVar* pVar = NULL; CBotStack* pile = pj->AddStack(this); CBotStack* pile1 = pile; if (pile1->GetState() == 0) { if (!ExecuteVar(pVar, pile, NULL, true)) return false; // Get the variable fields and indexes according if (pVar) pile1->SetCopyVar(pVar); // place a copy on the stack else { return pj->Return(pile1); } pile1->IncState(); } pVar = pile1->GetVar(); if (pVar == NULL) { return pj->Return(pile1); } if (pVar->GetInit() == IS_UNDEF) { CBotToken* pt = &m_token; while (pt->GetNext() != NULL) pt = pt->GetNext(); pile1->SetError(TX_NOTINIT, pt); return pj->Return(pile1); } return pj->Return(pile1); // operation completed } void CBotExprVar::RestoreState(CBotStack* &pj, bool bMain) { if (!bMain) return; CBotStack* pile = pj->RestoreStack(this); if (pile == NULL) return; CBotStack* pile1 = pile; if (pile1->GetState() == 0) { RestoreStateVar(pile, bMain); // retrieves the variable fields and indexes according return; } } // fetch a variable at runtime bool CBotExprVar::ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* prevToken, bool bStep) { CBotStack* pile = pj; pj = pj->AddStack(this); if (bStep && m_nIdent>0 && pj->IfStep()) return false; pVar = pj->FindVar(m_nIdent, true); // tries with the variable update if necessary if (pVar == NULL) { #ifdef _DEBUG assert(0); #endif pj->SetError(1, &m_token); return false; } if ( m_next3 != NULL && !m_next3->ExecuteVar(pVar, pj, &m_token, bStep, false) ) return false; // field of an instance, table, methode return pile->ReturnKeep(pj); // does not put on stack but get the result if a method was called } // fetch variable at runtime void CBotExprVar::RestoreStateVar(CBotStack* &pj, bool bMain) { pj = pj->RestoreStack(this); if (pj == NULL) return; if (m_next3 != NULL) m_next3->RestoreStateVar(pj, bMain); } ////////////////////////////////////////////////////////////////////////////////////////// // compile a list of parameters CBotInstr* CompileParams(CBotToken* &p, CBotCStack* pStack, CBotVar** ppVars) { bool first = true; CBotInstr* ret = NULL; // to return to the list CBotCStack* pile = pStack; int i = 0; if (IsOfType(p, ID_OPENPAR)) { int start, end; if (!IsOfType(p, ID_CLOSEPAR)) while (true) { start = p->GetStart(); pile = pile->TokenStack(); // keeps the result on the stack if (first) pStack->SetStartError(start); first = false; CBotInstr* param = CBotExpression::Compile(p, pile); end = p->GetStart(); if (!pile->IsOk()) { return pStack->Return(NULL, pile); } if (ret == NULL) ret = param; else ret->AddNext(param); // construct the list if (param != NULL) { if (pile->GetTypResult().Eq(99)) { delete pStack->TokenStack(); pStack->SetError(TX_VOID, p->GetStart()); return NULL; } ppVars[i] = pile->GetVar(); ppVars[i]->GetToken()->SetPos(start, end); i++; if (IsOfType(p, ID_COMMA)) continue; // skips the comma if (IsOfType(p, ID_CLOSEPAR)) break; } pStack->SetError(TX_CLOSEPAR, p->GetStart()); delete pStack->TokenStack(); return NULL; } } ppVars[i] = NULL; return ret; } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // compile a method call CBotInstrMethode::CBotInstrMethode() { m_Parameters = NULL; m_MethodeIdent = 0; name = "CBotInstrMethode"; } CBotInstrMethode::~CBotInstrMethode() { delete m_Parameters; } CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* var) { CBotInstrMethode* inst = new CBotInstrMethode(); inst->SetToken(p); // corresponding token if (NULL != var) { CBotToken* pp = p; p = p->GetNext(); if (p->GetType() == ID_OPENPAR) { inst->m_NomMethod = pp->GetString(); // compiles the list of parameters CBotVar* ppVars[1000]; inst->m_Parameters = CompileParams(p, pStack, ppVars); if (pStack->IsOk()) { CBotClass* pClass = var->GetClass(); // pointer to the class inst->m_ClassName = pClass->GetName(); // name of the class CBotTypResult r = pClass->CompileMethode(inst->m_NomMethod, var, ppVars, pStack, inst->m_MethodeIdent); delete pStack->TokenStack(); // release parameters on the stack inst->m_typRes = r; if (inst->m_typRes.GetType() > 20) { pStack->SetError(inst->m_typRes.GetType(), pp); delete inst; return NULL; } // put the result on the stack to have something if (inst->m_typRes.GetType() > 0) { CBotVar* pResult = CBotVar::Create("", inst->m_typRes); if (inst->m_typRes.Eq(CBotTypClass)) { pResult->SetClass(inst->m_typRes.GetClass()); } pStack->SetVar(pResult); } return inst; } delete inst; return NULL; } } pStack->SetError(1234, p); delete inst; return NULL; } // execute the method call bool CBotInstrMethode::ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* prevToken, bool bStep, bool bExtend) { CBotVar* ppVars[1000]; CBotStack* pile1 = pj->AddStack(this, true); // a place for the copy of This if (pVar->GetPointer() == NULL) { pj->SetError(TX_NULLPT, prevToken); } if (pile1->IfStep()) return false; CBotStack* pile2 = pile1->AddStack(); // for the next parameters if ( pile1->GetState() == 0) { CBotVar* pThis = CBotVar::Create(pVar); pThis->Copy(pVar); // this value should be taken before the evaluation parameters // Test.Action (Test = Other); // action must act on the value before test = Other! pThis->SetName("this"); pThis->SetUniqNum(-2); pile1->AddVar(pThis); pile1->IncState(); } int i = 0; CBotInstr* p = m_Parameters; // evaluate the parameters // and places the values on the stack // to be interrupted at any time if (p != NULL) while ( true) { if (pile2->GetState() == 0) { if (!p->Execute(pile2)) return false; // interrupted here? if (!pile2->SetState(1)) return false; // special mark to recognize parameters } ppVars[i++] = pile2->GetVar(); // construct the list of pointers pile2 = pile2->AddStack(); // space on the stack for the result p = p->GetNext(); if ( p == NULL) break; } ppVars[i] = NULL; CBotClass* pClass = CBotClass::Find(m_ClassName); CBotVar* pThis = pile1->FindVar(-2); CBotVar* pResult = NULL; if (m_typRes.GetType() > 0) pResult = CBotVar::Create("", m_typRes); if (m_typRes.Eq(CBotTypClass)) { pResult->SetClass(m_typRes.GetClass()); } CBotVar* pRes = pResult; if ( !pClass->ExecuteMethode(m_MethodeIdent, m_NomMethod, pThis, ppVars, pResult, pile2, GetToken())) return false; if (pRes != pResult) delete pRes; pVar = NULL; // does not return value for this return pj->Return(pile2); // release the entire stack } void CBotInstrMethode::RestoreStateVar(CBotStack* &pile, bool bMain) { if (!bMain) return; CBotVar* ppVars[1000]; CBotStack* pile1 = pile->RestoreStack(this); // place for the copy of This if (pile1 == NULL) return; CBotStack* pile2 = pile1->RestoreStack(); // and for the parameters coming if (pile2 == NULL) return; CBotVar* pThis = pile1->FindVar("this"); pThis->SetUniqNum(-2); int i = 0; CBotInstr* p = m_Parameters; // evaluate the parameters // and places the values on the stack // to be interrupted at any time if (p != NULL) while ( true) { if (pile2->GetState() == 0) { p->RestoreState(pile2, true); // interrupted here! return; } ppVars[i++] = pile2->GetVar(); // construct the list of pointers pile2 = pile2->RestoreStack(); if (pile2 == NULL) return; p = p->GetNext(); if ( p == NULL) break; } ppVars[i] = NULL; CBotClass* pClass = CBotClass::Find(m_ClassName); // CBotVar* pResult = NULL; // CBotVar* pRes = pResult; pClass->RestoreMethode(m_MethodeIdent, m_NomMethod, pThis, ppVars, pile2); } bool CBotInstrMethode::Execute(CBotStack* &pj) { CBotVar* ppVars[1000]; CBotStack* pile1 = pj->AddStack(this, true); // place for the copy of This if (pile1->IfStep()) return false; CBotStack* pile2 = pile1->AddStack(); // and for the parameters coming if ( pile1->GetState() == 0) { CBotVar* pThis = pile1->CopyVar(m_token); // this value should be taken before the evaluation parameters // Test.Action (Test = Other); // Action must act on the value before test = Other! pThis->SetName("this"); pile1->AddVar(pThis); pile1->IncState(); } int i = 0; CBotInstr* p = m_Parameters; // evaluate the parameters // and places the values on the stack // to be interrupted at any time if (p != NULL) while ( true) { if (pile2->GetState() == 0) { if (!p->Execute(pile2)) return false; // interrupted here? if (!pile2->SetState(1)) return false; // special mark to recognize parameters } ppVars[i++] = pile2->GetVar(); // construct the list of pointers pile2 = pile2->AddStack(); // space on the stack for the results p = p->GetNext(); if ( p == NULL) break; } ppVars[i] = NULL; CBotClass* pClass = CBotClass::Find(m_ClassName); CBotVar* pThis = pile1->FindVar("this"); CBotVar* pResult = NULL; if (m_typRes.GetType()>0) pResult = CBotVar::Create("", m_typRes); if (m_typRes.Eq(CBotTypClass)) { pResult->SetClass(m_typRes.GetClass()); } CBotVar* pRes = pResult; if ( !pClass->ExecuteMethode(m_MethodeIdent, m_NomMethod, pThis, ppVars, pResult, pile2, GetToken())) return false; // interupted // set the new value of this in place of the old variable CBotVar* old = pile1->FindVar(m_token); old->Copy(pThis, false); if (pRes != pResult) delete pRes; return pj->Return(pile2); // release the entire stack } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // compile an instruction "new" CBotNew::CBotNew() { name = "CBotNew"; m_Parameters = NULL; m_nMethodeIdent = 0; } CBotNew::~CBotNew() { } CBotInstr* CBotNew::Compile(CBotToken* &p, CBotCStack* pStack) { CBotToken* pp = p; if (!IsOfType(p, ID_NEW)) return NULL; // verifies that the token is a class name if (p->GetType() != TokenTypVar) return NULL; CBotClass* pClass = CBotClass::Find(p); if (pClass == NULL) { pStack->SetError(TX_BADNEW, p); return NULL; } CBotNew* inst = new CBotNew(); inst->SetToken(pp); inst->m_vartoken = p; p = p->GetNext(); // creates the object on the "job" // with a pointer to the object CBotVar* pVar = CBotVar::Create("", pClass); // do the call of the creator CBotCStack* pStk = pStack->TokenStack(); { // check if there are parameters CBotVar* ppVars[1000]; inst->m_Parameters = CompileParams(p, pStk, ppVars); if (!pStk->IsOk()) goto error; // constructor exist? CBotTypResult r = pClass->CompileMethode(pClass->GetName(), pVar, ppVars, pStk, inst->m_nMethodeIdent); delete pStk->TokenStack(); // release extra stack int typ = r.GetType(); // if there is no constructor, and no parameters either, it's ok if (typ == TX_UNDEFCALL && inst->m_Parameters == NULL) typ = 0; pVar->SetInit(true); // mark the instance as init if (typ>20) { pStk->SetError(typ, inst->m_vartoken.GetEnd()); goto error; } // if the constructor does not exist, but there are parameters if (typ<0 && inst->m_Parameters != NULL) { pStk->SetError(TX_NOCONST, &inst->m_vartoken); goto error; } // makes pointer to the object on the stack pStk->SetVar(pVar); return pStack->Return(inst, pStk); } error: delete inst; return pStack->Return(NULL, pStk); } // executes instruction "new" bool CBotNew::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this); //main stack if (pile->IfStep()) return false; CBotStack* pile1 = pj->AddStack2(); //secondary stack CBotVar* pThis = NULL; CBotToken* pt = &m_vartoken; CBotClass* pClass = CBotClass::Find(pt); // create the variable "this" pointer type to the stack if ( pile->GetState()==0) { // create an instance of the requested class // and initialize the pointer to that object pThis = CBotVar::Create("this", pClass); pThis->SetUniqNum(-2) ; pile1->SetVar(pThis); // place on stack1 pile->IncState(); } // fetch the this pointer if it was interrupted if ( pThis == NULL) { pThis = pile1->GetVar(); // find the pointer } // is there an assignment or parameters (constructor) if ( pile->GetState()==1) { // evaluates the constructor of the instance CBotVar* ppVars[1000]; CBotStack* pile2 = pile; int i = 0; CBotInstr* p = m_Parameters; // evaluate the parameters // and places the values on the stack // to be interrupted at any time if (p != NULL) while ( true) { pile2 = pile2->AddStack(); // space on the stack for the result if (pile2->GetState() == 0) { if (!p->Execute(pile2)) return false; // interrupted here? pile2->SetState(1); } ppVars[i++] = pile2->GetVar(); p = p->GetNext(); if ( p == NULL) break; } ppVars[i] = NULL; // create a variable for the result CBotVar* pResult = NULL; // constructos still void if ( !pClass->ExecuteMethode(m_nMethodeIdent, pClass->GetName(), pThis, ppVars, pResult, pile2, GetToken())) return false; // interrupt pThis->ConstructorSet(); // indicates that the constructor has been called } return pj->Return(pile1); // passes below } void CBotNew::RestoreState(CBotStack* &pj, bool bMain) { if (!bMain) return; CBotStack* pile = pj->RestoreStack(this); //primary stack if (pile == NULL) return; CBotStack* pile1 = pj->AddStack2(); //secondary stack CBotToken* pt = &m_vartoken; CBotClass* pClass = CBotClass::Find(pt); // create the variable "this" pointer type to the object if ( pile->GetState()==0) { return; } CBotVar* pThis = pile1->GetVar(); // find the pointer pThis->SetUniqNum(-2); // is ther an assignment or parameters (constructor) if ( pile->GetState()==1) { // evaluates the constructor of the instance CBotVar* ppVars[1000]; CBotStack* pile2 = pile; int i = 0; CBotInstr* p = m_Parameters; // evaluate the parameters // and places the values on the stack // to be interrupted at any time if (p != NULL) while ( true) { pile2 = pile2->RestoreStack(); // space on the stack for the result if (pile2 == NULL) return; if (pile2->GetState() == 0) { p->RestoreState(pile2, bMain); // interrupt here! return; } ppVars[i++] = pile2->GetVar(); p = p->GetNext(); if ( p == NULL) break; } ppVars[i] = NULL; pClass->RestoreMethode(m_nMethodeIdent, m_vartoken.GetString(), pThis, ppVars, pile2) ; // interrupt here! } } ///////////////////////////////////////////////////////////// // check if two results are consistent to make an operation bool TypeCompatible(CBotTypResult& type1, CBotTypResult& type2, int op) { int t1 = type1.GetType(); int t2 = type2.GetType(); int max = (t1 > t2) ? t1 : t2; if (max == 99) return false; // result is void? // special case for strin concatenation if (op == ID_ADD && max >= CBotTypString) return true; if (op == ID_ASSADD && max >= CBotTypString) return true; if (op == ID_ASS && t1 == CBotTypString) return true; if (max >= CBotTypBoolean) { if ( (op == ID_EQ || op == ID_NE) && (t1 == CBotTypPointer && t2 == CBotTypNullPointer)) return true; if ( (op == ID_EQ || op == ID_NE || op == ID_ASS) && (t2 == CBotTypPointer && t1 == CBotTypNullPointer)) return true; if ( (op == ID_EQ || op == ID_NE) && (t1 == CBotTypArrayPointer && t2 == CBotTypNullPointer)) return true; if ( (op == ID_EQ || op == ID_NE || op == ID_ASS) && (t2 == CBotTypArrayPointer && t1 == CBotTypNullPointer)) return true; if (t2 != t1) return false; if (t1 == CBotTypArrayPointer) return type1.Compare(type2); if (t1 == CBotTypPointer || t1 == CBotTypClass || t1 == CBotTypIntrinsic ) { CBotClass* c1 = type1.GetClass(); CBotClass* c2 = type2.GetClass(); return c1->IsChildOf(c2) || c2->IsChildOf(c1); // accept the case in reverse // the transaction will be denied at runtime if the pointer is not // compatible } return true; } type1.SetType(max); type2.SetType(max); return true; } // check if two variables are compatible for parameter passing bool TypesCompatibles(const CBotTypResult& type1, const CBotTypResult& type2) { int t1 = type1.GetType(); int t2 = type2.GetType(); if (t1 == CBotTypIntrinsic) t1 = CBotTypClass; if (t2 == CBotTypIntrinsic) t2 = CBotTypClass; int max = (t1 > t2) ? t1 : t2; if (max == 99) return false; // result is void? if (max >= CBotTypBoolean) { if (t2 != t1) return false; if (max == CBotTypArrayPointer) return TypesCompatibles(type1.GetTypElem(), type2.GetTypElem()); if (max == CBotTypClass || max == CBotTypPointer) return type1.GetClass() == type2.GetClass() ; return true ; } return true; } ///////////////////////////////////////////////////////////////////////////////////// // file management // necessary because it is not possible to do the fopen in the main program // fwrite and fread in a dll or using the FILE * returned. FILE* fOpen(const char* name, const char* mode) { return fopen(name, mode); } int fClose(FILE* filehandle) { return fclose(filehandle); } size_t fWrite(const void *buffer, size_t elemsize, size_t length, FILE* filehandle) { return fwrite(buffer, elemsize, length, filehandle); } size_t fRead(void *buffer, size_t elemsize, size_t length, FILE* filehandle) { return fread(buffer, elemsize, length, filehandle); } size_t fWrite(const void *buffer, size_t length, FILE* filehandle) { return fwrite(buffer, 1, length, filehandle); } size_t fRead(void *buffer, size_t length, FILE* filehandle) { return fread(buffer, 1, length, filehandle); } ////////////////////////////////////////