Command Line INterpreter with fixes

This commit is contained in:
IlushaShurupov 2023-07-19 21:29:57 +03:00 committed by Ilya Shurupov
parent 32c7512b69
commit 58db6fe058
14 changed files with 384 additions and 95 deletions

View file

@ -10,7 +10,7 @@ file(GLOB SOURCES "./private/*.cpp")
file(GLOB HEADERS "./public/*.hpp") file(GLOB HEADERS "./public/*.hpp")
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS}) add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME} PUBLIC ./public/) target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
target_link_libraries(${PROJECT_NAME} PUBLIC Tokenizer) target_link_libraries(${PROJECT_NAME} PUBLIC Tokenizer Storage)
### -------------------------- Tests -------------------------- ### ### -------------------------- Tests -------------------------- ###
enable_testing() enable_testing()

View file

@ -0,0 +1,120 @@
#include "CmdLineInterpreter.hpp"
#define MAX_LINE_LENGTH 1024
#include <cstdio>
#include <cstring>
using namespace tp;
static bool helpFunction(CmdLineInterpreter* self, const CommandLine& args) {
self->help(args);
return true;
}
void CmdLineInterpreter::help(const CommandLine& args) const {
auto commandName = args.getString("CommandName");
auto allCommands = commandName == "_none_";
if (allCommands) {
printf("Commands:\n");
for (auto command : mCommands) {
printf(" %s\n", command->key.read());
}
return;
}
auto idx = mCommands.presents(commandName);
if (!idx) {
printf("Command '%s' not found\n", commandName.read());
return;
}
printf("Command '%s'\n", commandName.read());
auto command = &mCommands.getSlotVal(idx);
command->args->help();
}
CmdLineInterpreter::CmdLineInterpreter() {
auto helpArgs = new CommandLine{
{ "CommandName", "_none_" }
};
addCommand("help", helpArgs, (CommandFunction) helpFunction, this);
}
void CmdLineInterpreter::addCommand(const String& id, CommandLine* args, CommandFunction function, void* customData) {
ASSERT(!mCommands.presents(id))
mCommands.put(id, { args, function, customData });
}
void CmdLineInterpreter::run() {
char buffer[MAX_LINE_LENGTH];
printf("Command Line Interpreter (or type 'exit' to quit):\n");
while (true) {
printf("%s >> ", mName.read());
// Read input from the user
if (fgets(buffer, MAX_LINE_LENGTH, stdin) == nullptr) {
printf("Error reading input.\n");
return;
}
// Remove the trailing newline character if it exists
auto length = strlen(buffer);
if (length > 0 && buffer[length - 1] == '\n') {
buffer[length - 1] = '\0';
}
// Check if the user wants to exit
if (strcmp(buffer, "exit") == 0) {
return;
}
// Process the input here
processCommand(buffer);
}
}
bool CmdLineInterpreter::processCommand(int1* commandString) {
ualni commandLen = 0;
for (auto i = commandString; *i != 0; i++) {
if (*i == ' ') break;
commandLen++;
}
commandString[commandLen] = 0;
auto args = commandString + commandLen + 1;
auto idx = mCommands.presents(commandString);
if (!idx) {
printf("ERROR: Command '%s' is not found\n", commandString);
printf("Please execute 'help' for more information\n\n");
return true;
}
auto command = &mCommands.getSlotVal(idx);
command->args->parse(args);
if (command->args->mError) {
printf("ERROR: Command call '%s' has invalid arguments\n", commandString);
command->args->resetError();
return true;
}
if (!command->function(command->customData, *command->args)) {
printf("ERROR: Command call '%s' failed\n", commandString);
return true;
}
return true;
}
CmdLineInterpreter::~CmdLineInterpreter() {
for (auto cmd : mCommands) {
delete cmd->val.args;
}
}

View file

@ -3,6 +3,9 @@
using namespace tp; using namespace tp;
static ModuleManifest* sModuleDependencies[] = { &gModuleTokenizer, &gModuleStorage, nullptr };
ModuleManifest tp::gModuleStorage = ModuleManifest("CommandLine", nullptr, nullptr, sModuleDependencies);
const char* regexSpace = "\n|\t| |\r"; const char* regexSpace = "\n|\t| |\r";
const char* regexFalse = "N|n|(False)|(false)"; const char* regexFalse = "N|n|(False)|(false)";
const char* regexTrue = "Y|y|(True)|(true)"; const char* regexTrue = "Y|y|(True)|(true)";
@ -18,6 +21,8 @@ CommandLine::Arg::Arg(const Arg& arg) {
case CommandLine::Arg::BOOL: mType = BOOL; mBool = arg.mBool; 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::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; case CommandLine::Arg::FILE_IN: mType = FILE_IN; new (&mFile) FileInputArg(); mFile = arg.mFile; break;
default:
ASSERT(false)
} }
mOptional = arg.mOptional; mOptional = arg.mOptional;
} }
@ -30,6 +35,7 @@ CommandLine::Arg::Arg(const String& id, Type type) {
case CommandLine::Arg::BOOL: mType = BOOL; new (&mBool) BoolArg(); break; case CommandLine::Arg::BOOL: mType = BOOL; new (&mBool) BoolArg(); break;
case CommandLine::Arg::STR: mType = STR; new (&mStr) StringArg(); break; case CommandLine::Arg::STR: mType = STR; new (&mStr) StringArg(); break;
case CommandLine::Arg::FILE_IN: mType = FILE_IN; new (&mFile) FileInputArg(); break; case CommandLine::Arg::FILE_IN: mType = FILE_IN; new (&mFile) FileInputArg(); break;
default: ASSERT(false)
} }
mOptional = false; mOptional = false;
} }
@ -119,6 +125,13 @@ CommandLine::CommandLine(const init_list<Arg>& args) {
}); });
ASSERT(mTokenizer.isBuild() && "Internal Error") ASSERT(mTokenizer.isBuild() && "Internal Error")
mArgumentTokenizer.build({
{ regexSpace, ArgTokType::SPACE },
{ "{ - }*", ArgTokType::ARG }
});
ASSERT(mTokenizer.isBuild() && "Internal Error")
} }
CommandLine::~CommandLine() { CommandLine::~CommandLine() {
@ -153,17 +166,60 @@ bool CommandLine::parse(char argc, const char* argv[], bool logError, ualni skip
} }
idx++; idx++;
} }
return true; return true;
//set_working_dir();
} }
alni CommandLine::getInt(const String& id) { auto& arg = getArg(id, Arg::INT); return arg.mInt.mVal; } bool CommandLine::parse(const char* args, bool logError) {
alnf CommandLine::getFloat(const String& id) { auto& arg = getArg(id, Arg::FLOAT); return arg.mFloat.mVal; } mArgumentTokenizer.bindSource(args);
bool CommandLine::getBool(const String& id) { auto& arg = getArg(id, Arg::BOOL); return arg.mBool.mFlag; } mArgumentTokenizer.reset();
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; } ualni argc = 0;
auto tok = mArgumentTokenizer.readTok();
auto argument = mArgsOrder.first();
while (tok != ArgTokType::FAILURE && tok != ArgTokType::END) {
if (tok != ArgTokType::ARG) {
tok = mArgumentTokenizer.readTok();
continue;
}
if (!argument) {
ErrInvalidArgCount();
if (logError) ErrLog();
return false;
}
parseArg(*argument->data, mArgumentTokenizer.extractVal().read());
if (mError && logError) {
ErrLog();
return false;
}
argc++;
argument = argument->next;
tok = mArgumentTokenizer.readTok();
}
ualni idx = 0;
for (auto arg : mArgsOrder) {
if (idx >= argc) {
initDefault(*arg.data());
}
if (mError) {
if (logError) ErrLog();
return false;
}
idx++;
}
return true;
}
alni CommandLine::getInt(const String& id) const { auto& arg = getArg(id, Arg::INT); return arg.mInt.mVal; }
alnf CommandLine::getFloat(const String& id) const { auto& arg = getArg(id, Arg::FLOAT); return arg.mFloat.mVal; }
bool CommandLine::getBool(const String& id) const { auto& arg = getArg(id, Arg::BOOL); return arg.mBool.mFlag; }
const String& CommandLine::getString(const String& id) const { auto& arg = getArg(id, Arg::STR); return arg.mStr.mStr; }
const FileLocation& CommandLine::getFile(const String& id) const { auto& arg = getArg(id, Arg::FILE_IN); return arg.mFile.mFileLocation; }
CommandLine::Arg& CommandLine::getArg(const String& id, Arg::Type type) { CommandLine::Arg& CommandLine::getArg(const String& id, Arg::Type type) {
@ -174,6 +230,14 @@ CommandLine::Arg& CommandLine::getArg(const String& id, Arg::Type type) {
return *arg; return *arg;
} }
const CommandLine::Arg& CommandLine::getArg(const String& id, Arg::Type type) const {
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::ErrInvalidArgCount() { mError = { "Invalid Number Of Arguments Passed", nullptr }; }
void CommandLine::ErrInvalidArgSyntax(Arg* arg) { mError = { "Invalid Syntax Of Argument", arg }; } void CommandLine::ErrInvalidArgSyntax(Arg* arg) { mError = { "Invalid Syntax Of Argument", arg }; }
void CommandLine::ErrInvalidArgType(Arg* arg) { mError = { "Invalid Type Of Argument", arg }; } void CommandLine::ErrInvalidArgType(Arg* arg) { mError = { "Invalid Type Of Argument", arg }; }
@ -192,7 +256,7 @@ void CommandLine::ErrLog() {
} }
printf("\nArgument: %lli\n", idx); printf("\nArgument: %lli\n", idx);
ArgLog(*mError.mArg); logArg(*mError.mArg);
} }
printf("Error Description: %s. \n", mError.mDescr); printf("Error Description: %s. \n", mError.mDescr);
@ -201,7 +265,7 @@ void CommandLine::ErrLog() {
ualni idx = 0; ualni idx = 0;
for (auto arg : mArgsOrder) { for (auto arg : mArgsOrder) {
printf(" -- %lli -- \n", idx); printf(" -- %lli -- \n", idx);
ArgLog(*arg.data()); logArg(*arg.data());
idx++; idx++;
} }
@ -214,7 +278,7 @@ void CommandLine::ErrLog() {
printf(" File - else\n"); printf(" File - else\n");
} }
void CommandLine::ArgLog(Arg& arg) { void CommandLine::logArg(const Arg& arg) {
switch (arg.mType) { switch (arg.mType) {
case Arg::INT: { case Arg::INT: {
printf("Type : Int\n"); printf("Type : Int\n");
@ -222,7 +286,7 @@ void CommandLine::ArgLog(Arg& arg) {
if (arg.mOptional) { if (arg.mOptional) {
printf("Default : %lli\n", arg.mInt.mDefault); printf("Default : %lli\n", arg.mInt.mDefault);
} }
printf("Given : %lli\n", arg.mInt.mVal); printf("Value : %lli\n", arg.mInt.mVal);
} break; } break;
case Arg::FLOAT: { case Arg::FLOAT: {
printf("Type : Float\n"); printf("Type : Float\n");
@ -230,26 +294,27 @@ void CommandLine::ArgLog(Arg& arg) {
if (arg.mOptional) { if (arg.mOptional) {
printf("Default : %f\n", arg.mFloat.mDefault); printf("Default : %f\n", arg.mFloat.mDefault);
} }
printf("Given : %f\n", arg.mFloat.mVal); printf("Value : %f\n", arg.mFloat.mVal);
} break; } break;
case Arg::BOOL: { case Arg::BOOL: {
printf("Type : Bool\n"); printf("Type : Bool\n");
if (arg.mOptional) { if (arg.mOptional) {
printf("Default : %s\n", arg.mBool.mDefault ? "True" : "False"); printf("Default : %s\n", arg.mBool.mDefault ? "True" : "False");
} }
printf("Given : %s\n", arg.mBool.mFlag ? "True" : "False"); printf("Value : %s\n", arg.mBool.mFlag ? "True" : "False");
} break; } break;
case Arg::STR: { case Arg::STR: {
printf("Type : String\n"); printf("Type : String\n");
if (arg.mOptional) { if (arg.mOptional) {
printf("Default : %s\n", arg.mStr.mDefault.read()); printf("Default : %s\n", arg.mStr.mDefault.read());
} }
printf("Given : %s\n", arg.mStr.mStr.read()); printf("Value : %s\n", arg.mStr.mStr.read());
} break; } break;
case Arg::FILE_IN: { case Arg::FILE_IN: {
printf("Type : File Input\n"); printf("Type : File Input\n");
printf("Given Path : %s\n", arg.mFile.mFilepath.read()); printf("Value Path : %s\n", arg.mFile.mFileLocation.getLocation().read());
} break; } break;
default: ASSERT(false)
} }
} }
@ -305,7 +370,8 @@ void CommandLine::parseArg(Arg& arg, const char* src) {
ErrInvalidArgType(&arg); ErrInvalidArgType(&arg);
return; return;
} }
arg.mStr.mStr = val; arg.mStr.mStr.resize(val.size() - 2);
memCopy(arg.mStr.mStr.write(), val.read() + 1, val.size() - 2);
} break; } break;
case Arg::BOOL: { case Arg::BOOL: {
@ -313,7 +379,7 @@ void CommandLine::parseArg(Arg& arg, const char* src) {
ErrInvalidArgType(&arg); ErrInvalidArgType(&arg);
return; return;
} }
arg.mBool.mFlag = bool(val); arg.mBool.mFlag = tok == TokType::BOOL_TRUE;
} break; } break;
case Arg::FILE_IN: { case Arg::FILE_IN: {
@ -322,24 +388,23 @@ void CommandLine::parseArg(Arg& arg, const char* src) {
return; return;
} }
arg.mFile.mFilepath = val; arg.mFile.mFileLocation.setLocation(val);
/* if (!arg.mFile.mFileLocation.exists()) {
// TODO : replace
if (!File::exists(arg.mFile.mFilepath.cstr())) {
ErrFileNotExists(&arg); ErrFileNotExists(&arg);
return; return;
} }
arg.mFile.mFile.open(arg.mFile.mFilepath.cstr(), osfile_openflags::LOAD);
if (!arg.mFile.mFile.opened) {
ErrFileCouldNotOpen(&arg);
return;
}
*/
} break; } break;
default: ASSERT(false)
} }
arg.mIsPassed = true; arg.mIsPassed = true;
} }
void CommandLine::help() const {
printf("Arguments:\n");
for (auto arg : mArgsOrder) {
logArg(*arg.data());
}
}

View file

@ -0,0 +1,36 @@
#pragma once
#include "CommandLine.hpp"
#include "Map.hpp"
namespace tp {
class CmdLineInterpreter {
public:
typedef bool (*CommandFunction)(void* customData, const CommandLine& args);
private:
struct Command {
CommandLine* args;
CommandFunction function;
void* customData = nullptr;
};
Map<String, Command> mCommands;
String mName = "InterpName";
public:
CmdLineInterpreter();
~CmdLineInterpreter();
void setName(const String& name) { mName = name; }
[[nodiscard]] const String& getName() const { return mName; }
void help(const CommandLine& args) const;
void addCommand(const String& id, CommandLine* args, CommandFunction function, void* customData = nullptr);
void run();
bool processCommand(int1* command);
};
}

View file

@ -2,13 +2,11 @@
#include "Tokenizer.hpp" #include "Tokenizer.hpp"
#include "Map.hpp" #include "Map.hpp"
#include "LocalStorage.hpp"
namespace tp { namespace tp {
// TODO : replace extern ModuleManifest gModuleCommandLine;
class File {
};
struct CommandLine { struct CommandLine {
@ -35,13 +33,12 @@ namespace tp {
}; };
struct FileInputArg { struct FileInputArg {
String mFilepath; FileLocation mFileLocation;
File mFile;
}; };
struct Arg { struct Arg {
String mId; String mId;
enum Type { INT, FLOAT, BOOL, STR, FILE_IN } mType; enum Type { INT, FLOAT, BOOL, STR, FILE_IN, NONE } mType = NONE;
union { union {
IntArg mInt; IntArg mInt;
FloatArg mFloat; FloatArg mFloat;
@ -73,16 +70,23 @@ namespace tp {
CommandLine(const init_list<Arg>& args); CommandLine(const init_list<Arg>& args);
~CommandLine(); ~CommandLine();
bool parse(char argc, const char* argv[], bool logError = true, ualni skipArgs = 1); void resetError() { mError.mDescr = nullptr; }
alni getInt(const String& id); bool parse(char argc, const char* argv[], bool logError = true, ualni skipArgs = 1);
alnf getFloat(const String& id); bool parse(const char* args, bool logError = true);
bool getBool(const String& id);
const String& getString(const String& id); void help() const;
File& getFile(const String& id);
[[nodiscard]] alni getInt(const String& id) const;
[[nodiscard]] alnf getFloat(const String& id) const;
[[nodiscard]] bool getBool(const String& id) const;
[[nodiscard]] const String& getString(const String& id) const;
[[nodiscard]] const FileLocation& getFile(const String& id) const;
const CommandLine& operator=(const CommandLine&) = delete;
private: private:
enum class TokType { SPACE, INT, FLOAT, BOOL_FALSE, BOOL_TRUE, STR, NONE, FAILURE, END, } mType; enum class TokType { SPACE, INT, FLOAT, BOOL_FALSE, BOOL_TRUE, STR, NONE, FAILURE, END, };
typedef SimpleTokenizer<char, TokType, TokType::NONE, TokType::FAILURE, TokType::END> Tokenizer; typedef SimpleTokenizer<char, TokType, TokType::NONE, TokType::FAILURE, TokType::END> Tokenizer;
Tokenizer mTokenizer; Tokenizer mTokenizer;
@ -90,7 +94,12 @@ namespace tp {
List<Arg*> mArgsOrder; List<Arg*> mArgsOrder;
ualni mOptionals = 0; ualni mOptionals = 0;
enum class ArgTokType { SPACE, ARG, NONE, FAILURE, END, };
SimpleTokenizer<char, ArgTokType, ArgTokType::NONE, ArgTokType::FAILURE, ArgTokType::END> mArgumentTokenizer;
Arg& getArg(const String& id, Arg::Type type); Arg& getArg(const String& id, Arg::Type type);
[[nodiscard]] const Arg& getArg(const String& id, Arg::Type type) const;
void ErrInvalidArgCount(); void ErrInvalidArgCount();
void ErrInvalidArgSyntax(Arg* arg); void ErrInvalidArgSyntax(Arg* arg);
void ErrInvalidArgType(Arg* arg); void ErrInvalidArgType(Arg* arg);
@ -98,8 +107,8 @@ namespace tp {
void ErrFileCouldNotOpen(Arg* arg); void ErrFileCouldNotOpen(Arg* arg);
void ErrValNotinRange(Arg* arg); void ErrValNotinRange(Arg* arg);
void ErrLog(); void ErrLog();
static void ArgLog(Arg& arg); static void logArg(const Arg& arg);
static void initDefault(Arg& arg); static void initDefault(Arg& arg);
void parseArg(Arg& arg, const char* src); void parseArg(Arg& arg, const char* src);
}; };
}; }

View file

@ -0,0 +1,34 @@
#include "Tests.hpp"
#include "CmdLineInterpreter.hpp"
using namespace tp;
TEST_DEF_STATIC(Simple) {
struct TestStruct {
int val = 0;
static bool print(TestStruct* self, const CommandLine& args) {
printf("Test Val - %i\n", (int) args.getBool("bool"));
return false;
}
};
TestStruct self;
CmdLineInterpreter interp;
interp.addCommand("test", new CommandLine{ { "bool", CommandLine::Arg::BOOL } }, (CmdLineInterpreter::CommandFunction) TestStruct::print);
printf("test false\n", stdin);
printf("help\n", stdin);
printf("help test\n", stdin);
printf("exit\n", stdin);
interp.run();
}
TEST_DEF(Interpreter) {
//testSimple();
}

View file

@ -12,7 +12,8 @@ int main() {
return 1; return 1;
} }
testCommandLine(); // testCommandLine();
testInterpreter();
testModule.deinitialize(); testModule.deinitialize();
} }

View file

@ -5,3 +5,4 @@
#include "CommandLine.hpp" #include "CommandLine.hpp"
void testCommandLine(); void testCommandLine();
void testInterpreter();

View file

@ -12,6 +12,10 @@ add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME} PUBLIC ./public/) target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
target_link_libraries(${PROJECT_NAME} PUBLIC Strings) target_link_libraries(${PROJECT_NAME} PUBLIC Strings)
### -------------------------- Applications -------------------------- ###
add_subdirectory(applications)
### -------------------------- Tests -------------------------- ### ### -------------------------- Tests -------------------------- ###
enable_testing() enable_testing()
file(GLOB TEST_SOURCES "./tests/*.cpp") file(GLOB TEST_SOURCES "./tests/*.cpp")
@ -20,7 +24,3 @@ target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} Utils)
add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests) add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests)
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib) install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib)
# todo :remove
add_subdirectory(applications)

View file

@ -3,9 +3,15 @@ cmake_minimum_required(VERSION 3.2)
project(Applications) project(Applications)
### ---------------------- Applications --------------------- ### file(GLOB SOURCES_CLIENT ./Client.cpp)
add_executable(Server ./Server.cpp) file(GLOB SOURCES_SERVER ./Server.cpp)
add_executable(Client ./Client.cpp)
include_directories(Client ./../../Externals/asio/asio/include) set(ASIO_INCLUDE ./../../Externals/asio/asio/include)
include_directories(Server ./../../Externals/asio/asio/include)
add_executable(Client ${SOURCES_CLIENT})
include_directories(Client ${ASIO_INCLUDE})
#link_libraries(Client Storage CommandLine)
add_executable(Server ${SOURCES_SERVER})
include_directories(Server ${ASIO_INCLUDE})
#link_libraries(Server Storage CommandLine)

View file

@ -2,8 +2,21 @@
#include "SystemHandle.hpp" #include "SystemHandle.hpp"
#include <cstdio>
using namespace tp; using namespace tp;
bool FileLocation::exists() const {
FILE* file = fopen(mLocation.read(), "r");
if (file) {
// File exists, close it and return 1
fclose(file);
return true;
}
return false;
}
bool File::connect(const FileLocation& location, const FileConnectionType& connectionInfo) { bool File::connect(const FileLocation& location, const FileConnectionType& connectionInfo) {
DEBUG_ASSERT(!mStatus.isOpened()); DEBUG_ASSERT(!mStatus.isOpened());
if (mStatus.isOpened()) return false; if (mStatus.isOpened()) return false;

View file

@ -13,6 +13,7 @@ namespace tp {
explicit FileLocation(const String& location) : mLocation(location) {} explicit FileLocation(const String& location) : mLocation(location) {}
void setLocation(const String& location) { mLocation = location; } void setLocation(const String& location) { mLocation = location; }
[[nodiscard]] const String& getLocation() const { return mLocation; } [[nodiscard]] const String& getLocation() const { return mLocation; }
[[nodiscard]] bool exists() const;
}; };
class FileConnectionType { class FileConnectionType {

View file

@ -188,17 +188,20 @@ namespace tp {
explicit operator alni() { explicit operator alni() {
alni out; alni out;
return Logic::convertStringToValue(read(), out); Logic::convertStringToValue(read(), out);
return out;
} }
explicit operator alnf() { explicit operator alnf() {
alnf out; alnf out;
return Logic::convertStringToValue(read(), out); Logic::convertStringToValue(read(), out);
return out;
} }
explicit operator bool() { explicit operator bool() {
bool out; bool out;
return Logic::convertStringToValue(read(), out); Logic::convertStringToValue(read(), out);
return out;
} }
Index size() const { Index size() const {

View file

@ -168,7 +168,7 @@ namespace tp {
String extractVal() { String extractVal() {
auto crs = getCursorPrev(); auto crs = getCursorPrev();
String out; String out;
out.resize(mLastTokLen + 1); out.resize(mLastTokLen);
memCopy(out.write(), crs.str(), mLastTokLen); memCopy(out.write(), crs.str(), mLastTokLen);
return out; return out;
} }