From a66b3d06723a5e97655c65e178ceb2f05da1a233 Mon Sep 17 00:00:00 2001 From: melex750 Date: Thu, 11 Apr 2019 04:13:13 -0400 Subject: [PATCH 1/7] Refactor SaveState and RestoreState to use streams --- src/CBot/CBotClass.cpp | 40 +- src/CBot/CBotClass.h | 16 +- src/CBot/CBotFileUtils.cpp | 380 ++++++++++++------ src/CBot/CBotFileUtils.h | 217 ++++++---- src/CBot/CBotProgram.cpp | 42 +- src/CBot/CBotProgram.h | 15 +- src/CBot/CBotStack.cpp | 157 ++++---- src/CBot/CBotStack.h | 4 +- src/CBot/CBotUtils.cpp | 32 -- src/CBot/CBotUtils.h | 29 +- src/CBot/CBotVar/CBotVar.cpp | 2 +- src/CBot/CBotVar/CBotVar.h | 12 +- src/CBot/CBotVar/CBotVarArray.cpp | 7 +- src/CBot/CBotVar/CBotVarArray.h | 2 +- src/CBot/CBotVar/CBotVarBoolean.cpp | 4 +- src/CBot/CBotVar/CBotVarBoolean.h | 2 +- src/CBot/CBotVar/CBotVarClass.cpp | 10 +- src/CBot/CBotVar/CBotVarClass.h | 2 +- src/CBot/CBotVar/CBotVarFloat.cpp | 4 +- src/CBot/CBotVar/CBotVarFloat.h | 2 +- src/CBot/CBotVar/CBotVarInt.cpp | 19 +- src/CBot/CBotVar/CBotVarInt.h | 4 +- src/CBot/CBotVar/CBotVarPointer.cpp | 11 +- src/CBot/CBotVar/CBotVarPointer.h | 2 +- src/CBot/CBotVar/CBotVarString.cpp | 4 +- src/CBot/CBotVar/CBotVarString.h | 2 +- src/CBot/CBotVar/CBotVarValue.h | 5 +- src/level/robotmain.cpp | 112 ++++-- src/level/robotmain.h | 4 +- .../implementation/programmable_impl.cpp | 45 ++- src/object/implementation/programmable_impl.h | 4 +- src/object/interface/programmable_object.h | 4 +- src/script/script.cpp | 20 +- src/script/script.h | 4 +- test/unit/CBot/CBot_test.cpp | 32 +- test/unit/main.cpp | 10 + 36 files changed, 767 insertions(+), 494 deletions(-) diff --git a/src/CBot/CBotClass.cpp b/src/CBot/CBotClass.cpp index f1a658ad..628bb907 100644 --- a/src/CBot/CBotClass.cpp +++ b/src/CBot/CBotClass.cpp @@ -36,7 +36,6 @@ #include "CBot/CBotCStack.h" #include "CBot/CBotDefParam.h" #include "CBot/CBotUtils.h" -#include "CBot/CBotFileUtils.h" #include @@ -364,69 +363,70 @@ void CBotClass::RestoreMethode(long& nIdent, } //////////////////////////////////////////////////////////////////////////////// -bool CBotClass::SaveStaticState(FILE* pf) +bool CBotClass::SaveStaticState(std::ostream &ostr) { - if (!WriteWord( pf, CBOTVERSION*2)) return false; + if (!WriteLong(ostr, CBOTVERSION*2)) return false; // saves the state of static variables in classes for (CBotClass* p : m_publicClasses) { - if (!WriteWord( pf, 1 )) return false; + if (!WriteWord(ostr, 1)) return false; // save the name of the class - if (!WriteString( pf, p->GetName() )) return false; + if (!WriteString(ostr, p->GetName())) return false; CBotVar* pv = p->GetVar(); while( pv != nullptr ) { if ( pv->IsStatic() ) { - if (!WriteWord( pf, 1 )) return false; - if (!WriteString( pf, pv->GetName() )) return false; + if (!WriteWord(ostr, 1)) return false; + if (!WriteString(ostr, pv->GetName())) return false; - if ( !pv->Save0State(pf) ) return false; // common header - if ( !pv->Save1State(pf) ) return false; // saves as the child class - if ( !WriteWord( pf, 0 ) ) return false; + if (!pv->Save0State(ostr)) return false; // common header + if (!pv->Save1State(ostr)) return false; // saves as the child class + if (!WriteWord(ostr, 0)) return false; } pv = pv->GetNext(); } - if (!WriteWord( pf, 0 )) return false; + if (!WriteWord(ostr, 0)) return false; } - if (!WriteWord( pf, 0 )) return false; + if (!WriteWord(ostr, 0)) return false; return true; } //////////////////////////////////////////////////////////////////////////////// -bool CBotClass::RestoreStaticState(FILE* pf) +bool CBotClass::RestoreStaticState(std::istream &istr) { std::string ClassName, VarName; CBotClass* pClass; unsigned short w; - if (!ReadWord( pf, w )) return false; - if ( w != CBOTVERSION*2 ) return false; + long version; + if (!ReadLong(istr, version)) return false; + if (version != CBOTVERSION*2) return false; while (true) { - if (!ReadWord( pf, w )) return false; + if (!ReadWord(istr, w)) return false; if ( w == 0 ) return true; - if (!ReadString( pf, ClassName )) return false; + if (!ReadString(istr, ClassName)) return false; pClass = Find(ClassName); while (true) { - if (!ReadWord( pf, w )) return false; + if (!ReadWord(istr, w)) return false; if ( w == 0 ) break; CBotVar* pVar = nullptr; CBotVar* pv = nullptr; - if (!ReadString( pf, VarName )) return false; + if (!ReadString(istr, VarName)) return false; if ( pClass != nullptr ) pVar = pClass->GetItem(VarName); - if (!CBotVar::RestoreState(pf, pv)) return false; // the temp variable + if (!CBotVar::RestoreState(istr, pv)) return false; // the temp variable if ( pVar != nullptr ) pVar->Copy(pv); delete pv; diff --git a/src/CBot/CBotClass.h b/src/CBot/CBotClass.h index 0f9a8ce4..eeafe2d1 100644 --- a/src/CBot/CBotClass.h +++ b/src/CBot/CBotClass.h @@ -331,18 +331,18 @@ public: static void ClearPublic(); /*! - * \brief SaveStaticState - * \param pf - * \return + * \brief Save all static variables from each public class + * \param ostr Output stream + * \return true on success */ - static bool SaveStaticState(FILE* pf); + static bool SaveStaticState(std::ostream &ostr); /*! - * \brief RestoreStaticState - * \param pf - * \return + * \brief Restore all static variables in each public class + * \param istr Input stream + * \return true on success */ - static bool RestoreStaticState(FILE* pf); + static bool RestoreStaticState(std::istream &istr); /** * \brief Request a lock on this class (for "synchronized" keyword) diff --git a/src/CBot/CBotFileUtils.cpp b/src/CBot/CBotFileUtils.cpp index c473493a..ccde313c 100644 --- a/src/CBot/CBotFileUtils.cpp +++ b/src/CBot/CBotFileUtils.cpp @@ -21,129 +21,251 @@ #include "CBot/CBotClass.h" #include "CBot/CBotEnums.h" -#include "CBot/CBotUtils.h" namespace CBot { - -// 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) +template +static bool WriteBinary(std::ostream &ostr, T value, unsigned padTo = 0) { - return fopen(name, mode); -} - -//////////////////////////////////////////////////////////////////////////////// -int fClose(FILE* filehandle) -{ - return fclose(filehandle); -} - -//////////////////////////////////////////////////////////////////////////////// -std::size_t fWrite(const void *buffer, - std::size_t elemsize, - std::size_t length, - FILE* filehandle) -{ - return fwrite(buffer, elemsize, length, filehandle); -} - -//////////////////////////////////////////////////////////////////////////////// -std::size_t fRead(void *buffer, - std::size_t elemsize, - std::size_t length, - FILE* filehandle) -{ - return fread(buffer, elemsize, length, filehandle); -} - - -//////////////////////////////////////////////////////////////////////////////// -bool ReadWord(FILE* pf, unsigned short& w) -{ - size_t lg; - - lg = fread(&w, sizeof( unsigned short ), 1, pf ); - - return (lg == 1); -} - -//////////////////////////////////////////////////////////////////////////////// -bool ReadFloat(FILE* pf, float& w) -{ - size_t lg; - - lg = fread(&w, sizeof( float ), 1, pf ); - - return (lg == 1); -} - -//////////////////////////////////////////////////////////////////////////////// -bool WriteLong(FILE* pf, long w) -{ - size_t lg; - - lg = fwrite(&w, sizeof( long ), 1, pf ); - - return (lg == 1); -} - -//////////////////////////////////////////////////////////////////////////////// -bool ReadLong(FILE* pf, long& w) -{ - size_t lg; - - lg = fread(&w, sizeof( long ), 1, pf ); - - return (lg == 1); -} - -//////////////////////////////////////////////////////////////////////////////// -bool ReadString(FILE* pf, std::string& s) -{ - unsigned short w; - char buf[1000]; - size_t lg1, lg2; - - if (!ReadWord(pf, w)) return false; - lg1 = w; - lg2 = fread(buf, 1, lg1, pf ); - buf[lg2] = 0; - - s = buf; - return (lg1 == lg2); -} - -//////////////////////////////////////////////////////////////////////////////// -bool WriteType(FILE* pf, const CBotTypResult &type) -{ - int typ = type.GetType(); - if ( typ == CBotTypIntrinsic ) typ = CBotTypClass; - if ( !WriteWord(pf, typ) ) return false; - if ( typ == CBotTypClass ) + unsigned char chr; + unsigned count = 1; + while (value > 127) // unsigned LEB128 { - CBotClass* p = type.GetClass(); - if ( !WriteString(pf, p->GetName()) ) return false; + ++count; + chr = (value & 0x7F) | 0x80; + if (!ostr.write(reinterpret_cast(&chr), 1)) return false; + value >>= 7; } - if ( type.Eq( CBotTypArrayBody ) || - type.Eq( CBotTypArrayPointer ) ) + chr = value & 0x7F; + if (count < padTo) chr |= 0x80; + if (!ostr.write(reinterpret_cast(&chr), 1)) return false; + + if (count < padTo) { - if ( !WriteWord(pf, type.GetLimite()) ) return false; - if ( !WriteType(pf, type.GetTypElem()) ) return false; + while (++count < padTo) + if (!(ostr << '\x80')) return false; + if (!(ostr << '\x00')) return false; } return true; } -//////////////////////////////////////////////////////////////////////////////// -bool ReadType(FILE* pf, CBotTypResult &type) +template +static bool ReadBinary(std::istream &istr, T &value) +{ + value = 0; + unsigned char chr; + unsigned shift = 0; + while (true) // unsigned LEB128 + { + if (!istr.read(reinterpret_cast(&chr), 1)) return false; + if (shift < sizeof(T) * 8 - 1) + value |= static_cast(chr & 0x7F) << shift; + shift += 7; + if ((chr & 0x80) == 0) break; + } + return true; +} + +template +static bool WriteSignedBinary(std::ostream &ostr, T value, unsigned padTo = 0) +{ + signed char sign = value >> (8 * sizeof(T) - 1); + unsigned count = 0; + while (true) // signed LEB128 + { + ++count; + unsigned char chr = value & 0x7F; + value >>= 7; + if (!(value != sign || ((chr ^ sign) & 0x40) != 0)) + { + if (count < padTo) chr |= 0x80; + if (!ostr.write(reinterpret_cast(&chr), 1)) return false; + break; + } + chr |= 0x80; + if (!ostr.put(chr)) return false; + } + + if (count < padTo) + { + char chr = (sign < 0) ? 0x7F : 0x00; + while (++count < padTo) + if (!ostr.put(chr | 0x80)) return false; + if (!ostr.put(chr)) return false; + } + return true; +} + +template +static bool ReadSignedBinary(std::istream &istr, T &value) +{ + value = 0; + unsigned char chr; + unsigned shift = 0; + while (true) // signed LEB128 + { + if (!istr.read(reinterpret_cast(&chr), 1)) return false; + if (shift < sizeof(T) * 8 - 1) + value |= (static_cast(chr & 0x7F) << shift); + shift += 7; + if ((chr & 0x80) == 0) break; + } + + if (shift >= 8 * sizeof(T) - 1) shift = 8 * sizeof(T) - 1; + if ((chr & 0x40) != 0) + value |= static_cast(-1) << shift; + + return true; +} + +bool WriteWord(std::ostream &ostr, unsigned short w) +{ + return WriteBinary(ostr, w); +} + +bool ReadWord(std::istream &istr, unsigned short &w) +{ + return ReadBinary(istr, w); +} + +bool WriteByte(std::ostream &ostr, char c) +{ + if (!ostr.put(c)) return false; + return true; +} + +bool ReadByte(std::istream &istr, char& c) +{ + if (!istr.get(c)) return false; + return true; +} + +bool WriteShort(std::ostream &ostr, short s) +{ + return WriteSignedBinary(ostr, s); +} + +bool ReadShort(std::istream &istr, short &s) +{ + return ReadSignedBinary(istr, s); +} + +bool WriteInt(std::ostream &ostr, int i) +{ + return WriteSignedBinary(ostr, i); +} + +bool ReadInt(std::istream &istr, int &i) +{ + return ReadSignedBinary(istr, i); +} + +bool WriteLong(std::ostream &ostr, long l, unsigned padTo) +{ + return WriteSignedBinary(ostr, l, padTo); +} + +bool ReadLong(std::istream &istr, long &l) +{ + return ReadSignedBinary(istr, l); +} + +bool WriteFloat(std::ostream &ostr, float f) +{ + union {float fValue; unsigned int iValue;} u; + u.fValue = 0.0f; + u.iValue = 0; + + u.fValue = f; + return WriteBinary(ostr, u.iValue); +} + +bool ReadFloat(std::istream &istr, float &f) +{ + union {float fValue; unsigned int iValue;} u; + u.fValue = 0.0f; + u.iValue = 0; + + if (!ReadBinary(istr, u.iValue)) return false; + f = u.fValue; + return true; +} + +bool WriteDouble(std::ostream &ostr, double d) +{ + union {double dValue; unsigned long iValue;} u; + u.dValue = 0.0; + u.iValue = 0; + + u.dValue = d; + return WriteBinary(ostr, u.iValue); +} + +bool ReadDouble(std::istream &istr, double &d) +{ + union {double dValue; unsigned long iValue;} u; + u.dValue = 0.0; + u.iValue = 0; + + if (!ReadBinary(istr, u.iValue)) return false; + d = u.dValue; + return true; +} + +bool WriteString(std::ostream &ostr, const std::string &s) +{ + if (!WriteBinary(ostr, s.size())) return false; + if (!ostr.write(&(s[0]), s.size())) return false; + + return true; +} + +bool ReadString(std::istream &istr, std::string &s) +{ + size_t length = 0; + if (!ReadBinary(istr, length)) return false; + + s.resize(length); + if (length != 0) + { + if (!istr.read(&(s[0]), length)) return false; + } + return true; +} + +bool WriteType(std::ostream &ostr, const CBotTypResult &type) +{ + int typ = type.GetType(); + if ( typ == CBotTypIntrinsic ) typ = CBotTypClass; + if ( !WriteWord(ostr, typ) ) return false; + if ( typ == CBotTypClass ) + { + CBotClass* p = type.GetClass(); + if (!WriteString(ostr, p->GetName())) return false; + } + if ( type.Eq( CBotTypArrayBody ) || + type.Eq( CBotTypArrayPointer ) ) + { + if (!WriteWord(ostr, type.GetLimite())) return false; + if (!WriteType(ostr, type.GetTypElem())) return false; + } + + if ( type.Eq(CBotTypPointer) ) + { + if (type.GetClass() != nullptr) + { + if (!WriteString(ostr, type.GetClass()->GetName())) return false; + } + else if (!WriteString(ostr, "")) return false; + } + return true; +} + +bool ReadType(std::istream &istr, CBotTypResult &type) { unsigned short w, ww; - if ( !ReadWord(pf, w) ) return false; + if (!ReadWord(istr, w)) return false; type.SetType(w); if ( type.Eq( CBotTypIntrinsic ) ) @@ -154,7 +276,7 @@ bool ReadType(FILE* pf, CBotTypResult &type) if ( type.Eq( CBotTypClass ) ) { std::string s; - if ( !ReadString(pf, s) ) return false; + if (!ReadString(istr, s)) return false; type = CBotTypResult( w, s ); } @@ -162,11 +284,45 @@ bool ReadType(FILE* pf, CBotTypResult &type) type.Eq( CBotTypArrayBody ) ) { CBotTypResult r; - if ( !ReadWord(pf, ww) ) return false; - if ( !ReadType(pf, r) ) return false; + if (!ReadWord(istr, ww)) return false; + if (!ReadType(istr, r)) return false; type = CBotTypResult( w, r ); type.SetLimite(static_cast(ww)); } + + if ( type.Eq(CBotTypPointer) ) + { + std::string className; + if (!ReadString(istr, className)) return false; + type = CBotTypResult(w, className); + } + return true; +} + +bool WriteStream(std::ostream &ostr, std::istream& istr) +{ + if (!istr.seekg(0, istr.end)) return false; + auto size = istr.tellg(); + + if (size == 0) return WriteLong(ostr, 0); + if (!WriteLong(ostr, size)) return false; + + if (!istr.seekg(0, istr.beg)) return false; + if (!(ostr << istr.rdbuf())) return false; + + return true; +} + +bool ReadStream(std::istream& istr, std::ostream &ostr) +{ + long length; + if (!ReadLong(istr, length)) return false; + if (length == 0) return true; + + while (length-- > 0) + { + if (!(ostr << istr.get())) return false; + } return true; } diff --git a/src/CBot/CBotFileUtils.h b/src/CBot/CBotFileUtils.h index fec6298a..8f739cb4 100644 --- a/src/CBot/CBotFileUtils.h +++ b/src/CBot/CBotFileUtils.h @@ -19,7 +19,7 @@ #pragma once -#include +#include #include namespace CBot @@ -28,128 +28,173 @@ namespace CBot class CBotVar; class CBotTypResult; -/////////////////////////////////////////////////////////////////////////////// -// routines for file management (* FILE) - /*! - * \brief fOpen - * \param name - * \param mode - * \return + * \brief Save a linked list if variables + * \param ostr Output stream + * \param pVar First variable in the list + * \return true on success */ -FILE* fOpen(const char* name, const char* mode); - -/*! - * \brief fClose - * \param filehandle - * \return - */ -int fClose(FILE* filehandle); - -/*! - * \brief fWrite - * \param buffer - * \param elemsize - * \param length - * \param filehandle - * \return - */ -std::size_t fWrite(const void *buffer, - std::size_t elemsize, - std::size_t length, - FILE* filehandle); - -/*! - * \brief fRead - * \param buffer - * \param elemsize - * \param length - * \param filehandle - * \return - */ -std::size_t fRead(void *buffer, - std::size_t elemsize, - std::size_t length, - FILE* filehandle); - -/*! - * \brief SaveVars - * \param pf - * \param pVar - * \return - */ -bool SaveVars(FILE* pf, CBotVar* pVar); +bool SaveVars(std::ostream &ostr, CBotVar* pVar); /*! * \brief WriteWord - * \param pf + * \param ostr Output stream * \param w - * \return + * \return true on success */ -bool WriteWord(FILE* pf, unsigned short w); +bool WriteWord(std::ostream &ostr, unsigned short w); /*! * \brief ReadWord - * \param pf - * \param w - * \return + * \param istr Input stream + * \param[out] w + * \return true on success */ -bool ReadWord(FILE* pf, unsigned short& w); +bool ReadWord(std::istream &istr, unsigned short &w); /*! - * \brief ReadLong - * \param pf - * \param w - * \return + * \brief WriteByte + * \param ostr Output stream + * \param c + * \return true on success */ -bool ReadLong(FILE* pf, long& w); +bool WriteByte(std::ostream &ostr, char c); /*! - * \brief WriteFloat - * \param pf - * \param w - * \return + * \brief ReadByte + * \param istr Input stream + * \param[out] c + * \return true on success */ -bool WriteFloat(FILE* pf, float w); +bool ReadByte(std::istream &istr, char& c); + +/*! + * \brief WriteShort + * \param ostr Output stream + * \param s + * \return true on success + */ +bool WriteShort(std::ostream &ostr, short s); + +/*! + * \brief ReadShort + * \param istr Input stream + * \param[out] s + * \return true on success + */ +bool ReadShort(std::istream &istr, short &s); + +/*! + * \brief WriteInt + * \param ostr Output stream + * \param i + * \return true on success + */ +bool WriteInt(std::ostream &ostr, int i); + +/*! + * \brief ReadInt + * \param istr Input stream + * \param[out] i + * \return true on success + */ +bool ReadInt(std::istream &istr, int &i); /*! * \brief WriteLong - * \param pf - * \param w - * \return + * \param ostr Output stream + * \param l + * \param padTo minimum number of bytes to write + * \return true on success */ -bool WriteLong(FILE* pf, long w); +bool WriteLong(std::ostream &ostr, long l, unsigned padTo = 0); + +/*! + * \brief ReadLong + * \param istr Input stream + * \param[out] l + * \return true on success + */ +bool ReadLong(std::istream &istr, long &l); + +/*! + * \brief WriteFloat + * \param ostr Output stream + * \param f + * \return true on success + */ +bool WriteFloat(std::ostream &ostr, float f); /*! * \brief ReadFloat - * \param pf - * \param w - * \return + * \param istr Input stream + * \param[out] f + * \return true on success */ -bool ReadFloat(FILE* pf, float& w); +bool ReadFloat(std::istream &istr, float &f); + +/*! + * \brief WriteDouble + * \param ostr Output stream + * \param d + * \return true on success + */ +bool WriteDouble(std::ostream &ostr, double d); + +/*! + * \brief ReadDouble + * \param istr Input stream + * \param[out] d + * \return true on success + */ +bool ReadDouble(std::istream &istr, double &d); + +/*! + * \brief WriteString + * \param ostr Output stream + * \param s + * \return true on success + */ +bool WriteString(std::ostream &ostr, const std::string &s); /*! * \brief ReadString - * \param pf - * \param s - * \return + * \param istr Input stream + * \param[out] s + * \return true on success */ -bool ReadString(FILE* pf, std::string& s); +bool ReadString(std::istream &istr, std::string &s); /*! * \brief WriteType - * \param pf + * \param ostr Output stream * \param type - * \return + * \return true on success */ -bool WriteType(FILE* pf, const CBotTypResult &type); +bool WriteType(std::ostream &ostr, const CBotTypResult &type); /*! * \brief ReadType - * \param pf - * \param type - * \return + * \param istr Input stream + * \param[out] type + * \return true on success */ -bool ReadType(FILE* pf, CBotTypResult &type); +bool ReadType(std::istream &istr, CBotTypResult &type); + +/*! + * \brief WriteStream + * \param ostr Output stream + * \param istr Input stream + * \return true on success + */ +bool WriteStream(std::ostream &ostr, std::istream& istr); + +/*! + * \brief ReadStream + * \param istr Input stream + * \param ostr Output stream + * \return true on success + */ +bool ReadStream(std::istream& istr, std::ostream &ostr); } // namespace CBot diff --git a/src/CBot/CBotProgram.cpp b/src/CBot/CBotProgram.cpp index e8065f47..14ec2b2e 100644 --- a/src/CBot/CBotProgram.cpp +++ b/src/CBot/CBotProgram.cpp @@ -24,7 +24,6 @@ #include "CBot/CBotCStack.h" #include "CBot/CBotClass.h" #include "CBot/CBotUtils.h" -#include "CBot/CBotFileUtils.h" #include "CBot/CBotInstr/CBotFunction.h" @@ -332,51 +331,56 @@ bool CBotProgram::DefineNum(const std::string& name, long val) } //////////////////////////////////////////////////////////////////////////////// -bool CBotProgram::SaveState(FILE* pf) +bool CBotProgram::SaveState(std::ostream &ostr) { - if (!WriteWord( pf, CBOTVERSION)) return false; + if (!WriteLong(ostr, CBOTVERSION)) return false; if (m_stack != nullptr ) { - if (!WriteWord( pf, 1)) return false; - if (!WriteString( pf, m_entryPoint->GetName() )) return false; - if (!m_stack->SaveState(pf)) return false; + if (!WriteWord(ostr, 1)) return false; + if (!WriteString(ostr, m_entryPoint->GetName())) return false; + if (!m_stack->SaveState(ostr)) return false; } else { - if (!WriteWord( pf, 0)) return false; + if (!WriteWord(ostr, 0)) return false; } return true; } -bool CBotProgram::RestoreState(FILE* pf) +bool CBotProgram::RestoreState(std::istream &istr) { unsigned short w; std::string s; Stop(); - if (!ReadWord( pf, w )) return false; - if ( w != CBOTVERSION ) return false; + long version; + if (!ReadLong(istr, version)) return false; + if ( version != CBOTVERSION ) return false; - if (!ReadWord( pf, w )) return false; + if (!ReadWord(istr, w)) return false; if ( w == 0 ) return true; - if (!ReadString( pf, s )) return false; - Start(s); // point de reprise + // don't restore if compile error exists + if (m_error != CBotNoErr) return false; - if (m_stack != nullptr) + if (!ReadString(istr, s)) return false; + if (!Start(s)) return false; // point de reprise + // Start() already created the new stack + // and called m_stack->SetProgram(this); + + // retrieves the stack from the memory + if (!m_stack->RestoreState(istr, m_stack)) { m_stack->Delete(); m_stack = nullptr; + m_stack = CBotStack::AllocateStack(); // start from the top + m_stack->SetProgram(this); + return false; // signal error } - // retrieves the stack from the memory - m_stack = CBotStack::AllocateStack(); - if (!m_stack->RestoreState(pf, m_stack)) return false; - m_stack->SetProgram(this); // bases for routines - // restored some states in the stack according to the structure m_entryPoint->RestoreState(nullptr, m_stack, m_thisVar); return true; diff --git a/src/CBot/CBotProgram.h b/src/CBot/CBotProgram.h index e258fff0..c448b82c 100644 --- a/src/CBot/CBotProgram.h +++ b/src/CBot/CBotProgram.h @@ -282,27 +282,20 @@ public: /** * \brief Save the current execution status into a file - * \param pf - * \parblock - * file handle - * - * This file handle must have been opened by this library! Otherwise crashes on Windows - * - * TODO: Verify this - * \endparblock + * \param ostr Output stream * \return true on success, false on write error */ - bool SaveState(FILE* pf); + bool SaveState(std::ostream &ostr); /** * \brief Restore the execution state from a file * * The previous program code must already have been recompiled with Compile() before calling this function * - * \param pf file handle + * \param istr Input stream * \return true on success, false on read error */ - bool RestoreState(FILE* pf); + bool RestoreState(std::istream &istr); /** * \brief GetPosition Gives the position of a routine in the original text diff --git a/src/CBot/CBotStack.cpp b/src/CBot/CBotStack.cpp index 9f9db2d6..7f3c0b9a 100644 --- a/src/CBot/CBotStack.cpp +++ b/src/CBot/CBotStack.cpp @@ -26,7 +26,6 @@ #include "CBot/CBotVar/CBotVarPointer.h" #include "CBot/CBotVar/CBotVarClass.h" -#include "CBot/CBotFileUtils.h" #include "CBot/CBotUtils.h" #include "CBot/CBotExternalCall.h" @@ -687,108 +686,106 @@ CBotVar* CBotStack::GetStackVars(std::string& functionName, int level) } //////////////////////////////////////////////////////////////////////////////// -bool CBotStack::SaveState(FILE* pf) +bool CBotStack::SaveState(std::ostream &ostr) { if (m_next2 != nullptr) { - if (!WriteWord(pf, 2)) return false; // a marker of type (m_next2) - if (!m_next2->SaveState(pf)) return false; // saves the next element + if (!WriteWord(ostr, 2)) return false; // a marker of type (m_next2) + if (!m_next2->SaveState(ostr)) return false; // saves the next element } else { - if (!WriteWord(pf, 1)) return false; // a marker of type (m_next) + if (!WriteWord(ostr, 1)) return false; // a marker of type (m_next) } - if (!WriteWord(pf, static_cast(m_block))) return false; - if (!WriteWord(pf, m_state)) return false; - if (!WriteWord(pf, 0)) return false; // for backwards combatibility (m_bDontDelete) - if (!WriteWord(pf, m_step)) return false; + if (!WriteWord(ostr, static_cast(m_block))) return false; + if (!WriteInt(ostr, m_state)) return false; + if (!WriteWord(ostr, 0)) return false; // for backwards combatibility (m_bDontDelete) + if (!WriteInt(ostr, m_step)) return false; - - if (!SaveVars(pf, m_var)) return false; // current result - if (!SaveVars(pf, m_listVar)) return false; // local variables + if (!SaveVars(ostr, m_var)) return false; // current result + if (!SaveVars(ostr, m_listVar)) return false; // local variables if (m_next != nullptr) { - if (!m_next->SaveState(pf)) return false; // saves the next element + if (!m_next->SaveState(ostr)) return false; // saves the next element } else { - if (!WriteWord(pf, 0)) return false; // terminator + if (!WriteWord(ostr, 0)) return false; // 0 - CBotStack::SaveState terminator } return true; } -bool SaveVars(FILE* pf, CBotVar* pVar) +bool SaveVars(std::ostream &ostr, CBotVar* pVar) { while (pVar != nullptr) { - if (!pVar->Save0State(pf)) return false; // common header - if (!pVar->Save1State(pf)) return false; // saves the data + if (!pVar->Save0State(ostr)) return false; // common header + if (!pVar->Save1State(ostr)) return false; // saves the data pVar = pVar->GetNext(); } - return WriteWord(pf, 0); // terminator + return WriteWord(ostr, 0); // 0 - CBot::SaveVars terminator } //////////////////////////////////////////////////////////////////////////////// -bool CBotStack::RestoreState(FILE* pf, CBotStack* &pStack) +bool CBotStack::RestoreState(std::istream &istr, CBotStack* &pStack) { unsigned short w; if (pStack != this) pStack = nullptr; - if (!ReadWord(pf, w)) return false; - if ( w == 0 ) return true; // 0 - terminator + if (!ReadWord(istr, w)) return false; + if ( w == 0 ) return true; // 0 - CBotStack::SaveState terminator if (pStack == nullptr) pStack = AddStack(); if ( w == 2 ) // 2 - m_next2 { - if (!pStack->RestoreState(pf, pStack->m_next2)) return false; + if (!pStack->RestoreState(istr, pStack->m_next2)) return false; } - if (!ReadWord(pf, w)) return false; + if (!ReadWord(istr, w)) return false; pStack->m_block = static_cast(w); - if (!ReadWord(pf, w)) return false; - pStack->SetState(static_cast(w)); + int state; + if (!ReadInt(istr, state)) return false; + pStack->SetState(state); - if (!ReadWord(pf, w)) return false; // backwards compatibility (m_bDontDelete) + if (!ReadWord(istr, w)) return false; // backwards compatibility (m_bDontDelete) - if (!ReadWord(pf, w)) return false; - pStack->m_step = w; + if (!ReadInt(istr, state)) return false; + pStack->m_step = state; - if (!CBotVar::RestoreState(pf, pStack->m_var)) return false; // temp variable - if (!CBotVar::RestoreState(pf, pStack->m_listVar)) return false;// local variables + if (!CBotVar::RestoreState(istr, pStack->m_var)) return false; // temp variable + if (!CBotVar::RestoreState(istr, pStack->m_listVar)) return false; // local variables - return pStack->RestoreState(pf, pStack->m_next); + return pStack->RestoreState(istr, pStack->m_next); } //////////////////////////////////////////////////////////////////////////////// -bool CBotVar::Save0State(FILE* pf) +bool CBotVar::Save0State(std::ostream &ostr) { - if (!WriteWord(pf, 100+static_cast(m_mPrivate)))return false; // private variable? - if (!WriteWord(pf, m_bStatic))return false; // static variable? - if (!WriteWord(pf, m_type.GetType()))return false; // saves the type (always non-zero) + if (!WriteWord(ostr, 100+static_cast(m_mPrivate)))return false; // private variable? + if (!WriteWord(ostr, m_bStatic))return false; // static variable? + if (!WriteWord(ostr, m_type.GetType()))return false; // saves the type (always non-zero) if (m_type.Eq(CBotTypPointer) && GetPointer() != nullptr) { if (GetPointer()->m_bConstructor) // constructor was called? { - if (!WriteWord(pf, (2000 + static_cast(m_binit)) )) return false; - return WriteString(pf, m_token->GetString()); // and variable name + if (!WriteWord(ostr, (2000 + static_cast(m_binit)) )) return false; + return WriteString(ostr, m_token->GetString()); // and variable name } } - if (!WriteWord(pf, static_cast(m_binit))) return false; // variable defined? - return WriteString(pf, m_token->GetString()); // and variable name + if (!WriteWord(ostr, static_cast(m_binit))) return false; // variable defined? + return WriteString(ostr, m_token->GetString()); // and variable name } //////////////////////////////////////////////////////////////////////////////// -bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) +bool CBotVar::RestoreState(std::istream &istr, CBotVar* &pVar) { unsigned short w, wi, prv, st; - float ww; - std::string name, s; delete pVar; @@ -798,27 +795,27 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) while ( true ) // retrieves a list { - if (!ReadWord(pf, w)) return false; // private or type? - if ( w == 0 ) return true; + if (!ReadWord(istr, w)) return false; // private or type? + if ( w == 0 ) return true; // 0 - CBot::SaveVars terminator std::string defnum; if ( w == 200 ) { - if (!ReadString(pf, defnum)) return false; // number with identifier - if (!ReadWord(pf, w)) return false; // type + if (!ReadString(istr, defnum)) return false; // number with identifier + if (!ReadWord(istr, w)) return false; // type } prv = 100; st = 0; if ( w >= 100 ) { prv = w; - if (!ReadWord(pf, st)) return false; // static - if (!ReadWord(pf, w)) return false; // type + if (!ReadWord(istr, st)) return false; // static + if (!ReadWord(istr, w)) return false; // type } if ( w == CBotTypClass ) w = CBotTypIntrinsic; // necessarily intrinsic - if (!ReadWord(pf, wi)) return false; // init ? + if (!ReadWord(istr, wi)) return false; // init ? bool bConstructor = false; if (w == CBotTypPointer && wi >= 2000) { @@ -827,31 +824,40 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) } CBotVar::InitType initType = static_cast(wi); - if (!ReadString(pf, name)) return false; // variable name - - CBotToken token(name, std::string()); + std::string varname; + if (!ReadString(istr, varname)) return false; // variable name + CBotToken token(varname, std::string()); bool isClass = false; switch (w) { - case CBotTypInt: case CBotTypBoolean: + char valByte; + if (!ReadByte(istr, valByte)) return false; pNew = CBotVar::Create(token, w); // creates a variable - if (!ReadWord(pf, w)) return false; - pNew->SetValInt(static_cast(w), defnum); + pNew->SetValInt(valByte); + break; + case CBotTypInt: + int valInt; + if (!ReadInt(istr, valInt)) return false; + pNew = CBotVar::Create(token, w); // creates a variable + pNew->SetValInt(valInt, defnum); break; case CBotTypFloat: + float valFloat; + if (!ReadFloat(istr, valFloat)) return false; pNew = CBotVar::Create(token, w); // creates a variable - if (!ReadFloat(pf, ww)) return false; - pNew->SetValFloat(ww); + pNew->SetValFloat(valFloat); break; case CBotTypString: - pNew = CBotVar::Create(token, w); // creates a variable - if (!ReadString(pf, s)) return false; - pNew->SetValString(s); - break; - + { + std::string valString; + if (!ReadString(istr, valString)) return false; + pNew = CBotVar::Create(token, w); // creates a variable + pNew->SetValString(valString); + break; + } // returns an intrinsic object or element of an array case CBotTypIntrinsic: isClass = true; @@ -859,17 +865,16 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) { CBotTypResult r; long id; - if (!ReadType(pf, r)) return false; // complete type - if (!ReadLong(pf, id) ) return false; + if (!ReadType(istr, r)) return false; // complete type + if (!ReadLong(istr, id)) return false; -// if (!ReadString(pf, s)) return false; { CBotVar* p = nullptr; if ( id ) p = CBotVarClass::Find(id) ; pNew = new CBotVarClass(token, r); // directly creates an instance // attention cptuse = 0 - if ( !RestoreState(pf, (static_cast(pNew))->m_pVar)) return false; + if (!RestoreState(istr, (static_cast(pNew))->m_pVar)) return false; pNew->SetIdent(id); if (isClass && p == nullptr) // set id for each item in this instance @@ -895,18 +900,20 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) case CBotTypPointer: case CBotTypNullPointer: - if (!ReadString(pf, s)) return false; // name of the class + { + std::string className; + if (!ReadString(istr, className)) return false; // name of the class { - CBotTypResult ptrType(w, s); - pNew = CBotVar::Create(token, ptrType);// creates a variable // CBotVarClass* p = nullptr; long id; - ReadLong(pf, id); + if (!ReadLong(istr, id)) return false; // if ( id ) p = CBotVarClass::Find(id); // found the instance (made by RestoreInstance) + CBotTypResult ptrType(w, className); + pNew = CBotVar::Create(token, ptrType); // creates a variable // returns a copy of the original instance CBotVar* pInstance = nullptr; - if ( !CBotVar::RestoreState( pf, pInstance ) ) return false; + if (!CBotVar::RestoreState(istr, pInstance)) return false; (static_cast(pNew))->SetPointer( pInstance ); // and point over if (bConstructor) pNew->ConstructorSet(); // constructor was called @@ -916,22 +923,22 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) } break; - + } case CBotTypArrayPointer: { CBotTypResult r; - if (!ReadType(pf, r)) return false; + if (!ReadType(istr, r)) return false; pNew = CBotVar::Create(token, r); // creates a variable // returns a copy of the original instance CBotVar* pInstance = nullptr; - if ( !CBotVar::RestoreState( pf, pInstance ) ) return false; + if (!CBotVar::RestoreState(istr, pInstance)) return false; (static_cast(pNew))->SetPointer( pInstance ); // and point over } break; default: - assert(0); + return false; // signal error } if ( pPrev != nullptr ) pPrev->m_next = pNew; diff --git a/src/CBot/CBotStack.h b/src/CBot/CBotStack.h index a72b3e4a..498ccd78 100644 --- a/src/CBot/CBotStack.h +++ b/src/CBot/CBotStack.h @@ -434,8 +434,8 @@ public: //! \name Write to file //@{ - bool SaveState(FILE* pf); - bool RestoreState(FILE* pf, CBotStack* &pStack); + bool SaveState(std::ostream &ostr); + bool RestoreState(std::istream &istr, CBotStack* &pStack); //@} diff --git a/src/CBot/CBotUtils.cpp b/src/CBot/CBotUtils.cpp index a6c6ef9c..a87e2943 100644 --- a/src/CBot/CBotUtils.cpp +++ b/src/CBot/CBotUtils.cpp @@ -108,38 +108,6 @@ CBotTypResult ArrayType(CBotToken* &p, CBotCStack* pile, CBotTypResult type) return type; } -//////////////////////////////////////////////////////////////////////////////// -bool WriteWord(FILE* pf, unsigned short w) -{ - size_t lg; - - lg = fwrite(&w, sizeof( unsigned short ), 1, pf ); - - return (lg == 1); -} - -//////////////////////////////////////////////////////////////////////////////// -bool WriteString(FILE* pf, std::string s) -{ - size_t lg1, lg2; - - lg1 = s.size(); - if (!WriteWord(pf, lg1)) return false; - - lg2 = fwrite(s.c_str(), 1, lg1, pf ); - return (lg1 == lg2); -} - -//////////////////////////////////////////////////////////////////////////////// -bool WriteFloat(FILE* pf, float w) -{ - size_t lg; - - lg = fwrite(&w, sizeof( float ), 1, pf ); - - return (lg == 1); -} - //////////////////////////////////////////////////////////////////////////////// long GetNumInt(const std::string& str) { diff --git a/src/CBot/CBotUtils.h b/src/CBot/CBotUtils.h index 9042b0f8..5b58535c 100644 --- a/src/CBot/CBotUtils.h +++ b/src/CBot/CBotUtils.h @@ -19,9 +19,8 @@ #pragma once -#include "CBot/CBotTypResult.h" +#include "CBot/CBotFileUtils.h" -#include #include #include @@ -31,6 +30,8 @@ namespace CBot class CBotVar; class CBotToken; class CBotCStack; +class CBotTypResult; + /*! * \brief MakeListVars Transforms the array of pointers to variables in a @@ -58,30 +59,6 @@ CBotTypResult TypeParam(CBotToken* &p, CBotCStack* pile); */ CBotTypResult ArrayType(CBotToken* &p, CBotCStack* pile, CBotTypResult type); -/*! - * \brief WriteWord - * \param pf - * \param w - * \return - */ -bool WriteWord(FILE* pf, unsigned short w); - -/*! - * \brief WriteString - * \param pf - * \param s - * \return - */ -bool WriteString(FILE* pf, std::string s); - -/*! - * \brief WriteFloat - * \param pf - * \param w - * \return - */ -bool WriteFloat(FILE* pf, float w); - /*! * \brief GetNumInt Converts a string into integer may be of the form 0xabc123. * \param str diff --git a/src/CBot/CBotVar/CBotVar.cpp b/src/CBot/CBotVar/CBotVar.cpp index 62ba41f4..4886c033 100644 --- a/src/CBot/CBotVar/CBotVar.cpp +++ b/src/CBot/CBotVar/CBotVar.cpp @@ -134,7 +134,7 @@ void* CBotVar::GetUserPtr() } //////////////////////////////////////////////////////////////////////////////// -bool CBotVar::Save1State(FILE* pf) +bool CBotVar::Save1State(std::ostream &ostr) { // this routine "virtual" must never be called, // there must be a routine for each of the subclasses (CBotVarInt, CBotVarFloat, etc) diff --git a/src/CBot/CBotVar/CBotVar.h b/src/CBot/CBotVar/CBotVar.h index d2805ddf..980b8a71 100644 --- a/src/CBot/CBotVar/CBotVar.h +++ b/src/CBot/CBotVar/CBotVar.h @@ -623,28 +623,28 @@ public: /** * \brief Save common variable header (name, type, etc.) - * \param pf file pointer + * \param ostr Output stream * \return false on write error */ - virtual bool Save0State(FILE* pf); + virtual bool Save0State(std::ostream &ostr); /** * \brief Save variable data * * Overriden in child classes * - * \param pf file pointer + * \param ostr Output stream * \return false on write error */ - virtual bool Save1State(FILE* pf); + virtual bool Save1State(std::ostream &ostr); /** * \brief Restore variable - * \param pf file pointer + * \param istr Input stream * \param[out] pVar Pointer to recieve the variable * \return false on read error */ - static bool RestoreState(FILE* pf, CBotVar* &pVar); + static bool RestoreState(std::istream &istr, CBotVar* &pVar); //@} diff --git a/src/CBot/CBotVar/CBotVarArray.cpp b/src/CBot/CBotVar/CBotVarArray.cpp index 6462d119..05575dc1 100644 --- a/src/CBot/CBotVar/CBotVarArray.cpp +++ b/src/CBot/CBotVar/CBotVarArray.cpp @@ -20,7 +20,6 @@ #include "CBot/CBotVar/CBotVarArray.h" #include "CBot/CBotVar/CBotVarClass.h" #include "CBot/CBotToken.h" -#include "CBot/CBotFileUtils.h" #include "CBot/CBotEnums.h" @@ -137,10 +136,10 @@ std::string CBotVarArray::GetValString() } //////////////////////////////////////////////////////////////////////////////// -bool CBotVarArray::Save1State(FILE* pf) +bool CBotVarArray::Save1State(std::ostream &ostr) { - if ( !WriteType(pf, m_type) ) return false; - return SaveVars(pf, m_pInstance); // saves the instance that manages the table + if (!WriteType(ostr, m_type)) return false; + return SaveVars(ostr, m_pInstance); // saves the instance that manages the table } } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarArray.h b/src/CBot/CBotVar/CBotVarArray.h index 68b00b28..fb211055 100644 --- a/src/CBot/CBotVar/CBotVarArray.h +++ b/src/CBot/CBotVar/CBotVarArray.h @@ -51,7 +51,7 @@ public: std::string GetValString() override; - bool Save1State(FILE* pf) override; + bool Save1State(std::ostream &ostr) override; private: //! Array data diff --git a/src/CBot/CBotVar/CBotVarBoolean.cpp b/src/CBot/CBotVar/CBotVarBoolean.cpp index d36de078..b733e933 100644 --- a/src/CBot/CBotVar/CBotVarBoolean.cpp +++ b/src/CBot/CBotVar/CBotVarBoolean.cpp @@ -40,9 +40,9 @@ void CBotVarBoolean::Not() SetValInt(!GetValInt()); } -bool CBotVarBoolean::Save1State(FILE* pf) +bool CBotVarBoolean::Save1State(std::ostream &ostr) { - return WriteWord(pf, m_val); // the value of the variable + return WriteByte(ostr, m_val); // the value of the variable } } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarBoolean.h b/src/CBot/CBotVar/CBotVarBoolean.h index 44774a9f..da810e98 100644 --- a/src/CBot/CBotVar/CBotVarBoolean.h +++ b/src/CBot/CBotVar/CBotVarBoolean.h @@ -37,7 +37,7 @@ public: void XOr(CBotVar* left, CBotVar* right) override; void Not() override; - bool Save1State(FILE* pf) override; + bool Save1State(std::ostream &ostr) override; }; } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarClass.cpp b/src/CBot/CBotVar/CBotVarClass.cpp index 36952d18..9d587c1f 100644 --- a/src/CBot/CBotVar/CBotVarClass.cpp +++ b/src/CBot/CBotVar/CBotVarClass.cpp @@ -23,8 +23,6 @@ #include "CBot/CBotStack.h" #include "CBot/CBotDefines.h" -#include "CBot/CBotFileUtils.h" - #include "CBot/CBotInstr/CBotInstr.h" #include @@ -464,12 +462,12 @@ bool CBotVarClass::Ne(CBotVar* left, CBotVar* right) } //////////////////////////////////////////////////////////////////////////////// -bool CBotVarClass::Save1State(FILE* pf) +bool CBotVarClass::Save1State(std::ostream &ostr) { - if ( !WriteType(pf, m_type) ) return false; - if ( !WriteLong(pf, m_ItemIdent) ) return false; + if (!WriteType(ostr, m_type)) return false; + if (!WriteLong(ostr, m_ItemIdent)) return false; - return SaveVars(pf, m_pVar); // content of the object + return SaveVars(ostr, m_pVar); // content of the object } } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarClass.h b/src/CBot/CBotVar/CBotVarClass.h index 55bdd891..34ec732e 100644 --- a/src/CBot/CBotVar/CBotVarClass.h +++ b/src/CBot/CBotVar/CBotVarClass.h @@ -54,7 +54,7 @@ public: CBotVar* GetItemList() override; std::string GetValString() override; - bool Save1State(FILE* pf) override; + bool Save1State(std::ostream &ostr) override; void Update(void* pUser) override; diff --git a/src/CBot/CBotVar/CBotVarFloat.cpp b/src/CBot/CBotVar/CBotVarFloat.cpp index a1c4217f..bfa1c1f9 100644 --- a/src/CBot/CBotVar/CBotVarFloat.cpp +++ b/src/CBot/CBotVar/CBotVarFloat.cpp @@ -22,9 +22,9 @@ namespace CBot { -bool CBotVarFloat::Save1State(FILE* pf) +bool CBotVarFloat::Save1State(std::ostream &ostr) { - return WriteFloat(pf, m_val); // the value of the variable + return WriteFloat(ostr, m_val); // the value of the variable } } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarFloat.h b/src/CBot/CBotVar/CBotVarFloat.h index 81cbcde0..dfdecd9b 100644 --- a/src/CBot/CBotVar/CBotVarFloat.h +++ b/src/CBot/CBotVar/CBotVarFloat.h @@ -32,7 +32,7 @@ class CBotVarFloat : public CBotVarNumber public: CBotVarFloat(const CBotToken &name) : CBotVarNumber(name) {} - bool Save1State(FILE* pf) override; + bool Save1State(std::ostream &ostr) override; }; } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarInt.cpp b/src/CBot/CBotVar/CBotVarInt.cpp index 477629ae..852566f9 100644 --- a/src/CBot/CBotVar/CBotVarInt.cpp +++ b/src/CBot/CBotVar/CBotVarInt.cpp @@ -45,17 +45,17 @@ std::string CBotVarInt::GetValString() void CBotVarInt::Neg() { CBotVarNumber::Neg(); - m_defnum.empty(); + m_defnum.clear(); } void CBotVarInt::Inc() { CBotVarNumber::Inc(); - m_defnum.empty(); + m_defnum.clear(); } void CBotVarInt::Dec() { CBotVarNumber::Dec(); - m_defnum.empty(); + m_defnum.clear(); } void CBotVarInt::XOr(CBotVar* left, CBotVar* right) @@ -90,22 +90,23 @@ void CBotVarInt::SR(CBotVar* left, CBotVar* right) void CBotVarInt::Not() { m_val = ~m_val; + m_defnum.clear(); } -bool CBotVarInt::Save0State(FILE* pf) +bool CBotVarInt::Save0State(std::ostream &ostr) { if (!m_defnum.empty()) { - if(!WriteWord(pf, 200)) return false; // special marker - if(!WriteString(pf, m_defnum)) return false; + if(!WriteWord(ostr, 200)) return false; // special marker + if(!WriteString(ostr, m_defnum)) return false; } - return CBotVar::Save0State(pf); + return CBotVar::Save0State(ostr); } -bool CBotVarInt::Save1State(FILE* pf) +bool CBotVarInt::Save1State(std::ostream &ostr) { - return WriteWord(pf, m_val); + return WriteInt(ostr, m_val); } } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarInt.h b/src/CBot/CBotVar/CBotVarInt.h index 906a402d..5e04135c 100644 --- a/src/CBot/CBotVar/CBotVarInt.h +++ b/src/CBot/CBotVar/CBotVarInt.h @@ -50,8 +50,8 @@ public: void SR(CBotVar* left, CBotVar* right) override; void ASR(CBotVar* left, CBotVar* right) override; - bool Save0State(FILE* pf) override; - bool Save1State(FILE* pf) override; + bool Save0State(std::ostream &ostr) override; + bool Save1State(std::ostream &ostr) override; protected: //! The name if given by DefineNum. diff --git a/src/CBot/CBotVar/CBotVarPointer.cpp b/src/CBot/CBotVar/CBotVarPointer.cpp index 2f78c90a..24f9a5cb 100644 --- a/src/CBot/CBotVar/CBotVarPointer.cpp +++ b/src/CBot/CBotVar/CBotVarPointer.cpp @@ -24,7 +24,6 @@ #include "CBot/CBotEnums.h" #include "CBot/CBotUtils.h" -#include "CBot/CBotFileUtils.h" #include @@ -171,21 +170,21 @@ CBotClass* CBotVarPointer::GetClass() } //////////////////////////////////////////////////////////////////////////////// -bool CBotVarPointer::Save1State(FILE* pf) +bool CBotVarPointer::Save1State(std::ostream &ostr) { if ( m_type.GetClass() != nullptr ) { - if (!WriteString(pf, m_type.GetClass()->GetName())) return false; // name of the class + if (!WriteString(ostr, m_type.GetClass()->GetName())) return false; // name of the class } else { - if (!WriteString(pf, "")) return false; + if (!WriteString(ostr, "")) return false; } - if (!WriteLong(pf, GetIdent())) return false; // the unique reference + if (!WriteLong(ostr, GetIdent())) return false; // the unique reference // also saves the proceedings copies - return SaveVars(pf, GetPointer()); + return SaveVars(ostr, GetPointer()); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/CBot/CBotVar/CBotVarPointer.h b/src/CBot/CBotVar/CBotVarPointer.h index 75c9bc98..6f9c0155 100644 --- a/src/CBot/CBotVar/CBotVarPointer.h +++ b/src/CBot/CBotVar/CBotVarPointer.h @@ -61,7 +61,7 @@ public: void ConstructorSet() override; - bool Save1State(FILE* pf) override; + bool Save1State(std::ostream &ostr) override; void Update(void* pUser) override; diff --git a/src/CBot/CBotVar/CBotVarString.cpp b/src/CBot/CBotVar/CBotVarString.cpp index 91a0e618..90856b82 100644 --- a/src/CBot/CBotVar/CBotVarString.cpp +++ b/src/CBot/CBotVar/CBotVarString.cpp @@ -37,9 +37,9 @@ bool CBotVarString::Ne(CBotVar* left, CBotVar* right) return left->GetValString() != right->GetValString(); } -bool CBotVarString::Save1State(FILE* pf) +bool CBotVarString::Save1State(std::ostream &ostr) { - return WriteString(pf, m_val); + return WriteString(ostr, m_val); } } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarString.h b/src/CBot/CBotVar/CBotVarString.h index d01a80f6..ac04a310 100644 --- a/src/CBot/CBotVar/CBotVarString.h +++ b/src/CBot/CBotVar/CBotVarString.h @@ -63,7 +63,7 @@ public: bool Eq(CBotVar* left, CBotVar* right) override; bool Ne(CBotVar* left, CBotVar* right) override; - bool Save1State(FILE* pf) override; + bool Save1State(std::ostream &ostr) override; private: template diff --git a/src/CBot/CBotVar/CBotVarValue.h b/src/CBot/CBotVar/CBotVarValue.h index 76e820e8..5e58075d 100644 --- a/src/CBot/CBotVar/CBotVarValue.h +++ b/src/CBot/CBotVar/CBotVarValue.h @@ -86,7 +86,10 @@ template class CBotVarNumberBase : public CBotVarValue { public: - CBotVarNumberBase(const CBotToken &name) : CBotVarValue(name) {} + CBotVarNumberBase(const CBotToken &name) : CBotVarValue(name) + { + this->m_val = static_cast(0); + } void SetValInt(int val, const std::string &s = "") override { diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 58d90541..dea86bcb 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -4449,10 +4449,8 @@ void CRobotMain::SaveOneScript(CObject *obj) } //! Saves the stack of the program in execution of a robot -bool CRobotMain::SaveFileStack(CObject *obj, FILE *file, int objRank) +bool CRobotMain::SaveFileStack(CObject *obj, std::ostream &ostr) { - if (objRank == -1) return true; - if (! obj->Implements(ObjectInterfaceType::Programmable)) return true; CProgrammableObject* programmable = dynamic_cast(obj); @@ -4460,14 +4458,24 @@ bool CRobotMain::SaveFileStack(CObject *obj, FILE *file, int objRank) ObjectType type = obj->GetType(); if (type == OBJECT_HUMAN) return true; - return programmable->WriteStack(file); + long status = 1; + std::stringstream sstr(""); + + if (!programmable->WriteStack(sstr)) + { + GetLogger()->Error("WriteStack failed at object id = %i\n", obj->GetID()); + status = 100; // marked bad + } + + if (!CBot::WriteLong(ostr, status)) return false; + if (!CBot::WriteStream(ostr, sstr)) return false; + + return true; } //! Resumes the execution stack of the program in a robot -bool CRobotMain::ReadFileStack(CObject *obj, FILE *file, int objRank) +bool CRobotMain::ReadFileStack(CObject *obj, std::istream &istr) { - if (objRank == -1) return true; - if (! obj->Implements(ObjectInterfaceType::Programmable)) return true; CProgrammableObject* programmable = dynamic_cast(obj); @@ -4475,7 +4483,29 @@ bool CRobotMain::ReadFileStack(CObject *obj, FILE *file, int objRank) ObjectType type = obj->GetType(); if (type == OBJECT_HUMAN) return true; - return programmable->ReadStack(file); + long status; + if (!CBot::ReadLong(istr, status)) return false; + + if (status == 100) // was marked bad ? + { + if (!CBot::ReadLong(istr, status)) return false; + if (!istr.seekg(status, istr.cur)) return false; + return true; // next program + } + + if (status == 1) + { + std::stringstream sstr(""); + if (!CBot::ReadStream(istr, sstr)) return false; + + if (!programmable->ReadStack(sstr)) + { + GetLogger()->Error("ReadStack failed at object id = %i\n", obj->GetID()); + } + return true; // next program + } + + return false; // error: status == ?? } std::vector CRobotMain::GetNewScriptNames(ObjectType type) @@ -4670,25 +4700,36 @@ bool CRobotMain::IOWriteScene(std::string filename, std::string filecbot, std::s } // Writes the file of stacks of execution. - FILE* file = CBot::fOpen((CResourceManager::GetSaveLocation() + "/" + filecbot).c_str(), "wb"); - if (file == nullptr) return false; + COutputStream ostr(filecbot); + if (!ostr.is_open()) return false; + bool bError = false; long version = 1; - CBot::fWrite(&version, sizeof(long), 1, file); // version of COLOBOT + CBot::WriteLong(ostr, version); // version of COLOBOT version = CBot::CBotProgram::GetVersion(); - CBot::fWrite(&version, sizeof(long), 1, file); // version of CBOT + CBot::WriteLong(ostr, version); // version of CBOT + CBot::WriteWord(ostr, 0); // TODO - objRank = 0; for (CObject* obj : m_objMan->GetAllObjects()) { if (obj->GetType() == OBJECT_TOTO) continue; if (IsObjectBeingTransported(obj)) continue; if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(obj)->IsDying()) continue; - if (!SaveFileStack(obj, file, objRank++)) break; + if (!SaveFileStack(obj, ostr)) + { + GetLogger()->Error("SaveFileStack failed at object id = %i\n", obj->GetID()); + bError = true; + break; + } } - CBot::CBotClass::SaveStaticState(file); - CBot::fClose(file); + + if (!bError && !CBot::CBotClass::SaveStaticState(ostr)) + { + GetLogger()->Error("CBotClass save static state failed\n"); + } + + ostr.close(); if (!emergencySave) { @@ -4846,29 +4887,48 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) m_ui->GetLoadingScreen()->SetProgress(0.95f, RT_LOADING_CBOT_SAVE); // Reads the file of stacks of execution. - FILE* file = CBot::fOpen((CResourceManager::GetSaveLocation() + "/" + filecbot).c_str(), "rb"); - if (file != nullptr) + CInputStream istr(filecbot); + + if (istr.is_open()) { - long version; - CBot::fRead(&version, sizeof(long), 1, file); // version of COLOBOT + bool bError = false; + long version = 0; + CBot::ReadLong(istr, version); // version of COLOBOT if (version == 1) { - CBot::fRead(&version, sizeof(long), 1, file); // version of CBOT + CBot::ReadLong(istr, version); // version of CBOT if (version == CBot::CBotProgram::GetVersion()) { - objRank = 0; - for (CObject* obj : m_objMan->GetAllObjects()) + unsigned short flag; + CBot::ReadWord(istr, flag); // TODO + bError = (flag != 0); + + if (!bError) for (CObject* obj : m_objMan->GetAllObjects()) { if (obj->GetType() == OBJECT_TOTO) continue; if (IsObjectBeingTransported(obj)) continue; if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(obj)->IsDying()) continue; - if (!ReadFileStack(obj, file, objRank++)) break; + if (!ReadFileStack(obj, istr)) + { + GetLogger()->Error("ReadFileStack failed at object id = %i\n", obj->GetID()); + bError = true; + break; + } + } + + if (!bError && !CBot::CBotClass::RestoreStaticState(istr)) + { + GetLogger()->Error("CBotClass restore static state failed\n"); + bError = true; } } + else + GetLogger()->Error("cbot.run file is wrong version: %i\n", version); } - CBot::CBotClass::RestoreStaticState(file); - CBot::fClose(file); + + if (bError) GetLogger()->Error("Restoring CBOT state failed at stream position: %li\n", istr.tellg()); + istr.close(); } m_ui->GetLoadingScreen()->SetProgress(1.0f, RT_LOADING_FINISHED); diff --git a/src/level/robotmain.h b/src/level/robotmain.h index 2614f2cc..2b4c7b03 100644 --- a/src/level/robotmain.h +++ b/src/level/robotmain.h @@ -308,8 +308,8 @@ public: void SaveAllScript(); void SaveOneScript(CObject *obj); - bool SaveFileStack(CObject *obj, FILE *file, int objRank); - bool ReadFileStack(CObject *obj, FILE *file, int objRank); + bool SaveFileStack(CObject *obj, std::ostream &ostr); + bool ReadFileStack(CObject *obj, std::istream &istr); //! Return list of scripts to load to robot created in BotFactory std::vector GetNewScriptNames(ObjectType type); diff --git a/src/object/implementation/programmable_impl.cpp b/src/object/implementation/programmable_impl.cpp index 5e370bdb..34be6c59 100644 --- a/src/object/implementation/programmable_impl.cpp +++ b/src/object/implementation/programmable_impl.cpp @@ -143,24 +143,43 @@ bool CProgrammableObjectImpl::IsProgram() // Load a stack of script implementation from a file. -bool CProgrammableObjectImpl::ReadStack(FILE *file) +bool CProgrammableObjectImpl::ReadStack(std::istream &istr) { short op; - CBot::fRead(&op, sizeof(short), 1, file); + if (!CBot::ReadShort(istr, op)) return false; if ( op == 1 ) // run ? { - CBot::fRead(&op, sizeof(short), 1, file); // program rank + if (!CBot::ReadShort(istr, op)) return false; // program rank if ( op >= 0 ) { if (m_object->Implements(ObjectInterfaceType::ProgramStorage)) { - assert(op < static_cast(dynamic_cast(m_object)->GetProgramCount())); + int count = static_cast(dynamic_cast(m_object)->GetProgramCount()); + if (!(op < count)) + { + GetLogger()->Info("Object program count: %i\n", count); + GetLogger()->Error("Error in file: program index out of range: %i\n", op); + return false; + } + m_currentProgram = dynamic_cast(m_object)->GetProgram(op); - if ( !m_currentProgram->script->ReadStack(file) ) return false; + if (!m_currentProgram->script->ReadStack(istr)) + { + GetLogger()->Error("Restore state failed at program index: %i\n", op); + int errNum = m_currentProgram->script->GetError(); + if (errNum != 0) + { + std::string errStr; + m_currentProgram->script->GetError(errStr); + GetLogger()->Error("Program reports error: %i:(%s)\n", errNum, errStr.c_str()); + } + return false; + } } else { + GetLogger()->Error("Object is not a program storage object\n"); return false; } } @@ -171,7 +190,7 @@ bool CProgrammableObjectImpl::ReadStack(FILE *file) // Save the script implementation stack of a file. -bool CProgrammableObjectImpl::WriteStack(FILE *file) +bool CProgrammableObjectImpl::WriteStack(std::ostream &ostr) { short op; @@ -179,21 +198,25 @@ bool CProgrammableObjectImpl::WriteStack(FILE *file) m_currentProgram->script->IsRunning() ) { op = 1; // run - CBot::fWrite(&op, sizeof(short), 1, file); + if (!CBot::WriteShort(ostr, op)) return false; op = -1; if (m_object->Implements(ObjectInterfaceType::ProgramStorage)) { op = dynamic_cast(m_object)->GetProgramIndex(m_currentProgram); } - CBot::fWrite(&op, sizeof(short), 1, file); + if (!CBot::WriteShort(ostr, op)) return false; - return m_currentProgram->script->WriteStack(file); + if (!m_currentProgram->script->WriteStack(ostr)) + { + GetLogger()->Error("Save state failed at program index: %i\n", op); + return false; + } + return true; } op = 0; // stop - CBot::fWrite(&op, sizeof(short), 1, file); - return true; + return CBot::WriteShort(ostr, op); } diff --git a/src/object/implementation/programmable_impl.h b/src/object/implementation/programmable_impl.h index a0b78096..38b8be7c 100644 --- a/src/object/implementation/programmable_impl.h +++ b/src/object/implementation/programmable_impl.h @@ -58,8 +58,8 @@ public: Program* GetCurrentProgram() override; void StopProgram() override; - bool ReadStack(FILE *file) override; - bool WriteStack(FILE *file) override; + bool ReadStack(std::istream &istr) override; + bool WriteStack(std::ostream &ostr) override; void TraceRecordStart() override; void TraceRecordStop() override; diff --git a/src/object/interface/programmable_object.h b/src/object/interface/programmable_object.h index 05f1a92c..1ce0d5d4 100644 --- a/src/object/interface/programmable_object.h +++ b/src/object/interface/programmable_object.h @@ -53,9 +53,9 @@ public: virtual bool IsProgram() = 0; //! Save current execution status to file - virtual bool WriteStack(FILE *file) = 0; + virtual bool WriteStack(std::ostream &ostr) = 0; //! Read current execution status from file - virtual bool ReadStack(FILE *file) = 0; + virtual bool ReadStack(std::istream &istr) = 0; //! Start recording trace virtual void TraceRecordStart() = 0; diff --git a/src/script/script.cpp b/src/script/script.cpp index a94e850f..22ae1024 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -1032,16 +1032,16 @@ bool CScript::WriteScript(const char* filename) // Reads a stack of script by execution as a file. -bool CScript::ReadStack(FILE *file) +bool CScript::ReadStack(std::istream &istr) { int nb; - CBot::fRead(&nb, sizeof(int), 1, file); - CBot::fRead(&m_ipf, sizeof(int), 1, file); - CBot::fRead(&m_errMode, sizeof(int), 1, file); + if (!CBot::ReadInt(istr, nb)) return false; + if (!CBot::ReadInt(istr, m_ipf)) return false; + if (!CBot::ReadInt(istr, m_errMode)) return false; if (m_botProg == nullptr) return false; - if ( !m_botProg->RestoreState(file) ) return false; + if (!m_botProg->RestoreState(istr)) return false; m_bRun = true; m_bContinue = false; @@ -1050,16 +1050,16 @@ bool CScript::ReadStack(FILE *file) // Writes a stack of script by execution as a file. -bool CScript::WriteStack(FILE *file) +bool CScript::WriteStack(std::ostream &ostr) { int nb; nb = 2; - CBot::fWrite(&nb, sizeof(int), 1, file); - CBot::fWrite(&m_ipf, sizeof(int), 1, file); - CBot::fWrite(&m_errMode, sizeof(int), 1, file); + if (!CBot::WriteInt(ostr, nb)) return false; + if (!CBot::WriteInt(ostr, m_ipf)) return false; + if (!CBot::WriteInt(ostr, m_errMode)) return false; - return m_botProg->SaveState(file); + return m_botProg->SaveState(ostr); } diff --git a/src/script/script.h b/src/script/script.h index f0f473fb..bc64dc23 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -88,8 +88,8 @@ public: bool SendScript(const char* text); bool ReadScript(const char* filename); bool WriteScript(const char* filename); - bool ReadStack(FILE *file); - bool WriteStack(FILE *file); + bool ReadStack(std::istream &istr); + bool WriteStack(std::ostream &ostr); bool Compare(CScript* other); void SetFilename(const std::string &filename); diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index cc3261b8..f16637bd 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -22,6 +22,9 @@ #include #include +extern bool g_cbotTestSaveState; +bool g_cbotTestSaveState = false; + using namespace CBot; class CBotUT : public testing::Test @@ -197,6 +200,23 @@ private: return ss.str(); } + static void TestSaveAndRestore(CBotProgram* program) + { + std::stringstream sstr(""); + // save + if (!program->SaveState(sstr)) + throw CBotTestFail("CBotProgram::SaveState Failed"); + + if (!CBotClass::SaveStaticState(sstr)) + throw CBotTestFail("CBotClass::SaveStaticState Failed"); + // restore + if (!program->RestoreState(sstr)) + throw CBotTestFail("CBotProgram::RestoreState Failed"); + + if (!CBotClass::RestoreStaticState(sstr)) + throw CBotTestFail("CBotClass::RestoreStaticState Failed"); + } + protected: std::unique_ptr ExecuteTest(const std::string& code, CBotError expectedError = CBotNoErr) { @@ -231,7 +251,17 @@ protected: try { program->Start(test); - while (!program->Run()); + if (g_cbotTestSaveState) + { + while (!program->Run(nullptr, 0)) // save/restore at each step + { + TestSaveAndRestore(program.get()); + } + } + else + { + while (!program->Run(nullptr, 0)); // execute in step mode + } program->GetError(error, cursor1, cursor2); if (error != expectedRuntimeError) { diff --git a/test/unit/main.cpp b/test/unit/main.cpp index 91d640de..8bed282c 100644 --- a/test/unit/main.cpp +++ b/test/unit/main.cpp @@ -21,11 +21,21 @@ #include +extern bool g_cbotTestSaveState; + int main(int argc, char* argv[]) { CLogger logger; ::testing::InitGoogleTest(&argc, argv); + // parse arguments not removed by InitGoogleTest + for (int i = 1; i < argc; ++i) + { + std::string arg(argv[i]); + if (arg == "--CBotUT_TestSaveState") + g_cbotTestSaveState = true; + } + return RUN_ALL_TESTS(); } From 250ea57e8b2dfd081116ae93b07122e582c87363 Mon Sep 17 00:00:00 2001 From: melex750 Date: Thu, 11 Apr 2019 04:15:27 -0400 Subject: [PATCH 2/7] Fix arithmetic operations with integers (#993) Also fixed unsigned right shift operator ">>>" --- src/CBot/CBotInstr/CBotInstrUtils.cpp | 7 +++ src/CBot/CBotToken.cpp | 2 +- src/CBot/CBotVar/CBotVar.cpp | 5 ++ src/CBot/CBotVar/CBotVar.h | 1 + src/CBot/CBotVar/CBotVarInt.cpp | 26 +------- src/CBot/CBotVar/CBotVarInt.h | 18 +++--- src/CBot/CBotVar/CBotVarValue.h | 91 ++++++++++++++++++++------- test/unit/CBot/CBot_test.cpp | 14 +++++ 8 files changed, 109 insertions(+), 55 deletions(-) diff --git a/src/CBot/CBotInstr/CBotInstrUtils.cpp b/src/CBot/CBotInstr/CBotInstrUtils.cpp index 04c42211..774a4e37 100644 --- a/src/CBot/CBotInstr/CBotInstrUtils.cpp +++ b/src/CBot/CBotInstr/CBotInstrUtils.cpp @@ -129,6 +129,13 @@ bool TypeCompatible(CBotTypResult& type1, CBotTypResult& type2, int op) return true; } + if (op == ID_ASR || op == ID_SR || op == ID_SL || + op == ID_ASSOR || op == ID_ASSSL || op == ID_ASSSR || + op == ID_ASSAND || op == ID_ASSXOR || op == ID_ASSASR) + { + if (max > CBotTypLong) return false; + } + type1.SetType(max); type2.SetType(max); return true; diff --git a/src/CBot/CBotToken.cpp b/src/CBot/CBotToken.cpp index ea31fad9..b3a5a461 100644 --- a/src/CBot/CBotToken.cpp +++ b/src/CBot/CBotToken.cpp @@ -103,7 +103,7 @@ static const boost::bimap KEYWORDS = makeBimap>>="}, {ID_ASSASR, ">>="}, {ID_SL, "<<"}, - {ID_SR, ">>"}, + {ID_SR, ">>>"}, {ID_ASR, ">>"}, {ID_INC, "++"}, {ID_DEC, "--"}, diff --git a/src/CBot/CBotVar/CBotVar.cpp b/src/CBot/CBotVar/CBotVar.cpp index 4886c033..d9947e5b 100644 --- a/src/CBot/CBotVar/CBotVar.cpp +++ b/src/CBot/CBotVar/CBotVar.cpp @@ -752,6 +752,11 @@ CBotClass* CBotVar::GetClass() return nullptr; } +CBotVar::operator bool() +{ + return static_cast(GetValInt()); +} + CBotVar::operator int() { return GetValInt(); diff --git a/src/CBot/CBotVar/CBotVar.h b/src/CBot/CBotVar/CBotVar.h index 980b8a71..1a9c1498 100644 --- a/src/CBot/CBotVar/CBotVar.h +++ b/src/CBot/CBotVar/CBotVar.h @@ -444,6 +444,7 @@ public: */ //@{ + operator bool(); operator int(); operator float(); operator std::string(); diff --git a/src/CBot/CBotVar/CBotVarInt.cpp b/src/CBot/CBotVar/CBotVarInt.cpp index 852566f9..cd602614 100644 --- a/src/CBot/CBotVar/CBotVarInt.cpp +++ b/src/CBot/CBotVar/CBotVarInt.cpp @@ -58,33 +58,9 @@ void CBotVarInt::Dec() m_defnum.clear(); } -void CBotVarInt::XOr(CBotVar* left, CBotVar* right) -{ - SetValInt(left->GetValInt() ^ right->GetValInt()); -} -void CBotVarInt::And(CBotVar* left, CBotVar* right) -{ - SetValInt(left->GetValInt() & right->GetValInt()); -} -void CBotVarInt::Or(CBotVar* left, CBotVar* right) -{ - SetValInt(left->GetValInt() | right->GetValInt()); -} - -void CBotVarInt::SL(CBotVar* left, CBotVar* right) -{ - SetValInt(left->GetValInt() << right->GetValInt()); -} -void CBotVarInt::ASR(CBotVar* left, CBotVar* right) -{ - SetValInt(left->GetValInt() >> right->GetValInt()); -} void CBotVarInt::SR(CBotVar* left, CBotVar* right) { - int source = left->GetValInt(); - int shift = right->GetValInt(); - if (shift >= 1) source &= 0x7fffffff; - SetValInt(source >> shift); + SetValInt(static_cast(left->GetValInt()) >> right->GetValInt()); } void CBotVarInt::Not() diff --git a/src/CBot/CBotVar/CBotVarInt.h b/src/CBot/CBotVar/CBotVarInt.h index 5e04135c..6fb51208 100644 --- a/src/CBot/CBotVar/CBotVarInt.h +++ b/src/CBot/CBotVar/CBotVarInt.h @@ -27,10 +27,10 @@ namespace CBot /** * \brief CBotVar subclass for managing integer values (::CBotTypInt) */ -class CBotVarInt : public CBotVarNumber +class CBotVarInt : public CBotVarInteger { public: - CBotVarInt(const CBotToken &name) : CBotVarNumber(name) {} + CBotVarInt(const CBotToken &name) : CBotVarInteger(name) {} void SetValInt(int val, const std::string& s = "") override; std::string GetValString() override; @@ -40,19 +40,21 @@ public: void Neg() override; void Inc() override; void Dec() override; - - void XOr(CBotVar* left, CBotVar* right) override; - void Or(CBotVar* left, CBotVar* right) override; - void And(CBotVar* left, CBotVar* right) override; void Not() override; - void SL(CBotVar* left, CBotVar* right) override; void SR(CBotVar* left, CBotVar* right) override; - void ASR(CBotVar* left, CBotVar* right) override; bool Save0State(std::ostream &ostr) override; bool Save1State(std::ostream &ostr) override; +protected: + + void SetValue(int val) override + { + CBotVarNumberBase::SetValue(val); + m_defnum.clear(); + } + protected: //! The name if given by DefineNum. std::string m_defnum; diff --git a/src/CBot/CBotVar/CBotVarValue.h b/src/CBot/CBotVar/CBotVarValue.h index 5e58075d..9c0b0361 100644 --- a/src/CBot/CBotVar/CBotVarValue.h +++ b/src/CBot/CBotVar/CBotVarValue.h @@ -74,6 +74,13 @@ public: return s.str(); } +protected: + virtual void SetValue(T val) + { + this->m_val = val; + this->m_binit = CBotVar::InitType::DEF; + } + protected: //! The value T m_val; @@ -93,14 +100,12 @@ public: void SetValInt(int val, const std::string &s = "") override { - this->m_val = static_cast(val); - this->m_binit = CBotVar::InitType::DEF; + this->SetValue(static_cast(val)); } void SetValFloat(float val) override { - this->m_val = static_cast(val); - this->m_binit = CBotVar::InitType::DEF; + this->SetValue(static_cast(val)); } int GetValInt() override @@ -116,11 +121,11 @@ public: bool Eq(CBotVar* left, CBotVar* right) override { - return left->GetValFloat() == right->GetValFloat(); + return static_cast(*left) == static_cast(*right); } bool Ne(CBotVar* left, CBotVar* right) override { - return left->GetValFloat() != right->GetValFloat(); + return static_cast(*left) != static_cast(*right); } }; @@ -135,33 +140,33 @@ public: void Mul(CBotVar* left, CBotVar* right) override { - this->SetValFloat(left->GetValFloat() * right->GetValFloat()); + this->SetValue(static_cast(*left) * static_cast(*right)); } void Power(CBotVar* left, CBotVar* right) override { - this->SetValFloat(pow(left->GetValFloat(), right->GetValFloat())); + this->SetValue(pow(static_cast(*left), static_cast(*right))); } CBotError Div(CBotVar* left, CBotVar* right) override { - float r = right->GetValFloat(); - if (r == 0) return CBotErrZeroDiv; - this->SetValFloat(left->GetValFloat() / r); + T r = static_cast(*right); + if ( r == static_cast(0) ) return CBotErrZeroDiv; + this->SetValue(static_cast(*left) / r); return CBotNoErr; } CBotError Modulo(CBotVar* left, CBotVar* right) override { - float r = right->GetValFloat(); - if (r == 0) return CBotErrZeroDiv; - this->SetValFloat(fmod(left->GetValFloat(), r)); + T r = static_cast(*right); + if ( r == static_cast(0) ) return CBotErrZeroDiv; + this->SetValue(fmod(static_cast(*left), r)); return CBotNoErr; } void Add(CBotVar* left, CBotVar* right) override { - this->SetValFloat(left->GetValFloat() + right->GetValFloat()); + this->SetValue(static_cast(*left) + static_cast(*right)); } void Sub(CBotVar* left, CBotVar* right) override { - this->SetValFloat(left->GetValFloat() - right->GetValFloat()); + this->SetValue(static_cast(*left) - static_cast(*right)); } void Neg() override @@ -179,21 +184,65 @@ public: bool Lo(CBotVar* left, CBotVar* right) override { - return left->GetValFloat() < right->GetValFloat(); + return static_cast(*left) < static_cast(*right); } bool Hi(CBotVar* left, CBotVar* right) override { - return left->GetValFloat() > right->GetValFloat(); + return static_cast(*left) > static_cast(*right); } bool Ls(CBotVar* left, CBotVar* right) override { - return left->GetValFloat() <= right->GetValFloat(); + return static_cast(*left) <= static_cast(*right); } bool Hs(CBotVar* left, CBotVar* right) override { - return left->GetValFloat() >= right->GetValFloat(); + return static_cast(*left) >= static_cast(*right); } }; -} +/** + * \brief An integer variable (byte, short, char, int, long) + */ +template +class CBotVarInteger : public CBotVarNumber +{ +public: + CBotVarInteger(const CBotToken &name) : CBotVarNumber(name) {} + CBotError Modulo(CBotVar* left, CBotVar* right) override + { + T r = static_cast(*right); + if ( r == static_cast(0) ) return CBotErrZeroDiv; + this->SetValue(static_cast(*left) % r); + return CBotNoErr; + } + + void XOr(CBotVar* left, CBotVar* right) override + { + this->SetValue(static_cast(*left) ^ static_cast(*right)); + } + void And(CBotVar* left, CBotVar* right) override + { + this->SetValue(static_cast(*left) & static_cast(*right)); + } + void Or(CBotVar* left, CBotVar* right) override + { + this->SetValue(static_cast(*left) | static_cast(*right)); + } + + void SL(CBotVar* left, CBotVar* right) override + { + this->SetValue(static_cast(*left) << right->GetValInt()); + } + void ASR(CBotVar* left, CBotVar* right) override + { + this->SetValue(static_cast(*left) >> right->GetValInt()); + } + + void Not() override + { + this->m_val = ~(this->m_val); + } +}; + +} // namespace CBot diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index f16637bd..6f2201b9 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -605,6 +605,20 @@ TEST_F(CBotUT, VarImplicitCast) ); } +TEST_F(CBotUT, IntegerMathNearLimits_Issue993) +{ + ExecuteTest( + "extern void Test_Issue993() {\n" + " ASSERT(-2147483600 * 1 == -2147483600);\n" + " ASSERT( 2147483600 * 1 == 2147483600);\n" + " ASSERT( 2147483646 * 1 == 2147483646);\n" + " ASSERT( 2147483646 * -1 == -2147483646);\n" + " ASSERT( 2147483000 * -1 == -2147483000);\n" + " ASSERT( 2147483000 * 1 == 2147483000);\n" + "}\n" + ); +} + TEST_F(CBotUT, ToString) { ExecuteTest( From c0cdd84e856e1a69a3eaddcd3f790f4e03115775 Mon Sep 17 00:00:00 2001 From: melex750 Date: Thu, 11 Apr 2019 04:19:29 -0400 Subject: [PATCH 3/7] Add missing primitive data types in CBOT --- src/CBot/CBotDefParam.cpp | 20 ++++ src/CBot/CBotEnums.h | 15 ++- src/CBot/CBotFileUtils.cpp | 10 ++ src/CBot/CBotFileUtils.h | 16 +++ src/CBot/CBotInstr/CBotCase.cpp | 2 +- src/CBot/CBotInstr/CBotDefFloat.cpp | 23 ++-- src/CBot/CBotInstr/CBotDefFloat.h | 2 +- src/CBot/CBotInstr/CBotDefInt.cpp | 26 +++-- src/CBot/CBotInstr/CBotDefInt.h | 2 +- src/CBot/CBotInstr/CBotExprLitNum.cpp | 102 +++++++++++------ src/CBot/CBotInstr/CBotExprLitNum.h | 21 ++-- src/CBot/CBotInstr/CBotInstr.cpp | 5 + src/CBot/CBotInstr/CBotParExpr.cpp | 2 +- src/CBot/CBotInstr/CBotTwoOpExpr.cpp | 5 +- src/CBot/CBotStack.cpp | 32 +++++- src/CBot/CBotToken.cpp | 5 + src/CBot/CBotTypResult.cpp | 5 + src/CBot/CBotUtils.cpp | 19 +++- src/CBot/CBotUtils.h | 2 +- src/CBot/CBotVar/CBotVar.cpp | 155 ++++++++++++++++++++++++-- src/CBot/CBotVar/CBotVar.h | 30 +++++ src/CBot/CBotVar/CBotVarByte.h | 46 ++++++++ src/CBot/CBotVar/CBotVarChar.h | 59 ++++++++++ src/CBot/CBotVar/CBotVarDouble.h | 41 +++++++ src/CBot/CBotVar/CBotVarLong.h | 46 ++++++++ src/CBot/CBotVar/CBotVarShort.h | 46 ++++++++ src/CBot/CBotVar/CBotVarValue.h | 49 ++++++++ src/CBot/CMakeLists.txt | 5 + src/script/cbottoken.cpp | 10 ++ 29 files changed, 717 insertions(+), 84 deletions(-) create mode 100644 src/CBot/CBotVar/CBotVarByte.h create mode 100644 src/CBot/CBotVar/CBotVarChar.h create mode 100644 src/CBot/CBotVar/CBotVarDouble.h create mode 100644 src/CBot/CBotVar/CBotVarLong.h create mode 100644 src/CBot/CBotVar/CBotVarShort.h diff --git a/src/CBot/CBotDefParam.cpp b/src/CBot/CBotDefParam.cpp index af4fb6ec..fe4a37b6 100644 --- a/src/CBot/CBotDefParam.cpp +++ b/src/CBot/CBotDefParam.cpp @@ -173,14 +173,34 @@ bool CBotDefParam::Execute(CBotVar** ppVars, CBotStack* &pj) { switch (p->m_type.GetType()) { + case CBotTypByte: + newvar->SetValByte(pVar->GetValByte()); + newvar->SetInit(pVar->GetInit()); // copy nan + break; + case CBotTypShort: + newvar->SetValShort(pVar->GetValShort()); + newvar->SetInit(pVar->GetInit()); // copy nan + break; + case CBotTypChar: + newvar->SetValChar(pVar->GetValChar()); + newvar->SetInit(pVar->GetInit()); // copy nan + break; case CBotTypInt: newvar->SetValInt(pVar->GetValInt()); newvar->SetInit(pVar->GetInit()); // copy nan break; + case CBotTypLong: + newvar->SetValLong(pVar->GetValLong()); + newvar->SetInit(pVar->GetInit()); // copy nan + break; case CBotTypFloat: newvar->SetValFloat(pVar->GetValFloat()); newvar->SetInit(pVar->GetInit()); // copy nan break; + case CBotTypDouble: + newvar->SetValDouble(pVar->GetValDouble()); + newvar->SetInit(pVar->GetInit()); // copy nan + break; case CBotTypString: newvar->SetValString(pVar->GetValString()); break; diff --git a/src/CBot/CBotEnums.h b/src/CBot/CBotEnums.h index 0b33bfdc..3dff66eb 100644 --- a/src/CBot/CBotEnums.h +++ b/src/CBot/CBotEnums.h @@ -35,13 +35,13 @@ namespace CBot enum CBotType { CBotTypVoid = 0, //!< void - CBotTypByte = 1, //!< byte (NOT IMPLEMENTED) - CBotTypShort = 2, //!< short (NOT IMPLEMENTED) - CBotTypChar = 3, //!< char (NOT IMPLEMENTED) + CBotTypByte = 1, //!< byte + CBotTypShort = 2, //!< short + CBotTypChar = 3, //!< char CBotTypInt = 4, //!< int - CBotTypLong = 5, //!< long (NOT IMPLEMENTED) + CBotTypLong = 5, //!< long CBotTypFloat = 6, //!< float - CBotTypDouble = 7, //!< double (NOT IMPLEMENTED) + CBotTypDouble = 7, //!< double CBotTypBoolean = 8, //!< bool CBotTypString = 9, //!< string @@ -106,6 +106,11 @@ enum TokenId ID_STRING, ID_VOID, ID_BOOL, + ID_BYTE, + ID_SHORT, + ID_CHAR, + ID_LONG, + ID_DOUBLE, TokenKeyVal = 2200, //!< keywords that represent values (true, false, null, nan) ID_TRUE = 2200, diff --git a/src/CBot/CBotFileUtils.cpp b/src/CBot/CBotFileUtils.cpp index ccde313c..b2a19cb1 100644 --- a/src/CBot/CBotFileUtils.cpp +++ b/src/CBot/CBotFileUtils.cpp @@ -151,6 +151,16 @@ bool ReadShort(std::istream &istr, short &s) return ReadSignedBinary(istr, s); } +bool WriteUInt32(std::ostream &ostr, uint32_t i) +{ + return WriteBinary(ostr, i); +} + +bool ReadUInt32(std::istream &istr, uint32_t &i) +{ + return ReadBinary(istr, i); +} + bool WriteInt(std::ostream &ostr, int i) { return WriteSignedBinary(ostr, i); diff --git a/src/CBot/CBotFileUtils.h b/src/CBot/CBotFileUtils.h index 8f739cb4..900dd81d 100644 --- a/src/CBot/CBotFileUtils.h +++ b/src/CBot/CBotFileUtils.h @@ -84,6 +84,22 @@ bool WriteShort(std::ostream &ostr, short s); */ bool ReadShort(std::istream &istr, short &s); +/*! + * \brief WriteUInt32 + * \param ostr Output stream + * \param i + * \return true on success + */ +bool WriteUInt32(std::ostream &ostr, uint32_t i); + +/*! + * \brief ReadUInt32 + * \param istr Input stream + * \param[out] i + * \return true on success + */ +bool ReadUInt32(std::istream &istr, uint32_t &i); + /*! * \brief WriteInt * \param ostr Output stream diff --git a/src/CBot/CBotInstr/CBotCase.cpp b/src/CBot/CBotInstr/CBotCase.cpp index 35e17d3d..15c8d4f7 100644 --- a/src/CBot/CBotInstr/CBotCase.cpp +++ b/src/CBot/CBotInstr/CBotCase.cpp @@ -51,7 +51,7 @@ CBotInstr* CBotCase::Compile(CBotToken* &p, CBotCStack* pStack) if ( pp->GetType() == ID_CASE ) { pp = p; - inst->m_value = CBotExprLitNum::Compile(p, pStack); + inst->m_value = CBot::CompileExprLitNum(p, pStack); if (inst->m_value == nullptr ) { pStack->SetError( CBotErrBadNum, pp ); diff --git a/src/CBot/CBotInstr/CBotDefFloat.cpp b/src/CBot/CBotInstr/CBotDefFloat.cpp index 65449b02..0bb6f5f8 100644 --- a/src/CBot/CBotInstr/CBotDefFloat.cpp +++ b/src/CBot/CBotInstr/CBotDefFloat.cpp @@ -46,13 +46,22 @@ CBotDefFloat::~CBotDefFloat() } //////////////////////////////////////////////////////////////////////////////// -CBotInstr* CBotDefFloat::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, bool noskip) +CBotInstr* CBotDefFloat::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, bool noskip, CBotTypResult vartype) { CBotToken* pp = cont ? nullptr : p; - if (!cont && !IsOfType(p, ID_FLOAT)) return nullptr; + if (!cont) + { + switch (p->GetType()) + { + case ID_FLOAT: vartype.SetType(CBotTypFloat ); break; + case ID_DOUBLE: vartype.SetType(CBotTypDouble); break; + default: return nullptr; + } + p = p->GetNext(); + } - CBotDefFloat* inst = static_cast(CompileArray(p, pStack, CBotTypFloat)); + CBotDefFloat* inst = static_cast(CompileArray(p, pStack, vartype)); if (inst != nullptr || !pStack->IsOk()) return inst; CBotCStack* pStk = pStack->TokenStack(pp); @@ -67,7 +76,7 @@ CBotInstr* CBotDefFloat::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, b if (nullptr != (inst->m_var = CBotLeftExprVar::Compile( p, pStk ))) { - (static_cast(inst->m_var))->m_typevar = CBotTypFloat; + (static_cast(inst->m_var))->m_typevar = vartype; if (pStk->CheckVarLocal(vartoken)) // redefinition of a variable { pStk->SetStartError(vartoken->GetStart()); @@ -79,7 +88,7 @@ CBotInstr* CBotDefFloat::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, b { delete inst; p = vartoken; - inst = static_cast(CBotDefArray::Compile(p, pStk, CBotTypFloat)); + inst = static_cast(CBotDefArray::Compile(p, pStk, vartype)); goto suite; // no assignment, variable already created } @@ -103,7 +112,7 @@ CBotInstr* CBotDefFloat::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, b } } - var = CBotVar::Create(*vartoken, CBotTypFloat); + var = CBotVar::Create(*vartoken, vartype); var->SetInit(inst->m_expr != nullptr ? CBotVar::InitType::DEF : CBotVar::InitType::UNDEF); var->SetUniqNum( (static_cast(inst->m_var))->m_nIdent = CBotVar::NextUniqNum()); @@ -111,7 +120,7 @@ CBotInstr* CBotDefFloat::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, b suite: if (pStk->IsOk() && IsOfType(p, ID_COMMA)) { - if (nullptr != ( inst->m_next2b = CBotDefFloat::Compile(p, pStk, true, noskip))) + if (nullptr != ( inst->m_next2b = CBotDefFloat::Compile(p, pStk, true, noskip, vartype))) { return pStack->Return(inst, pStk); } diff --git a/src/CBot/CBotInstr/CBotDefFloat.h b/src/CBot/CBotInstr/CBotDefFloat.h index a8060b41..8c3fa44d 100644 --- a/src/CBot/CBotInstr/CBotDefFloat.h +++ b/src/CBot/CBotInstr/CBotDefFloat.h @@ -41,7 +41,7 @@ public: * \param noskip * \return */ - static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, bool cont = false, bool noskip=false); + static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, bool cont = false, bool noskip=false, CBotTypResult vartype = CBotTypFloat); /*! * \brief Execute Executes the definition of a real variable. diff --git a/src/CBot/CBotInstr/CBotDefInt.cpp b/src/CBot/CBotInstr/CBotDefInt.cpp index 6cfb30e6..6835f901 100644 --- a/src/CBot/CBotInstr/CBotDefInt.cpp +++ b/src/CBot/CBotInstr/CBotDefInt.cpp @@ -47,13 +47,25 @@ CBotDefInt::~CBotDefInt() } //////////////////////////////////////////////////////////////////////////////// -CBotInstr* CBotDefInt::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, bool noskip) +CBotInstr* CBotDefInt::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, bool noskip, CBotTypResult vartype) { CBotToken* pp = cont ? nullptr : p; // no repetition of the token "int" - if (!cont && !IsOfType(p, ID_INT)) return nullptr; + if (!cont) + { + switch (p->GetType()) + { + case ID_BYTE: vartype.SetType(CBotTypByte ); break; + case ID_SHORT: vartype.SetType(CBotTypShort); break; + case ID_CHAR: vartype.SetType(CBotTypChar ); break; + case ID_INT: vartype.SetType(CBotTypInt ); break; + case ID_LONG: vartype.SetType(CBotTypLong ); break; + default: return nullptr; + } + p = p->GetNext(); + } - CBotDefInt* inst = static_cast(CompileArray(p, pStack, CBotTypInt)); + CBotDefInt* inst = static_cast(CompileArray(p, pStack, vartype)); if (inst != nullptr || !pStack->IsOk()) return inst; CBotCStack* pStk = pStack->TokenStack(pp); @@ -68,7 +80,7 @@ CBotInstr* CBotDefInt::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, boo // determines the expression is valid for the item on the left side if (nullptr != (inst->m_var = CBotLeftExprVar::Compile( p, pStk ))) { - (static_cast(inst->m_var))->m_typevar = CBotTypInt; + (static_cast(inst->m_var))->m_typevar = vartype; if (pStk->CheckVarLocal(vartoken)) // redefinition of the variable { pStk->SetError(CBotErrRedefVar, vartoken); @@ -82,7 +94,7 @@ CBotInstr* CBotDefInt::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, boo // compiles an array declaration - CBotInstr* inst2 = CBotDefArray::Compile(p, pStk, CBotTypInt); + CBotInstr* inst2 = CBotDefArray::Compile(p, pStk, vartype); inst = static_cast(inst2); goto suite; // no assignment, variable already created @@ -108,7 +120,7 @@ CBotInstr* CBotDefInt::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, boo } { - CBotVar* var = CBotVar::Create(*vartoken, CBotTypInt);// create the variable (evaluated after the assignment) + CBotVar* var = CBotVar::Create(*vartoken, vartype); // create the variable (evaluated after the assignment) var->SetInit(inst->m_expr != nullptr ? CBotVar::InitType::DEF : CBotVar::InitType::UNDEF); // if initialized with assignment var->SetUniqNum( //set it with a unique number (static_cast(inst->m_var))->m_nIdent = CBotVar::NextUniqNum()); @@ -117,7 +129,7 @@ CBotInstr* CBotDefInt::Compile(CBotToken* &p, CBotCStack* pStack, bool cont, boo suite: if (pStk->IsOk() && IsOfType(p, ID_COMMA)) // chained several definitions { - if (nullptr != ( inst->m_next2b = CBotDefInt::Compile(p, pStk, true, noskip))) // compile next one + if (nullptr != ( inst->m_next2b = CBotDefInt::Compile(p, pStk, true, noskip, vartype))) // compile next one { return pStack->Return(inst, pStk); } diff --git a/src/CBot/CBotInstr/CBotDefInt.h b/src/CBot/CBotInstr/CBotDefInt.h index 2962eab3..867ab4dd 100644 --- a/src/CBot/CBotInstr/CBotDefInt.h +++ b/src/CBot/CBotInstr/CBotDefInt.h @@ -41,7 +41,7 @@ public: * \param noskip * \return */ - static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, bool cont = false, bool noskip = false); + static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, bool cont = false, bool noskip = false, CBotTypResult vartype = CBotTypInt); /*! * \brief Execute Execute the definition of the integer variable. diff --git a/src/CBot/CBotInstr/CBotExprLitNum.cpp b/src/CBot/CBotInstr/CBotExprLitNum.cpp index 9273e8ee..9842cf57 100644 --- a/src/CBot/CBotInstr/CBotExprLitNum.cpp +++ b/src/CBot/CBotInstr/CBotExprLitNum.cpp @@ -25,52 +25,87 @@ #include "CBot/CBotUtils.h" +#include #include namespace CBot { -//////////////////////////////////////////////////////////////////////////////// -CBotExprLitNum::CBotExprLitNum() +template <> +CBotExprLitNum::CBotExprLitNum(int val) : m_numtype(CBotTypInt), m_value(val) { } -//////////////////////////////////////////////////////////////////////////////// -CBotExprLitNum::~CBotExprLitNum() +template <> +CBotExprLitNum::CBotExprLitNum(long val) : m_numtype(CBotTypLong), m_value(val) { } -//////////////////////////////////////////////////////////////////////////////// -CBotInstr* CBotExprLitNum::Compile(CBotToken* &p, CBotCStack* pStack) +template <> +CBotExprLitNum::CBotExprLitNum(float val) : m_numtype(CBotTypFloat), m_value(val) +{ +} + +template <> +CBotExprLitNum::CBotExprLitNum(double val) : m_numtype(CBotTypDouble), m_value(val) +{ +} + +template +CBotExprLitNum::~CBotExprLitNum() +{ +} + +CBotInstr* CompileExprLitNum(CBotToken* &p, CBotCStack* pStack) { CBotCStack* pStk = pStack->TokenStack(); - CBotExprLitNum* inst = new CBotExprLitNum(); + const auto& s = p->GetString(); - inst->SetToken(p); - std::string s = p->GetString(); + CBotInstr* inst = nullptr; + CBotType numtype = CBotTypInt; - inst->m_numtype = CBotTypInt; if (p->GetType() == TokenTypDef) { - inst->m_valint = p->GetKeywordId(); + inst = new CBotExprLitNum(static_cast(p->GetKeywordId())); } else { if (s.find('.') != std::string::npos || ( s.find('x') == std::string::npos && ( s.find_first_of("eE") != std::string::npos ) )) { - inst->m_numtype = CBotTypFloat; - inst->m_valfloat = GetNumFloat(s); + double val = GetNumFloat(s); + if (val < static_cast(std::numeric_limits::min()) && + val > static_cast(std::numeric_limits::max()) ) + { + numtype = CBotTypDouble; + inst = new CBotExprLitNum(val); + } + else + { + numtype = CBotTypFloat; + inst = new CBotExprLitNum(static_cast(val)); + } } else { - inst->m_valint = GetNumInt(s); + long val = GetNumInt(s); + if (val < std::numeric_limits::min() && + val > std::numeric_limits::max() ) + { + numtype = CBotTypLong; + inst = new CBotExprLitNum(val); + } + else + { + inst = new CBotExprLitNum(static_cast(val)); + } } } + inst->SetToken(p); if (pStk->NextToken(p)) { - CBotVar* var = CBotVar::Create("", inst->m_numtype); + CBotVar* var = CBotVar::Create("", numtype); pStk->SetVar(var); return pStack->Return(inst, pStk); @@ -79,8 +114,8 @@ CBotInstr* CBotExprLitNum::Compile(CBotToken* &p, CBotCStack* pStack) return pStack->Return(nullptr, pStk); } -//////////////////////////////////////////////////////////////////////////////// -bool CBotExprLitNum::Execute(CBotStack* &pj) +template +bool CBotExprLitNum::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this); @@ -88,39 +123,38 @@ bool CBotExprLitNum::Execute(CBotStack* &pj) CBotVar* var = CBotVar::Create("", m_numtype); - std::string nombre ; if (m_token.GetType() == TokenTypDef) { - nombre = m_token.GetString(); + var->SetValInt(m_value, m_token.GetString()); } - - switch (m_numtype) + else { - case CBotTypShort: - case CBotTypInt: - var->SetValInt(m_valint, nombre); - break; - case CBotTypFloat: - var->SetValFloat(m_valfloat); - break; - default: - assert(false); + *var = m_value; } pile->SetVar(var); // place on the stack return pj->Return(pile); // it's ok } -//////////////////////////////////////////////////////////////////////////////// -void CBotExprLitNum::RestoreState(CBotStack* &pj, bool bMain) +template +void CBotExprLitNum::RestoreState(CBotStack* &pj, bool bMain) { if (bMain) pj->RestoreStack(this); } -std::string CBotExprLitNum::GetDebugData() +template +std::string CBotExprLitNum::GetDebugData() { std::stringstream ss; - ss << "(" << (m_numtype == CBotTypFloat ? "float" : "int") << ") " << (m_numtype == CBotTypFloat ? m_valfloat : m_valint); + switch (m_numtype) + { + case CBotTypInt : ss << "(int) "; break; + case CBotTypLong : ss << "(long) "; break; + case CBotTypFloat : ss << "(float) "; break; + case CBotTypDouble: ss << "(double) "; break; + default: assert(false); + } + ss << m_value; return ss.str(); } diff --git a/src/CBot/CBotInstr/CBotExprLitNum.h b/src/CBot/CBotInstr/CBotExprLitNum.h index 414f2d8e..92cd302d 100644 --- a/src/CBot/CBotInstr/CBotExprLitNum.h +++ b/src/CBot/CBotInstr/CBotExprLitNum.h @@ -24,26 +24,21 @@ namespace CBot { +CBotInstr* CompileExprLitNum(CBotToken* &p, CBotCStack* pStack); + /** * \brief A number literal - 5, 1, 2.5, 3.75, etc. or a predefined numerical constant (see CBotToken::DefineNum()) * - * Can be of type ::CBotTypInt or ::CBotTypFloat + * Can be of type ::CBotTypInt, ::CBotTypLong, ::CBotTypFloat, or ::CBotTypDouble */ +template class CBotExprLitNum : public CBotInstr { public: - CBotExprLitNum(); + CBotExprLitNum(T val); ~CBotExprLitNum(); - /*! - * \brief Compile - * \param p - * \param pStack - * \return - */ - static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); - /*! * \brief Execute Execute, returns the corresponding number. * \param pj @@ -65,10 +60,8 @@ protected: private: //! The type of number. CBotType m_numtype; - //! Value for an int. - long m_valint; - //! Value for a float. - float m_valfloat; + //! Value + T m_value; }; diff --git a/src/CBot/CBotInstr/CBotInstr.cpp b/src/CBot/CBotInstr/CBotInstr.cpp index f69785f6..04b13204 100644 --- a/src/CBot/CBotInstr/CBotInstr.cpp +++ b/src/CBot/CBotInstr/CBotInstr.cpp @@ -208,10 +208,15 @@ CBotInstr* CBotInstr::Compile(CBotToken* &p, CBotCStack* pStack) case ID_THROW: return CBotThrow::Compile(p, pStack); + case ID_BYTE: + case ID_SHORT: + case ID_CHAR: case ID_INT: + case ID_LONG: return CBotDefInt::Compile(p, pStack); case ID_FLOAT: + case ID_DOUBLE: return CBotDefFloat::Compile(p, pStack); case ID_STRING: diff --git a/src/CBot/CBotInstr/CBotParExpr.cpp b/src/CBot/CBotInstr/CBotParExpr.cpp index b879ebec..24bdaafe 100644 --- a/src/CBot/CBotInstr/CBotParExpr.cpp +++ b/src/CBot/CBotInstr/CBotParExpr.cpp @@ -151,7 +151,7 @@ CBotInstr* CBotParExpr::CompileLitExpr(CBotToken* &p, CBotCStack* pStack) if (p->GetType() == TokenTypNum || p->GetType() == TokenTypDef ) { - CBotInstr* inst = CBotExprLitNum::Compile(p, pStk); + CBotInstr* inst = CBot::CompileExprLitNum(p, pStk); return pStack->Return(inst, pStk); } diff --git a/src/CBot/CBotInstr/CBotTwoOpExpr.cpp b/src/CBot/CBotInstr/CBotTwoOpExpr.cpp index 3d5fc6a4..ab8b0da9 100644 --- a/src/CBot/CBotInstr/CBotTwoOpExpr.cpp +++ b/src/CBot/CBotInstr/CBotTwoOpExpr.cpp @@ -399,7 +399,10 @@ bool CBotTwoOpExpr::Execute(CBotStack* &pStack) TypeRes = CBotTypBoolean; break; case ID_DIV: - TypeRes = std::max(TypeRes, static_cast(CBotTypFloat)); + if (TypeRes == CBotTypFloat) + { + if (type1.Eq(CBotTypLong) || type2.Eq(CBotTypLong)) TypeRes = CBotTypDouble; + } } // creates a variable for the result diff --git a/src/CBot/CBotStack.cpp b/src/CBot/CBotStack.cpp index 7f3c0b9a..5101bbf2 100644 --- a/src/CBot/CBotStack.cpp +++ b/src/CBot/CBotStack.cpp @@ -833,10 +833,28 @@ bool CBotVar::RestoreState(std::istream &istr, CBotVar* &pVar) switch (w) { case CBotTypBoolean: + char valBool; + if (!ReadByte(istr, valBool)) return false; + pNew = CBotVar::Create(token, w); // creates a variable + pNew->SetValInt(valBool); + break; + case CBotTypByte: char valByte; if (!ReadByte(istr, valByte)) return false; pNew = CBotVar::Create(token, w); // creates a variable - pNew->SetValInt(valByte); + pNew->SetValByte(valByte); + break; + case CBotTypShort: + short valShort; + if (!ReadShort(istr, valShort)) return false; + pNew = CBotVar::Create(token, w); // creates a variable + pNew->SetValShort(valShort); + break; + case CBotTypChar: + uint32_t valChar; + if (!ReadUInt32(istr, valChar)) return false; + pNew = CBotVar::Create(token, w); // creates a variable + pNew->SetValChar(valChar); break; case CBotTypInt: int valInt; @@ -844,12 +862,24 @@ bool CBotVar::RestoreState(std::istream &istr, CBotVar* &pVar) pNew = CBotVar::Create(token, w); // creates a variable pNew->SetValInt(valInt, defnum); break; + case CBotTypLong: + long valLong; + if (!ReadLong(istr, valLong)) return false; + pNew = CBotVar::Create(token, w); // creates a variable + pNew->SetValInt(valLong); + break; case CBotTypFloat: float valFloat; if (!ReadFloat(istr, valFloat)) return false; pNew = CBotVar::Create(token, w); // creates a variable pNew->SetValFloat(valFloat); break; + case CBotTypDouble: + double valDouble; + if (!ReadDouble(istr, valDouble)) return false; + pNew = CBotVar::Create(token, w); // creates a variable + pNew->SetValDouble(valDouble); + break; case CBotTypString: { std::string valString; diff --git a/src/CBot/CBotToken.cpp b/src/CBot/CBotToken.cpp index b3a5a461..d5afe1fe 100644 --- a/src/CBot/CBotToken.cpp +++ b/src/CBot/CBotToken.cpp @@ -71,6 +71,11 @@ static const boost::bimap KEYWORDS = makeBimapToString() + "[]"; diff --git a/src/CBot/CBotUtils.cpp b/src/CBot/CBotUtils.cpp index a87e2943..4d663bea 100644 --- a/src/CBot/CBotUtils.cpp +++ b/src/CBot/CBotUtils.cpp @@ -62,12 +62,27 @@ CBotTypResult TypeParam(CBotToken* &p, CBotCStack* pile) switch (p->GetType()) { + case ID_BYTE: + p = p->GetNext(); + return ArrayType(p, pile, CBotTypResult( CBotTypByte )); + case ID_SHORT: + p = p->GetNext(); + return ArrayType(p, pile, CBotTypResult( CBotTypShort )); + case ID_CHAR: + p = p->GetNext(); + return ArrayType(p, pile, CBotTypResult( CBotTypChar )); case ID_INT: p = p->GetNext(); return ArrayType(p, pile, CBotTypResult( CBotTypInt )); + case ID_LONG: + p = p->GetNext(); + return ArrayType(p, pile, CBotTypResult( CBotTypLong )); case ID_FLOAT: p = p->GetNext(); return ArrayType(p, pile, CBotTypResult( CBotTypFloat )); + case ID_DOUBLE: + p = p->GetNext(); + return ArrayType(p, pile, CBotTypResult( CBotTypDouble )); case ID_BOOLEAN: case ID_BOOL: p = p->GetNext(); @@ -144,7 +159,7 @@ long GetNumInt(const std::string& str) } //////////////////////////////////////////////////////////////////////////////// -float GetNumFloat(const std::string& str) +double GetNumFloat(const std::string& str) { const char* p = str.c_str(); double num = 0; @@ -201,7 +216,7 @@ float GetNumFloat(const std::string& str) } if (bNeg) num = -num; - return static_cast(num); + return num; } bool CharInList(const char c, const char* list) diff --git a/src/CBot/CBotUtils.h b/src/CBot/CBotUtils.h index 5b58535c..5bc4fe33 100644 --- a/src/CBot/CBotUtils.h +++ b/src/CBot/CBotUtils.h @@ -71,7 +71,7 @@ long GetNumInt(const std::string& str); * \param str * \return */ -float GetNumFloat(const std::string& str); +double GetNumFloat(const std::string& str); /*! * \brief Search a null-terminated string for a char value. diff --git a/src/CBot/CBotVar/CBotVar.cpp b/src/CBot/CBotVar/CBotVar.cpp index d9947e5b..83438745 100644 --- a/src/CBot/CBotVar/CBotVar.cpp +++ b/src/CBot/CBotVar/CBotVar.cpp @@ -24,12 +24,17 @@ #include "CBot/CBotInstr/CBotInstr.h" #include "CBot/CBotVar/CBotVarArray.h" -#include "CBot/CBotVar/CBotVarPointer.h" -#include "CBot/CBotVar/CBotVarClass.h" #include "CBot/CBotVar/CBotVarBoolean.h" -#include "CBot/CBotVar/CBotVarString.h" +#include "CBot/CBotVar/CBotVarByte.h" +#include "CBot/CBotVar/CBotVarChar.h" +#include "CBot/CBotVar/CBotVarClass.h" +#include "CBot/CBotVar/CBotVarDouble.h" #include "CBot/CBotVar/CBotVarFloat.h" #include "CBot/CBotVar/CBotVarInt.h" +#include "CBot/CBotVar/CBotVarLong.h" +#include "CBot/CBotVar/CBotVarPointer.h" +#include "CBot/CBotVar/CBotVarShort.h" +#include "CBot/CBotVar/CBotVarString.h" #include "CBot/CBotClass.h" #include "CBot/CBotToken.h" @@ -160,11 +165,20 @@ CBotVar* CBotVar::Create(const CBotToken& name, CBotTypResult type) { switch (type.GetType()) { + case CBotTypByte: + return new CBotVarByte(name); case CBotTypShort: + return new CBotVarShort(name); + case CBotTypChar: + return new CBotVarChar(name); case CBotTypInt: return new CBotVarInt(name); + case CBotTypLong: + return new CBotVarLong(name); case CBotTypFloat: return new CBotVarFloat(name); + case CBotTypDouble: + return new CBotVarDouble(name); case CBotTypBoolean: return new CBotVarBoolean(name); case CBotTypString: @@ -223,11 +237,20 @@ CBotVar* CBotVar::Create(const std::string& n, CBotTypResult type) switch (type.GetType()) { + case CBotTypByte: + return new CBotVarByte(name); case CBotTypShort: + return new CBotVarShort(name); + case CBotTypChar: + return new CBotVarChar(name); case CBotTypInt: return new CBotVarInt(name); + case CBotTypLong: + return new CBotVarLong(name); case CBotTypFloat: return new CBotVarFloat(name); + case CBotTypDouble: + return new CBotVarDouble(name); case CBotTypBoolean: return new CBotVarBoolean(name); case CBotTypString: @@ -472,12 +495,27 @@ void CBotVar::SetVal(CBotVar* var) case CBotTypBoolean: SetValInt(var->GetValInt()); break; + case CBotTypByte: + SetValByte(var->GetValByte()); + break; + case CBotTypShort: + SetValShort(var->GetValShort()); + break; + case CBotTypChar: + SetValChar(var->GetValChar()); + break; case CBotTypInt: SetValInt(var->GetValInt(), (static_cast(var))->m_defnum); break; + case CBotTypLong: + SetValLong(var->GetValLong()); + break; case CBotTypFloat: SetValFloat(var->GetValFloat()); break; + case CBotTypDouble: + SetValDouble(var->GetValDouble()); + break; case CBotTypString: SetValString(var->GetValString()); break; @@ -545,33 +583,84 @@ CBotVarClass* CBotVar::GetPointer() // All these functions must be defined in the subclasses // derived from class CBotVar -//////////////////////////////////////////////////////////////////////////////// + +signed char CBotVar::GetValByte() +{ + assert(0); + return 0; +} + +short CBotVar::GetValShort() +{ + assert(0); + return 0; +} + +uint32_t CBotVar::GetValChar() +{ + assert(0); + return 0; +} + int CBotVar::GetValInt() { assert(0); return 0; } -//////////////////////////////////////////////////////////////////////////////// +long CBotVar::GetValLong() +{ + assert(0); + return 0; +} + float CBotVar::GetValFloat() { assert(0); return 0; } -//////////////////////////////////////////////////////////////////////////////// +double CBotVar::GetValDouble() +{ + assert(0); + return 0; +} + +void CBotVar::SetValByte(signed char val) +{ + assert(false); +} + +void CBotVar::SetValShort(short val) +{ + assert(false); +} + +void CBotVar::SetValChar(uint32_t val) +{ + assert(false); +} + void CBotVar::SetValInt(int c, const std::string& s) { assert(0); } -//////////////////////////////////////////////////////////////////////////////// +void CBotVar::SetValLong(long val) +{ + assert(false); +} + void CBotVar::SetValFloat(float c) { assert(0); } -//////////////////////////////////////////////////////////////////////////////// +void CBotVar::SetValDouble(double val) +{ + assert(false); +} + void CBotVar::Mul(CBotVar* left, CBotVar* right) { assert(0); @@ -757,16 +846,41 @@ CBotVar::operator bool() return static_cast(GetValInt()); } +CBotVar::operator signed char() +{ + return GetValByte(); +} + +CBotVar::operator short() +{ + return GetValShort(); +} + +CBotVar::operator uint32_t() +{ + return GetValChar(); +} + CBotVar::operator int() { return GetValInt(); } +CBotVar::operator long() +{ + return GetValLong(); +} + CBotVar::operator float() { return GetValFloat(); } +CBotVar::operator double() +{ + return GetValDouble(); +} + CBotVar::operator std::string() { return GetValString(); @@ -777,16 +891,41 @@ void CBotVar::operator=(const CBotVar &var) SetVal(const_cast(&var)); } +void CBotVar::operator=(signed char x) +{ + SetValByte(x); +} + +void CBotVar::operator=(short x) +{ + SetValShort(x); +} + +void CBotVar::operator=(uint32_t x) +{ + SetValChar(x); +} + void CBotVar::operator=(int x) { SetValInt(x); } +void CBotVar::operator=(long x) +{ + SetValLong(x); +} + void CBotVar::operator=(float x) { SetValFloat(x); } +void CBotVar::operator=(double x) +{ + SetValDouble(x); +} + void CBotVar::operator=(const std::string &x) { SetValString(x); diff --git a/src/CBot/CBotVar/CBotVar.h b/src/CBot/CBotVar/CBotVar.h index 1a9c1498..ef86f40f 100644 --- a/src/CBot/CBotVar/CBotVar.h +++ b/src/CBot/CBotVar/CBotVar.h @@ -445,12 +445,22 @@ public: //@{ operator bool(); + operator signed char(); + operator short(); + operator uint32_t(); operator int(); + operator long(); operator float(); + operator double(); operator std::string(); void operator=(const CBotVar& var); + void operator=(signed char x); + void operator=(short x); + void operator=(uint32_t x); void operator=(int x); + void operator=(long x); void operator=(float x); + void operator=(double x); void operator=(const std::string &x); /** @@ -466,6 +476,12 @@ public: */ virtual void Copy(CBotVar* pSrc, bool bName = true); + virtual void SetValByte(signed char val); + + virtual void SetValShort(short val); + + virtual void SetValChar(uint32_t val); + /** * \brief Set value as an integer * @@ -476,30 +492,44 @@ public: */ virtual void SetValInt(int val, const std::string& name = ""); + virtual void SetValLong(long val); + /** * \brief Set value as float * \param val New value */ virtual void SetValFloat(float val); + virtual void SetValDouble(double val); + /** * \brief Set value as string * \param val New value */ virtual void SetValString(const std::string& val); + virtual signed char GetValByte(); + + virtual short GetValShort(); + + virtual uint32_t GetValChar(); + /** * \brief Get value as integer * \return Current value */ virtual int GetValInt(); + virtual long GetValLong(); + /** * \brief Get value as float * \return Current value */ virtual float GetValFloat(); + virtual double GetValDouble(); + /** * \brief Get value as string * diff --git a/src/CBot/CBotVar/CBotVarByte.h b/src/CBot/CBotVar/CBotVarByte.h new file mode 100644 index 00000000..68c9088e --- /dev/null +++ b/src/CBot/CBotVar/CBotVarByte.h @@ -0,0 +1,46 @@ +/* + * 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 + */ + +#pragma once + +#include "CBot/CBotVar/CBotVarValue.h" + +namespace CBot +{ + +/** + * \brief CBotVar subclass for managing 1-byte integer values (::CBotTypByte) + */ +class CBotVarByte : public CBotVarInteger +{ +public: + CBotVarByte(const CBotToken &name) : CBotVarInteger(name) {} + + void SR(CBotVar* left, CBotVar* right) override + { + SetValByte(static_cast(left->GetValByte()) >> right->GetValInt()); + } + + bool Save1State(std::ostream &ostr) override + { + return WriteByte(ostr, m_val); + } +}; + +} // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarChar.h b/src/CBot/CBotVar/CBotVarChar.h new file mode 100644 index 00000000..07f51358 --- /dev/null +++ b/src/CBot/CBotVar/CBotVarChar.h @@ -0,0 +1,59 @@ +/* + * 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 + */ + +#pragma once + +#include "CBot/CBotVar/CBotVarValue.h" + +namespace CBot +{ + +/** + * \brief CBotVar subclass for managing 32-bit Unicode values (::CBotTypChar) + */ +class CBotVarChar : public CBotVarInteger +{ +public: + CBotVarChar(const CBotToken &name) : CBotVarInteger(name) {} + + std::string GetValString() override + { + if (m_binit == CBotVar::InitType::UNDEF) + return LoadString(TX_UNDEF); + if (m_binit == CBotVar::InitType::IS_NAN) + return LoadString(TX_NAN); + + if (0x10FFFF < m_val || (0xD7FF < m_val && m_val < 0xE000)) + return "\xEF\xBF\xBD"; // replacement character U+FFFD + + return CodePointToUTF8(m_val); + } + + void SR(CBotVar* left, CBotVar* right) override + { + SetValChar(left->GetValChar() >> right->GetValInt()); + } + + bool Save1State(std::ostream &ostr) override + { + return WriteUInt32(ostr, m_val); + } +}; + +} // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarDouble.h b/src/CBot/CBotVar/CBotVarDouble.h new file mode 100644 index 00000000..1df79cfb --- /dev/null +++ b/src/CBot/CBotVar/CBotVarDouble.h @@ -0,0 +1,41 @@ +/* + * 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 + */ + +#pragma once + +#include "CBot/CBotVar/CBotVarValue.h" + +namespace CBot +{ + +/** + * \brief CBotVar subclass for managing double values (::CBotTypDouble) + */ +class CBotVarDouble : public CBotVarNumber +{ +public: + CBotVarDouble(const CBotToken &name) : CBotVarNumber(name) {} + + bool Save1State(std::ostream &ostr) override + { + return WriteDouble(ostr, m_val); + } +}; + +} // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarLong.h b/src/CBot/CBotVar/CBotVarLong.h new file mode 100644 index 00000000..84164c41 --- /dev/null +++ b/src/CBot/CBotVar/CBotVarLong.h @@ -0,0 +1,46 @@ +/* + * 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 + */ + +#pragma once + +#include "CBot/CBotVar/CBotVarValue.h" + +namespace CBot +{ + +/** + * \brief CBotVar subclass for managing long integer values (::CBotTypLong) + */ +class CBotVarLong : public CBotVarInteger +{ +public: + CBotVarLong(const CBotToken &name) : CBotVarInteger(name) {} + + void SR(CBotVar* left, CBotVar* right) override + { + SetValLong(static_cast(left->GetValLong()) >> right->GetValInt()); + } + + bool Save1State(std::ostream &ostr) override + { + return WriteLong(ostr, m_val); + } +}; + +} // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarShort.h b/src/CBot/CBotVar/CBotVarShort.h new file mode 100644 index 00000000..3f0723e3 --- /dev/null +++ b/src/CBot/CBotVar/CBotVarShort.h @@ -0,0 +1,46 @@ +/* + * 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 + */ + +#pragma once + +#include "CBot/CBotVar/CBotVarValue.h" + +namespace CBot +{ + +/** + * \brief CBotVar subclass for managing short integer values (::CBotTypShort) + */ +class CBotVarShort : public CBotVarInteger +{ +public: + CBotVarShort(const CBotToken &name) : CBotVarInteger(name) {} + + void SR(CBotVar* left, CBotVar* right) override + { + SetValShort(static_cast(left->GetValShort()) >> right->GetValInt()); + } + + bool Save1State(std::ostream &ostr) override + { + return WriteShort(ostr, m_val); + } +}; + +} // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarValue.h b/src/CBot/CBotVar/CBotVarValue.h index 9c0b0361..035d7930 100644 --- a/src/CBot/CBotVar/CBotVarValue.h +++ b/src/CBot/CBotVar/CBotVarValue.h @@ -98,26 +98,75 @@ public: this->m_val = static_cast(0); } + void SetValByte(signed char val) override + { + this->SetValue(static_cast(val)); + } + + void SetValShort(short val) override + { + this->SetValue(static_cast(val)); + } + + void SetValChar(uint32_t val) override + { + this->SetValue(static_cast(val)); + } + void SetValInt(int val, const std::string &s = "") override { this->SetValue(static_cast(val)); } + void SetValLong(long val) override + { + this->SetValue(static_cast(val)); + } + void SetValFloat(float val) override { this->SetValue(static_cast(val)); } + void SetValDouble(double val) override + { + this->SetValue(static_cast(val)); + } + + signed char GetValByte() override + { + return static_cast(this->m_val); + } + + short GetValShort() override + { + return static_cast(this->m_val); + } + + uint32_t GetValChar() override + { + return static_cast(this->m_val); + } + int GetValInt() override { return static_cast(this->m_val); } + long GetValLong() override + { + return static_cast(this->m_val); + } + float GetValFloat() override { return static_cast(this->m_val); } + double GetValDouble() override + { + return static_cast(this->m_val); + } bool Eq(CBotVar* left, CBotVar* right) override { diff --git a/src/CBot/CMakeLists.txt b/src/CBot/CMakeLists.txt index a9a83e2e..4be97841 100644 --- a/src/CBot/CMakeLists.txt +++ b/src/CBot/CMakeLists.txt @@ -127,14 +127,19 @@ set(SOURCES CBotVar/CBotVarArray.h CBotVar/CBotVarBoolean.cpp CBotVar/CBotVarBoolean.h + CBotVar/CBotVarByte.h + CBotVar/CBotVarChar.h CBotVar/CBotVarClass.cpp CBotVar/CBotVarClass.h + CBotVar/CBotVarDouble.h CBotVar/CBotVarFloat.cpp CBotVar/CBotVarFloat.h CBotVar/CBotVarInt.cpp CBotVar/CBotVarInt.h + CBotVar/CBotVarLong.h CBotVar/CBotVarPointer.cpp CBotVar/CBotVarPointer.h + CBotVar/CBotVarShort.h CBotVar/CBotVarString.cpp CBotVar/CBotVarString.h stdlib/Compilation.cpp diff --git a/src/script/cbottoken.cpp b/src/script/cbottoken.cpp index 24b60a09..02bc4525 100644 --- a/src/script/cbottoken.cpp +++ b/src/script/cbottoken.cpp @@ -236,8 +236,13 @@ std::string GetHelpFilename(const char *token) if ( strcmp(token, "continue" ) == 0 ) helpfile = "cbot/continue"; if ( strcmp(token, "return" ) == 0 ) helpfile = "cbot/return"; if ( strcmp(token, "sizeof" ) == 0 ) helpfile = "cbot/sizeof"; + if ( strcmp(token, "byte" ) == 0 ) helpfile = "cbot/byte"; + if ( strcmp(token, "short" ) == 0 ) helpfile = "cbot/short"; + if ( strcmp(token, "char" ) == 0 ) helpfile = "cbot/char"; if ( strcmp(token, "int" ) == 0 ) helpfile = "cbot/int"; + if ( strcmp(token, "long" ) == 0 ) helpfile = "cbot/long"; if ( strcmp(token, "float" ) == 0 ) helpfile = "cbot/float"; + if ( strcmp(token, "double" ) == 0 ) helpfile = "cbot/double"; if ( strcmp(token, "bool" ) == 0 ) helpfile = "cbot/bool"; if ( strcmp(token, "string" ) == 0 ) helpfile = "cbot/string"; if ( strcmp(token, "point" ) == 0 ) helpfile = "cbot/point"; @@ -388,8 +393,13 @@ std::string GetHelpFilename(const char *token) bool IsType(const char *token) { if ( strcmp(token, "void" ) == 0 ) return true; + if ( strcmp(token, "byte" ) == 0 ) return true; + if ( strcmp(token, "short" ) == 0 ) return true; + if ( strcmp(token, "char" ) == 0 ) return true; if ( strcmp(token, "int" ) == 0 ) return true; + if ( strcmp(token, "long" ) == 0 ) return true; if ( strcmp(token, "float" ) == 0 ) return true; + if ( strcmp(token, "double" ) == 0 ) return true; if ( strcmp(token, "bool" ) == 0 ) return true; if ( strcmp(token, "string" ) == 0 ) return true; if ( strcmp(token, "point" ) == 0 ) return true; From bc572aa52f89619cb75ffb45c3abff7ed27f2121 Mon Sep 17 00:00:00 2001 From: melex750 Date: Thu, 11 Apr 2019 04:21:22 -0400 Subject: [PATCH 4/7] Add sizeof() operator for numeric data types --- src/CBot/CBotInstr/CBotExprLitNum.cpp | 40 +++++++++++++++++++++++++++ src/CBot/CBotInstr/CBotExprLitNum.h | 2 ++ src/CBot/CBotInstr/CBotParExpr.cpp | 14 ++++++++++ 3 files changed, 56 insertions(+) diff --git a/src/CBot/CBotInstr/CBotExprLitNum.cpp b/src/CBot/CBotInstr/CBotExprLitNum.cpp index 9842cf57..61978bf0 100644 --- a/src/CBot/CBotInstr/CBotExprLitNum.cpp +++ b/src/CBot/CBotInstr/CBotExprLitNum.cpp @@ -114,6 +114,46 @@ CBotInstr* CompileExprLitNum(CBotToken* &p, CBotCStack* pStack) return pStack->Return(nullptr, pStk); } +CBotInstr* CompileSizeOf(CBotToken* &p, CBotCStack* pStack) +{ + CBotToken* pp = p; + + if (!IsOfType(p, TokenTypVar)) return nullptr; + if (pp->GetString() == "sizeof" && IsOfType(p, ID_OPENPAR)) + { + CBotCStack* pStk = pStack->TokenStack(); + + int value; + + if (IsOfType(p, ID_BYTE)) value = sizeof(signed char); + else if (IsOfType(p, ID_SHORT)) value = sizeof(short); + else if (IsOfType(p, ID_CHAR)) value = sizeof(uint32_t); + else if (IsOfType(p, ID_INT)) value = sizeof(int); + else if (IsOfType(p, ID_LONG)) value = sizeof(long); + else if (IsOfType(p, ID_FLOAT)) value = sizeof(float); + else if (IsOfType(p, ID_DOUBLE)) value = sizeof(double); + else + { + p = pp; + return pStack->Return(nullptr, pStk); + } + + if (IsOfType(p, ID_CLOSEPAR)) + { + auto inst = new CBotExprLitNum(value); + inst->SetToken(pp); + + CBotVar* var = CBotVar::Create("", CBotTypInt); + pStk->SetVar(var); + return pStack->Return(inst, pStk); + } + pStk->SetError(CBotErrClosePar, p->GetStart()); + return pStack->Return(nullptr, pStk); + } + p = pp; + return nullptr; +} + template bool CBotExprLitNum::Execute(CBotStack* &pj) { diff --git a/src/CBot/CBotInstr/CBotExprLitNum.h b/src/CBot/CBotInstr/CBotExprLitNum.h index 92cd302d..8d7edcc1 100644 --- a/src/CBot/CBotInstr/CBotExprLitNum.h +++ b/src/CBot/CBotInstr/CBotExprLitNum.h @@ -26,6 +26,8 @@ namespace CBot CBotInstr* CompileExprLitNum(CBotToken* &p, CBotCStack* pStack); +CBotInstr* CompileSizeOf(CBotToken* &p, CBotCStack* pStack); + /** * \brief A number literal - 5, 1, 2.5, 3.75, etc. or a predefined numerical constant (see CBotToken::DefineNum()) * diff --git a/src/CBot/CBotInstr/CBotParExpr.cpp b/src/CBot/CBotInstr/CBotParExpr.cpp index 24bdaafe..9572ff5e 100644 --- a/src/CBot/CBotInstr/CBotParExpr.cpp +++ b/src/CBot/CBotInstr/CBotParExpr.cpp @@ -68,6 +68,13 @@ CBotInstr* CBotParExpr::Compile(CBotToken* &p, CBotCStack* pStack) if (inst != nullptr || !pStk->IsOk()) return pStack->Return(inst, pStk); + // is it sizeof operator ? + inst = CBot::CompileSizeOf(p, pStk); + if (inst != nullptr || !pStk->IsOk()) + { + return pStack->Return(inst, pStk); + } + // is it a variable name? if (p->GetType() == TokenTypVar) { @@ -155,6 +162,13 @@ CBotInstr* CBotParExpr::CompileLitExpr(CBotToken* &p, CBotCStack* pStack) return pStack->Return(inst, pStk); } + // is it sizeof operator ? + inst = CBot::CompileSizeOf(p, pStk); + if (inst != nullptr || !pStk->IsOk()) + { + return pStack->Return(inst, pStk); + } + // is this a chaine? if (p->GetType() == TokenTypString) { From 1058a326adad910b11f40b861d932993b64db468 Mon Sep 17 00:00:00 2001 From: melex750 Date: Thu, 11 Apr 2019 05:14:11 -0400 Subject: [PATCH 5/7] Add binary and character literals in CBOT --- po/colobot.pot | 3 + po/cs.po | 3 + po/de.po | 3 + po/fr.po | 3 + po/pl.po | 3 + po/pt.po | 3 + po/ru.po | 3 + src/CBot/CBotEnums.h | 4 +- src/CBot/CBotInstr/CBotExprLitChar.cpp | 151 +++++++++++++++++++++++ src/CBot/CBotInstr/CBotExprLitChar.h | 60 +++++++++ src/CBot/CBotInstr/CBotExprLitString.cpp | 5 +- src/CBot/CBotInstr/CBotParExpr.cpp | 8 ++ src/CBot/CBotToken.cpp | 48 +++++++ src/CBot/CBotUtils.cpp | 12 ++ src/CBot/CMakeLists.txt | 2 + src/common/restext.cpp | 1 + src/script/script.cpp | 9 +- test/unit/CBot/CBot_test.cpp | 122 ++++++++++++++++++ 18 files changed, 436 insertions(+), 7 deletions(-) create mode 100644 src/CBot/CBotInstr/CBotExprLitChar.cpp create mode 100644 src/CBot/CBotInstr/CBotExprLitChar.h diff --git a/po/colobot.pot b/po/colobot.pot index b91aba04..266d13b1 100644 --- a/po/colobot.pot +++ b/po/colobot.pot @@ -1795,6 +1795,9 @@ msgstr "" msgid "Invalid universal character name" msgstr "" +msgid "Empty character constant" +msgstr "" + msgid "Dividing by zero" msgstr "" diff --git a/po/cs.po b/po/cs.po index b5c41e3a..7f1b50d4 100644 --- a/po/cs.po +++ b/po/cs.po @@ -507,6 +507,9 @@ msgstr "Upravit vybraný program" msgid "Egg" msgstr "Vejce" +msgid "Empty character constant" +msgstr "" + msgid "End of block missing" msgstr "Chybí konec bloku" diff --git a/po/de.po b/po/de.po index 66d9918a..61f72697 100644 --- a/po/de.po +++ b/po/de.po @@ -508,6 +508,9 @@ msgstr "Gewähltes Programm bearbeiten" msgid "Egg" msgstr "Ei" +msgid "Empty character constant" +msgstr "" + msgid "End of block missing" msgstr "Es fehlt eine geschlossene geschweifte Klammer \"}\" (Ende des Blocks)" diff --git a/po/fr.po b/po/fr.po index c8d48217..f0229e72 100644 --- a/po/fr.po +++ b/po/fr.po @@ -510,6 +510,9 @@ msgstr "Éditer le programme sélectionné" msgid "Egg" msgstr "Oeuf" +msgid "Empty character constant" +msgstr "" + msgid "End of block missing" msgstr "Il manque la fin du bloc" diff --git a/po/pl.po b/po/pl.po index 073c7b58..5b9bab87 100644 --- a/po/pl.po +++ b/po/pl.po @@ -506,6 +506,9 @@ msgstr "Edytuj zaznaczony program" msgid "Egg" msgstr "Jajo" +msgid "Empty character constant" +msgstr "" + msgid "End of block missing" msgstr "Brak końca bloku" diff --git a/po/pt.po b/po/pt.po index f99a9cbf..53bdd07c 100644 --- a/po/pt.po +++ b/po/pt.po @@ -505,6 +505,9 @@ msgstr "Alterar o programa selecionado" msgid "Egg" msgstr "Ovo" +msgid "Empty character constant" +msgstr "" + msgid "End of block missing" msgstr "Fim do bloco ausente" diff --git a/po/ru.po b/po/ru.po index ca44c0c5..df68516f 100644 --- a/po/ru.po +++ b/po/ru.po @@ -514,6 +514,9 @@ msgstr "Изменить выбранную программу" msgid "Egg" msgstr "Яйцо" +msgid "Empty character constant" +msgstr "" + msgid "End of block missing" msgstr "Отсутствует конец блока" diff --git a/src/CBot/CBotEnums.h b/src/CBot/CBotEnums.h index 3dff66eb..35775295 100644 --- a/src/CBot/CBotEnums.h +++ b/src/CBot/CBotEnums.h @@ -182,7 +182,8 @@ enum TokenType TokenTypNum = 2, //!< number TokenTypString = 3, //!< string TokenTypVar = 4, //!< a variable name - TokenTypDef = 5 //!< value according DefineNum + TokenTypDef = 5, //!< value according DefineNum + TokenTypChar = 6, //!< character literal }; /** @@ -252,6 +253,7 @@ enum CBotError : int CBotErrHexDigits = 5052, //!< missing hex digits after escape sequence CBotErrHexRange = 5053, //!< hex value out of range CBotErrUnicodeName = 5054, //!< invalid universal character name + CBotErrCharEmpty = 5055, //!< empty character constant // Runtime errors CBotErrZeroDiv = 6000, //!< division by zero diff --git a/src/CBot/CBotInstr/CBotExprLitChar.cpp b/src/CBot/CBotInstr/CBotExprLitChar.cpp new file mode 100644 index 00000000..dc7de906 --- /dev/null +++ b/src/CBot/CBotInstr/CBotExprLitChar.cpp @@ -0,0 +1,151 @@ +/* + * 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/CBotExprLitChar.h" + +#include "CBot/CBotStack.h" +#include "CBot/CBotCStack.h" + +#include "CBot/CBotVar/CBotVar.h" + +namespace CBot +{ + +CBotExprLitChar::CBotExprLitChar() +{ +} + +CBotExprLitChar::~CBotExprLitChar() +{ +} + +CBotInstr* CBotExprLitChar::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotCStack* pStk = pStack->TokenStack(); + + const auto& s = p->GetString(); + + auto it = s.cbegin(); + if (++it != s.cend()) + { + if (*it != '\'') // not empty quotes ? + { + uint32_t valchar = 0; + int pos = p->GetStart() + 1; + + if (*it != '\\') valchar = *(it++); // not escape sequence ? + else if (++it != s.cend()) + { + pStk->SetStartError(pos++); + unsigned char c = *(it++); + if (c == '\"' || c == '\'' || c == '\\') valchar = c; + else if (c == 'a') valchar = '\a'; // alert bell + else if (c == 'b') valchar = '\b'; // backspace + else if (c == 'f') valchar = '\f'; // form feed + else if (c == 'n') valchar = '\n'; // new line + else if (c == 'r') valchar = '\r'; // carriage return + else if (c == 't') valchar = '\t'; // horizontal tab + else if (c == 'v') valchar = '\v'; // vertical tab + else if (c == 'u' || c == 'U') // unicode escape + { + if (it != s.cend()) + { + std::string hex = ""; + size_t maxlen = (c == 'u') ? 4 : 8; + + for (size_t i = 0; i < maxlen; i++) + { + if (!CharInList(*it, "0123456789ABCDEFabcdef")) break; + ++pos; + hex += *it; + if (++it == s.cend()) break; + } + + if (maxlen == hex.length()) // unicode character + { + try + { + valchar = std::stoi(hex, nullptr, 16); + if (0x10FFFF < valchar || (0xD7FF < valchar && valchar < 0xE000)) + pStk->SetError(CBotErrUnicodeName, pos + 1); + } + catch (const std::out_of_range& e) + { + pStk->SetError(CBotErrHexRange, pos + 1); + } + } + else + pStk->SetError(CBotErrHexDigits, pos + 1); + } + else + pStk->SetError(CBotErrHexDigits, pos + 1); + } + else + pStk->SetError(CBotErrBadEscape, pos + 1); + } + + if (it == s.cend() || *it != '\'') + pStk->SetError(CBotErrEndQuote, p); + + if (pStk->IsOk()) + { + CBotExprLitChar* inst = new CBotExprLitChar(); + inst->m_valchar = valchar; + inst->SetToken(p); + p = p->GetNext(); + + CBotVar* var = CBotVar::Create("", CBotTypChar); + pStk->SetVar(var); + + return pStack->Return(inst, pStk); + } + } + pStk->SetError(CBotErrCharEmpty, p); + } + + pStk->SetError(CBotErrEndQuote, p); + return pStack->Return(nullptr, pStk); +} + +bool CBotExprLitChar::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); + + if (pile->IfStep()) return false; + + CBotVar* var = CBotVar::Create("", CBotTypChar); + + var->SetValChar(m_valchar); + + pile->SetVar(var); + + return pj->Return(pile); +} + +void CBotExprLitChar::RestoreState(CBotStack* &pj, bool bMain) +{ + if (bMain) pj->RestoreStack(this); +} + +std::string CBotExprLitChar::GetDebugData() +{ + return m_token.GetString(); +} + +} // namespace CBot diff --git a/src/CBot/CBotInstr/CBotExprLitChar.h b/src/CBot/CBotInstr/CBotExprLitChar.h new file mode 100644 index 00000000..1f31d6d2 --- /dev/null +++ b/src/CBot/CBotInstr/CBotExprLitChar.h @@ -0,0 +1,60 @@ +/* + * 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 + */ + +#pragma once + +#include "CBot/CBotInstr/CBotInstr.h" + +namespace CBot +{ + +/** + * \brief A character literal + * \verbatim 'a', '\n', '\t', '\uFFFD', '\U0000FFFD', etc. \endverbatim + */ +class CBotExprLitChar : public CBotInstr +{ +public: + CBotExprLitChar(); + ~CBotExprLitChar(); + + /*! + * \brief Compile a character literal + */ + static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + + /*! + * \brief Execute, returns the corresponding char. + */ + bool Execute(CBotStack* &pj) override; + + /*! + * \brief RestoreState + */ + void RestoreState(CBotStack* &pj, bool bMain) override; + +protected: + virtual const std::string GetDebugName() override { return "CBotExprLitChar"; } + virtual std::string GetDebugData() override; + +private: + uint32_t m_valchar = 0; +}; + +} // namespace CBot diff --git a/src/CBot/CBotInstr/CBotExprLitString.cpp b/src/CBot/CBotInstr/CBotExprLitString.cpp index 72cca72b..fc254162 100644 --- a/src/CBot/CBotInstr/CBotExprLitString.cpp +++ b/src/CBot/CBotInstr/CBotExprLitString.cpp @@ -42,7 +42,7 @@ CBotInstr* CBotExprLitString::Compile(CBotToken* &p, CBotCStack* pStack) { CBotCStack* pStk = pStack->TokenStack(); - std::string s = p->GetString(); + const auto& s = p->GetString(); auto it = s.cbegin(); if (++it != s.cend()) @@ -51,7 +51,7 @@ CBotInstr* CBotExprLitString::Compile(CBotToken* &p, CBotCStack* pStack) std::string valstring = ""; while (it != s.cend() && *it != '\"') { - pStk->SetStartError(++pos); + ++pos; if (*it != '\\') // not escape sequence ? { valstring += *(it++); @@ -59,6 +59,7 @@ CBotInstr* CBotExprLitString::Compile(CBotToken* &p, CBotCStack* pStack) } if (++it == s.cend()) break; + pStk->SetStartError(pos); if (CharInList(*it, "01234567")) // octal { diff --git a/src/CBot/CBotInstr/CBotParExpr.cpp b/src/CBot/CBotInstr/CBotParExpr.cpp index 9572ff5e..fa455c27 100644 --- a/src/CBot/CBotInstr/CBotParExpr.cpp +++ b/src/CBot/CBotInstr/CBotParExpr.cpp @@ -21,6 +21,7 @@ #include "CBot/CBotInstr/CBotExpression.h" #include "CBot/CBotInstr/CBotExprLitBool.h" +#include "CBot/CBotInstr/CBotExprLitChar.h" #include "CBot/CBotInstr/CBotExprLitNan.h" #include "CBot/CBotInstr/CBotExprLitNull.h" #include "CBot/CBotInstr/CBotExprLitNum.h" @@ -169,6 +170,13 @@ CBotInstr* CBotParExpr::CompileLitExpr(CBotToken* &p, CBotCStack* pStack) return pStack->Return(inst, pStk); } + // is this a character? + if (p->GetType() == TokenTypChar) + { + CBotInstr* inst = CBotExprLitChar::Compile(p, pStk); + return pStack->Return(inst, pStk); + } + // is this a chaine? if (p->GetType() == TokenTypString) { diff --git a/src/CBot/CBotToken.cpp b/src/CBot/CBotToken.cpp index d5afe1fe..9764d237 100644 --- a/src/CBot/CBotToken.cpp +++ b/src/CBot/CBotToken.cpp @@ -304,12 +304,53 @@ CBotToken* CBotToken::NextToken(const char*& program, bool first) stop = true; } + // special case for characters + if (token[0] == '\'') + { + if (c == '\\') // escape sequence + { + token += c; + c = *(program++); + + if (c == 'u' || c == 'U') // unicode escape + { + int maxlen = (c == 'u') ? 4 : 8; + token += c; + c = *(program++); + for (int i = 0; i < maxlen; i++) + { + if (c == 0 || !CharInList(c, hexnum)) break; + token += c; + c = *(program++); + } + } + else if (c != 0 && !CharInList(c, nch)) // other escape char + { + token += c; + c = *(program++); + } + } + else if (c != 0 && c != '\'' && !CharInList(c, nch)) // single character + { + token += c; + c = *(program++); + } + + if (c == '\'') // close quote + { + token += c; + c = *(program++); + } + stop = true; + } + // special case for numbers if ( CharInList(token[0], num )) { bool bdot = false; // found a point? bool bexp = false; // found an exponent? + char bin[] = "01"; char* liste = num; if (token[0] == '0' && c == 'x') // hexadecimal value? { @@ -317,6 +358,12 @@ CBotToken* CBotToken::NextToken(const char*& program, bool first) c = *(program++); // next character liste = hexnum; } + else if (token[0] == '0' && c == 'b') // binary literal + { + liste = bin; + token += c; + c = *(program++); + } cw: while (c != 0 && CharInList(c, liste)) { @@ -399,6 +446,7 @@ bis: if (CharInList(token[0], num )) t->m_type = TokenTypNum; if (token[0] == '\"') t->m_type = TokenTypString; + if (token[0] == '\'') t->m_type = TokenTypChar; if (first) t->m_type = TokenTypNone; t->m_keywordId = GetKeyWord(token); diff --git a/src/CBot/CBotUtils.cpp b/src/CBot/CBotUtils.cpp index 4d663bea..afa05bbc 100644 --- a/src/CBot/CBotUtils.cpp +++ b/src/CBot/CBotUtils.cpp @@ -155,6 +155,18 @@ long GetNumInt(const std::string& str) break; } } + else if (*p == 'b') + { + while (*++p != 0) + { + if (*p == '0' || *p == '1') + { + num = (num << 1) + *p - '0'; + continue; + } + break; + } + } return num; } diff --git a/src/CBot/CMakeLists.txt b/src/CBot/CMakeLists.txt index 4be97841..45af8639 100644 --- a/src/CBot/CMakeLists.txt +++ b/src/CBot/CMakeLists.txt @@ -44,6 +44,8 @@ set(SOURCES CBotInstr/CBotEmpty.h CBotInstr/CBotExprLitBool.cpp CBotInstr/CBotExprLitBool.h + CBotInstr/CBotExprLitChar.cpp + CBotInstr/CBotExprLitChar.h CBotInstr/CBotExprLitNan.cpp CBotInstr/CBotExprLitNan.h CBotInstr/CBotExprLitNull.cpp diff --git a/src/common/restext.cpp b/src/common/restext.cpp index 4aa21564..065f6c92 100644 --- a/src/common/restext.cpp +++ b/src/common/restext.cpp @@ -742,6 +742,7 @@ void InitializeRestext() stringsCbot[CBot::CBotErrHexDigits] = TR("Missing hex digits after escape sequence"); stringsCbot[CBot::CBotErrHexRange] = TR("Hex value out of range"); stringsCbot[CBot::CBotErrUnicodeName] = TR("Invalid universal character name"); + stringsCbot[CBot::CBotErrCharEmpty] = TR("Empty character constant"); stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero"); stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized"); diff --git a/src/script/script.cpp b/src/script/script.cpp index 22ae1024..0b5828aa 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -589,16 +589,17 @@ void CScript::UpdateList(Ui::CList* list) list->SetState(Ui::STATE_ENABLE); } -// Colorize a string literal with escape sequences also colored +// Colorize a string or character literal with escape sequences also colored static void HighlightString(Ui::CEdit* edit, const std::string& s, int start) { edit->SetFormat(start, start + 1, Gfx::FONT_HIGHLIGHT_STRING); - auto it = s.cbegin() + 1; + auto it = s.cbegin(); + char endQuote = *(it++); ++start; - while (it != s.cend() && *it != '\"') + while (it != s.cend() && *it != endQuote) { if (*(it++) != '\\') // not escape sequence { @@ -697,7 +698,7 @@ void CScript::ColorizeScript(Ui::CEdit* edit, int rangeStart, int rangeEnd) { color = Gfx::FONT_HIGHLIGHT_STRING; } - else if (type == CBot::TokenTypString) // string literals + else if (type == CBot::TokenTypString || type == CBot::TokenTypChar) // string literals and character literals { HighlightString(edit, token, cursor1); bt = bt->GetNext(); diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index 6f2201b9..4c60b8fa 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -619,6 +619,27 @@ TEST_F(CBotUT, IntegerMathNearLimits_Issue993) ); } +TEST_F(CBotUT, BinaryLiterals) +{ + ExecuteTest( + "extern void TestBinaryLiterals() {\n" + " ASSERT( 8 == 0b00001000);\n" + " ASSERT( 12 == 0b00001100);\n" + " ASSERT( 16 == 0b00010000);\n" + " ASSERT( 24 == 0b00011000);\n" + " ASSERT( 32 == 0b00100000);\n" + " ASSERT( 48 == 0b00110000);\n" + " ASSERT( 64 == 0b01000000);\n" + " ASSERT( 96 == 0b01100000);\n" + " ASSERT(128 == 0b10000000);\n" + " ASSERT(192 == 0b11000000);\n" + " ASSERT(256 == 0b100000000);\n" + " ASSERT(384 == 0b110000000);\n" + " ASSERT(512 == 0b1000000000);\n" + "}\n" + ); +} + TEST_F(CBotUT, ToString) { ExecuteTest( @@ -1752,6 +1773,107 @@ TEST_F(CBotUT, StringFunctions) ); } +TEST_F(CBotUT, LiteralCharacters) +{ + ExecuteTest( + "extern void TestCharValue()\n" + "{\n" + " ASSERT('A' == 65);\n" + " ASSERT('B' == 66);\n" + " ASSERT('C' == 67);\n" + " ASSERT('\\a' == 0x07);\n" + " ASSERT('\\b' == 0x08);\n" + " ASSERT('\\t' == 0x09);\n" + " ASSERT('\\n' == 0x0A);\n" + " ASSERT('\\v' == 0x0B);\n" + " ASSERT('\\f' == 0x0C);\n" + " ASSERT('\\r' == 0x0D);\n" + " ASSERT('\\\"' == 0x22);\n" + " ASSERT('\\\'' == 0x27);\n" + " ASSERT('\\\\' == 0x5C);\n" + "}\n" + "extern void TestCharUnicodeEscape()\n" + "{\n" + " ASSERT('\\u0007' == '\\a');\n" + " ASSERT('\\u0008' == '\\b');\n" + " ASSERT('\\u0009' == '\\t');\n" + " ASSERT('\\u000A' == '\\n');\n" + " ASSERT('\\u000B' == '\\v');\n" + " ASSERT('\\u000C' == '\\f');\n" + " ASSERT('\\u000D' == '\\r');\n" + " ASSERT('\\u0022' == '\\\"');\n" + " ASSERT('\\u0027' == '\\\'');\n" + " ASSERT('\\u005C' == '\\\\');\n" + "}\n" + "extern void AssignCharToString_ToUTF_8()\n" + "{\n" + " string test = '\\u00A9';\n" + " test += '\\u00AE';\n" + " ASSERT(test == \"\\xC2\\xA9\\xC2\\xAE\");\n" + "}\n" + "extern void AddCharToString_ToUTF_8()\n" + "{\n" + " ASSERT(\"\" + 'A' + 'B' + 'C' == \"ABC\");\n" + " ASSERT(\"\" + '\\u00A9' == \"\\xC2\\xA9\");\n" + " ASSERT(\"\" + '\\u00AE' == \"\\xC2\\xAE\");\n" + " ASSERT(\"\" + '\\u262E' == \"\\xE2\\x98\\xAE\");\n" + " ASSERT(\"\" + '\\u262F' == \"\\xE2\\x98\\xAF\");\n" + " ASSERT(\"\" + '\\U0001F60E' == \"\\xF0\\x9F\\x98\\x8E\");\n" + " ASSERT(\"\" + '\\U0001F61C' == \"\\xF0\\x9F\\x98\\x9C\");\n" + " ASSERT(\"\" + '\\U0001F6E0' == \"\\xF0\\x9F\\x9B\\xA0\");\n" + " ASSERT(\"\" + '\\U0010FFFF' == \"\\xF4\\x8F\\xBF\\xBF\");\n" + "}\n" + ); + + ExecuteTest( + "extern void MissingEndQuote()\n" + "{\n" + " '\n" + "}\n", + CBotErrEndQuote + ); + + ExecuteTest( + "extern void MissingEndQuote()\n" + "{\n" + " 'a\n" + "}\n", + CBotErrEndQuote + ); + + ExecuteTest( + "extern void EmptyQuotes()\n" + "{\n" + " '';\n" + "}\n", + CBotErrCharEmpty + ); + + ExecuteTest( + "extern void UnknownEscapeSequence()\n" + "{\n" + " '\\p';\n" + "}\n", + CBotErrBadEscape + ); + + ExecuteTest( + "extern void MissingHexDigits()\n" + "{\n" + " '\\u';\n" + "}\n", + CBotErrHexDigits + ); + + ExecuteTest( + "extern void BadUnicodeCharacterName()\n" + "{\n" + " '\\U00110000';\n" + "}\n", + CBotErrUnicodeName + ); +} + TEST_F(CBotUT, TestNANParam_Issue642) { ExecuteTest( From 5f089f4a9bc2276ee2879e7fc77af222736c7595 Mon Sep 17 00:00:00 2001 From: melex750 Date: Thu, 11 Apr 2019 05:34:00 -0400 Subject: [PATCH 6/7] Fix and improve switch...case (#1008) --- po/colobot.pot | 3 + po/cs.po | 3 + po/de.po | 3 + po/fr.po | 3 + po/pl.po | 3 + po/pt.po | 3 + po/ru.po | 3 + src/CBot/CBotEnums.h | 1 + src/CBot/CBotInstr/CBotCase.cpp | 98 +++++++++++++++++++-------- src/CBot/CBotInstr/CBotCase.h | 19 ++---- src/CBot/CBotInstr/CBotExprUnaire.cpp | 9 +-- src/CBot/CBotInstr/CBotExprUnaire.h | 2 +- src/CBot/CBotInstr/CBotInstr.cpp | 7 -- src/CBot/CBotInstr/CBotInstr.h | 11 --- src/CBot/CBotInstr/CBotParExpr.cpp | 58 +++++++++++++++- src/CBot/CBotInstr/CBotParExpr.h | 4 +- src/CBot/CBotInstr/CBotSwitch.cpp | 76 ++++++++++----------- src/CBot/CBotInstr/CBotSwitch.h | 10 ++- src/CBot/CBotInstr/CBotTwoOpExpr.cpp | 13 ++-- src/CBot/CBotInstr/CBotTwoOpExpr.h | 2 +- src/common/restext.cpp | 1 + test/unit/CBot/CBot_test.cpp | 86 +++++++++++++++++++++++ 22 files changed, 298 insertions(+), 120 deletions(-) diff --git a/po/colobot.pot b/po/colobot.pot index 266d13b1..7cbb92d7 100644 --- a/po/colobot.pot +++ b/po/colobot.pot @@ -1798,6 +1798,9 @@ msgstr "" msgid "Empty character constant" msgstr "" +msgid "Duplicate label in switch" +msgstr "" + msgid "Dividing by zero" msgstr "" diff --git a/po/cs.po b/po/cs.po index 7f1b50d4..b14eb434 100644 --- a/po/cs.po +++ b/po/cs.po @@ -489,6 +489,9 @@ msgstr "Dolů (\\key gdown;)" msgid "Drawer bot" msgstr "Tužkobot" +msgid "Duplicate label in switch" +msgstr "" + msgid "Dust\\Dust and dirt on bots and buildings" msgstr "Prach\\Prach a špína na robotech a budovách" diff --git a/po/de.po b/po/de.po index 61f72697..f031a049 100644 --- a/po/de.po +++ b/po/de.po @@ -490,6 +490,9 @@ msgstr "Sinkt (\\key gdown;)" msgid "Drawer bot" msgstr "Zeichner" +msgid "Duplicate label in switch" +msgstr "" + msgid "Dust\\Dust and dirt on bots and buildings" msgstr "Schmutz\\Schmutz auf Robotern und Bauten" diff --git a/po/fr.po b/po/fr.po index f0229e72..5c904b95 100644 --- a/po/fr.po +++ b/po/fr.po @@ -492,6 +492,9 @@ msgstr "Descend (\\key gdown;)" msgid "Drawer bot" msgstr "Robot dessinateur" +msgid "Duplicate label in switch" +msgstr "" + msgid "Dust\\Dust and dirt on bots and buildings" msgstr "Salissures\\Salissures des robots et bâtiments" diff --git a/po/pl.po b/po/pl.po index 5b9bab87..35be67e1 100644 --- a/po/pl.po +++ b/po/pl.po @@ -488,6 +488,9 @@ msgstr "Dół (\\key gdown;)" msgid "Drawer bot" msgstr "Robot rysownik" +msgid "Duplicate label in switch" +msgstr "" + msgid "Dust\\Dust and dirt on bots and buildings" msgstr "Kurz\\Kurz i bród na robotach i budynkach" diff --git a/po/pt.po b/po/pt.po index 53bdd07c..06bb43e3 100644 --- a/po/pt.po +++ b/po/pt.po @@ -487,6 +487,9 @@ msgstr "Baixo (\\key gdown;)" msgid "Drawer bot" msgstr "Robô cartoonista" +msgid "Duplicate label in switch" +msgstr "" + msgid "Dust\\Dust and dirt on bots and buildings" msgstr "Poeira\\Poeira e sujeira nos robôs e prédios" diff --git a/po/ru.po b/po/ru.po index df68516f..37852277 100644 --- a/po/ru.po +++ b/po/ru.po @@ -496,6 +496,9 @@ msgstr "Вниз (\\key gdown;)" msgid "Drawer bot" msgstr "Рисовальщик" +msgid "Duplicate label in switch" +msgstr "" + msgid "Dust\\Dust and dirt on bots and buildings" msgstr "Пыль\\Пыль и грязь на ботах и зданиях" diff --git a/src/CBot/CBotEnums.h b/src/CBot/CBotEnums.h index 35775295..2f04707d 100644 --- a/src/CBot/CBotEnums.h +++ b/src/CBot/CBotEnums.h @@ -254,6 +254,7 @@ enum CBotError : int CBotErrHexRange = 5053, //!< hex value out of range CBotErrUnicodeName = 5054, //!< invalid universal character name CBotErrCharEmpty = 5055, //!< empty character constant + CBotErrRedefCase = 5056, //!< duplicate label in switch // Runtime errors CBotErrZeroDiv = 6000, //!< division by zero diff --git a/src/CBot/CBotInstr/CBotCase.cpp b/src/CBot/CBotInstr/CBotCase.cpp index 15c8d4f7..2e0cc7f0 100644 --- a/src/CBot/CBotInstr/CBotCase.cpp +++ b/src/CBot/CBotInstr/CBotCase.cpp @@ -19,7 +19,7 @@ #include "CBot/CBotInstr/CBotCase.h" -#include "CBot/CBotInstr/CBotExprLitNum.h" +#include "CBot/CBotInstr/CBotTwoOpExpr.h" #include "CBot/CBotStack.h" #include "CBot/CBotCStack.h" @@ -30,69 +30,107 @@ namespace CBot //////////////////////////////////////////////////////////////////////////////// CBotCase::CBotCase() { - m_value = nullptr; + m_instr = nullptr; } //////////////////////////////////////////////////////////////////////////////// CBotCase::~CBotCase() { - delete m_value; + delete m_instr; } -//////////////////////////////////////////////////////////////////////////////// -CBotInstr* CBotCase::Compile(CBotToken* &p, CBotCStack* pStack) +CBotInstr* CBotCase::Compile(CBotToken* &p, CBotCStack* pStack, std::unordered_map& labels) { - CBotCase* inst = new CBotCase(); // creates the object CBotToken* pp = p; // preserves at the ^ token (starting position) - inst->SetToken(p); if (!IsOfType(p, ID_CASE, ID_DEFAULT)) return nullptr; // should never happen + pStack->SetStartError(pp->GetStart()); - if ( pp->GetType() == ID_CASE ) + long labelValue = 0; + + if (pp->GetType() == ID_CASE) { - pp = p; - inst->m_value = CBot::CompileExprLitNum(p, pStack); - if (inst->m_value == nullptr ) + CBotInstr* i = nullptr; + if (nullptr != (i = CBotTwoOpExpr::Compile(p, pStack, nullptr, true))) { - pStack->SetError( CBotErrBadNum, pp ); - delete inst; - return nullptr; + if (pStack->GetType() <= CBotTypLong) + { + CBotStack* pile = CBotStack::AllocateStack(); + while ( !i->Execute(pile) ); + labelValue = pile->GetVar()->GetValLong(); + pile->Delete(); + + if (labels.count(labelValue) > 0) + { + pStack->SetError(CBotErrRedefCase, p->GetStart()); + } + } + else + pStack->SetError(CBotErrBadNum, p->GetStart()); + delete i; } - } - if ( !IsOfType( p, ID_DOTS )) - { - pStack->SetError( CBotErrNoDoubleDots, p->GetStart() ); - delete inst; - return nullptr; + else + pStack->SetError(CBotErrBadNum, p->GetStart()); } - return inst; + if (pStack->IsOk() && IsOfType(p, ID_DOTS)) + { + CBotCase* newCase = new CBotCase(); + newCase->SetToken(pp); + if (pp->GetType() == ID_CASE) + labels[labelValue] = newCase; + return newCase; + } + + pStack->SetError(CBotErrNoDoubleDots, p->GetStart()); + return nullptr; } //////////////////////////////////////////////////////////////////////////////// bool CBotCase::Execute(CBotStack* &pj) { - return true; // the "case" statement does nothing! + if (m_instr == nullptr) return true; + CBotStack* pile = pj->AddStack(this, CBotStack::BlockVisibilityType::BLOCK); + + int state = pile->GetState(); + CBotInstr* p = m_instr; + while (state-- > 0) p = p->GetNext(); + + while (p != nullptr) + { + if (!p->Execute(pile)) return false; + pile->IncState(); + p = p->GetNext(); + } + + pile->Delete(); + return pj->IsOk(); } //////////////////////////////////////////////////////////////////////////////// void CBotCase::RestoreState(CBotStack* &pj, bool bMain) { -} + if (!bMain) return; -//////////////////////////////////////////////////////////////////////////////// -bool CBotCase::CompCase(CBotStack* &pile, int val) -{ - if (m_value == nullptr ) return true; // "default" case + CBotStack* pile = pj->RestoreStack(this); + if (pile == nullptr) return; - while (!m_value->Execute(pile)); // puts the value on the correspondent stack (without interruption) - return (pile->GetVal() == val); // compared with the given value + CBotInstr* p = m_instr; + + int state = pile->GetState(); + while (p != nullptr && state-- > 0) + { + p->RestoreState(pile, bMain); + p = p->GetNext(); + } + + if (p != nullptr) p->RestoreState(pile, bMain); } std::map CBotCase::GetDebugLinks() { auto links = CBotInstr::GetDebugLinks(); - links["m_value"] = m_value; + links["m_instr"] = m_instr; return links; } diff --git a/src/CBot/CBotInstr/CBotCase.h b/src/CBot/CBotInstr/CBotCase.h index e2863cde..ccd031e8 100644 --- a/src/CBot/CBotInstr/CBotCase.h +++ b/src/CBot/CBotInstr/CBotCase.h @@ -21,6 +21,8 @@ #include "CBot/CBotInstr/CBotInstr.h" +#include + namespace CBot { @@ -42,7 +44,7 @@ public: * \param pStack * \return */ - static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, std::unordered_map& labels); /*! * \brief Execute Execution of instruction "case". @@ -58,22 +60,15 @@ public: */ void RestoreState(CBotStack* &pj, bool bMain) override; - /*! - * \brief CompCase Routine to find the entry point of "case" corresponding - * to the value seen. - * \param pj - * \param val - * \return - */ - bool CompCase(CBotStack* &pj, int val) override; - protected: virtual const std::string GetDebugName() override { return "CBotCase"; } virtual std::map GetDebugLinks() override; private: - //! Value to compare. - CBotInstr* m_value; + //! List of instructions after case label + CBotInstr* m_instr; + + friend class CBotSwitch; }; } // namespace CBot diff --git a/src/CBot/CBotInstr/CBotExprUnaire.cpp b/src/CBot/CBotInstr/CBotExprUnaire.cpp index c39fd89e..d9a6c4d4 100644 --- a/src/CBot/CBotInstr/CBotExprUnaire.cpp +++ b/src/CBot/CBotInstr/CBotExprUnaire.cpp @@ -40,8 +40,7 @@ CBotExprUnaire::~CBotExprUnaire() delete m_expr; } -//////////////////////////////////////////////////////////////////////////////// -CBotInstr* CBotExprUnaire::Compile(CBotToken* &p, CBotCStack* pStack, bool bLiteral) +CBotInstr* CBotExprUnaire::Compile(CBotToken* &p, CBotCStack* pStack, bool bLiteral, bool bConstExpr) { int op = p->GetType(); CBotToken* pp = p; @@ -52,8 +51,10 @@ CBotInstr* CBotExprUnaire::Compile(CBotToken* &p, CBotCStack* pStack, bool bLite CBotExprUnaire* inst = new CBotExprUnaire(); inst->SetToken(pp); - if (!bLiteral) inst->m_expr = CBotParExpr::Compile(p, pStk); - else inst->m_expr = CBotParExpr::CompileLitExpr(p, pStk); + if (bConstExpr || !bLiteral) + inst->m_expr = CBotParExpr::Compile(p, pStk, bConstExpr); + else + inst->m_expr = CBotParExpr::CompileLitExpr(p, pStk); if (inst->m_expr != nullptr) { diff --git a/src/CBot/CBotInstr/CBotExprUnaire.h b/src/CBot/CBotInstr/CBotExprUnaire.h index 8a743a81..82d124a5 100644 --- a/src/CBot/CBotInstr/CBotExprUnaire.h +++ b/src/CBot/CBotInstr/CBotExprUnaire.h @@ -40,7 +40,7 @@ public: * \param bLiteral If true, compiles only literal expressions Ex: ~11, -4.0, !false, not true * \return The compiled instruction or nullptr */ - static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, bool bLiteral = false); + static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, bool bLiteral = false, bool bConstExpr = false); /*! * \brief Execute diff --git a/src/CBot/CBotInstr/CBotInstr.cpp b/src/CBot/CBotInstr/CBotInstr.cpp index 04b13204..54d9cee9 100644 --- a/src/CBot/CBotInstr/CBotInstr.cpp +++ b/src/CBot/CBotInstr/CBotInstr.cpp @@ -317,13 +317,6 @@ void CBotInstr::RestoreStateVar(CBotStack* &pile, bool bMain) assert(0); // dad do not know, see the girls } -//////////////////////////////////////////////////////////////////////////////// -bool CBotInstr::CompCase(CBotStack* &pj, int val) -{ - return false; -} - -//////////////////////////////////////////////////////////////////////////////// CBotInstr* CBotInstr::CompileArray(CBotToken* &p, CBotCStack* pStack, CBotTypResult type, bool first) { if (IsOfType(p, ID_OPBRK)) diff --git a/src/CBot/CBotInstr/CBotInstr.h b/src/CBot/CBotInstr/CBotInstr.h index 712b379d..2c4d5884 100644 --- a/src/CBot/CBotInstr/CBotInstr.h +++ b/src/CBot/CBotInstr/CBotInstr.h @@ -191,17 +191,6 @@ public: virtual void RestoreStateVar(CBotStack* &pile, bool bMain); - /** - * \brief CompCase 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.. - * \param pj - * \param val - * \return - */ - virtual bool CompCase(CBotStack* &pj, - int val); - /** * \brief SetToken Set the token corresponding to the instruction. * \param p diff --git a/src/CBot/CBotInstr/CBotParExpr.cpp b/src/CBot/CBotInstr/CBotParExpr.cpp index fa455c27..9dd784bc 100644 --- a/src/CBot/CBotInstr/CBotParExpr.cpp +++ b/src/CBot/CBotInstr/CBotParExpr.cpp @@ -32,6 +32,7 @@ #include "CBot/CBotInstr/CBotNew.h" #include "CBot/CBotInstr/CBotPostIncExpr.h" #include "CBot/CBotInstr/CBotPreIncExpr.h" +#include "CBot/CBotInstr/CBotTwoOpExpr.h" #include "CBot/CBotVar/CBotVar.h" @@ -40,13 +41,15 @@ namespace CBot { -//////////////////////////////////////////////////////////////////////////////// -CBotInstr* CBotParExpr::Compile(CBotToken* &p, CBotCStack* pStack) +CBotInstr* CBotParExpr::Compile(CBotToken* &p, CBotCStack* pStack, bool bConstExpr) { CBotCStack* pStk = pStack->TokenStack(); pStk->SetStartError(p->GetStart()); + if (bConstExpr) + return CBotParExpr::CompileConstExpr(p, pStack); + // is it an expression in parentheses? if (IsOfType(p, ID_OPENPAR)) { @@ -224,4 +227,55 @@ CBotInstr* CBotParExpr::CompileLitExpr(CBotToken* &p, CBotCStack* pStack) return pStack->Return(nullptr, pStk); } +CBotInstr* CBotParExpr::CompileConstExpr(CBotToken* &p, CBotCStack* pStack) +{ + CBotCStack* pStk = pStack->TokenStack(); + + // is it an expression in parentheses? + if (IsOfType(p, ID_OPENPAR)) + { + CBotInstr* inst = CBotTwoOpExpr::Compile(p, pStk, nullptr, true); + + if (nullptr != inst) + { + if (IsOfType(p, ID_CLOSEPAR)) + { + return pStack->Return(inst, pStk); + } + pStk->SetError(CBotErrClosePar, p->GetStart()); + } + delete inst; + return pStack->Return(nullptr, pStk); + } + + // is this a unary operation? + CBotInstr* inst = CBotExprUnaire::Compile(p, pStk, true, true); + if (inst != nullptr || !pStk->IsOk()) + return pStack->Return(inst, pStk); + + // is it a number or DefineNum? + if (p->GetType() == TokenTypNum || + p->GetType() == TokenTypDef ) + { + CBotInstr* inst = CBot::CompileExprLitNum(p, pStk); + return pStack->Return(inst, pStk); + } + + // is this a character? + if (p->GetType() == TokenTypChar) + { + CBotInstr* inst = CBotExprLitChar::Compile(p, pStk); + return pStack->Return(inst, pStk); + } + + // is it sizeof operator ? + inst = CBot::CompileSizeOf(p, pStk); + if (inst != nullptr || !pStk->IsOk()) + { + return pStack->Return(inst, pStk); + } + + return pStack->Return(nullptr, pStk); +} + } // namespace CBot diff --git a/src/CBot/CBotInstr/CBotParExpr.h b/src/CBot/CBotInstr/CBotParExpr.h index 235dab11..826b1cd1 100644 --- a/src/CBot/CBotInstr/CBotParExpr.h +++ b/src/CBot/CBotInstr/CBotParExpr.h @@ -52,7 +52,7 @@ public: * \param pStack * \return */ - static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, bool bConstExpr = false); /*! * \brief Compile a literal expression ("string", number, true, false, null, nan, new) @@ -62,6 +62,8 @@ public: */ static CBotInstr* CompileLitExpr(CBotToken* &p, CBotCStack* pStack); + static CBotInstr* CompileConstExpr(CBotToken* &p, CBotCStack* pStack); + private: CBotParExpr() = delete; CBotParExpr(const CBotParExpr&) = delete; diff --git a/src/CBot/CBotInstr/CBotSwitch.cpp b/src/CBot/CBotInstr/CBotSwitch.cpp index e8d1b24d..a889348b 100644 --- a/src/CBot/CBotInstr/CBotSwitch.cpp +++ b/src/CBot/CBotInstr/CBotSwitch.cpp @@ -57,7 +57,7 @@ CBotInstr* CBotSwitch::Compile(CBotToken* &p, CBotCStack* pStack) { if ( nullptr != (inst->m_value = CBotExpression::Compile(p, pStk )) ) { - if ( pStk->GetType() < CBotTypLong ) + if ( pStk->GetType() <= CBotTypLong ) { if ( IsOfType(p, ID_CLOSEPAR ) ) { @@ -65,21 +65,35 @@ CBotInstr* CBotSwitch::Compile(CBotToken* &p, CBotCStack* pStack) { IncLvl(); + CBotCase* caseInst = nullptr; + CBotCStack* pStk2 = nullptr; while( !IsOfType( p, ID_CLBLK ) ) { if ( p->GetType() == ID_CASE || p->GetType() == ID_DEFAULT) { - CBotCStack* pStk2 = pStk->TokenStack(p); // un petit bout de pile svp + delete pStk2; + pStk2 = pStk->TokenStack(p, true); // un petit bout de pile svp - CBotInstr* i = CBotCase::Compile( p, pStk2 ); - if (i == nullptr) + caseInst = static_cast(CBotCase::Compile(p, pStk2, inst->m_labels)); + if (caseInst == nullptr) { delete inst; return pStack->Return(nullptr, pStk2); } - delete pStk2; - if (inst->m_block == nullptr ) inst->m_block = i; - else inst->m_block->AddNext(i); + + if (inst->m_block == nullptr ) inst->m_block = caseInst; + else inst->m_block->AddNext(caseInst); + + if (ID_DEFAULT == caseInst->GetTokenType()) + { + if (inst->m_default != nullptr) + { + pStk->SetError(CBotErrRedefCase, caseInst->GetToken()); + delete inst; + return pStack->Return(nullptr, pStk); + } + inst->m_default = caseInst; + } continue; } @@ -90,13 +104,14 @@ CBotInstr* CBotSwitch::Compile(CBotToken* &p, CBotCStack* pStack) return pStack->Return(nullptr, pStk); } - CBotInstr* i = CBotBlock::CompileBlkOrInst( p, pStk, true ); - if ( !pStk->IsOk() ) + CBotInstr* i = CBotBlock::CompileBlkOrInst(p, pStk2); + if ( !pStk2->IsOk() ) { delete inst; - return pStack->Return(nullptr, pStk); + return pStack->Return(nullptr, pStk2); } - inst->m_block->AddNext(i); + if (caseInst->m_instr == nullptr ) caseInst->m_instr = i; + else caseInst->m_instr->AddNext(i); if ( p == nullptr ) { @@ -133,40 +148,21 @@ CBotInstr* CBotSwitch::Compile(CBotToken* &p, CBotCStack* pStack) bool CBotSwitch :: Execute(CBotStack* &pj) { CBotStack* pile1 = pj->AddStack(this); // adds an item to the stack -// if ( pile1 == EOX ) return true; - - CBotInstr* p = m_block; // first expression int state = pile1->GetState(); if (state == 0) { if ( !m_value->Execute(pile1) ) return false; - pile1->SetState(state = -1); + pile1->SetState(state = 1); } if ( pile1->IfStep() ) return false; - if ( state == -1 ) - { - state = 0; - int val = pile1->GetVal(); // result of the value + auto it = m_labels.find(pile1->GetVar()->GetValLong()); - CBotStack* pile2 = pile1->AddStack(); - while ( p != nullptr ) // search for the corresponding case in a list - { - state++; - if ( p->CompCase( pile2, val ) ) break; // found the case - p = p->GetNext(); - } - pile2->Delete(); + CBotInstr* p = (it != m_labels.end()) ? it->second : m_default; - if ( p == nullptr ) return pj->Return(pile1); // completed if nothing - - if ( !pile1->SetState(state) ) return false; - } - - p = m_block; // returns to the beginning - while (state-->0) p = p->GetNext(); // advance in the list + while (--state > 0) p = p->GetNext(); while( p != nullptr ) { @@ -185,8 +181,6 @@ void CBotSwitch :: RestoreState(CBotStack* &pj, bool bMain) CBotStack* pile1 = pj->RestoreStack(this); // adds an item to the stack if ( pile1 == nullptr ) return; - CBotInstr* p = m_block; // first expression - int state = pile1->GetState(); if (state == 0) { @@ -194,13 +188,11 @@ void CBotSwitch :: RestoreState(CBotStack* &pj, bool bMain) return; } - if ( state == -1 ) - { - return; - } + auto it = m_labels.find(pile1->GetVar()->GetValLong()); -// p = m_block; // returns to the beginning - while ( p != nullptr && state-- > 0 ) + CBotInstr* p = (it != m_labels.end()) ? it->second : m_default; + + while (p != nullptr && --state > 0) { p->RestoreState(pile1, false); p = p->GetNext(); // advance in the list diff --git a/src/CBot/CBotInstr/CBotSwitch.h b/src/CBot/CBotInstr/CBotSwitch.h index ca6b2b20..0ebcddf0 100644 --- a/src/CBot/CBotInstr/CBotSwitch.h +++ b/src/CBot/CBotInstr/CBotSwitch.h @@ -21,6 +21,8 @@ #include "CBot/CBotInstr/CBotInstr.h" +#include + namespace CBot { @@ -64,8 +66,12 @@ protected: private: //! Value to seek CBotInstr* m_value; - //! Instructions - CBotInstr* m_block; + //! List of case instructions + CBotInstr* m_block = nullptr; + //! Pointer to default label + CBotInstr* m_default = nullptr; + //! Map of case labels + std::unordered_map m_labels; }; } // namespace CBot diff --git a/src/CBot/CBotInstr/CBotTwoOpExpr.cpp b/src/CBot/CBotInstr/CBotTwoOpExpr.cpp index ab8b0da9..c1b2d278 100644 --- a/src/CBot/CBotInstr/CBotTwoOpExpr.cpp +++ b/src/CBot/CBotInstr/CBotTwoOpExpr.cpp @@ -133,8 +133,7 @@ static bool TypeOk(int type, int test) } } -//////////////////////////////////////////////////////////////////////////////// -CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOperations) +CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOperations, bool bConstExpr) { int typeMask; @@ -146,8 +145,8 @@ CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOpera // search the intructions that may be suitable to the left of the operation CBotInstr* left = (*pOp == 0) ? - CBotParExpr::Compile( p, pStk ) : // expression (...) left - CBotTwoOpExpr::Compile( p, pStk, pOp ); // expression A * B left + CBotParExpr::Compile(p, pStk, bConstExpr) : // expression (...) left + CBotTwoOpExpr::Compile(p, pStk, pOp, bConstExpr); // expression A * B left if (left == nullptr) return pStack->Return(nullptr, pStk); // if error, transmit @@ -158,7 +157,7 @@ CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOpera CBotTypResult type1, type2; type1 = pStk->GetTypResult(); // what kind of the first operand? - if (typeOp == ID_LOGIC) // special case provided for: ? op1: op2; + if (!bConstExpr && typeOp == ID_LOGIC) // special case provided for: ? op1: op2; { if ( !type1.Eq(CBotTypBoolean) ) { @@ -207,7 +206,7 @@ CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOpera // looking statements that may be suitable for right - if ( nullptr != (inst->m_rightop = CBotTwoOpExpr::Compile( p, pStk, pOp )) ) + if ( nullptr != (inst->m_rightop = CBotTwoOpExpr::Compile(p, pStk, pOp, bConstExpr)) ) // expression (...) right { // there is an second operand acceptable @@ -264,7 +263,7 @@ CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOpera type1 = TypeRes; p = p->GetNext(); // advance after - i->m_rightop = CBotTwoOpExpr::Compile( p, pStk, pOp ); + i->m_rightop = CBotTwoOpExpr::Compile(p, pStk, pOp, bConstExpr); type2 = pStk->GetTypResult(); if ( !TypeCompatible (type1, type2, typeOp) ) // the results are compatible diff --git a/src/CBot/CBotInstr/CBotTwoOpExpr.h b/src/CBot/CBotInstr/CBotTwoOpExpr.h index c1110e13..baf70a14 100644 --- a/src/CBot/CBotInstr/CBotTwoOpExpr.h +++ b/src/CBot/CBotInstr/CBotTwoOpExpr.h @@ -65,7 +65,7 @@ public: * \param pOperations * \return */ - static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, int* pOperations = nullptr); + static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, int* pOperations = nullptr, bool bConstExpr = false); /*! * \brief Execute Performes the operation on two operands. diff --git a/src/common/restext.cpp b/src/common/restext.cpp index 065f6c92..a79f3f55 100644 --- a/src/common/restext.cpp +++ b/src/common/restext.cpp @@ -743,6 +743,7 @@ void InitializeRestext() stringsCbot[CBot::CBotErrHexRange] = TR("Hex value out of range"); stringsCbot[CBot::CBotErrUnicodeName] = TR("Invalid universal character name"); stringsCbot[CBot::CBotErrCharEmpty] = TR("Empty character constant"); + stringsCbot[CBot::CBotErrRedefCase] = TR("Duplicate label in switch"); stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero"); stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized"); diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index 4c60b8fa..78009ffc 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -640,6 +640,92 @@ TEST_F(CBotUT, BinaryLiterals) ); } +TEST_F(CBotUT, TestSwitchCase) +{ + ExecuteTest( + "extern void Test_Switch_Case() {\n" + " int n = 0, c = 0;\n" + " for (int i = -9; i < 11; ++i) {\n" + " switch (i) {\n" + " case -9: n = -9; ++c; break;\n" + " case -8: n = -8; ++c; break;\n" + " case -7: n = -7; ++c; break;\n" + " case -6: n = -6; ++c; break;\n" + " case -5: n = -5; ++c; break;\n" + " case -4: n = -4; ++c; break;\n" + " case -3: n = -3; ++c; break;\n" + " case -2: n = -2; ++c; break;\n" + " case -1: n = -1; ++c; break;\n" + " case 0: n = 0; ++c; break;\n" + " case 1: n = 1; ++c; break;\n" + " case 2: n = 2; ++c; break;\n" + " case 3: n = 3; ++c; break;\n" + " case 4: n = 4; ++c; break;\n" + " case 5: n = 5; ++c; break;\n" + " case 6: n = 6; ++c; break;\n" + " case 7: n = 7; ++c; break;\n" + " case 8: n = 8; ++c; break;\n" + " case 9: n = 9; ++c; break;\n" + " default: n = 10; ++c; break;\n" + " }\n" + " ASSERT(n == i);\n" + " }\n" + " ASSERT(n == 10);\n" + " ASSERT(c == 20);\n" + "}\n" + "extern void Test_Case_With_Math() {\n" + " int n = 0, c = 0;\n" + " for (int i = -9; i < 11; ++i) {\n" + " switch (i * 10) {\n" + " case -9*10: n = -90; ++c; break;\n" + " case -8*10: n = -80; ++c; break;\n" + " case -7*10: n = -70; ++c; break;\n" + " case -6*10: n = -60; ++c; break;\n" + " case -5*10: n = -50; ++c; break;\n" + " case -4*10: n = -40; ++c; break;\n" + " case -3*10: n = -30; ++c; break;\n" + " case -2*10: n = -20; ++c; break;\n" + " case -1*10: n = -10; ++c; break;\n" + " case 0*10: n = 0; ++c; break;\n" + " case 1*10: n = 10; ++c; break;\n" + " case 2*10: n = 20; ++c; break;\n" + " case 3*10: n = 30; ++c; break;\n" + " case 4*10: n = 40; ++c; break;\n" + " case 5*10: n = 50; ++c; break;\n" + " case 6*10: n = 60; ++c; break;\n" + " case 7*10: n = 70; ++c; break;\n" + " case 8*10: n = 80; ++c; break;\n" + " case 9*10: n = 90; ++c; break;\n" + " default: n = 100; ++c; break;\n" + " }\n" + " ASSERT(n == i * 10);\n" + " }\n" + " ASSERT(n == 100);\n" + " ASSERT(c == 20);\n" + "}\n" + ); + + ExecuteTest( + "extern void Duplicate_Case() {\n" + " switch(0) {\n" + " case 1000:\n" + " case 10*100:\n" + " }\n" + "}\n", + CBotErrRedefCase + ); + + ExecuteTest( + "extern void Duplicate_Default() {\n" + " switch(0) {\n" + " default:\n" + " default:\n" + " }\n" + "}\n", + CBotErrRedefCase + ); +} + TEST_F(CBotUT, ToString) { ExecuteTest( From 17d0d2abb14d7ec9a73e0b15b30d60f2a2f9838d Mon Sep 17 00:00:00 2001 From: melex750 Date: Sun, 12 May 2019 08:33:53 -0400 Subject: [PATCH 7/7] Fix compiling literal numbers --- src/CBot/CBotInstr/CBotExprLitNum.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/CBot/CBotInstr/CBotExprLitNum.cpp b/src/CBot/CBotInstr/CBotExprLitNum.cpp index 61978bf0..0e0f8f88 100644 --- a/src/CBot/CBotInstr/CBotExprLitNum.cpp +++ b/src/CBot/CBotInstr/CBotExprLitNum.cpp @@ -74,8 +74,7 @@ CBotInstr* CompileExprLitNum(CBotToken* &p, CBotCStack* pStack) if (s.find('.') != std::string::npos || ( s.find('x') == std::string::npos && ( s.find_first_of("eE") != std::string::npos ) )) { double val = GetNumFloat(s); - if (val < static_cast(std::numeric_limits::min()) && - val > static_cast(std::numeric_limits::max()) ) + if (val > static_cast(std::numeric_limits::max())) { numtype = CBotTypDouble; inst = new CBotExprLitNum(val); @@ -89,8 +88,7 @@ CBotInstr* CompileExprLitNum(CBotToken* &p, CBotCStack* pStack) else { long val = GetNumInt(s); - if (val < std::numeric_limits::min() && - val > std::numeric_limits::max() ) + if (val > std::numeric_limits::max()) { numtype = CBotTypLong; inst = new CBotExprLitNum(val);