Mon, 21 Jul 2014 17:14:42 +0300
- massive refactoring continues (doesn't compile yet)
--- a/CMakeLists.txt Sun Jul 20 17:25:36 2014 +0300 +++ b/CMakeLists.txt Mon Jul 21 17:14:42 2014 +0300 @@ -14,7 +14,7 @@ src/main.h src/parser.h src/property.h - src/string.h + src/stringClass.h src/stringTable.h src/tokens.h src/types.h @@ -30,31 +30,36 @@ src/lexerScanner.cpp src/main.cpp src/parser.cpp - src/string.cpp + src/stringClass.cpp src/stringTable.cpp ) add_subdirectory (updaterevision) add_subdirectory (namedenums) - get_target_property (UPDATEREVISION_EXE updaterevision LOCATION) +get_target_property (NAMEDENUMS_EXE namedenums LOCATION) add_custom_target (revision_check ALL - COMMAND ${UPDATEREVISION_EXE} src/gitinfo.h + COMMAND ${UPDATEREVISION_EXE} ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}/hginfo.h WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} DEPENDS updaterevision) -get_target_property (NAMEDENUMS_EXE namedenums LOCATION) - -add_custom_target (botc_enum_strings ALL - COMMAND ${NAMEDENUMS_EXE} ${BOTC_HEADERS} src/enumstrings.h +add_custom_target (enumstrings ALL + COMMAND ${NAMEDENUMS_EXE} ${BOTC_HEADERS} + ${CMAKE_BINARY_DIR}/enumstrings.h + ${CMAKE_BINARY_DIR}/enumstrings.cpp WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} DEPENDS namedenums) -add_executable (botc ${BOTC_SOURCES}) -add_dependencies(botc revision_check botc_enum_strings) -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -W -Wall") +add_executable (botc ${BOTC_SOURCES} ${CMAKE_BINARY_DIR}/enumstrings.cpp) +add_dependencies (botc revision_check enumstrings) +include_directories (${CMAKE_BINARY_DIR}) +include_directories (${CMAKE_SOURCE_DIR}/src) -if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG") +if (NOT MSVC) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -W -Wall") + + if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG") + endif() endif()
--- a/namedenums/CMakeLists.txt Sun Jul 20 17:25:36 2014 +0300 +++ b/namedenums/CMakeLists.txt Mon Jul 21 17:14:42 2014 +0300 @@ -1,3 +1,3 @@ cmake_minimum_required (VERSION 2.4) -add_executable (namedenums namedenums.cpp) +add_executable (namedenums namedenums.cpp md5.cpp) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -W -Wall")
--- a/namedenums/namedenums.cpp Sun Jul 20 17:25:36 2014 +0300 +++ b/namedenums/namedenums.cpp Mon Jul 21 17:14:42 2014 +0300 @@ -27,49 +27,23 @@ */ #include <string> -#include <deque> +#include <vector> #include <algorithm> #include <cerrno> #include <cstdio> #include <cstdlib> #include <cstring> #include <cstdarg> +#include "md5.h" using std::string; -using std::deque; - -static int gLineNumber; -static std::string gCurrentFile; - -// ============================================================================= -// -struct NamedEnumInfo -{ - string name; - deque<string> enumerators; -}; +using std::vector; -// ============================================================================= -// -void SkipWhitespace (char*& cp) -{ - while (isspace (*cp)) - { - if (*cp == '\n') - gLineNumber++; +static int LineNumber; +static std::string CurrentFile; +static auto const null = nullptr; - ++cp; - } - - if (strncmp (cp, "//", 2) == 0) - { - while (*(++cp) != '\n') - ; - - gLineNumber++; - SkipWhitespace (cp); - } -} +#define NAMED_ENUM_MACRO "named_enum" // ============================================================================= // @@ -85,12 +59,159 @@ // ============================================================================= // +struct NamedEnumInfo +{ + string name; + vector<string> enumerators; + bool scoped; + string underlyingtype; + bool valuedefs; + + NamedEnumInfo() : + scoped (false), + valuedefs (false) {} + + // + // Generate a string containing a C++ stub declaration for this enum. + // + string makeStub() const + { + return string ("enum ") + + (scoped ? "class " : "") + + name + + (underlyingtype.size() ? (" : " + underlyingtype) : "") + + ";"; + } + + string enumeratorString (string const& e) const + { + if (scoped) + return name + "::" + e; + else + return e; + } +}; + +// ============================================================================= +// +void Normalize (string& a) +{ + char const* start; + char const* end; + + for (start = &a.c_str()[0]; isspace (*start); ++start) + ; + + for (end = start; not isspace (*end) and *end != '\0'; ++end) + ; + + a = a.substr (start - a.c_str(), end - start); +} + +// ============================================================================= +// +class OutputFile +{ + string _buffer; + string _md5; + string _filepath; + +public: + OutputFile (string const& filepath) : + _filepath (filepath) + { + FILE* readhandle = fopen (_filepath.c_str(), "r"); + + if (readhandle) + { + char line[1024]; + + if (fgets (line, sizeof line, readhandle) == null) + Error ("I/O error while reading %s", _filepath.c_str()); + + if (strncmp (line, "// ", 3) == 0) + { + // Get rid of the newline at the end + char* cp; + for (cp = &line[3]; *cp != '\0' and *cp != '\n'; ++cp) + ; + + *cp = '\0'; + _md5 = string (&line[3]); + } + + fclose (readhandle); + } + } + + void append (char const* fmtstr, ...) + { + char buf[1024]; + va_list va; + va_start (va, fmtstr); + vsprintf (buf, fmtstr, va); + va_end (va); + _buffer += buf; + } + + void writeToDisk() + { + char checksum[33]; + + // See if this is necessary first. + CalculateMD5 (reinterpret_cast<unsigned char const*> (_buffer.c_str()), + _buffer.size(), checksum); + checksum[32] = '\0'; + + if (_md5.size() and string (checksum) == _md5) + { + fprintf (stdout, "%s is up to date.\n", _filepath.c_str()); + return; + } + + FILE* handle = fopen (_filepath.c_str(), "w"); + + if (not handle) + Error ("couldn't open %s for writing", _filepath.c_str()); + + string md5header (string ("// ") + checksum + "\n"); + fwrite (md5header.c_str(), 1, md5header.size(), handle); + fwrite (_buffer.c_str(), 1, _buffer.size(), handle); + fclose (handle); + fprintf (stdout, "Wrote output file %s.\n", _filepath.c_str()); + } +}; + +// ============================================================================= +// +void SkipWhitespace (char*& cp) +{ + while (isspace (*cp)) + { + if (*cp == '\n') + LineNumber++; + + ++cp; + } + + if (strncmp (cp, "//", 2) == 0) + { + while (*(++cp) != '\n') + ; + + LineNumber++; + SkipWhitespace (cp); + } +} + +// ============================================================================= +// int main (int argc, char* argv[]) { try { - deque<NamedEnumInfo> namedEnumerations; - deque<string> filesToInclude; + vector<NamedEnumInfo> namedEnumerations; + vector<string> filesToInclude; if (argc < 3) { @@ -98,20 +219,23 @@ return EXIT_FAILURE; } - for (int i = 1; i < argc - 1; ++ i) + OutputFile header (argv[argc - 2]); + OutputFile source (argv[argc - 1]); + + for (int i = 1; i < argc - 2; ++ i) { - gCurrentFile = argv[i]; FILE* fp = fopen (argv[i], "r"); char* buf; - gLineNumber = 1; if (fp == NULL) { - fprintf (stderr, "could not open %s for writing: %s\n", + CurrentFile = ""; + Error ("could not open %s for reading: %s", argv[i], strerror (errno)); - exit (EXIT_FAILURE); } + CurrentFile = argv[i]; + LineNumber = 1; fseek (fp, 0, SEEK_END); long int filesize = ftell (fp); rewind (fp); @@ -122,11 +246,12 @@ } catch (std::bad_alloc) { - Error ("could not allocate %ld bytes for %s\n", filesize, argv[i]); + Error ("out of memory: could not allocate %ld bytes for opening %s\n", + filesize, argv[i]); } - if (static_cast<long> (fread (buf, 1, filesize, fp)) < filesize) - Error ("could not read %ld bytes from %s\n", filesize, argv[i]); + if (long (fread (buf, 1, filesize, fp)) < filesize) + Error ("filesystem error: could not read %ld bytes from %s\n", filesize, argv[i]); char* const end = &buf[0] + filesize; @@ -143,28 +268,56 @@ } if ((cp != &buf[0] && isspace (* (cp - 1)) == false) || - strncmp (cp, "named_enum ", strlen ("named_enum ")) != 0) + strncmp (cp, NAMED_ENUM_MACRO " ", strlen (NAMED_ENUM_MACRO " ")) != 0) { continue; } - cp += strlen ("named_enum "); + cp += strlen (NAMED_ENUM_MACRO " "); SkipWhitespace (cp); NamedEnumInfo nenum; auto& enumname = nenum.name; auto& enumerators = nenum.enumerators; + // See if it's a scoped enum + if (strncmp (cp, "class ", strlen ("class ")) == 0) + { + nenum.scoped = true; + cp += strlen ("class "); + } + if (isalpha (*cp) == false && *cp != '_') - Error ("anonymous named_enum"); + Error ("anonymous " NAMED_ENUM_MACRO); while (isalnum (*cp) || *cp == '_') enumname += *cp++; SkipWhitespace (cp); + // We need an underlying type if this is not a scoped enum + if (*cp == ':') + { + SkipWhitespace (cp); + + for (++cp; *cp != '\0' and *cp != '{'; ++cp) + nenum.underlyingtype += *cp; + + if (not nenum.underlyingtype.size()) + Error ("underlying type left empty"); + + Normalize (nenum.underlyingtype); + } + + if (not nenum.scoped and not nenum.underlyingtype.size()) + { + Error (NAMED_ENUM_MACRO " %s must be forward-declarable and thus must either " + "be scoped (" NAMED_ENUM_MACRO " class) or define an underlying type " + "(enum A : int)", nenum.name.c_str()); + } + if (*cp++ != '{') - Error ("expected '{' after named_enum"); + Error ("expected '{' after " NAMED_ENUM_MACRO); for (;;) { @@ -186,12 +339,23 @@ SkipWhitespace (cp); + if (*cp == '=') + { + nenum.valuedefs = true; + + while (*cp != ',' && *cp != '\0') + cp++; + + if (*cp == '\0') + { + Error ("unexpected EOF while processing " NAMED_ENUM_MACRO " %s ", + nenum.name.c_str()); + } + } + if (*cp == ',') SkipWhitespace (++cp); - if (*cp == '=') - Error ("named enums must not have defined values"); - enumerators.push_back (enumerator); } @@ -205,45 +369,90 @@ namedEnumerations.push_back (nenum); filesToInclude.push_back (argv[i]); } + else + { + printf ("warning: " NAMED_ENUM_MACRO " %s left empty\n", nenum.name.c_str()); + } } } - FILE* fp; + header.append ("#pragma once\n\n"); + + for (NamedEnumInfo& e : namedEnumerations) + header.append ("%s\n", e.makeStub().c_str()); + + header.append ("\n"); - if ((fp = fopen (argv[argc - 1], "w")) == NULL) - Error ("couldn't open %s for writing: %s", argv[argc - 1], strerror (errno)); + for (NamedEnumInfo& e : namedEnumerations) + header.append ("const char* Get%sString (%s value);\n", e.name.c_str(), e.name.c_str()); - fprintf (fp, "#pragma once\n"); + header.append ("\n"); + + // MakeFormatArgument overloads so enums can be passed to that + for (NamedEnumInfo& e : namedEnumerations) + header.append ("String MakeFormatArgument (%s value);\n", e.name.c_str()); std::sort (filesToInclude.begin(), filesToInclude.end()); auto pos = std::unique (filesToInclude.begin(), filesToInclude.end()); filesToInclude.resize (std::distance (filesToInclude.begin(), pos)); - for (const string& a : filesToInclude) - fprintf (fp, "#include \"%s\"\n", basename (a.c_str())); + for (string const& a : filesToInclude) + source.append ("#include \"%s\"\n", basename (a.c_str())); + + source.append ("#include \"%s\"\n", basename (argv[argc - 2])); for (NamedEnumInfo& e : namedEnumerations) { - fprintf (fp, "\nstatic const char* g_%sNames[] =\n{\n", e.name.c_str()); + if (not e.valuedefs) + { + source.append ("\nstatic const char* %sNames[] =\n{\n", e.name.c_str()); - for (const string& a : e.enumerators) - fprintf (fp, "\t\"%s\",\n", a.c_str()); + for (const string& a : e.enumerators) + source.append ("\t\"%s\",\n", e.enumeratorString (a).c_str()); + + source.append ("};\n\n"); - fprintf (fp, "};\n\n"); + source.append ("const char* Get%sString (%s value)\n" + "{\n" + "\treturn %sNames[value];\n" + "}\n", + e.name.c_str(), e.name.c_str(), e.name.c_str()); + } + else + { + source.append ("const char* Get%sString (%s value)\n" + "{\n" + "\tswitch (value)\n" + "\t{\n", e.name.c_str(), e.name.c_str()); - fprintf (fp, "inline const char* get%sString (%s a)\n" - "{\n" - "\treturn g_%sNames[a];\n" - "}\n", - e.name.c_str(), e.name.c_str(), e.name.c_str()); + for (const string& a : e.enumerators) + { + source.append ("\t\tcase %s: return \"%s\";\n", + e.enumeratorString (a).c_str(), e.enumeratorString (a).c_str()); + } + + source.append ("\t\tdefault: return (\"[[[unknown " + "value passed to Get%sString]]]\");\n\t}\n}\n", e.name.c_str()); + } + + source.append ("String MakeFormatArgument (%s value)\n{\n" + "\treturn Get%sString (value);\n}\n", e.name.c_str(), e.name.c_str()); } - printf ("Wrote named enumerations to %s\n", argv[argc - 1]); - fclose (fp); + source.writeToDisk(); + header.writeToDisk(); } catch (std::string a) { - fprintf (stderr, "%s:%d: error: %s\n", gCurrentFile.c_str(), gLineNumber, a.c_str()); + if (CurrentFile.size() > 0) + { + fprintf (stderr, "%s: %s:%d: error: %s\n", + argv[0], CurrentFile.c_str(), LineNumber, a.c_str()); + } + else + { + fprintf (stderr, "%s: error: %s\n", argv[0], a.c_str()); + } return 1; }
--- a/src/commands.cpp Sun Jul 20 17:25:36 2014 +0300 +++ b/src/commands.cpp Mon Jul 21 17:14:42 2014 +0300 @@ -30,30 +30,34 @@ #include <stdio.h> #include <string.h> #include "main.h" -#include "string.h" +#include "stringClass.h" #include "commands.h" #include "lexer.h" -static List<CommandInfo*> gCommands; +static List<CommandInfo*> Commands; // ============================================================================ // void addCommandDefinition (CommandInfo* comm) { // Ensure that there is no conflicts - for (CommandInfo* it : gCommands) + for (CommandInfo* it : Commands) + { if (it->number == comm->number) + { error ("Attempted to redefine command #%1 (%2) as %3", - gCommands[comm->number]->name, comm->name); + Commands[comm->number]->name, comm->name); + } + } - gCommands << comm; + Commands << comm; } // ============================================================================ // Finds a command by name CommandInfo* findCommandByName (String fname) { - for (CommandInfo* comm : gCommands) + for (CommandInfo* comm : Commands) { if (fname.toUppercase() == comm->name.toUppercase()) return comm; @@ -119,5 +123,5 @@ const List<CommandInfo*>& getCommands() { - return gCommands; + return Commands; }
--- a/src/commands.h Sun Jul 20 17:25:36 2014 +0300 +++ b/src/commands.h Mon Jul 21 17:14:42 2014 +0300 @@ -30,7 +30,7 @@ #define BOTC_COMMANDS_H #include "main.h" -#include "string.h" +#include "stringClass.h" struct CommandArgument {
--- a/src/dataBuffer.cpp Sun Jul 20 17:25:36 2014 +0300 +++ b/src/dataBuffer.cpp Mon Jul 21 17:14:42 2014 +0300 @@ -26,14 +26,15 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <cstring> #include "dataBuffer.h" // ------------------------------------------------------------------------------------------------- // DataBuffer::DataBuffer (int size) : m_buffer (new char[size]), - m_position (&buffer()[0]), - m_allocatedSize (size) {} + m_allocatedSize (size), + m_position (&buffer()[0]) {} // ------------------------------------------------------------------------------------------------- // @@ -162,12 +163,21 @@ // void DataBuffer::writeStringIndex (const String& a) { - writeDWord (DataHeader::PushStringIndex); + writeHeader (DataHeader::PushStringIndex); writeDWord (getStringTableIndex (a)); } // ------------------------------------------------------------------------------------------------- // +// Writes a data header. 4 bytes. +// +void DataBuffer::writeHeader (DataHeader data) +{ + writeDWord (static_cast<int32_t> (data)); +} + +// ------------------------------------------------------------------------------------------------- +// // Prints the buffer to stdout. // void DataBuffer::dump() @@ -192,7 +202,7 @@ // the stuff - thus resize. First, store the old // buffer temporarily: char* copy = new char[allocatedSize()]; - memcpy (copy, buffer(), allocatedSize()); + std::memcpy (copy, buffer(), allocatedSize()); // Remake the buffer with the new size. Have enough space // for the stuff we're going to write, as well as a bit @@ -204,7 +214,7 @@ setAllocatedSize (newsize); // Now, copy the stuff back. - memcpy (m_buffer, copy, allocatedSize()); + std::memcpy (m_buffer, copy, allocatedSize()); setPosition (buffer() + writesize); delete copy; }
--- a/src/dataBuffer.h Sun Jul 20 17:25:36 2014 +0300 +++ b/src/dataBuffer.h Mon Jul 21 17:14:42 2014 +0300 @@ -29,8 +29,6 @@ #ifndef BOTC_DATABUFFER_H #define BOTC_DATABUFFER_H -#include <stdio.h> -#include <string.h> #include "main.h" #include "stringTable.h" @@ -79,6 +77,7 @@ void writeByte (int8_t data); void writeWord (int16_t data); void writeDWord (int32_t data); + void writeHeader (DataHeader data); inline int writtenSize() const; private:
--- a/src/events.cpp Sun Jul 20 17:25:36 2014 +0300 +++ b/src/events.cpp Mon Jul 21 17:14:42 2014 +0300 @@ -31,7 +31,7 @@ #include <stdlib.h> #include <stdio.h> #include "main.h" -#include "string.h" +#include "stringClass.h" #include "events.h" #include "lexer.h"
--- a/src/events.h Sun Jul 20 17:25:36 2014 +0300 +++ b/src/events.h Mon Jul 21 17:14:42 2014 +0300 @@ -29,7 +29,7 @@ #ifndef BOTC_EVENTS_H #define BOTC_EVENTS_H -#include "string.h" +#include "stringClass.h" struct EventDefinition {
--- a/src/expression.cpp Sun Jul 20 17:25:36 2014 +0300 +++ b/src/expression.cpp Mon Jul 21 17:14:42 2014 +0300 @@ -4,7 +4,7 @@ struct OperatorInfo { - Token token; + Token token; int priority; int numoperands; DataHeader header; @@ -129,7 +129,7 @@ Expression expr (m_parser, m_lexer, TYPE_Int); expr.getResult()->convertToBuffer(); DataBuffer* buf = expr.getResult()->buffer()->clone(); - buf->writeDWord (DataHeader::PushGlobalArray); + buf->writeHeader (DataHeader::PushGlobalArray); buf->writeDWord (var->index); op->setBuffer (buf); m_lexer->mustGetNext (Token::BracketEnd); @@ -143,9 +143,9 @@ DataBuffer* buf = new DataBuffer (8); if (var->isGlobal()) - buf->writeDWord (DataHeader::PushGlobalVar); + buf->writeHeader (DataHeader::PushGlobalVar); else - buf->writeDWord (DataHeader::PushLocalVar); + buf->writeHeader (DataHeader::PushLocalVar); buf->writeDWord (var->index); op->setBuffer (buf); @@ -228,7 +228,7 @@ // // Verifies a single value. Helper function for Expression::verify. // -void Expression::tryVerifyValue (bool* verified, SymbolList::Iterator it) +void Expression::tryVerifyValue (List<bool>& verified, SymbolList::Iterator it) { // If it's an unary operator we skip to its value. The actual operator will // be verified separately. @@ -265,8 +265,11 @@ if (m_type == TYPE_String) error ("Cannot perform operations on strings"); - bool* verified = new bool[m_symbols.size()]; - memset (verified, 0, m_symbols.size() * sizeof (decltype (*verified))); + List<bool> verified (m_symbols.size()); + + for (bool& a : verified) + a = false; + const auto last = m_symbols.end() - 1; const auto first = m_symbols.begin(); @@ -358,8 +361,6 @@ if (verified[i] == false) error ("malformed expression: expr symbol #%1 is was left unverified", i); } - - delete verified; } @@ -438,10 +439,10 @@ ByteMark* mark1 = buf->addMark (""); // start of "else" case ByteMark* mark2 = buf->addMark (""); // end of expression buf->mergeAndDestroy (b0); - buf->writeDWord (DataHeader::IfNotGoto); // if the first operand (condition) + buf->writeHeader (DataHeader::IfNotGoto); // if the first operand (condition) buf->addReference (mark1); // didn't eval true, jump into mark1 buf->mergeAndDestroy (b1); // otherwise, perform second operand (true case) - buf->writeDWord (DataHeader::Goto); // afterwards, jump to the end, which is + buf->writeHeader (DataHeader::Goto); // afterwards, jump to the end, which is buf->addReference (mark2); // marked by mark2. buf->adjustMark (mark1); // move mark1 at the end of the true case buf->mergeAndDestroy (b2); // perform third operand (false case) @@ -465,7 +466,7 @@ val->setBuffer (null); } - newval->buffer()->writeDWord (info->header); + newval->buffer()->writeHeader (info->header); } } else @@ -473,7 +474,7 @@ // We have a constant expression. We know all the values involved and // can thus compute the result of this expression on compile-time. List<int> nums; - int a; + int a = 0; for (ExpressionValue* val : values) nums << val->value(); @@ -542,36 +543,33 @@ List<SymbolList::Iterator> operands; ExpressionOperator* op = static_cast<ExpressionOperator*> (*it); const OperatorInfo* info = &g_Operators[op->id()]; - int lower, upper; // Boundaries of area to replace + + // Boundaries of area to replace + int lower = 0; + int upper = 0; switch (info->numoperands) { case 1: - { lower = i; upper = i + 1; operands << it + 1; break; - } case 2: - { lower = i - 1; upper = i + 1; operands << it - 1 << it + 1; break; - } case 3: - { lower = i - 1; upper = i + 3; operands << it - 1 << it + 1 << it + 3; break; - } default: error ("WTF bad expression with %1 operands", info->numoperands); @@ -644,15 +642,15 @@ { case TYPE_Bool: case TYPE_Int: - buffer()->writeDWord (DataHeader::PushNumber); + buffer()->writeHeader (DataHeader::PushNumber); buffer()->writeDWord (abs (value())); if (value() < 0) - buffer()->writeDWord (DataHeader::UnaryMinus); + buffer()->writeHeader (DataHeader::UnaryMinus); break; case TYPE_String: - buffer()->writeDWord (DataHeader::PushStringIndex); + buffer()->writeHeader (DataHeader::PushStringIndex); buffer()->writeDWord (value()); break;
--- a/src/expression.h Sun Jul 20 17:25:36 2014 +0300 +++ b/src/expression.h Mon Jul 21 17:14:42 2014 +0300 @@ -9,7 +9,7 @@ // ============================================================================= // -named_enum ExpressionOperatorType +named_enum ExpressionOperatorType : char { OPER_NegateLogical, OPER_UnaryMinus, @@ -64,7 +64,8 @@ String getTokenString(); void adjustOperators(); void verify(); // Ensure the expr is valid - void tryVerifyValue (bool* verified, SymbolList::Iterator it); + void tryVerifyValue (List<bool>& verified, List< ExpressionSymbol* +>::Iterator it); ExpressionValue* evaluateOperator (const ExpressionOperator* op, const List<ExpressionValue*>& values); SymbolList::Iterator findPrioritizedOperator();
--- a/src/format.h Sun Jul 20 17:25:36 2014 +0300 +++ b/src/format.h Mon Jul 21 17:14:42 2014 +0300 @@ -29,52 +29,80 @@ #ifndef BOTC_FORMAT_H #define BOTC_FORMAT_H -#include "string.h" +#include "stringClass.h" #include "list.h" +#include "enumstrings.h" + +String MakeFormatArgument (const String& a) +{ + return a; +} + +String MakeFormatArgument (char a) +{ + return String (a); +} + +String MakeFormatArgument (int a) +{ + return String::fromNumber (a); +} + +String MakeFormatArgument (long a) +{ + return String::fromNumber (a); +} + +String MakeFormatArgument (size_t a) +{ + return String::fromNumber (long (a)); +} + +String MakeFormatArgument (const char* a) +{ + return a; +} + +String MakeFormatArgument (const void* a) +{ + String text; + text.sprintf ("%p", a); + return text; +} + +String MakeFormatArgument (std::nullptr_t) +{ + return "(nullptr)"; +} + +template<class T> +String MakeFormatArgument (List<T> const& list) +{ + String result; + + if (list.isEmpty()) + return "{}"; + + result = "{ "; + + for (auto it = list.begin(); it != list.end(); ++it) + { + if (it != list.begin()) + result += ", "; + + result += MakeFormatArgument (*it); + } + + result += " }"; + return result; +} class FormatArgument { public: - FormatArgument (const String& a) : m_text (a) {} - FormatArgument (char a) : m_text (a) {} - FormatArgument (int a) : m_text (String::fromNumber (a)) {} - FormatArgument (long a) : m_text (String::fromNumber (a)) {} - FormatArgument (size_t a) : m_text (String::fromNumber ((long) a)) {} - FormatArgument (const char* a) : m_text (a) {} - - FormatArgument (void* a) - { - m_text.sprintf ("%p", a); - } - - FormatArgument (const void* a) - { - m_text.sprintf ("%p", a); - } - - FormatArgument (std::nullptr_t) : - m_text (FormatArgument ((void*) 0).text()) {} - - template<class T> FormatArgument (const List<T>& list) - { - if (list.isEmpty()) - { - m_text = "{}"; - return; - } - - m_text = "{ "; - - for (const T& a : list) - { - if (&a != &list[0]) - m_text += ", "; - - m_text += FormatArgument (a).text(); - } - - m_text += " }"; - } + template<typename T> + FormatArgument (const T& a) : + m_text (MakeFormatArgument (a)) {} inline const String& text() const {
--- a/src/lexer.cpp Sun Jul 20 17:25:36 2014 +0300 +++ b/src/lexer.cpp Mon Jul 21 17:14:42 2014 +0300 @@ -63,7 +63,7 @@ while (sc.getNextToken()) { // Preprocessor commands: - if (sc.getTokenType() ==Token::Hash) + if (sc.getTokenType() == Token::Hash) { mustGetFromScanner (sc,Token::Symbol); @@ -256,7 +256,7 @@ // String Lexer::describeTokenPrivate (Token tokType, Lexer::TokenInfo* tok) { - if (tokType <gLastNamedToken) + if (tokType < Token::LastNamedToken) return "\"" + LexerScanner::GetTokenString (tokType) + "\""; switch (tokType)
--- a/src/lexerScanner.cpp Sun Jul 20 17:25:36 2014 +0300 +++ b/src/lexerScanner.cpp Mon Jul 21 17:14:42 2014 +0300 @@ -48,8 +48,8 @@ ">>", ">=", "<=", - "and", - "or", + "&&", + "||", "++", "--", "'", @@ -111,7 +111,7 @@ "return", }; -static_assert (countof (gTokenStrings) == (int)gLastNamedToken + 1, +static_assert (countof (gTokenStrings) == (int) Token::LastNamedToken + 1, "Count of gTokenStrings is not the same as the amount of named token identifiers."); // ============================================================================= @@ -192,7 +192,7 @@ { int flags = 0; - if (i >= gFirstNamedToken) + if (i >= int (Token::FirstNamedToken)) flags |= FCheckWord; if (checkString (gTokenStrings[i], flags)) @@ -290,8 +290,8 @@ // String LexerScanner::GetTokenString (Token a) { - ASSERT_LT_EQ (a, gLastNamedToken); - return gTokenStrings[a]; + ASSERT_LT_EQ (a, Token::LastNamedToken); + return gTokenStrings[int (a)]; } // =============================================================================
--- a/src/lexerScanner.h Sun Jul 20 17:25:36 2014 +0300 +++ b/src/lexerScanner.h Mon Jul 21 17:14:42 2014 +0300 @@ -91,7 +91,7 @@ char* m_lineBreakPosition; String m_tokenText, m_lastToken; - Token m_tokenType; + Token m_tokenType; int m_line; bool checkString (const char* c, int flags = 0);
--- a/src/list.h Sun Jul 20 17:25:36 2014 +0300 +++ b/src/list.h Mon Jul 21 17:14:42 2014 +0300 @@ -44,8 +44,9 @@ using ConstReverseIterator = typename std::deque<T>::const_reverse_iterator; List(); + List (std::size_t numvalues); List (const std::deque<T>& a); - List (std::initializer_list<T>and a); + List (std::initializer_list<T>&& a); inline T& append (const T& value); inline Iterator begin(); @@ -101,10 +102,14 @@ _deque (other) {} template<typename T> -List<T>::List (std::initializer_list<T>and a) : +List<T>::List (std::initializer_list< T > && a) : _deque (a) {} template<typename T> +List<T>::List (std::size_t numvalues) : + _deque (numvalues) {} + +template<typename T> inline typename List<T>::Iterator List<T>::begin() { return _deque.begin();
--- a/src/main.cpp Sun Jul 20 17:25:36 2014 +0300 +++ b/src/main.cpp Mon Jul 21 17:14:42 2014 +0300 @@ -33,7 +33,7 @@ #include "dataBuffer.h" #include "parser.h" #include "lexer.h" -#include "gitinfo.h" +#include "hginfo.h" int main (int argc, char** argv) { @@ -71,13 +71,6 @@ String headerline; header = format (APPNAME " version %1", versionString (true)); - if (String (GIT_BRANCH) != "master") - { - header += " ("; - header += GIT_BRANCH; - header += " branch"; - } - #ifdef DEBUG if (header.firstIndexOf ("(") != -1) header += ", "; @@ -196,7 +189,7 @@ // ============================================================================= // -String versionString (bool longform) +String versionString (bool) { #if defined(GIT_DESCRIPTION) and defined (DEBUG) String tag (GIT_DESCRIPTION);
--- a/src/main.h Sun Jul 20 17:25:36 2014 +0300 +++ b/src/main.h Mon Jul 21 17:14:42 2014 +0300 @@ -36,7 +36,7 @@ #include "property.h" #include "types.h" #include "list.h" -#include "string.h" +#include "stringClass.h" #include "format.h" #include "botStuff.h" #include "tokens.h"
--- a/src/parser.cpp Sun Jul 20 17:25:36 2014 +0300 +++ b/src/parser.cpp Mon Jul 21 17:14:42 2014 +0300 @@ -26,6 +26,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <cstring> #include "parser.h" #include "events.h" #include "commands.h" @@ -49,7 +50,7 @@ m_lexer (new Lexer), m_numStates (0), m_numEvents (0), - m_currentMode (PARSERMODE_TopLevel), + m_currentMode (ParserMode::TopLevel), m_isStateSpawnDefined (false), m_gotMainLoop (false), m_scopeCursor (-1), @@ -70,7 +71,7 @@ // void BotscriptParser::checkToplevel() { - if (m_currentMode != PARSERMODE_TopLevel) + if (m_currentMode != ParserMode::TopLevel) error ("%1-statements may only be defined at top level!", getTokenString()); } @@ -78,7 +79,7 @@ // void BotscriptParser::checkNotToplevel() { - if (m_currentMode == PARSERMODE_TopLevel) + if (m_currentMode == ParserMode::TopLevel) error ("%1-statements must not be defined at top level!", getTokenString()); } @@ -215,7 +216,7 @@ } // Script file ended. Do some last checks and write the last things to main buffer - if (m_currentMode != PARSERMODE_TopLevel) + if (m_currentMode != ParserMode::TopLevel) error ("script did not end at top level; a `}` is missing somewhere"); if (isReadOnly() == false) @@ -265,9 +266,9 @@ if (m_currentState.isEmpty() == false) writeMemberBuffers(); - currentBuffer()->writeDWord (DataHeader::StateName); + currentBuffer()->writeHeader (DataHeader::StateName); currentBuffer()->writeString (statename); - currentBuffer()->writeDWord (DataHeader::StateIndex); + currentBuffer()->writeHeader (DataHeader::StateIndex); currentBuffer()->writeDWord (m_numStates); m_numStates++; @@ -288,8 +289,8 @@ error ("bad event, got `%1`\n", getTokenString()); m_lexer->mustGetNext (Token::BraceStart); - m_currentMode = PARSERMODE_Event; - currentBuffer()->writeDWord (DataHeader::Event); + m_currentMode = ParserMode::Event; + currentBuffer()->writeHeader (DataHeader::Event); currentBuffer()->writeDWord (e->number); m_numEvents++; } @@ -301,8 +302,8 @@ checkToplevel(); m_lexer->mustGetNext (Token::BraceStart); - m_currentMode = PARSERMODE_MainLoop; - m_mainLoopBuffer->writeDWord (DataHeader::MainLoop); + m_currentMode = ParserMode::MainLoop; + m_mainLoopBuffer->writeHeader (DataHeader::MainLoop); } // ============================================================================ @@ -312,9 +313,8 @@ checkToplevel(); bool onenter = (tokenIs (Token::Onenter)); m_lexer->mustGetNext (Token::BraceStart); - - m_currentMode = onenter ? PARSERMODE_Onenter : PARSERMODE_Onexit; - currentBuffer()->writeDWord (onenter ? DataHeader::OnEnter : DataHeader::OnExit); + m_currentMode = onenter ? ParserMode::Onenter : ParserMode::Onexit; + currentBuffer()->writeHeader (onenter ? DataHeader::OnEnter : DataHeader::OnExit); } // ============================================================================ @@ -410,7 +410,8 @@ suggestHighestVarIndex (isInGlobalState(), var->index); m_lexer->mustGetNext (Token::Semicolon); - print ("Declared %3 variable #%1 $%2\n", var->index, var->name, isInGlobalState() ? "global" : "state-local"); + print ("Declared %3 variable #%1 $%2\n", var->index, var->name, isInGlobalState() ? "global" : +"state-local"); } // ============================================================================ @@ -436,7 +437,7 @@ // Use DataHeader::IfNotGoto - if the expression is not true, we goto the mark // we just defined - and this mark will be at the end of the scope block. - currentBuffer()->writeDWord (DataHeader::IfNotGoto); + currentBuffer()->writeHeader (DataHeader::IfNotGoto); currentBuffer()->addReference (mark); // Store it @@ -460,7 +461,7 @@ SCOPE (0).mark2 = currentBuffer()->addMark (""); // Instruction to jump to the end after if block is complete - currentBuffer()->writeDWord (DataHeader::Goto); + currentBuffer()->writeHeader (DataHeader::Goto); currentBuffer()->addReference (SCOPE (0).mark2); // Move the ifnot mark here and set type to else @@ -492,7 +493,7 @@ currentBuffer()->mergeAndDestroy (expr); // Instruction to go to the end if it fails - currentBuffer()->writeDWord (DataHeader::IfNotGoto); + currentBuffer()->writeHeader (DataHeader::IfNotGoto); currentBuffer()->addReference (mark2); // Store the needed stuff @@ -543,7 +544,7 @@ // Add the condition currentBuffer()->mergeAndDestroy (cond); - currentBuffer()->writeDWord (DataHeader::IfNotGoto); + currentBuffer()->writeHeader (DataHeader::IfNotGoto); currentBuffer()->addReference (mark2); // Store the marks and incrementor @@ -623,7 +624,7 @@ // We null the switch buffer for the case-go-to statement as // we want it all under the switch, not into the case-buffers. m_switchBuffer = null; - currentBuffer()->writeDWord (DataHeader::CaseGoto); + currentBuffer()->writeHeader (DataHeader::CaseGoto); currentBuffer()->writeDWord (num); addSwitchCase (null); SCOPE (0).casecursor->number = num; @@ -650,8 +651,8 @@ // a default. DataBuffer* buf = new DataBuffer; SCOPE (0).buffer1 = buf; - buf->writeDWord (DataHeader::Drop); - buf->writeDWord (DataHeader::Goto); + buf->writeHeader (DataHeader::Drop); + buf->writeHeader (DataHeader::Goto); addSwitchCase (buf); } @@ -662,7 +663,7 @@ if (m_scopeCursor == 0) error ("unexpected `break`"); - currentBuffer()->writeDWord (DataHeader::Goto); + currentBuffer()->writeHeader (DataHeader::Goto); // switch and if use mark1 for the closing point, // for and while use mark2. @@ -703,7 +704,7 @@ case SCOPE_For: case SCOPE_While: case SCOPE_Do: - currentBuffer()->writeDWord (DataHeader::Goto); + currentBuffer()->writeHeader (DataHeader::Goto); currentBuffer()->addReference (m_scopeStack[curs].mark1); found = true; break; @@ -752,7 +753,7 @@ } case SCOPE_While: { // write down the instruction to go back to the start of the loop - currentBuffer()->writeDWord (DataHeader::Goto); + currentBuffer()->writeHeader (DataHeader::Goto); currentBuffer()->addReference (SCOPE (0).mark1); // Move the closing mark here since we're at the end of the while loop @@ -770,7 +771,7 @@ // If the condition runs true, go back to the start. currentBuffer()->mergeAndDestroy (expr); - currentBuffer()->writeDWord (DataHeader::IfGoto); + currentBuffer()->writeHeader (DataHeader::IfGoto); currentBuffer()->addReference (SCOPE (0).mark1); break; } @@ -791,8 +792,8 @@ currentBuffer()->mergeAndDestroy (SCOPE (0).buffer1); else { - currentBuffer()->writeDWord (DataHeader::Drop); - currentBuffer()->writeDWord (DataHeader::Goto); + currentBuffer()->writeHeader (DataHeader::Drop); + currentBuffer()->writeHeader (DataHeader::Goto); currentBuffer()->addReference (SCOPE (0).mark1); } @@ -818,20 +819,21 @@ return; } - int dataheader = (m_currentMode == PARSERMODE_Event) ? DataHeader::EndEvent - : (m_currentMode == PARSERMODE_MainLoop) ? DataHeader::EndMainLoop - : (m_currentMode == PARSERMODE_Onenter) ? DataHeader::EndOnEnter - : (m_currentMode == PARSERMODE_Onexit) ? DataHeader::EndOnExit - : -1; + DataHeader dataheader = + (m_currentMode == ParserMode::Event) ? DataHeader::EndEvent + : (m_currentMode == ParserMode::MainLoop) ? DataHeader::EndMainLoop + : (m_currentMode == ParserMode::Onenter) ? DataHeader::EndOnEnter + : (m_currentMode == ParserMode::Onexit) ? DataHeader::EndOnExit + : DataHeader::NumDataHeaders; - if (dataheader == -1) + if (dataheader == DataHeader::NumDataHeaders) error ("unexpected `}`"); // Data header must be written before mode is changed because // onenter and mainloop go into special buffers, and we want // the closing data headers into said buffers too. - currentBuffer()->writeDWord (dataheader); - m_currentMode = PARSERMODE_TopLevel; + currentBuffer()->writeHeader (dataheader); + m_currentMode = ParserMode::TopLevel; m_lexer->next (Token::Semicolon); } @@ -935,7 +937,8 @@ m_lexer->mustGetSymbol ("zandronum"); String versionText; - while (m_lexer->next() and (m_lexer->tokenType() == Token::Number or m_lexer->tokenType() == Token::Dot)) + while (m_lexer->next() and (m_lexer->tokenType() == Token::Number or m_lexer->tokenType() == +Token::Dot)) versionText += getTokenString(); // Note: at this point the lexer's pointing at the token after the version. @@ -943,7 +946,8 @@ error ("expected version string, got `%1`", getTokenString()); if (g_validZandronumVersions.contains (versionText) == false) - error ("unknown version string `%2`: valid versions: `%1`\n", g_validZandronumVersions, versionText); + error ("unknown version string `%2`: valid versions: `%1`\n", g_validZandronumVersions, +versionText); StringList versionTokens = versionText.split ("."); m_zandronumVersion = versionTokens[0].toLong() * 10000 + versionTokens[1].toLong() * 100; @@ -959,7 +963,7 @@ { DataBuffer* r = new DataBuffer (64); - if (m_currentMode == PARSERMODE_TopLevel and comm->returnvalue == TYPE_Void) + if (m_currentMode == ParserMode::TopLevel and comm->returnvalue == TYPE_Void) error ("command call at top level"); m_lexer->mustGetNext (Token::ParenStart); @@ -1013,12 +1017,12 @@ // If the script skipped any optional arguments, fill in defaults. while (curarg < comm->args.size()) { - r->writeDWord (DataHeader::PushNumber); + r->writeHeader (DataHeader::PushNumber); r->writeDWord (comm->args[curarg].defvalue); curarg++; } - r->writeDWord (DataHeader::Command); + r->writeHeader (DataHeader::Command); r->writeDWord (comm->number); r->writeDWord (comm->args.size()); @@ -1158,7 +1162,7 @@ // Get an operator AssignmentOperator oper = parseAssignmentOperator(); - if (m_currentMode == PARSERMODE_TopLevel) + if (m_currentMode == ParserMode::TopLevel) error ("can't alter variables at top level"); if (var->isarray) @@ -1184,8 +1188,7 @@ retbuf->WriteDWord (var->index); #endif - DataHeader dh = getAssigmentDataHeader (oper, var); - retbuf->writeDWord (dh); + retbuf->writeHeader (getAssigmentDataHeader (oper, var)); retbuf->writeDWord (var->index); return retbuf; } @@ -1314,10 +1317,10 @@ if (m_switchBuffer != null) return m_switchBuffer; - if (m_currentMode == PARSERMODE_MainLoop) + if (m_currentMode == ParserMode::MainLoop) return m_mainLoopBuffer; - if (m_currentMode == PARSERMODE_Onenter) + if (m_currentMode == ParserMode::Onenter) return m_onenterBuffer; return m_mainBuffer; @@ -1330,8 +1333,8 @@ // If there was no mainloop defined, write a dummy one now. if (m_gotMainLoop == false) { - m_mainLoopBuffer->writeDWord (DataHeader::MainLoop); - m_mainLoopBuffer->writeDWord (DataHeader::EndMainLoop); + m_mainLoopBuffer->writeHeader (DataHeader::MainLoop); + m_mainLoopBuffer->writeHeader (DataHeader::EndMainLoop); } // Write the onenter and mainloop buffers, in that order in particular. @@ -1359,7 +1362,7 @@ return; // Write header - m_mainBuffer->writeDWord (DataHeader::StringList); + m_mainBuffer->writeHeader (DataHeader::StringList); m_mainBuffer->writeDWord (stringcount); // Write all strings @@ -1376,7 +1379,7 @@ FILE* fp = fopen (outfile, "wb"); if (fp == null) - error ("couldn't open %1 for writing: %2", outfile, strerror (errno)); + error ("couldn't open %1 for writing: %2", outfile, std::strerror (errno)); // First, resolve references for (MarkReference* ref : m_mainBuffer->references())
--- a/src/parser.h Sun Jul 20 17:25:36 2014 +0300 +++ b/src/parser.h Mon Jul 21 17:14:42 2014 +0300 @@ -42,7 +42,7 @@ // ============================================================================ // Mark types // -named_enum MarkType +named_enum MarkType : char { MARK_Label, MARK_If, @@ -52,7 +52,7 @@ // ============================================================================ // Scope types // -named_enum ScopeType +named_enum ScopeType : char { SCOPE_Unknown, SCOPE_If, @@ -63,7 +63,7 @@ SCOPE_Else, }; -named_enum AssignmentOperator +named_enum AssignmentOperator : char { ASSIGNOP_Assign, ASSIGNOP_Add, @@ -75,7 +75,7 @@ ASSIGNOP_Decrease, }; -named_enum Writability +named_enum Writability : char { WRITE_Mutable, // normal read-many-write-many variable WRITE_Const, // write-once const variable @@ -86,13 +86,13 @@ // // Parser mode: where is the parser at? // -named_enum ParserMode +named_enum class ParserMode { - PARSERMODE_TopLevel, // at top level - PARSERMODE_Event, // inside event definition - PARSERMODE_MainLoop, // inside mainloop - PARSERMODE_Onenter, // inside onenter - PARSERMODE_Onexit, // inside onexit + TopLevel, // at top level + Event, // inside event definition + MainLoop, // inside mainloop + Onenter, // inside onenter + Onexit, // inside onexit }; // ============================================================================
--- a/src/property.h Sun Jul 20 17:25:36 2014 +0300 +++ b/src/property.h Mon Jul 21 17:14:42 2014 +0300 @@ -46,4 +46,4 @@ m_##READ = a; \ } -#define PROPERTY_CUSTOM_WRITE( READ ) ; \ No newline at end of file +#define PROPERTY_CUSTOM_WRITE( READ ) ;
--- a/src/string.cpp Sun Jul 20 17:25:36 2014 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,448 +0,0 @@ -/* - Copyright 2012-2014 Teemu Piippo - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include <cstring> -#include "main.h" -#include "string.h" - -// ============================================================================= -// -int String::compare (const String& other) const -{ - return _string.compare (other.stdString()); -} - -// ============================================================================= -// -void String::trim (int n) -{ - if (n > 0) - _string = mid (0, length() - n).stdString(); - else - _string = mid (n, -1).stdString(); -} - -// ============================================================================= -// -String String::strip (const List<char>& unwanted) -{ - String copy (_string); - - for (char c : unwanted) - { - int pos = 0; - while ((pos = copy.firstIndexOf (String (c))) != -1) - copy.removeAt (pos--); - } - - return copy; -} - -// ============================================================================= -// -String String::toUppercase() const -{ - String newstr (_string); - - for (char& c : newstr) - { - if (c >= 'a' and c <= 'z') - c -= 'a' - 'A'; - } - - return newstr; -} - -// ============================================================================= -// -String String::toLowercase() const -{ - String newstr (_string); - - for (char& c : newstr) - { - if (c >= 'A' and c <= 'Z') - c += 'a' - 'A'; - } - - return newstr; -} - -// ============================================================================= -// -StringList String::split (char del) const -{ - String delimstr; - delimstr += del; - return split (delimstr); -} - -// ============================================================================= -// -StringList String::split (const String& del) const -{ - StringList res; - int a = 0; - int b; - - // Find all separators and store the text left to them. - while ((b = firstIndexOf (del, a)) != -1) - { - String sub = mid (a, b); - - if (sub.length() > 0) - res << sub; - - a = b + del.length(); - } - - // Add the string at the right of the last separator - if (a < (int) length()) - res.append (mid (a, length())); - - return res; -} - -// ============================================================================= -// -void String::replace (const char* a, const char* b) -{ - long pos; - - while ((pos = firstIndexOf (a)) != -1) - _string = _string.replace (pos, strlen (a), b); -} - -// ============================================================================= -// -int String::count (char needle) const -{ - int needles = 0; - - for (const char & c : _string) - if (c == needle) - needles++; - - return needles; -} - -// ============================================================================= -// -String String::mid (long a, long b) const -{ - if (b == -1) - b = length(); - - if (b == a) - return ""; - - if (b < a) - { - // Swap the variables - int c = a; - a = b; - b = c; - } - - char* newstr = new char[b - a + 1]; - strncpy (newstr, _string.c_str() + a, b - a); - newstr[b - a] = '\0'; - - String other (newstr); - delete[] newstr; - return other; -} - -// ============================================================================= -// -int String::wordPosition (int n) const -{ - int count = 0; - - for (int i = 0; i < length(); ++i) - { - if (_string[i] != ' ') - continue; - - if (++count < n) - continue; - - return i; - } - - return -1; -} - -// ============================================================================= -// -int String::firstIndexOf (const char* c, int a) const -{ - int pos = _string.find (c, a); - - if (pos == (int) std::string::npos) - return -1; - - return pos; -} - -// ============================================================================= -// -int String::lastIndexOf (const char* c, int a) const -{ - if (a == -1 or a >= length()) - a = length() - 1; - - for (; a > 0; a--) - if (_string[a] == c[0] and strncmp (_string.c_str() + a, c, strlen (c)) == 0) - return a; - - return -1; -} - -// ============================================================================= -// -void String::dump() const -{ - print ("`%1`:\n", c_str()); - int i = 0; - - for (char u : _string) - print ("\t%1. [%d2] `%3`\n", i++, u, String (u)); -} - -// ============================================================================= -// -long String::toLong (bool* ok, int base) const -{ - errno = 0; - char* endptr; - long i = strtol (_string.c_str(), &endptr, base); - - if (ok) - *ok = (errno == 0 and *endptr == '\0'); - - return i; -} - -// ============================================================================= -// -float String::toFloat (bool* ok) const -{ - errno = 0; - char* endptr; - float i = strtof (_string.c_str(), &endptr); - - if (ok) - *ok = (errno == 0 and *endptr == '\0'); - - return i; -} - -// ============================================================================= -// -double String::toDouble (bool* ok) const -{ - errno = 0; - char* endptr; - double i = strtod (_string.c_str(), &endptr); - - if (ok) - *ok = (errno == 0 and *endptr == '\0'); - - return i; -} - -// ============================================================================= -// -String String::operator+ (const String& data) const -{ - String newString = *this; - newString.append (data); - return newString; -} - -// ============================================================================= -// -String String::operator+ (const char* data) const -{ - String newstr = *this; - newstr.append (data); - return newstr; -} - -// ============================================================================= -// -bool String::isNumeric() const -{ - bool gotDot = false; - - for (const char & c : _string) - { - // Allow leading hyphen for negatives - if (&c == &_string[0] and c == '-') - continue; - - // Check for decimal point - if (!gotDot and c == '.') - { - gotDot = true; - continue; - } - - if (c >= '0' and c <= '9') - continue; // Digit - - // If the above cases didn't catch this character, it was - // illegal and this is therefore not a number. - return false; - } - - return true; -} - -// ============================================================================= -// -bool String::endsWith (const String& other) -{ - if (length() < other.length()) - return false; - - const int ofs = length() - other.length(); - return strncmp (c_str() + ofs, other.c_str(), other.length()) == 0; -} - -// ============================================================================= -// -bool String::startsWith (const String& other) -{ - if (length() < other.length()) - return false; - - return strncmp (c_str(), other.c_str(), other.length()) == 0; -} - -// ============================================================================= -// -void String::sprintf (const char* fmtstr, ...) -{ - char* buf; - int bufsize = 256; - va_list va; - va_start (va, fmtstr); - - do - buf = new char[bufsize]; - while (vsnprintf (buf, bufsize, fmtstr, va) >= bufsize); - - va_end (va); - _string = buf; - delete[] buf; -} - -// ============================================================================= -// -String StringList::join (const String& delim) -{ - String result; - - for (const String& it : deque()) - { - if (result.isEmpty() == false) - result += delim; - - result += it; - } - - return result; -} - -// ============================================================================= -// -bool String::maskAgainst (const String& pattern) const -{ - // Elevate to uppercase for case-insensitive matching - String pattern_upper = pattern.toUppercase(); - String this_upper = toUppercase(); - const char* maskstring = pattern_upper.c_str(); - const char* mptr = &maskstring[0]; - - for (const char* sptr = this_upper.c_str(); *sptr != '\0'; sptr++) - { - if (*mptr == '?') - { - if (*(sptr + 1) == '\0') - { - // ? demands that there's a character here and there wasn't. - // Therefore, mask matching fails - return false; - } - } - elif (*mptr == '*') - { - char end = *(++mptr); - - // If '*' is the final character of the message, all of the remaining - // string matches against the '*'. We don't need to bother checking - // the string any further. - if (end == '\0') - return true; - - // Skip to the end character - while (*sptr != end and *sptr != '\0') - sptr++; - - // String ended while the mask still had stuff - if (*sptr == '\0') - return false; - } - elif (*sptr != *mptr) - return false; - - mptr++; - } - - return true; -} - -// ============================================================================= -// -String String::fromNumber (int a) -{ - char buf[32]; - ::sprintf (buf, "%d", a); - return String (buf); -} - -// ============================================================================= -// -String String::fromNumber (long a) -{ - char buf[32]; - ::sprintf (buf, "%ld", a); - return String (buf); -}
--- a/src/string.h Sun Jul 20 17:25:36 2014 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,357 +0,0 @@ -/* - Copyright 2012-2014 Teemu Piippo - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef BOTC_STRING_H -#define BOTC_STRING_H - -#include <deque> -#include <string> -#include <stdarg.h> -#include "types.h" -#include "list.h" - -class String; -class StringList; - -// ============================================================================= -// -class String -{ -public: - String() {} - - explicit String (char a) : - _string ({ a, '\0' }) {} - - String (const char* data) : - _string (data) {} - - String (const std::string& data) : - _string (data) {} - - inline void append (const char* data); - inline void append (char data); - inline void append (const String& data); - inline std::string::iterator begin(); - inline std::string::const_iterator begin() const; - void dump() const; - inline void clear(); - int compare (const String& other) const; - int count (char needle) const; - inline const char* c_str() const; - inline std::string::iterator end(); - inline std::string::const_iterator end() const; - bool endsWith (const String& other); - int firstIndexOf (const char* c, int a = 0) const; - String toLowercase() const; - inline int indexDifference (int a, int b); - inline void insert (int pos, char c); - inline bool isEmpty() const; - bool isNumeric() const; - int lastIndexOf (const char* c, int a = -1) const; - inline int length() const; - bool maskAgainst (const String& pattern) const; - String mid (long a, long b = -1) const; - inline void modifyIndex (int& a); - inline void prepend (String a); - inline void removeAt (int pos); - inline void remove (int pos, int len); - inline void removeFromEnd (int len); - inline void removeFromStart (int len); - void replace (const char* a, const char* b); - inline void replace (int pos, int n, const String& a); - inline void shrinkToFit(); - StringList split (const String& del) const; - StringList split (char del) const; - void sprintf (const char* fmtstr, ...); - bool startsWith (const String& other); - inline const std::string& stdString() const; - inline String strip (char unwanted); - String strip (const List<char>& unwanted); - double toDouble (bool* ok = nullptr) const; - float toFloat (bool* ok = nullptr) const; - long toLong (bool* ok = nullptr, int base = 10) const; - void trim (int n); - String toUppercase() const; - int wordPosition (int n) const; - - static String fromNumber (int a); - static String fromNumber (long a); - - String operator+ (const String& data) const; - String operator+ (const char* data) const; - inline String operator+ (int num) const; - inline String& operator+= (const String data); - inline String& operator+= (const char* data); - inline String& operator+= (int num); - inline String& operator+= (const char data); - inline String operator- (int n) const; - inline String& operator-= (int n); - inline bool operator== (const String& other) const; - inline bool operator== (const char* other) const; - inline bool operator!= (const String& other) const; - inline bool operator!= (const char* other) const; - inline bool operator> (const String& other) const; - inline bool operator< (const String& other) const; - inline operator const char*() const; - inline operator const std::string&() const; - -private: - std::string _string; -}; - -class StringList : public List<String> -{ - public: - StringList() {} - StringList (std::initializer_list<String> vals) : - List<String> (vals) {} - StringList (const List<String>& a) : List<String> (a.deque()) {} - StringList (const std::deque<String>& a) : List<String> (a) {} - - String join (const String& delim); -}; - -inline bool operator== (const char* a, const String& b); -inline String operator+ (const char* a, const String& b); - -// ============================================================================= -// -// IMPLEMENTATIONS -// - -inline bool String::isEmpty() const -{ - return _string[0] == '\0'; -} - -inline void String::append (const char* data) -{ - _string.append (data); -} - -inline void String::append (char data) -{ - _string.push_back (data); -} - -inline void String::append (const String& data) -{ - _string.append (data.c_str()); -} - -inline std::string::iterator String::begin() -{ - return _string.begin(); -} - -inline std::string::const_iterator String::begin() const -{ - return _string.cbegin(); -} - -inline const char* String::c_str() const -{ - return _string.c_str(); -} - -inline std::string::iterator String::end() -{ - return _string.end(); -} - -inline std::string::const_iterator String::end() const -{ - return _string.end(); -} - -inline void String::clear() -{ - _string.clear(); -} - -inline void String::removeAt (int pos) -{ - _string.erase (_string.begin() + pos); -} - -inline void String::insert (int pos, char c) -{ - _string.insert (_string.begin() + pos, c); -} - -inline int String::length() const -{ - return _string.length(); -} - -inline void String::remove (int pos, int len) -{ - _string.replace (pos, len, ""); -} - -inline void String::removeFromStart (int len) -{ - remove (0, len); -} - -inline void String::removeFromEnd (int len) -{ - remove (length() - len, len); -} - -inline void String::replace (int pos, int n, const String& a) -{ - _string.replace (pos, n, a.c_str()); -} - -inline void String::shrinkToFit() -{ - _string.shrink_to_fit(); -} - -inline const std::string& String::stdString() const -{ - return _string; -} - -inline String String::strip (char unwanted) -{ - return strip ({unwanted}); -} - -inline String String::operator+ (int num) const -{ - return *this + String::fromNumber (num); -} - -inline String& String::operator+= (const String data) -{ - append (data); - return *this; -} - -inline String& String::operator+= (const char* data) -{ - append (data); - return *this; -} - -inline String& String::operator+= (int num) -{ - return operator+= (String::fromNumber (num)); -} - -inline void String::prepend (String a) -{ - _string = (a + _string).stdString(); -} - -inline String& String::operator+= (const char data) -{ - append (data); - return *this; -} - -inline String String::operator- (int n) const -{ - String newString = _string; - newString -= n; - return newString; -} - -inline String& String::operator-= (int n) -{ - trim (n); - return *this; -} - -inline bool String::operator== (const String& other) const -{ - return stdString() == other.stdString(); -} - -inline bool String::operator== (const char* other) const -{ - return operator== (String (other)); -} - -inline bool String::operator!= (const String& other) const -{ - return stdString() != other.stdString(); -} - -inline bool String::operator!= (const char* other) const -{ - return operator!= (String (other)); -} - -inline bool String::operator> (const String& other) const -{ - return stdString() > other.stdString(); -} - -inline bool String::operator< (const String& other) const -{ - return stdString() < other.stdString(); -} - -inline String::operator const char*() const -{ - return c_str(); -} - -inline String::operator const std::string&() const -{ - return stdString(); -} - -inline void String::modifyIndex (int& a) -{ - if (a < 0) - a = length() - a; -} - -inline int String::indexDifference (int a, int b) -{ - modifyIndex (a); - modifyIndex (b); - return b - a; -} - -inline bool operator== (const char* a, const String& b) -{ - return b == a; -} - -inline String operator+ (const char* a, const String& b) -{ - return String (a) + b; -} - -#endif // BOTC_STRING_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/stringClass.cpp Mon Jul 21 17:14:42 2014 +0300 @@ -0,0 +1,448 @@ +/* + Copyright 2012-2014 Teemu Piippo + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <cstring> +#include "main.h" +#include "stringClass.h" + +// ============================================================================= +// +int String::compare (const String& other) const +{ + return _string.compare (other.stdString()); +} + +// ============================================================================= +// +void String::trim (int n) +{ + if (n > 0) + _string = mid (0, length() - n).stdString(); + else + _string = mid (n, -1).stdString(); +} + +// ============================================================================= +// +String String::strip (const List<char>& unwanted) +{ + String copy (_string); + + for (char c : unwanted) + { + int pos = 0; + while ((pos = copy.firstIndexOf (String (c))) != -1) + copy.removeAt (pos--); + } + + return copy; +} + +// ============================================================================= +// +String String::toUppercase() const +{ + String newstr (_string); + + for (char& c : newstr) + { + if (c >= 'a' and c <= 'z') + c -= 'a' - 'A'; + } + + return newstr; +} + +// ============================================================================= +// +String String::toLowercase() const +{ + String newstr (_string); + + for (char& c : newstr) + { + if (c >= 'A' and c <= 'Z') + c += 'a' - 'A'; + } + + return newstr; +} + +// ============================================================================= +// +StringList String::split (char del) const +{ + String delimstr; + delimstr += del; + return split (delimstr); +} + +// ============================================================================= +// +StringList String::split (const String& del) const +{ + StringList res; + int a = 0; + int b; + + // Find all separators and store the text left to them. + while ((b = firstIndexOf (del, a)) != -1) + { + String sub = mid (a, b); + + if (sub.length() > 0) + res << sub; + + a = b + del.length(); + } + + // Add the string at the right of the last separator + if (a < (int) length()) + res.append (mid (a, length())); + + return res; +} + +// ============================================================================= +// +void String::replace (const char* a, const char* b) +{ + long pos; + + while ((pos = firstIndexOf (a)) != -1) + _string = _string.replace (pos, strlen (a), b); +} + +// ============================================================================= +// +int String::count (char needle) const +{ + int needles = 0; + + for (const char & c : _string) + if (c == needle) + needles++; + + return needles; +} + +// ============================================================================= +// +String String::mid (long a, long b) const +{ + if (b == -1) + b = length(); + + if (b == a) + return ""; + + if (b < a) + { + // Swap the variables + int c = a; + a = b; + b = c; + } + + char* newstr = new char[b - a + 1]; + strncpy (newstr, _string.c_str() + a, b - a); + newstr[b - a] = '\0'; + + String other (newstr); + delete[] newstr; + return other; +} + +// ============================================================================= +// +int String::wordPosition (int n) const +{ + int count = 0; + + for (int i = 0; i < length(); ++i) + { + if (_string[i] != ' ') + continue; + + if (++count < n) + continue; + + return i; + } + + return -1; +} + +// ============================================================================= +// +int String::firstIndexOf (const char* c, int a) const +{ + int pos = _string.find (c, a); + + if (pos == (int) std::string::npos) + return -1; + + return pos; +} + +// ============================================================================= +// +int String::lastIndexOf (const char* c, int a) const +{ + if (a == -1 or a >= length()) + a = length() - 1; + + for (; a > 0; a--) + if (_string[a] == c[0] and strncmp (_string.c_str() + a, c, strlen (c)) == 0) + return a; + + return -1; +} + +// ============================================================================= +// +void String::dump() const +{ + print ("`%1`:\n", c_str()); + int i = 0; + + for (char u : _string) + print ("\t%1. [%d2] `%3`\n", i++, u, String (u)); +} + +// ============================================================================= +// +long String::toLong (bool* ok, int base) const +{ + errno = 0; + char* endptr; + long i = strtol (_string.c_str(), &endptr, base); + + if (ok) + *ok = (errno == 0 and *endptr == '\0'); + + return i; +} + +// ============================================================================= +// +float String::toFloat (bool* ok) const +{ + errno = 0; + char* endptr; + float i = strtof (_string.c_str(), &endptr); + + if (ok) + *ok = (errno == 0 and *endptr == '\0'); + + return i; +} + +// ============================================================================= +// +double String::toDouble (bool* ok) const +{ + errno = 0; + char* endptr; + double i = strtod (_string.c_str(), &endptr); + + if (ok) + *ok = (errno == 0 and *endptr == '\0'); + + return i; +} + +// ============================================================================= +// +String String::operator+ (const String& data) const +{ + String newString = *this; + newString.append (data); + return newString; +} + +// ============================================================================= +// +String String::operator+ (const char* data) const +{ + String newstr = *this; + newstr.append (data); + return newstr; +} + +// ============================================================================= +// +bool String::isNumeric() const +{ + bool gotDot = false; + + for (const char & c : _string) + { + // Allow leading hyphen for negatives + if (&c == &_string[0] and c == '-') + continue; + + // Check for decimal point + if (!gotDot and c == '.') + { + gotDot = true; + continue; + } + + if (c >= '0' and c <= '9') + continue; // Digit + + // If the above cases didn't catch this character, it was + // illegal and this is therefore not a number. + return false; + } + + return true; +} + +// ============================================================================= +// +bool String::endsWith (const String& other) +{ + if (length() < other.length()) + return false; + + const int ofs = length() - other.length(); + return strncmp (c_str() + ofs, other.c_str(), other.length()) == 0; +} + +// ============================================================================= +// +bool String::startsWith (const String& other) +{ + if (length() < other.length()) + return false; + + return strncmp (c_str(), other.c_str(), other.length()) == 0; +} + +// ============================================================================= +// +void String::sprintf (const char* fmtstr, ...) +{ + char* buf; + int bufsize = 256; + va_list va; + va_start (va, fmtstr); + + do + buf = new char[bufsize]; + while (vsnprintf (buf, bufsize, fmtstr, va) >= bufsize); + + va_end (va); + _string = buf; + delete[] buf; +} + +// ============================================================================= +// +String StringList::join (const String& delim) +{ + String result; + + for (const String& it : deque()) + { + if (result.isEmpty() == false) + result += delim; + + result += it; + } + + return result; +} + +// ============================================================================= +// +bool String::maskAgainst (const String& pattern) const +{ + // Elevate to uppercase for case-insensitive matching + String pattern_upper = pattern.toUppercase(); + String this_upper = toUppercase(); + const char* maskstring = pattern_upper.c_str(); + const char* mptr = &maskstring[0]; + + for (const char* sptr = this_upper.c_str(); *sptr != '\0'; sptr++) + { + if (*mptr == '?') + { + if (*(sptr + 1) == '\0') + { + // ? demands that there's a character here and there wasn't. + // Therefore, mask matching fails + return false; + } + } + elif (*mptr == '*') + { + char end = *(++mptr); + + // If '*' is the final character of the message, all of the remaining + // string matches against the '*'. We don't need to bother checking + // the string any further. + if (end == '\0') + return true; + + // Skip to the end character + while (*sptr != end and *sptr != '\0') + sptr++; + + // String ended while the mask still had stuff + if (*sptr == '\0') + return false; + } + elif (*sptr != *mptr) + return false; + + mptr++; + } + + return true; +} + +// ============================================================================= +// +String String::fromNumber (int a) +{ + char buf[32]; + ::sprintf (buf, "%d", a); + return String (buf); +} + +// ============================================================================= +// +String String::fromNumber (long a) +{ + char buf[32]; + ::sprintf (buf, "%ld", a); + return String (buf); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/stringClass.h Mon Jul 21 17:14:42 2014 +0300 @@ -0,0 +1,357 @@ +/* + Copyright 2012-2014 Teemu Piippo + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef BOTC_STRING_H +#define BOTC_STRING_H + +#include <deque> +#include <string> +#include <stdarg.h> +#include "types.h" +#include "list.h" + +class String; +class StringList; + +// ============================================================================= +// +class String +{ +public: + String() {} + + explicit String (char a) : + _string ({ a, '\0' }) {} + + String (const char* data) : + _string (data) {} + + String (const std::string& data) : + _string (data) {} + + inline void append (const char* data); + inline void append (char data); + inline void append (const String& data); + inline std::string::iterator begin(); + inline std::string::const_iterator begin() const; + void dump() const; + inline void clear(); + int compare (const String& other) const; + int count (char needle) const; + inline const char* c_str() const; + inline std::string::iterator end(); + inline std::string::const_iterator end() const; + bool endsWith (const String& other); + int firstIndexOf (const char* c, int a = 0) const; + String toLowercase() const; + inline int indexDifference (int a, int b); + inline void insert (int pos, char c); + inline bool isEmpty() const; + bool isNumeric() const; + int lastIndexOf (const char* c, int a = -1) const; + inline int length() const; + bool maskAgainst (const String& pattern) const; + String mid (long a, long b = -1) const; + inline void modifyIndex (int& a); + inline void prepend (String a); + inline void removeAt (int pos); + inline void remove (int pos, int len); + inline void removeFromEnd (int len); + inline void removeFromStart (int len); + void replace (const char* a, const char* b); + inline void replace (int pos, int n, const String& a); + inline void shrinkToFit(); + StringList split (const String& del) const; + StringList split (char del) const; + void sprintf (const char* fmtstr, ...); + bool startsWith (const String& other); + inline const std::string& stdString() const; + inline String strip (char unwanted); + String strip (const List<char>& unwanted); + double toDouble (bool* ok = nullptr) const; + float toFloat (bool* ok = nullptr) const; + long toLong (bool* ok = nullptr, int base = 10) const; + void trim (int n); + String toUppercase() const; + int wordPosition (int n) const; + + static String fromNumber (int a); + static String fromNumber (long a); + + String operator+ (const String& data) const; + String operator+ (const char* data) const; + inline String operator+ (int num) const; + inline String& operator+= (const String data); + inline String& operator+= (const char* data); + inline String& operator+= (int num); + inline String& operator+= (const char data); + inline String operator- (int n) const; + inline String& operator-= (int n); + inline bool operator== (const String& other) const; + inline bool operator== (const char* other) const; + inline bool operator!= (const String& other) const; + inline bool operator!= (const char* other) const; + inline bool operator> (const String& other) const; + inline bool operator< (const String& other) const; + inline operator const char*() const; + inline operator const std::string&() const; + +private: + std::string _string; +}; + +class StringList : public List<String> +{ + public: + StringList() {} + StringList (std::initializer_list<String> vals) : + List<String> (vals) {} + StringList (const List<String>& a) : List<String> (a.deque()) {} + StringList (const std::deque<String>& a) : List<String> (a) {} + + String join (const String& delim); +}; + +inline bool operator== (const char* a, const String& b); +inline String operator+ (const char* a, const String& b); + +// ============================================================================= +// +// IMPLEMENTATIONS +// + +inline bool String::isEmpty() const +{ + return _string[0] == '\0'; +} + +inline void String::append (const char* data) +{ + _string.append (data); +} + +inline void String::append (char data) +{ + _string.push_back (data); +} + +inline void String::append (const String& data) +{ + _string.append (data.c_str()); +} + +inline std::string::iterator String::begin() +{ + return _string.begin(); +} + +inline std::string::const_iterator String::begin() const +{ + return _string.cbegin(); +} + +inline const char* String::c_str() const +{ + return _string.c_str(); +} + +inline std::string::iterator String::end() +{ + return _string.end(); +} + +inline std::string::const_iterator String::end() const +{ + return _string.end(); +} + +inline void String::clear() +{ + _string.clear(); +} + +inline void String::removeAt (int pos) +{ + _string.erase (_string.begin() + pos); +} + +inline void String::insert (int pos, char c) +{ + _string.insert (_string.begin() + pos, c); +} + +inline int String::length() const +{ + return _string.length(); +} + +inline void String::remove (int pos, int len) +{ + _string.replace (pos, len, ""); +} + +inline void String::removeFromStart (int len) +{ + remove (0, len); +} + +inline void String::removeFromEnd (int len) +{ + remove (length() - len, len); +} + +inline void String::replace (int pos, int n, const String& a) +{ + _string.replace (pos, n, a.c_str()); +} + +inline void String::shrinkToFit() +{ + _string.shrink_to_fit(); +} + +inline const std::string& String::stdString() const +{ + return _string; +} + +inline String String::strip (char unwanted) +{ + return strip ({unwanted}); +} + +inline String String::operator+ (int num) const +{ + return *this + String::fromNumber (num); +} + +inline String& String::operator+= (const String data) +{ + append (data); + return *this; +} + +inline String& String::operator+= (const char* data) +{ + append (data); + return *this; +} + +inline String& String::operator+= (int num) +{ + return operator+= (String::fromNumber (num)); +} + +inline void String::prepend (String a) +{ + _string = (a + _string).stdString(); +} + +inline String& String::operator+= (const char data) +{ + append (data); + return *this; +} + +inline String String::operator- (int n) const +{ + String newString = _string; + newString -= n; + return newString; +} + +inline String& String::operator-= (int n) +{ + trim (n); + return *this; +} + +inline bool String::operator== (const String& other) const +{ + return stdString() == other.stdString(); +} + +inline bool String::operator== (const char* other) const +{ + return operator== (String (other)); +} + +inline bool String::operator!= (const String& other) const +{ + return stdString() != other.stdString(); +} + +inline bool String::operator!= (const char* other) const +{ + return operator!= (String (other)); +} + +inline bool String::operator> (const String& other) const +{ + return stdString() > other.stdString(); +} + +inline bool String::operator< (const String& other) const +{ + return stdString() < other.stdString(); +} + +inline String::operator const char*() const +{ + return c_str(); +} + +inline String::operator const std::string&() const +{ + return stdString(); +} + +inline void String::modifyIndex (int& a) +{ + if (a < 0) + a = length() - a; +} + +inline int String::indexDifference (int a, int b) +{ + modifyIndex (a); + modifyIndex (b); + return b - a; +} + +inline bool operator== (const char* a, const String& b) +{ + return b == a; +} + +inline String operator+ (const char* a, const String& b) +{ + return String (a) + b; +} + +#endif // BOTC_STRING_H
--- a/src/stringTable.cpp Sun Jul 20 17:25:36 2014 +0300 +++ b/src/stringTable.cpp Mon Jul 21 17:14:42 2014 +0300 @@ -27,10 +27,6 @@ */ // TODO: Another freeloader... - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> #include "stringTable.h" static StringList g_StringTable;
--- a/src/tokens.h Sun Jul 20 17:25:36 2014 +0300 +++ b/src/tokens.h Mon Jul 21 17:14:42 2014 +0300 @@ -33,7 +33,7 @@ #include "macros.h" // ======================================================= -named_enum Token +named_enum class Token { // Non-word tokens LeftShiftAssign, @@ -124,8 +124,8 @@ String, Any, - FirstNamedToken = Bool, - LastNamedToken = Token (int (Symbol) - 1) + FirstNamedToken = Token::Bool, + LastNamedToken = Token::Return, }; #endif
--- a/src/types.h Sun Jul 20 17:25:36 2014 +0300 +++ b/src/types.h Mon Jul 21 17:14:42 2014 +0300 @@ -32,13 +32,13 @@ #include <cstdlib> #include <stdexcept> #include "macros.h" -#include "string.h" +#include "stringClass.h" static const std::nullptr_t null = nullptr; // ============================================================================= // -named_enum DataType +named_enum DataType : char { TYPE_Unknown, TYPE_Void,
--- a/updaterevision/updaterevision.c Sun Jul 20 17:25:36 2014 +0300 +++ b/updaterevision/updaterevision.c Mon Jul 21 17:14:42 2014 +0300 @@ -1,145 +1,192 @@ -/* updaterevision.c - * - * Public domain. This program uses git commands command to get - * various bits of repository status for a particular directory - * and writes it into a header file so that it can be used for a - * project's versioning. - * - * 2014-03-30: [crimsondusk] added GIT_BRANCH, doubled buffer sizes - */ - -#define _CRT_SECURE_NO_DEPRECATE - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> -#include <errno.h> - -#ifdef _WIN32 -#define popen _popen -#define pclose _pclose -#endif - -// Used to strip newline characters from lines read by fgets. -void stripnl(char *str) -{ - if (*str != '\0') - { - size_t len = strlen(str); - if (str[len - 1] == '\n') - { - str[len - 1] = '\0'; - } - } -} - -int main(int argc, char **argv) -{ - char vertag[128], lastlog[128], lasthash[128], branch[128], *hash = NULL; - FILE *stream = NULL; - int gotrev = 0, needupdate = 1; - - vertag[0] = '\0'; - lastlog[0] = '\0'; - branch[0] = '\0'; - - if (argc != 2) - { - fprintf(stderr, "Usage: %s <path to gitinfo.h>\n", argv[0]); - return 1; - } - - // Use git describe --tags to get a version string. If we are sitting directly - // on a tag, it returns that tag. Otherwise it returns <most recent tag>-<number of - // commits since the tag>-<short hash>. - // Use git log to get the time of the latest commit in ISO 8601 format and its full hash. - // [crimsondusk] Use git rev-parse --abbrev-ref HEAD to get the branch. - stream = popen("git describe --tags && git log -1 --format=%ai*%H && git rev-parse --abbrev-ref HEAD", "r"); - - if (NULL != stream) - { - if (fgets(vertag, sizeof vertag, stream) == vertag && - fgets(lastlog, sizeof lastlog, stream) == lastlog && - fgets(branch, sizeof branch, stream) == branch) - { - stripnl(vertag); - stripnl(lastlog); - stripnl(branch); - gotrev = 1; - } - - pclose(stream); - } - - if (gotrev) - { - hash = strchr(lastlog, '*'); - if (hash != NULL) - { - *hash = '\0'; - hash++; - } - } - if (hash == NULL) - { - fprintf(stderr, "Failed to get commit info: %s\n", strerror(errno)); - strcpy(vertag, "<unknown version>"); - lastlog[0] = '\0'; - lastlog[1] = '0'; - lastlog[2] = '\0'; - hash = lastlog + 1; - strcpy(branch, "<unknown branch>"); - } - - stream = fopen (argv[1], "r"); - if (stream != NULL) - { - if (!gotrev) - { // If we didn't get a revision but the file does exist, leave it alone. - fclose (stream); - return 0; - } - // Read the revision that's in this file already. If it's the same as - // what we've got, then we don't need to modify it and can avoid rebuilding - // dependant files. - if (fgets(lasthash, sizeof lasthash, stream) == lasthash) - { - stripnl(lasthash); - if (strncmp(hash, lasthash + 3, 40) == 0 && - strcmp(branch, lasthash + 44) == 0) - { - needupdate = 0; - } - } - fclose (stream); - } - - if (needupdate) - { - stream = fopen (argv[1], "w"); - if (stream == NULL) - { - return 1; - } - fprintf(stream, -"// %s %s\n" -"//\n" -"// This file was automatically generated by the\n" -"// updaterevision tool. Do not edit by hand.\n" -"\n" -"#define GIT_DESCRIPTION \"%s\"\n" -"#define GIT_HASH \"%s\"\n" -"#define GIT_TIME \"%s\"\n" -"#define GIT_BRANCH \"%s\"\n", - hash, branch, vertag, hash, lastlog, branch); - fclose(stream); - fprintf(stderr, "%s updated to commit %s (%s).\n", argv[1], vertag, branch); - } - else - { - fprintf (stderr, "%s is up to date at commit %s (%s).\n", argv[1], vertag, branch); - } - - return 0; -} +/* updaterevision.c + * + * Public domain. This program uses the svnversion command to get the + * repository revision for a particular directory and writes it into + * a header file so that it can be used as a project's build number. + */ + +#define _CRT_SECURE_NO_DEPRECATE + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +// [BB] New #includes. +#include <sys/stat.h> +#include <time.h> + +int main(int argc, char **argv) +{ + char *name; + char currev[64], lastrev[64], run[256], *rev; + unsigned long urev; + FILE *stream = NULL; + int gotrev = 0, needupdate = 1; + // [BB] Are we working with a SVN checkout? + int svnCheckout = 0; + char hgdateString[64]; + time_t hgdate = 0; + char hgHash[13]; + hgHash[0] = '\0'; + + if (argc != 3) + { + fprintf (stderr, "Usage: %s <repository directory> <path to svnrevision.h>\n", argv[0]); + return 1; + } + + // [BB] Try to figure out whether this is a SVN or a Hg checkout. + { + struct stat st; + char filename[1024]; + sprintf ( filename, "%s/.svn/entries", argv[1] ); + if ( stat ( filename, &st ) == 0 ) + svnCheckout = 1; + // [BB] If stat failed we have to manually clear errno, otherwise the code below doesn't work. + else + errno = 0; + } + + // Use svnversion to get the revision number. If that fails, pretend it's + // revision 0. Note that this requires you have the command-line svn tools installed. + // [BB] Depending on whether this is a SVN or Hg checkout we have to use the appropriate tool. + if ( svnCheckout ) + sprintf (run, "svnversion -cn %s", argv[1]); + else + sprintf (run, "hg identify -n"); + if ((name = tempnam(NULL, "svnout")) != NULL) + { +#ifdef __APPLE__ + // tempnam will return errno of 2 even though it is successful for our purposes. + errno = 0; +#endif + if((stream = freopen(name, "w+b", stdout)) != NULL && + system(run) == 0 && +#ifndef __FreeBSD__ + errno == 0 && +#endif + fseek(stream, 0, SEEK_SET) == 0 && + fgets(currev, sizeof currev, stream) == currev && + (isdigit(currev[0]) || (currev[0] == '-' && currev[1] == '1'))) + { + gotrev = 1; + // [BB] Find the date the revision of the working copy was created. + if ( ( svnCheckout == 0 ) && + ( system("hg log -r. --template \"{date|hgdate} {node|short}\"") == 0 ) && + ( fseek(stream, strlen(currev), SEEK_SET) == 0 ) && + ( fgets(hgdateString, sizeof ( hgdateString ), stream) == hgdateString ) ) + { + // [BB] Find the hash in the output and store it. + char *p = strrchr ( hgdateString, ' ' ); + strncpy ( hgHash, p ? ( p+1 ) : "hashnotfound" , sizeof( hgHash ) - 1 ); + hgHash[ sizeof ( hgHash ) - 1 ] = '\0'; + // [BB] Extract the date from the output and store it. + hgdate = atoi ( hgdateString ); + } + } + } + if (stream != NULL) + { + fclose (stream); + remove (name); + } + if (name != NULL) + { + free (name); + } + + if (!gotrev) + { + fprintf (stderr, "Failed to get current revision: %s\n", strerror(errno)); + strcpy (currev, "0"); + rev = currev; + } + else + { + rev = strchr (currev, ':'); + if (rev == NULL) + { + rev = currev; + } + else + { + rev += 1; + } + } + + // [BB] Create date version string. + if ( gotrev && ( svnCheckout == 0 ) ) + { + char *endptr; + unsigned long parsedRev = strtoul(rev, &endptr, 10); + unsigned int localChanges = ( *endptr == '+' ); + struct tm *lt = gmtime( &hgdate ); + if ( localChanges ) + sprintf ( rev, "%d%02d%02d-%02d%02dM", lt->tm_year - 100, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min); + else + sprintf ( rev, "%d%02d%02d-%02d%02d", lt->tm_year - 100, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min); + } + + stream = fopen (argv[2], "r"); + if (stream != NULL) + { + if (!gotrev) + { // If we didn't get a revision but the file does exist, leave it alone. + fprintf( stderr, "No revision found.\n" ); + fclose (stream); + return 0; + } + // Read the revision that's in this file already. If it's the same as + // what we've got, then we don't need to modify it and can avoid rebuilding + // dependant files. + if (fgets(lastrev, sizeof lastrev, stream) == lastrev) + { + if (lastrev[0] != '\0') + { // Strip trailing \n + lastrev[strlen(lastrev) - 1] = '\0'; + } + if (strcmp(rev, lastrev + 3) == 0) + { + needupdate = 0; + } + } + fclose (stream); + } + + if (needupdate) + { + stream = fopen (argv[2], "w"); + if (stream == NULL) + { + return 1; + } + // [BB] Use hgdate as revision number. + if ( hgdate ) + urev = hgdate; + else + urev = strtoul(rev, NULL, 10); + fprintf (stream, +"// %s\n" +"//\n" +"// This file was automatically generated by the\n" +"// updaterevision tool. Do not edit by hand.\n" +"\n" +"#define SVN_REVISION_STRING \"%s\"\n" +"#define SVN_REVISION_NUMBER %lu\n", + rev, rev, urev); + + // [BB] Also save the hg hash. + if ( svnCheckout == 0 ) + fprintf (stream, "#define HG_REVISION_HASH_STRING \"%s\"\n", hgHash); + + fclose (stream); + fprintf (stderr, "%s updated to revision %s.\n", argv[2], rev); + } + else + { + fprintf (stderr, "%s is up to date at revision %s.\n", argv[2], rev); + } + + return 0; +}