From df4e7777de3c8ef981075dea72fdef8e5a85205f Mon Sep 17 00:00:00 2001 From: Pimpest <82343504+Pimpest@users.noreply.github.com> Date: Fri, 29 Aug 2025 00:02:45 +0200 Subject: [PATCH] Added error handling --- CmakeLists.txt | 1 + include/ast/ast_base.hpp | 25 +++++++++++- include/cmd/cmd.hpp | 2 +- include/cmd/cmd_base.hpp | 1 + include/cmd/cmd_rm.hpp | 2 +- include/cmd/cmd_touch.hpp | 2 +- include/cmd/cmd_tr.hpp | 2 +- include/cmd/cmd_truncate.hpp | 4 +- include/util/error.hpp | 76 ++++++++++++++++++++++++++++++++++++ include/util/text.hpp | 7 ++-- meson.build | 1 + src/ast/ast.cpp | 4 +- src/cmd/arg.cpp | 3 +- src/cmd/cmd_base.cpp | 3 +- src/lexer.cpp | 5 ++- src/util/error.cpp | 22 +++++++++++ util.hpp | 1 + 17 files changed, 144 insertions(+), 17 deletions(-) create mode 100644 include/util/error.hpp create mode 100644 src/util/error.cpp diff --git a/CmakeLists.txt b/CmakeLists.txt index 95dd909..d265aa1 100644 --- a/CmakeLists.txt +++ b/CmakeLists.txt @@ -15,6 +15,7 @@ set(FSH_SOURCE_FILES src/ast/ast_print.cpp src/util/input.cpp src/util/text.cpp + src/util/error.cpp src/cmd/arg.cpp src/cmd/cmd_base.cpp ) diff --git a/include/ast/ast_base.hpp b/include/ast/ast_base.hpp index c6fcbdc..494fc45 100644 --- a/include/ast/ast_base.hpp +++ b/include/ast/ast_base.hpp @@ -7,6 +7,7 @@ #include "lexer.hpp" #include "util/text.hpp" +#include "util/error.hpp" namespace fsh { @@ -67,9 +68,29 @@ namespace fsh { private: }; - class AstBuildError : public std::runtime_error { + class AstBuildError : public util::LazyError { public: - AstBuildError(std::string err) : std::runtime_error(err) {} + AstBuildError() {} }; + class AstUnexpectedTypeError : public AstBuildError { + public: + AstUnexpectedTypeError(Lexer::TokenType type) : type(type){}; + private: + Lexer::TokenType type; + + std::string build_msg() const noexcept override { + return (std::string) "Unexpected " + util::tokens[type]; + }; + }; + + class AstNoRedirectError : public AstBuildError { + private: + std::string build_msg() const noexcept override { + return "No redirects provided"; + }; + }; + + + } \ No newline at end of file diff --git a/include/cmd/cmd.hpp b/include/cmd/cmd.hpp index d4d636b..69950d6 100644 --- a/include/cmd/cmd.hpp +++ b/include/cmd/cmd.hpp @@ -27,7 +27,7 @@ namespace fsh { } Command& get(const std::string n) { - if (cmds.find(n) == cmds.end()) { throw std::runtime_error("Command not found " + n); } + if (cmds.find(n) == cmds.end()) { throw util::CmdNotFoundError(n); } return *(cmds[n]); } diff --git a/include/cmd/cmd_base.hpp b/include/cmd/cmd_base.hpp index 796ef10..5dcdb77 100644 --- a/include/cmd/cmd_base.hpp +++ b/include/cmd/cmd_base.hpp @@ -8,6 +8,7 @@ #include "ast/ast.hpp" #include "cmd/args/arg.hpp" +#include "util/error.hpp" namespace fsh { diff --git a/include/cmd/cmd_rm.hpp b/include/cmd/cmd_rm.hpp index 4b5c424..9155ffa 100644 --- a/include/cmd/cmd_rm.hpp +++ b/include/cmd/cmd_rm.hpp @@ -17,7 +17,7 @@ namespace fsh { virtual void run(std::istream& in, std::ostream& out, ArgManager& args) override { if(std::remove(args.get(0).value().c_str())) { - throw std::runtime_error("Could not delete file"); + throw util::FileDeleteError(); } } diff --git a/include/cmd/cmd_touch.hpp b/include/cmd/cmd_touch.hpp index 0b06322..909636f 100644 --- a/include/cmd/cmd_touch.hpp +++ b/include/cmd/cmd_touch.hpp @@ -15,7 +15,7 @@ namespace fsh { virtual void run(std::istream& in, std::ostream& out, ArgManager& args) override { auto name = *(args.get(0)); if (std::ifstream(name, std::ios::in).good() || !std::ofstream(name, std::ios::out).is_open()) { - throw std::runtime_error("File exists"); + throw util::FileExistsError(); } } }; diff --git a/include/cmd/cmd_tr.hpp b/include/cmd/cmd_tr.hpp index 3cb95fd..a7f9e81 100644 --- a/include/cmd/cmd_tr.hpp +++ b/include/cmd/cmd_tr.hpp @@ -33,7 +33,7 @@ namespace fsh { protected: std::string replace_all(std::string str,const std::string &what,const std::string &with) { - if(what == "") throw std::runtime_error("\"\" cannot be used as what"); + if(what == "") throw util::CmdError("What cannot be empty string"); unsigned long long pos = str.find(what); diff --git a/include/cmd/cmd_truncate.hpp b/include/cmd/cmd_truncate.hpp index 39bc925..05946ae 100644 --- a/include/cmd/cmd_truncate.hpp +++ b/include/cmd/cmd_truncate.hpp @@ -21,11 +21,11 @@ namespace fsh { std::string filename = args.get(0).value(); if(!std::ifstream(filename, std::ios_base::in)) { - throw std::runtime_error("File does not exist"); + throw util::FileExistsError(); } if(!std::ofstream(filename, std::ios_base::out)) { - throw std::runtime_error("Failed to truncate"); + throw util::CmdError("Failed to truncate"); } } diff --git a/include/util/error.hpp b/include/util/error.hpp new file mode 100644 index 0000000..67808eb --- /dev/null +++ b/include/util/error.hpp @@ -0,0 +1,76 @@ +#pragma once + +#include +#include + +namespace fsh::util { + // Generates msg only once what has been called + // Usefull for errors meant to be silently discarded frequently + class LazyError : public std::exception { + + public: + LazyError() {} + LazyError(const std::string& msg_) : msg_(msg_) {} + LazyError(const char* msg_) : msg_(msg_) {} + + const char* what() const noexcept override; + + protected: + mutable std::optional msg_; + + virtual std::string build_msg() const noexcept; + }; + + class CmdNotFoundError : public LazyError { + public: + CmdNotFoundError(std::string cmd_) : cmd_(cmd_) {} + + protected: + std::string build_msg() const noexcept override; + + private: + std::string cmd_; + }; + + class CmdError : public LazyError { + public: + CmdError(const std::string& msg_) : LazyError(msg_) {} + CmdError(const char* msg_) : LazyError(msg_) {} + }; + + class RuntimeError : public std::exception { + public: + const char* msg_; + + RuntimeError() = delete; + RuntimeError(const char* msg_) : msg_(msg_) {} + + const char* what() const noexcept override; + }; + + class FileDeleteError : public RuntimeError { + public: + FileDeleteError() : RuntimeError("Error deleting file") {} + }; + + class FileExistsError : public RuntimeError { + public: + FileExistsError() : RuntimeError("File already exists") {} + }; + + class FileOpenError : public RuntimeError { + public: + FileOpenError() : RuntimeError("Failed to open file") {} + }; + + class DoubleInputError : public RuntimeError { + public: + DoubleInputError() : RuntimeError("Tried to set input twice") {} + }; + + class LexerError : public LazyError { + public: + LexerError(const std::string& msg_) : LazyError(msg_) {} + LexerError(const char* msg_) : LazyError(msg_) {} + }; +} \ No newline at end of file diff --git a/include/util/text.hpp b/include/util/text.hpp index 9a34251..43f898b 100644 --- a/include/util/text.hpp +++ b/include/util/text.hpp @@ -4,6 +4,7 @@ #include #include #include +#include "error.hpp" // Razlicite pomocne funkcije za tekstove namespace fsh::util { @@ -25,19 +26,19 @@ namespace fsh::util { static inline std::ifstream input(const std::string& x) { std::ifstream in(x, std::ios::in); - if (!in) { throw std::runtime_error("Can't open file"); } + if (!in) { throw FileOpenError(); } return in; } static inline std::ofstream output(const std::string& x) { std::ofstream out(x, std::ios::out); - if (!out.is_open()) { throw std::runtime_error("Can't open file"); } + if (!out.is_open()) { throw FileOpenError(); } return out; } static inline std::ofstream output_append(const std::string& x) { std::ofstream out(x, std::ios::app | std::ios::out); - if (!out.is_open()) { throw std::runtime_error("Can't open file"); } + if (!out.is_open()) { throw FileOpenError(); } return out; } diff --git a/meson.build b/meson.build index 2d8d29c..d2161b7 100644 --- a/meson.build +++ b/meson.build @@ -14,6 +14,7 @@ fsh_source_files = [ 'src/ast/ast_print.cpp', 'src/util/input.cpp', 'src/util/text.cpp', + 'src/util/error.cpp', 'src/cmd/arg.cpp', 'src/cmd/cmd_base.cpp' ] diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index f4ef16f..bf896b4 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -21,7 +21,7 @@ namespace fsh { it++; return std::shared_ptr>(new TokenNode(value)); } else { - throw AstBuildError(std::string("Got unexcpected: ") + util::tokens[token_type]); + throw AstUnexpectedTypeError(token_type); } return nullptr; } @@ -56,7 +56,7 @@ namespace fsh { auto Rr = Optional(it); if (!Lr) Lr = Optional(it); - if (!Lr && !Rr) throw AstBuildError("No redirects where provided"); + if (!Lr && !Rr) throw AstNoRedirectError(); return std::shared_ptr(new RedirectsNode(Lr, Rr)); } diff --git a/src/cmd/arg.cpp b/src/cmd/arg.cpp index 393ea83..bc74512 100644 --- a/src/cmd/arg.cpp +++ b/src/cmd/arg.cpp @@ -1,5 +1,6 @@ #include "cmd/args/arg.hpp" #include "cmd/args/arg_generic.hpp" +#include "util/error.hpp" #include #include @@ -12,7 +13,7 @@ namespace fsh { str = std::stringstream(txt); } else { file = std::ifstream(txt, std::ios::in); - if (!*file) { throw std::runtime_error("Failed to open file"); } + if (!*file) { throw util::FileOpenError(); } } } diff --git a/src/cmd/cmd_base.cpp b/src/cmd/cmd_base.cpp index cff130f..e3f1c88 100644 --- a/src/cmd/cmd_base.cpp +++ b/src/cmd/cmd_base.cpp @@ -1,6 +1,7 @@ #include "cmd/cmd_base.hpp" #include "cmd/args/arg.hpp" #include "util/input.hpp" +#include "util/error.hpp" namespace fsh { @@ -13,7 +14,7 @@ namespace fsh { arg_factory.parse(args, vec, flag); if (arg_factory.ghas_input()) { if (&args.get_input(0) != &util::cin && &in != &util::cin) { - throw std::runtime_error("Tried to set input to command twice"); + throw util::DoubleInputError(); } if (&args.get_input(0) != &util::cin) run(args.get_input(0), out, args); else diff --git a/src/lexer.cpp b/src/lexer.cpp index cd2f8b1..48c15b4 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -4,6 +4,7 @@ #include #include "lexer.hpp" #include "util/text.hpp" +#include "util/error.hpp" namespace fsh { @@ -57,7 +58,7 @@ namespace fsh { LexerStateMachine::State LexerStateMachine::handle_string_literal() { const int c = parser.peek(); if (c == std::char_traits::eof()) { - throw std::runtime_error("Excpected \" before \\n"); + throw util::LexerError("Excpected \" before \\n"); } else if (c == '\"') { parser.consume(); return set_type(Lexer::TokenType::STRING_LITERAL); @@ -72,7 +73,7 @@ namespace fsh { if (util::contains(c, "\t><| ") || c == std::char_traits::eof()) { return set_type(type); } else if (c == '\"') { - throw std::runtime_error("Invalid \""); + throw util::LexerError("Invalid \""); } else { token += parser.consume(); return state; diff --git a/src/util/error.cpp b/src/util/error.cpp new file mode 100644 index 0000000..2dd9cc8 --- /dev/null +++ b/src/util/error.cpp @@ -0,0 +1,22 @@ +#include + +namespace fsh::util { + +const char* LazyError::what() const noexcept { + if(!msg_) { + msg_.emplace(build_msg()); + } + return msg_->c_str(); +} + +std::string CmdNotFoundError::build_msg() const noexcept { + return "Command not found " + cmd_; +} + +std::string LazyError::build_msg() const noexcept { return ""; } + +const char* RuntimeError::what() const noexcept { + return msg_; +} + +} diff --git a/util.hpp b/util.hpp index 356d84a..79f2059 100644 --- a/util.hpp +++ b/util.hpp @@ -14,6 +14,7 @@ set(FSH_SOURCE_FILES src/ast/ast_print.cpp src/util/input.cpp src/util/text.cpp + src/util/error.cpp src/cmd/arg.cpp src/cmd/cmd_base.cpp )