diff --git a/CMakeLists.txt b/CMakeLists.txt index 4845590..5fd8d46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,4 +16,5 @@ add_subdirectory(Containers) add_subdirectory(Math) add_subdirectory(Allocators) add_subdirectory(Strings) -add_subdirectory(Tokenizer) \ No newline at end of file +add_subdirectory(Tokenizer) +add_subdirectory(CommandLine) \ No newline at end of file diff --git a/CommandLine/CMakeLists.txt b/CommandLine/CMakeLists.txt new file mode 100644 index 0000000..7e8c179 --- /dev/null +++ b/CommandLine/CMakeLists.txt @@ -0,0 +1,22 @@ + +cmake_minimum_required(VERSION 3.2) + +set(CMAKE_CXX_STANDARD 23) + +project(CommandLine) + +### ---------------------- Static Library --------------------- ### +file(GLOB SOURCES "./private/*.cpp") +file(GLOB HEADERS "./public/*.hpp") +add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS}) +target_include_directories(${PROJECT_NAME} PUBLIC ./public/) +target_link_libraries(${PROJECT_NAME} PUBLIC Tokenizer) + +### -------------------------- Tests -------------------------- ### +enable_testing() +file(GLOB TEST_SOURCES "./tests/*.cpp") +add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES}) +target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} Utils) +add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests) + +install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib) \ No newline at end of file diff --git a/CommandLine/private/CommandLine.cpp b/CommandLine/private/CommandLine.cpp new file mode 100644 index 0000000..39cea49 --- /dev/null +++ b/CommandLine/private/CommandLine.cpp @@ -0,0 +1,345 @@ + +#include "CommandLine.hpp" + +using namespace tp; + +const char* regexSpace = "\n|\t| |\r"; +const char* regexFalse = "N|n|(False)|(false)"; +const char* regexTrue = "Y|y|(True)|(true)"; +const char* regexInt = "((\\-)|(\\+))?[0-9]+"; +const char* regexFloat = R"(((\-)|(\+))?([0-9]+)(\.)([0-9]*)?)"; +const char* regexString = "'{'-'}*'"; + +CommandLine::Arg::Arg(const Arg& arg) { + mId = arg.mId; + switch (arg.mType) { + case CommandLine::Arg::INT: mType = INT; mInt = arg.mInt; break; + case CommandLine::Arg::FLOAT: mType = FLOAT; mFloat = arg.mFloat; break; + case CommandLine::Arg::BOOL: mType = BOOL; mBool = arg.mBool; break; + case CommandLine::Arg::STR: mType = STR; new (&mStr) StringArg(); mStr = arg.mStr; break; + case CommandLine::Arg::FILE_IN: mType = FILE_IN; new (&mFile) FileInputArg(); mFile = arg.mFile; break; + } + mOptional = arg.mOptional; +} + +CommandLine::Arg::Arg(const String& id, Type type) { + mId = id; + switch (type) { + case CommandLine::Arg::INT: mType = INT; new (&mInt) IntArg(); break; + case CommandLine::Arg::FLOAT: mType = FLOAT; new (&mFloat) FloatArg(); break; + case CommandLine::Arg::BOOL: mType = BOOL; new (&mBool) BoolArg(); break; + case CommandLine::Arg::STR: mType = STR; new (&mStr) StringArg(); break; + case CommandLine::Arg::FILE_IN: mType = FILE_IN; new (&mFile) FileInputArg(); break; + } + mOptional = false; +} + +CommandLine::Arg::Arg(const String& id, const Range& aAcceptingRange) { + mId = id; + mType = FLOAT; + mInt = { 0, 0, aAcceptingRange }; + mOptional = false; +} + +CommandLine::Arg::Arg(const String& id, const Range& aAcceptingRange) { + mId = id; + mType = FLOAT; + mFloat = { 0, 0, aAcceptingRange }; + mOptional = false; +} + +CommandLine::Arg::Arg(const String& id, const Range& aAcceptingRange, alni aDefault) { + mId = id; + mType = INT; + mInt = { 0, aDefault, aAcceptingRange }; +} + +CommandLine::Arg::Arg(const String& id, const Range& aAcceptingRange, alnf aDefault) { + mId = id; + mType = FLOAT; + mFloat = { 0, aDefault, aAcceptingRange }; +} + +CommandLine::Arg::Arg(const String& id, bool aDefault) { + mId = id; + mType = BOOL; + mBool.mDefault = aDefault; +} + +CommandLine::Arg::Arg(const String& id, const char* aDefault) { + mId = id; + mType = STR; + new (&mStr) StringArg(); + mStr.mDefault = aDefault; +} + +CommandLine::Arg::~Arg() { + switch (mType) { + case CommandLine::Arg::STR: + mStr.~StringArg(); + break; + case CommandLine::Arg::FILE_IN: + mFile.~FileInputArg(); + break; + default: + break; + } +} + +CommandLine::CommandLine(const init_list& args) { + bool optional_start = false; + + for (auto& arg : args) { + ASSERT(!mArgs.presents(arg.mId) && "Argument Redefinition") + + auto copy_arg = new Arg(arg); + + mArgs.put(arg.mId, copy_arg); + mArgsOrder.pushBack(mArgs.get(arg.mId)); + + if (arg.mOptional) { + mOptionals++; + } + + if (optional_start) { + ASSERT(arg.mOptional && "Not Optional Argument After Optionals") + } + else if (arg.mOptional) { + optional_start = true; + } + } + + mTokenizer.build({ + { regexSpace, TokType::SPACE }, + { regexFalse, TokType::BOOL_FALSE }, + { regexTrue, TokType::BOOL_TRUE }, + { regexInt, TokType::INT }, + { regexFloat, TokType::FLOAT }, + { regexString, TokType::STR }, + }); + + ASSERT(mTokenizer.isBuild() && "Internal Error") +} + +CommandLine::~CommandLine() { + for (auto arg : mArgsOrder) { + delete arg.data(); + } +} + +bool CommandLine::parse(char argc, const char* argv[], bool logError, ualni skipArgs) { + // discard some arguments + argc -= (char) skipArgs; + argv += skipArgs; + + if (argc < mArgs.size() - mOptionals) { + ErrInvalidArgCount(); + if (logError) { + ErrLog(); + } + return false; + } + + ualni idx = 0; + for (auto arg : mArgsOrder) { + if (idx < argc) { + parseArg(*arg.data(), argv[idx]); + } else { + initDefault(*arg.data()); + } + if (mError) { + if (logError) ErrLog(); + return false; + } + idx++; + } + + return true; + + //set_working_dir(); +} + +alni CommandLine::getInt(const String& id) { auto& arg = getArg(id, Arg::INT); return arg.mInt.mVal; } +alnf CommandLine::getFloat(const String& id) { auto& arg = getArg(id, Arg::FLOAT); return arg.mFloat.mVal; } +bool CommandLine::getBool(const String& id) { auto& arg = getArg(id, Arg::BOOL); return arg.mBool.mFlag; } +const String& CommandLine::getString(const String& id) { auto& arg = getArg(id, Arg::STR); return arg.mStr.mStr; } +File& CommandLine::getFile(const String& id) { auto& arg = getArg(id, Arg::FILE_IN); return arg.mFile.mFile; } + + +CommandLine::Arg& CommandLine::getArg(const String& id, Arg::Type type) { + auto idx = mArgs.presents(id); + ASSERT(idx && "Invalid Id") + auto& arg = mArgs.getSlotVal(idx); + ASSERT(arg->mType == type && "Invalid Type Requested") + return *arg; +} + +void CommandLine::ErrInvalidArgCount() { mError = { "Invalid Number Of Arguments Passed", nullptr }; } +void CommandLine::ErrInvalidArgSyntax(Arg* arg) { mError = { "Invalid Syntax Of Argument", arg }; } +void CommandLine::ErrInvalidArgType(Arg* arg) { mError = { "Invalid Type Of Argument", arg }; } +void CommandLine::ErrFileNotExists(Arg* arg) { mError = { "File Not Exists", arg }; } +void CommandLine::ErrFileCouldNotOpen(Arg* arg) { mError = { "Could Not Open File", arg }; } +void CommandLine::ErrValNotinRange(Arg* arg) { mError = { "Value Not In Range", arg }; } + +void CommandLine::ErrLog() { + printf("\nERROR : Invalid Command Line\n"); + + if (mError.mArg) { + alni idx = 0; + for (auto arg : mArgsOrder) { + if (mError.mArg == arg.data()) break; + idx++; + } + + printf("\nArgument: %lli\n", idx); + ArgLog(*mError.mArg); + } + + printf("Error Description: %s. \n", mError.mDescr); + + printf("\nExpected Arguments are: \n"); + ualni idx = 0; + for (auto arg : mArgsOrder) { + printf(" -- %lli -- \n", idx); + ArgLog(*arg.data()); + idx++; + } + + printf("\nNote regex for types are:\n"); + printf(" False - %s\n", regexFalse); + printf(" True - %s\n", regexTrue); + printf(" Int - %s\n", regexInt); + printf(" Float - %s\n", regexFloat); + printf(" String - %s\n", regexString); + printf(" File - else\n"); +} + +void CommandLine::ArgLog(Arg& arg) { + switch (arg.mType) { + case Arg::INT: { + printf("Type : Int\n"); + printf("Range : %lli - %lli\n", arg.mInt.mAcceptingRange.mBegin, arg.mInt.mAcceptingRange.mEnd); + if (arg.mOptional) { + printf("Default : %lli\n", arg.mInt.mDefault); + } + printf("Given : %lli\n", arg.mInt.mVal); + } break; + case Arg::FLOAT: { + printf("Type : Float\n"); + printf("Range : %f - %f\n", (halnf) arg.mFloat.mAcceptingRange.mBegin, (halnf) arg.mFloat.mAcceptingRange.mEnd); + if (arg.mOptional) { + printf("Default : %f\n", arg.mFloat.mDefault); + } + printf("Given : %f\n", arg.mFloat.mVal); + } break; + case Arg::BOOL: { + printf("Type : Bool\n"); + if (arg.mOptional) { + printf("Default : %s\n", arg.mBool.mDefault ? "True" : "False"); + } + printf("Given : %s\n", arg.mBool.mFlag ? "True" : "False"); + } break; + case Arg::STR: { + printf("Type : String\n"); + if (arg.mOptional) { + printf("Default : %s\n", arg.mStr.mDefault.read()); + } + printf("Given : %s\n", arg.mStr.mStr.read()); + } break; + case Arg::FILE_IN: { + printf("Type : File Input\n"); + printf("Given Path : %s\n", arg.mFile.mFilepath.read()); + } break; + } +} + +void CommandLine::initDefault(Arg& arg) { + switch (arg.mType) { + case Arg::INT: arg.mInt.mVal = arg.mInt.mDefault; break; + case Arg::FLOAT: arg.mFloat.mVal = arg.mFloat.mDefault; break; + case Arg::BOOL: arg.mBool.mFlag = arg.mBool.mDefault; break; + case Arg::STR: arg.mStr.mStr = arg.mStr.mDefault; break; + default: + break; + } +} + +void CommandLine::parseArg(Arg& arg, const char* src) { + mTokenizer.reset(); + mTokenizer.bindSource(src); + + auto tok = mTokenizer.readTok(); + if (tok < TokType::INT || tok > TokType::STR) { + ErrInvalidArgSyntax(&arg); + } + + auto val = mTokenizer.extractVal(); + + switch (arg.mType) { + case Arg::INT: { + if (tok != TokType::INT) { + ErrInvalidArgType(&arg); + return; + } + auto converted = String::Logic::convertStringToValue(val.read(), arg.mInt.mVal); + if (converted && arg.mInt.mVal < arg.mInt.mAcceptingRange.mBegin || arg.mInt.mVal > arg.mInt.mAcceptingRange.mEnd) { + ErrValNotinRange(&arg); + return; + } + } break; + + case Arg::FLOAT: { + if (tok != TokType::FLOAT) { + ErrInvalidArgType(&arg); + return; + } + auto converted = String::Logic::convertStringToValue(val.read(), arg.mFloat.mVal); + if (converted && arg.mFloat.mVal < arg.mFloat.mAcceptingRange.mBegin || arg.mFloat.mVal > arg.mFloat.mAcceptingRange.mEnd) { + ErrValNotinRange(&arg); + return; + } + } break; + + case Arg::STR: { + if (tok != TokType::STR) { + ErrInvalidArgType(&arg); + return; + } + arg.mStr.mStr = val; + } break; + + case Arg::BOOL: { + if (tok != TokType::BOOL_FALSE && tok != TokType::BOOL_TRUE) { + ErrInvalidArgType(&arg); + return; + } + arg.mBool.mFlag = bool(val); + } break; + + case Arg::FILE_IN: { + if (tok != TokType::STR) { + ErrInvalidArgType(&arg); + return; + } + + arg.mFile.mFilepath = val; + + /* + // TODO : replace + if (!File::exists(arg.mFile.mFilepath.cstr())) { + ErrFileNotExists(&arg); + return; + } + + arg.mFile.mFile.open(arg.mFile.mFilepath.cstr(), osfile_openflags::LOAD); + if (!arg.mFile.mFile.opened) { + ErrFileCouldNotOpen(&arg); + return; + } + */ + + } break; + } + + arg.mIsPassed = true; +} diff --git a/CommandLine/public/CommandLine.hpp b/CommandLine/public/CommandLine.hpp new file mode 100644 index 0000000..9e406e1 --- /dev/null +++ b/CommandLine/public/CommandLine.hpp @@ -0,0 +1,105 @@ +#pragma once + +#include "Tokenizer.hpp" +#include "Map.hpp" + +namespace tp { + + // TODO : replace + class File { + + }; + + struct CommandLine { + + struct IntArg { + alni mVal = 0; + alni mDefault = 0; + Range mAcceptingRange = { -(std::numeric_limits::max() - 1), std::numeric_limits::max() }; + }; + + struct FloatArg { + alnf mVal = 0.f; + alnf mDefault = 0.f; + Range mAcceptingRange = { -(std::numeric_limits::max() - 1), std::numeric_limits::max() }; + }; + + struct BoolArg { + bool mFlag = false; + bool mDefault = false; + }; + + struct StringArg { + String mStr; + String mDefault; + }; + + struct FileInputArg { + String mFilepath; + File mFile; + }; + + struct Arg { + String mId; + enum Type { INT, FLOAT, BOOL, STR, FILE_IN } mType; + union { + IntArg mInt; + FloatArg mFloat; + BoolArg mBool; + StringArg mStr; + FileInputArg mFile; + }; + + bool mOptional = true; + bool mIsPassed = false; + + Arg(const Arg& arg); + Arg(const String& id, Type type); + Arg(const String& id, const Range& aAcceptingRange); + Arg(const String& id, const Range& aAcceptingRange); + Arg(const String& id, const Range& aAcceptingRange, alni aDefault); + Arg(const String& id, const Range& aAcceptingRange, alnf aDefault); + Arg(const String& id, bool aDefault); + Arg(const String& id, const char* aDefault); + ~Arg(); + }; + + struct Error { + const char* mDescr = nullptr; + Arg* mArg = nullptr; + operator bool() { return mDescr != nullptr; } + } mError; + + CommandLine(const init_list& args); + ~CommandLine(); + + bool parse(char argc, const char* argv[], bool logError = true, ualni skipArgs = 1); + + alni getInt(const String& id); + alnf getFloat(const String& id); + bool getBool(const String& id); + const String& getString(const String& id); + File& getFile(const String& id); + + private: + enum class TokType { SPACE, INT, FLOAT, BOOL_FALSE, BOOL_TRUE, STR, NONE, FAILURE, END, } mType; + typedef SimpleTokenizer Tokenizer; + + Tokenizer mTokenizer; + Map mArgs; + List mArgsOrder; + ualni mOptionals = 0; + + Arg& getArg(const String& id, Arg::Type type); + void ErrInvalidArgCount(); + void ErrInvalidArgSyntax(Arg* arg); + void ErrInvalidArgType(Arg* arg); + void ErrFileNotExists(Arg* arg); + void ErrFileCouldNotOpen(Arg* arg); + void ErrValNotinRange(Arg* arg); + void ErrLog(); + static void ArgLog(Arg& arg); + static void initDefault(Arg& arg); + void parseArg(Arg& arg, const char* src); + }; +}; \ No newline at end of file diff --git a/CommandLine/tests/TestCommandLine.cpp b/CommandLine/tests/TestCommandLine.cpp new file mode 100644 index 0000000..a6ccd55 --- /dev/null +++ b/CommandLine/tests/TestCommandLine.cpp @@ -0,0 +1,23 @@ +#include "Tests.hpp" + +using namespace tp; + +static CommandLine createCmdLine() { + CommandLine cmd({ + { "bool", CommandLine::Arg::BOOL }, + { "int", CommandLine::Arg::INT }, + { "string", CommandLine::Arg::STR }, + { "float", CommandLine::Arg::FLOAT }, + }); + return cmd; +} + +TEST_DEF_STATIC(Simple) { + auto cmd = createCmdLine(); + const char* args[] = { "false", "2", "'str'", "-0.15" }; + cmd.parse(4, args, true, 0); +} + +TEST_DEF(CommandLine) { + testSimple(); +} diff --git a/CommandLine/tests/Tests.cpp b/CommandLine/tests/Tests.cpp new file mode 100644 index 0000000..c1d284f --- /dev/null +++ b/CommandLine/tests/Tests.cpp @@ -0,0 +1,18 @@ + +#include "Tests.hpp" +#include "Testing.hpp" + + +int main() { + + tp::ModuleManifest* deps[] = { &tp::gModuleUtils, &tp::gModuleTokenizer, nullptr }; + tp::ModuleManifest testModule("CommandLineTest", nullptr, nullptr, deps); + + if (!testModule.initialize()) { + return 1; + } + + testCommandLine(); + + testModule.deinitialize(); +} diff --git a/CommandLine/tests/Tests.hpp b/CommandLine/tests/Tests.hpp new file mode 100644 index 0000000..13f4290 --- /dev/null +++ b/CommandLine/tests/Tests.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include "Testing.hpp" +#include "Allocators.hpp" +#include "CommandLine.hpp" + +void testCommandLine(); diff --git a/Strings/public/StringLogic.hpp b/Strings/public/StringLogic.hpp index 32346e6..f66be01 100644 --- a/Strings/public/StringLogic.hpp +++ b/Strings/public/StringLogic.hpp @@ -80,16 +80,6 @@ namespace tp { goto LOOP; } - static bool toValue(const tChar* in, bool& val) { - if (!calcLength(in)) return false; - if (isEqual(in, "False") || isEqual(in, "false") || isEqual(in, "0")) { - val = false; - } else { - val = true; - } - return true; - } - // returns 0 : nl1 + 1 : nl2 + 1 : nl3 + 1 : ... : size static void calcLineOffsets(const tChar* aBuff, Index aLength, Buffer& aOut) { Index lines = 0; @@ -111,34 +101,34 @@ namespace tp { } - [[nodiscard]] static bool convertStringToValue(const tChar* input, alni& output) { + static bool convertStringToValue(const tChar* input, alni& output) { char* endPtr; output = std::strtoll(input, &endPtr, 10); return (endPtr == input + calcLength(input)); } - [[nodiscard]] static bool convertStringToValue(const tChar* input, alnf& output) { + static bool convertStringToValue(const tChar* input, alnf& output) { char* endPtr; output = std::strtod(input, &endPtr); return (endPtr == input + calcLength(input)); } - [[nodiscard]] static bool convertStringToValue(const tChar* input, bool& output) { + static bool convertStringToValue(const tChar* input, bool& output) { output = !(isEqualLogic(input, "False") || isEqualLogic(input, "false") || isEqualLogic(input, "0")); return true; } - [[nodiscard]] static bool convertValueToString(alni input, tChar* output, Index bufferSize) { + static bool convertValueToString(alni input, tChar* output, Index bufferSize) { int result = std::snprintf(output, bufferSize, "%lld", input); return result >= 0; } - [[nodiscard]] static bool convertValueToString(alnf input, tChar* output, Index bufferSize) { + static bool convertValueToString(alnf input, tChar* output, Index bufferSize) { int result = std::snprintf(output, bufferSize, "%f", input); return result >= 0; } - [[nodiscard]] static bool convertValueToString(bool input, tChar* output, Index bufferSize) { + static bool convertValueToString(bool input, tChar* output, Index bufferSize) { const char* str = (input ? "true" : "false"); Index length = calcLength(str); if (length >= bufferSize) diff --git a/Strings/public/Strings.hpp b/Strings/public/Strings.hpp index 254943c..46d124b 100644 --- a/Strings/public/Strings.hpp +++ b/Strings/public/Strings.hpp @@ -42,11 +42,12 @@ namespace tp { MODULE_SANITY_CHECK(gModuleStrings) DEBUG_ASSERT(aBuff) auto const len = Logic::calcLength(aBuff); - resize(len); - memCopy(mBuff, aBuff, len); mReferenceCount = 0; mIsConst = false; mEditedIdx = 0; + mBuff = new tChar[len + 1]; + mBuff[len] = Logic::getEndChar(); + memCopy(mBuff, aBuff, len); } [[nodiscard]] tChar* getBuffer() { @@ -72,7 +73,12 @@ namespace tp { typedef StringData Data; typedef typename Logic::Index Index; - enum { STRINGS_POOL_SIZE = 1024, }; + enum { + STRINGS_POOL_SIZE = 1024, + MAX_INT_STRING_LENGTH = 10, + MAX_BOOL_STRING_LENGTH = 10, + MAX_FLOAT_STRING_LENGTH = 10, + }; static PoolAlloc, STRINGS_POOL_SIZE> sStringPool; static Data sNullString; @@ -137,8 +143,9 @@ namespace tp { // output will be null if in TextEditing mode tChar* resize(uint1 size) { - if (!mData->getEditor()) mData->resize(size); - return mData->getBuffer(); + makeModifiable(); + mData->resize(size); + return write(); } public: @@ -160,6 +167,40 @@ namespace tp { } public: // Syntax sugars + + explicit StringTemplate(alni val) { + auto raw = new tChar[MAX_INT_STRING_LENGTH]; + Logic::convertValueToString(val, raw, MAX_INT_STRING_LENGTH); + mData = new (sStringPool.allocate(0)) StringData(raw); + } + + explicit StringTemplate(alnf val) { + auto raw = new tChar[MAX_FLOAT_STRING_LENGTH]; + Logic::convertValueToString(val, raw, MAX_FLOAT_STRING_LENGTH); + mData = new (sStringPool.allocate(0)) StringData(raw); + } + + explicit StringTemplate(bool val) { + auto raw = new tChar[MAX_BOOL_STRING_LENGTH]; + Logic::convertValueToString(val, raw, MAX_BOOL_STRING_LENGTH); + mData = new (sStringPool.allocate(0)) StringData(raw); + } + + explicit operator alni() { + alni out; + return Logic::convertStringToValue(read(), out); + } + + explicit operator alnf() { + alnf out; + return Logic::convertStringToValue(read(), out); + } + + explicit operator bool() { + bool out; + return Logic::convertStringToValue(read(), out); + } + Index size() const { return Logic::calcLength(read()); } @@ -181,11 +222,18 @@ namespace tp { return *this; } + bool operator==(const StringTemplate& in) const { + if (&in == this) return true; + if (in.mData == mData) return true; + return Logic::isEqualLogic(in.read(), read()); + } + public: // Debugging [[nodiscard]] ualni getReferenceCount() const { return mData->mReferenceCount; } [[nodiscard]] bool getIsConstFlag() const { return mData->mIsConst; } }; + template PoolAlloc, StringTemplate::STRINGS_POOL_SIZE> StringTemplate::sStringPool; @@ -193,4 +241,9 @@ namespace tp { StringData StringTemplate::sNullString; using String = StringTemplate; + + template + ualni hash(StringTemplate in) { + return hash(in.read()); + } } \ No newline at end of file diff --git a/Tokenizer/public/AutomataGraph.h b/Tokenizer/public/AutomataGraph.h index cbb7ee5..23c4aec 100644 --- a/Tokenizer/public/AutomataGraph.h +++ b/Tokenizer/public/AutomataGraph.h @@ -9,6 +9,8 @@ namespace tp { + extern ModuleManifest gModuleTokenizer; + template class TransitionMatrix; diff --git a/Tokenizer/public/RegularExpression.h b/Tokenizer/public/RegularExpression.h index 9294698..07f7bbc 100644 --- a/Tokenizer/public/RegularExpression.h +++ b/Tokenizer/public/RegularExpression.h @@ -3,6 +3,10 @@ #include "AutomataGraph.h" +namespace tp { + extern ModuleManifest gModuleTokenizer; +} + namespace tp::RegEx { struct AstNode {