diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..2cff155 --- /dev/null +++ b/.clang-format @@ -0,0 +1,66 @@ +--- +Language: Cpp +BasedOnStyle: Mozilla + +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: true +AlignConsecutiveAssignments: true +AlignEscapedNewlines: Right +AlignOperands: false +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +ColumnLimit: 120 +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +IncludeBlocks: Preserve +IndentCaseLabels: true +IndentWidth: 4 +PointerAlignment: Left +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 4 +UseTab: Never + +AllowShortEnumsOnASingleLine: false + +BraceWrapping: + AfterEnum: false + +AlignConsecutiveDeclarations: + Enabled: true + +NamespaceIndentation: All \ No newline at end of file diff --git a/include/ast/ast.hpp b/include/ast/ast.hpp index b54b882..9f71542 100644 --- a/include/ast/ast.hpp +++ b/include/ast/ast.hpp @@ -1,18 +1,18 @@ /* * Fsh Grammar - * - * Command_Line ::= Command EOF + * + * Command_Line ::= Command EOF * | Command Pipeline_Command - * + * * Pipeline_Command ::= "|" Command EOF * | "|" Command "|" Pipeline_Command - * + * * Command ::= Command_Name [Flag_Opt] {Command_Argument} [Redirect] - * + * * Redirects ::= [LRedirect Word] RRedirect Word | [RRedirect Word] LRedirect Word - * + * * Command_Argument ::= Word | String_Literal - * + * * Command_Name ::= Word */ #pragma once @@ -21,26 +21,24 @@ #include "util/text.hpp" #include "ast/ast_base.hpp" -#include "ast/ast_token.hpp" #include "ast/ast_component.hpp" #include "ast/ast_executable.hpp" - +#include "ast/ast_token.hpp" namespace fsh { -class AstFactory { - public: + class AstFactory { + public: + // Generates an abstract syntax tree + static std::shared_ptr generate_ast(std::list& list) { + auto it = list.begin(); + return CommandLineNode::build(it); + } - // Generates an abstract syntax tree - static std::shared_ptr generate_ast(std::list& list) { - auto it = list.begin(); - return CommandLineNode::build(it); - } + private: + static AstFactory& get_factory(); + AstFactory() {} + AstFactory(const AstFactory&) = default; + }; - private: - static AstFactory& get_factory(); - AstFactory() {} - AstFactory(const AstFactory&) = default; -}; - -} \ No newline at end of file +} // namespace fsh \ No newline at end of file diff --git a/include/ast/ast_base.hpp b/include/ast/ast_base.hpp index 4ff8327..06edf4c 100644 --- a/include/ast/ast_base.hpp +++ b/include/ast/ast_base.hpp @@ -1,67 +1,73 @@ #pragma once -#include #include -#include #include +#include +#include #include "lexer.hpp" #include "util/text.hpp" namespace fsh { -enum NodeType { - COMMAND_LINE, - COMMAND, - PIPELINE_COMMAND, - LREDIRECTS, - RREDIRECTS, - REDIRECTS, - STRING_LITERAL, - COMMAND_ARGUMENT, - TOKEN -}; + enum NodeType { + COMMAND_LINE, + COMMAND, + PIPELINE_COMMAND, + LREDIRECTS, + RREDIRECTS, + REDIRECTS, + STRING_LITERAL, + COMMAND_ARGUMENT, + TOKEN + }; -class AstNode { - friend class std::shared_ptr; - public: - NodeType gtype() {return type;} - const std::string& gname() {return name;}; + class AstNode { + friend class std::shared_ptr; - protected: - using TokenType = Lexer::TokenType; - AstNode(NodeType type, std::string name) : type(type), name(name) {} + public: + NodeType gtype() { return type; } + const std::string& gname() { return name; }; + protected: + using TokenType = Lexer::TokenType; + AstNode(NodeType type, std::string name) : type(type), name(name) {} - template - static std::shared_ptr Optional(std::list::iterator& it, std::shared_ptr& node_ptr); - template - static std::shared_ptr Optional(std::list::iterator& it) { - std::shared_ptr node_ptr; - return Optional(it, node_ptr); - } - template - static std::shared_ptr Mandatory(std::list::iterator& it) {return T::build(it);} + template + static std::shared_ptr Optional(std::list::iterator& it, std::shared_ptr& node_ptr); - private: - AstNode(); - AstNode(const AstNode&); - NodeType type; - const std::string name; -}; + template + static std::shared_ptr Optional(std::list::iterator& it) { + std::shared_ptr node_ptr; + return Optional(it, node_ptr); + } -class ExecutableNode : public AstNode { - public: - virtual void print(int indent) {}; - virtual void execute(std::istream &in, std::ostream &out) {} - protected: - ExecutableNode(NodeType type, std::string Name) : AstNode(type, Name) {} - private: -}; + template + static std::shared_ptr Mandatory(std::list::iterator& it) { + return T::build(it); + } -class AstBuildError : public std::runtime_error { -public: - AstBuildError(std::string err) : std::runtime_error(err) {} -}; + private: + AstNode(); + AstNode(const AstNode&); + NodeType type; + const std::string name; + }; -} \ No newline at end of file + class ExecutableNode : public AstNode { + public: + virtual void print(int indent) {}; + virtual void execute(std::istream& in, std::ostream& out) {} + + protected: + ExecutableNode(NodeType type, std::string Name) : AstNode(type, Name) {} + + private: + }; + + class AstBuildError : public std::runtime_error { + public: + AstBuildError(std::string err) : std::runtime_error(err) {} + }; + +} // namespace fsh \ No newline at end of file diff --git a/include/ast/ast_component.hpp b/include/ast/ast_component.hpp index 8514e1d..7285079 100644 --- a/include/ast/ast_component.hpp +++ b/include/ast/ast_component.hpp @@ -11,80 +11,73 @@ namespace fsh { -class CommandArgumentNode : public AstNode { - public: - static std::shared_ptr build(std::list::iterator& it); + class CommandArgumentNode : public AstNode { + public: + static std::shared_ptr build(std::list::iterator& it); - Lexer::TokenType gtoken_type() {return type;} - std::string& gvalue() {return value;} + Lexer::TokenType gtoken_type() { return type; } + std::string& gvalue() { return value; } - protected: - CommandArgumentNode(TokenNode::shared word) - : AstNode(NodeType::COMMAND_ARGUMENT, "StringArgument"), - type(word->gtoken_type()), - value(word->gvalue()) {} - CommandArgumentNode(TokenNode::shared word) - : AstNode(NodeType::COMMAND_ARGUMENT, "StringArgument"), - type(word->gtoken_type()), - value(word->gvalue()) {} + protected: + CommandArgumentNode(TokenNode::shared word) : + AstNode(NodeType::COMMAND_ARGUMENT, "StringArgument"), type(word->gtoken_type()), value(word->gvalue()) {} - private: - Lexer::TokenType type; - std::string value; -}; + CommandArgumentNode(TokenNode::shared word) : + AstNode(NodeType::COMMAND_ARGUMENT, "StringArgument"), type(word->gtoken_type()), value(word->gvalue()) {} -class LRedirectNode : public AstNode { - public: - static std::shared_ptr build(std::list::iterator& it); + private: + Lexer::TokenType type; + std::string value; + }; - std::string gvalue() {return input;} + class LRedirectNode : public AstNode { + public: + static std::shared_ptr build(std::list::iterator& it); - protected: - LRedirectNode(TokenNode::shared, TokenNode::shared in) - : AstNode(NodeType::LREDIRECTS, "LRedirectNode"), - input(in->gvalue()) {} + std::string gvalue() { return input; } - private: - std::string input; -}; + protected: + LRedirectNode(TokenNode::shared, TokenNode::shared in) : + AstNode(NodeType::LREDIRECTS, "LRedirectNode"), input(in->gvalue()) {} + private: + std::string input; + }; -class RRedirectNode : public AstNode { - public: - static std::shared_ptr build(std::list::iterator& it); + class RRedirectNode : public AstNode { + public: + static std::shared_ptr build(std::list::iterator& it); - std::string gvalue() {return output;} - bool gappend() {return append;} + std::string gvalue() { return output; } + bool gappend() { return append; } - protected: - RRedirectNode(TokenNode::shared rr, TokenNode::shared out) - : AstNode(NodeType::RREDIRECTS, "LRedirectNode"), - output(out->gvalue()), - append(rr->gvalue().size()>1) {} + protected: + RRedirectNode(TokenNode::shared rr, TokenNode::shared out) : + AstNode(NodeType::RREDIRECTS, "LRedirectNode"), output(out->gvalue()), append(rr->gvalue().size() > 1) {} - private: - std::string output; - bool append; -}; + private: + std::string output; + bool append; + }; -class RedirectsNode : public AstNode { - public: - static std::shared_ptr build(std::list::iterator& it); + class RedirectsNode : public AstNode { + public: + static std::shared_ptr build(std::list::iterator& it); - std::optional ginput() {return input;} - std::optional goutput() {return output;} - bool gappend() {return append;} + std::optional ginput() { return input; } + std::optional goutput() { return output; } + bool gappend() { return append; } - protected: - RedirectsNode(std::shared_ptr in, std::shared_ptr out) - : AstNode(NodeType::REDIRECTS, "RedirectNode"), - input(util::mk_optional(in)), - output(util::mk_optional(out)) {if(out) append = out->gappend();} + protected: + RedirectsNode(std::shared_ptr in, std::shared_ptr out) : + AstNode(NodeType::REDIRECTS, "RedirectNode"), input(util::mk_optional(in)), output(util::mk_optional(out)) { + if (out) append = out->gappend(); + } - private: - std::optional input; - std::optional output; - bool append = false; -}; + private: + std::optional input; + std::optional output; + bool append = false; + }; -} \ No newline at end of file +} // namespace fsh \ No newline at end of file diff --git a/include/ast/ast_executable.hpp b/include/ast/ast_executable.hpp index eac63fc..b63eaae 100644 --- a/include/ast/ast_executable.hpp +++ b/include/ast/ast_executable.hpp @@ -1,78 +1,69 @@ #pragma once #include -#include #include +#include #include "ast/ast_base.hpp" -#include "ast/ast_token.hpp" #include "ast/ast_component.hpp" - +#include "ast/ast_token.hpp" #include "lexer.hpp" #include "util/text.hpp" namespace fsh { -class CommandNode : public ExecutableNode { - using ArgumentVec = std::vector >; - public: - static std::shared_ptr build(std::list::iterator& it); - virtual void print(int indent) override; - virtual void execute(std::istream &in, std::ostream &out) override; + class CommandNode : public ExecutableNode { + using ArgumentVec = std::vector >; - protected: - CommandNode( - TokenNode::shared cmd_name, - TokenNode::shared flag, - ArgumentVec args, - std::shared_ptr redirects - ) : ExecutableNode(NodeType::COMMAND, "CommandNode"), - cmd_name(cmd_name->gvalue()), - flag(util::mk_optional(flag)), - redirects(redirects), - args(args) {} - - private: - std::string cmd_name; - std::optional flag; - std::shared_ptr redirects; - ArgumentVec args; -}; + public: + static std::shared_ptr build(std::list::iterator& it); + virtual void print(int indent) override; + virtual void execute(std::istream& in, std::ostream& out) override; -class PipeLineNode : public ExecutableNode { - public: - static std::shared_ptr build(std::list::iterator& it); - virtual void print(int indent) override; - virtual void execute(std::istream &in, std::ostream &out) override; + protected: + CommandNode(TokenNode::shared cmd_name, + TokenNode::shared flag, + ArgumentVec args, + std::shared_ptr redirects) : + ExecutableNode(NodeType::COMMAND, "CommandNode"), cmd_name(cmd_name->gvalue()), flag(util::mk_optional(flag)), + redirects(redirects), args(args) {} - protected: - PipeLineNode(std::shared_ptr l, std::shared_ptr r) - : ExecutableNode(NodeType::PIPELINE_COMMAND, "PipeLine"), - l_command(l), - r_command(r) {} - - private: - std::shared_ptr l_command; - std::shared_ptr r_command; -}; + private: + std::string cmd_name; + std::optional flag; + std::shared_ptr redirects; + ArgumentVec args; + }; + class PipeLineNode : public ExecutableNode { + public: + static std::shared_ptr build(std::list::iterator& it); + virtual void print(int indent) override; + virtual void execute(std::istream& in, std::ostream& out) override; -class CommandLineNode : public ExecutableNode { - public: - static std::shared_ptr build(std::list::iterator& it); - virtual void print(int indent) override; - virtual void execute(std::istream &in, std::ostream &out) override; + protected: + PipeLineNode(std::shared_ptr l, std::shared_ptr r) : + ExecutableNode(NodeType::PIPELINE_COMMAND, "PipeLine"), l_command(l), r_command(r) {} - protected: - CommandLineNode(std::shared_ptr command, std::shared_ptr pipe = nullptr) - : ExecutableNode(NodeType::COMMAND_LINE, "CommandLine"), - command(command), - pipe(pipe) {} - - private: - std::shared_ptr command; - std::shared_ptr pipe; -}; + private: + std::shared_ptr l_command; + std::shared_ptr r_command; + }; -} \ No newline at end of file + class CommandLineNode : public ExecutableNode { + public: + static std::shared_ptr build(std::list::iterator& it); + virtual void print(int indent) override; + virtual void execute(std::istream& in, std::ostream& out) override; + + protected: + CommandLineNode(std::shared_ptr command, std::shared_ptr pipe = nullptr) : + ExecutableNode(NodeType::COMMAND_LINE, "CommandLine"), command(command), pipe(pipe) {} + + private: + std::shared_ptr command; + std::shared_ptr pipe; + }; + +} // namespace fsh \ No newline at end of file diff --git a/include/ast/ast_token.hpp b/include/ast/ast_token.hpp index 393a335..42f8dae 100644 --- a/include/ast/ast_token.hpp +++ b/include/ast/ast_token.hpp @@ -4,23 +4,21 @@ #include "ast/ast_base.hpp" #include "util/text.hpp" - namespace fsh { -template -class TokenNode : public AstNode { - public: - using shared = std::shared_ptr; - Lexer::TokenType gtoken_type() {return T;} - std::string& gvalue() {return value;}; - static std::shared_ptr build(std::list::iterator& it); + template + class TokenNode : public AstNode { + public: + using shared = std::shared_ptr; + Lexer::TokenType gtoken_type() { return T; } + std::string& gvalue() { return value; }; + static std::shared_ptr build(std::list::iterator& it); - protected: - TokenNode(std::string value) - : AstNode(NodeType::TOKEN, util::tokens[T]), value(value) {} + protected: + TokenNode(std::string value) : AstNode(NodeType::TOKEN, util::tokens[T]), value(value) {} - private: - std::string value; -}; + private: + std::string value; + }; } \ No newline at end of file diff --git a/include/cmd/args/arg.hpp b/include/cmd/args/arg.hpp index 0654a38..328a2cc 100644 --- a/include/cmd/args/arg.hpp +++ b/include/cmd/args/arg.hpp @@ -13,55 +13,53 @@ #include "cmd/args/arg_input.hpp" #include "cmd/args/arg_manager.hpp" -namespace fsh{ +namespace fsh { -class ArgFactory { -public: - using FlagNode = std::optional&; - using ArgNodes = std::vector >; - using BuildFunc = std::shared_ptr<_Argument> (*) (void); + class ArgFactory { + public: + using FlagNode = std::optional&; + using ArgNodes = std::vector >; + using BuildFunc = std::shared_ptr<_Argument> (*)(void); - struct ArgRule - { - BuildFunc build; - bool mandatory; - bool extends; + struct ArgRule { + BuildFunc build; + bool mandatory; + bool extends; + }; + + struct FlagRule { + BuildFunc build; + bool specialinput; //Not implemented + bool capturing; + bool extends; //Not implemented + }; + + void add_input_rule() { + has_input = true; + pos_arg_rules.push_back({ &_Argument::create, false, false }); + } + + template + void add_rule(bool mandatory, bool extends = false) { + pos_arg_rules.push_back({ &_Argument::create >, mandatory, extends }); + } + + template + void add_rule(const std::string name, bool capturing = false) { + flag_rules[name] = { _Argument::create >, false, capturing, false }; + } + + void parse(ArgManager& manager, ArgNodes& vec, FlagNode flag); + + bool ghas_input() { return has_input; } + + private: + std::vector pos_arg_rules; + std::unordered_map flag_rules; + bool has_input = false; + + void parse_flag(ArgManager& manager, FlagNode flag); + std::shared_ptr<_Argument> build_arg(BuildFunc build_func, const std::string& str); + std::shared_ptr<_Argument> build_arg(BuildFunc build_func, std::shared_ptr cmd_arg); }; - - struct FlagRule - { - BuildFunc build; - bool specialinput; //Not implemented - bool capturing; - bool extends; //Not implemented - }; - - - void add_input_rule() { - has_input = true; - pos_arg_rules.push_back({&_Argument::create, false, false}); - } - - template - void add_rule(bool mandatory, bool extends = false) { - pos_arg_rules.push_back({&_Argument::create >, mandatory, extends}); - } - - template - void add_rule(const std::string name, bool capturing = false) { - flag_rules[name] = {_Argument::create >, false, capturing, false}; - } - - void parse(ArgManager& manager, ArgNodes& vec, FlagNode flag); - bool ghas_input() {return has_input;} - -private: - std::vector pos_arg_rules; - std::unordered_map flag_rules; - bool has_input = false; - - void parse_flag(ArgManager& manager, FlagNode flag); - std::shared_ptr<_Argument> build_arg(BuildFunc build_func, const std::string& str); - std::shared_ptr<_Argument> build_arg(BuildFunc build_func, std::shared_ptr cmd_arg); -}; } \ No newline at end of file diff --git a/include/cmd/args/arg_base.hpp b/include/cmd/args/arg_base.hpp index bdc3640..39d3751 100644 --- a/include/cmd/args/arg_base.hpp +++ b/include/cmd/args/arg_base.hpp @@ -4,19 +4,20 @@ #include "ast/ast_component.hpp" -namespace fsh{ +namespace fsh { -class _Argument { -public: - virtual void svalue(const std::string& can, const Lexer::TokenType& type = Lexer::TokenType::FLAG) {} - virtual void svalue(std::shared_ptr can) { - svalue(can->gvalue(), can->gtoken_type()); - } + class _Argument { + public: + virtual void svalue(const std::string& can, const Lexer::TokenType& type = Lexer::TokenType::FLAG) {} + virtual void svalue(std::shared_ptr can) { svalue(can->gvalue(), can->gtoken_type()); } - template - static std::shared_ptr<_Argument> create() {return std::make_shared();} -protected: - _Argument(){} -}; + template + static std::shared_ptr<_Argument> create() { + return std::make_shared(); + } + + protected: + _Argument() {} + }; } \ No newline at end of file diff --git a/include/cmd/args/arg_generic.hpp b/include/cmd/args/arg_generic.hpp index 8c3ea72..fabfd09 100644 --- a/include/cmd/args/arg_generic.hpp +++ b/include/cmd/args/arg_generic.hpp @@ -5,35 +5,32 @@ #include "cmd/args/arg_base.hpp" #include "ast/ast_component.hpp" -namespace fsh{ +namespace fsh { -template -class Argument : public _Argument { -public: - static T& get(std::shared_ptr<_Argument> a) { - return std::dynamic_pointer_cast >(a)->gvalue(); - } + template + class Argument : public _Argument { + public: + static T& get(std::shared_ptr<_Argument> a) { return std::dynamic_pointer_cast >(a)->gvalue(); } - Argument() {} + Argument() {} - virtual void svalue(const std::string& val, const Lexer::TokenType& type) override; - virtual T& gvalue() {return value;} -private: - bool is_string_literal; // Currently no getter - T value; -}; + virtual void svalue(const std::string& val, const Lexer::TokenType& type) override; + virtual T& gvalue() { return value; } -template -void Argument::svalue(const std::string& val, const Lexer::TokenType& type) { - if constexpr (std::is_same_v) { - value = val; - } else { - std::stringstream ss_val(val); - if(!(ss_val >> value)) { - throw std::invalid_argument("Incorrect type"); + private: + bool is_string_literal; // Currently no getter + T value; + }; + + template + void Argument::svalue(const std::string& val, const Lexer::TokenType& type) { + if constexpr (std::is_same_v) { + value = val; + } else { + std::stringstream ss_val(val); + if (!(ss_val >> value)) { throw std::invalid_argument("Incorrect type"); } } + is_string_literal = type == Lexer::TokenType::STRING_LITERAL; } - is_string_literal = type == Lexer::TokenType::STRING_LITERAL; -} } \ No newline at end of file diff --git a/include/cmd/args/arg_input.hpp b/include/cmd/args/arg_input.hpp index c0352b4..346b2cb 100644 --- a/include/cmd/args/arg_input.hpp +++ b/include/cmd/args/arg_input.hpp @@ -5,22 +5,22 @@ #include "cmd/args/arg_base.hpp" -namespace fsh{ +namespace fsh { -class ArgInput : public _Argument { -public: - static std::istream& get(std::shared_ptr<_Argument> a) { - return std::dynamic_pointer_cast(a)->gvalue(); - } + class ArgInput : public _Argument { + public: + static std::istream& get(std::shared_ptr<_Argument> a) { + return std::dynamic_pointer_cast(a)->gvalue(); + } - ArgInput() {} + ArgInput() {} - virtual void svalue(const std::string& val, const Lexer::TokenType& type) override; - virtual std::istream& gvalue(); + virtual void svalue(const std::string& val, const Lexer::TokenType& type) override; + virtual std::istream& gvalue(); -private: - std::optional str; - std::optional file; -}; + private: + std::optional str; + std::optional file; + }; } \ No newline at end of file diff --git a/include/cmd/args/arg_manager.hpp b/include/cmd/args/arg_manager.hpp index 71f9c84..99d344f 100644 --- a/include/cmd/args/arg_manager.hpp +++ b/include/cmd/args/arg_manager.hpp @@ -3,46 +3,40 @@ #include "cmd/args/arg_input.hpp" #include "util/input.hpp" -namespace fsh{ +namespace fsh { -class ArgManager { - friend class ArgFactory; + class ArgManager { + friend class ArgFactory; - using PosArgs = std::vector >; - using FlagOpts = std::unordered_map< std::string ,std::shared_ptr<_Argument> >; + using PosArgs = std::vector >; + using FlagOpts = std::unordered_map >; -public: - ArgManager() {} + public: + ArgManager() {} + std::istream& get_input(const unsigned int id) { + if (id < pos_argument.size()) return ArgInput::get(pos_argument[id]); + return util::cin; + } - std::istream& get_input(const unsigned int id) { - if(id < pos_argument.size()) return ArgInput::get(pos_argument[id]); - return util::cin; - } + template + std::optional get(const unsigned int id) { + if ((unsigned int)id < pos_argument.size()) return Argument::get(pos_argument[id]); + return std::make_optional(); + } - template - std::optional get(const unsigned int id) { - if((unsigned int) id < pos_argument.size()) return Argument::get(pos_argument[id]); - return std::make_optional(); - } - template - std::optional get(const std::string& id) { - if(flags.find(id) != flags.end()) return Argument::get(flags[id]); - return std::make_optional(); - } - bool get(const std::string& id) { - return flags.find(id) != flags.end(); - } + template + std::optional get(const std::string& id) { + if (flags.find(id) != flags.end()) return Argument::get(flags[id]); + return std::make_optional(); + } + bool get(const std::string& id) { return flags.find(id) != flags.end(); } - void push_arg(std::shared_ptr<_Argument> arg) { - pos_argument.push_back(arg); - } - void push_flag(const std::string &id,std::shared_ptr<_Argument> arg = nullptr) { - flags[id] = arg; - } - - private: - PosArgs pos_argument; - FlagOpts flags; -}; + void push_arg(std::shared_ptr<_Argument> arg) { pos_argument.push_back(arg); } + void push_flag(const std::string& id, std::shared_ptr<_Argument> arg = nullptr) { flags[id] = arg; } + + private: + PosArgs pos_argument; + FlagOpts flags; + }; } \ No newline at end of file diff --git a/include/cmd/cmd.hpp b/include/cmd/cmd.hpp index 4352eb9..ddcd5aa 100644 --- a/include/cmd/cmd.hpp +++ b/include/cmd/cmd.hpp @@ -3,51 +3,41 @@ #include #include "cmd/cmd_base.hpp" -#include "cmd/cmd_wc.hpp" -#include "cmd/cmd_time.hpp" #include "cmd/cmd_date.hpp" #include "cmd/cmd_echo.hpp" -#include "cmd/cmd_touch.hpp" #include "cmd/cmd_misc.hpp" +#include "cmd/cmd_time.hpp" +#include "cmd/cmd_touch.hpp" +#include "cmd/cmd_wc.hpp" -namespace fsh -{ +namespace fsh { -class CommandRegistry { - - public: - - static CommandRegistry& instance() { - static CommandRegistry cmd_registry; - return cmd_registry; - } - - Command& get(const std::string n) { - if(cmds.find(n) == cmds.end()) { - throw std::runtime_error("Command not found"); + class CommandRegistry { + public: + static CommandRegistry& instance() { + static CommandRegistry cmd_registry; + return cmd_registry; } - return *(cmds[n]); - } - Command& operator[](const std::string n) { - return get(n); - } + Command& get(const std::string n) { + if (cmds.find(n) == cmds.end()) { throw std::runtime_error("Command not found"); } + return *(cmds[n]); + } + Command& operator[](const std::string n) { return get(n); } - private: - CommandRegistry() { - cmds["wc" ] = Command::register_cmd(); - cmds["date" ] = Command::register_cmd(); - cmds["time" ] = Command::register_cmd(); - cmds["echo" ] = Command::register_cmd(); - cmds["exit" ] = Command::register_cmd(); - cmds["touch"] = Command::register_cmd(); - cmds["debug"] = Command::register_cmd(); - } + private: + CommandRegistry() { + cmds["wc"] = Command::register_cmd(); + cmds["date"] = Command::register_cmd(); + cmds["time"] = Command::register_cmd(); + cmds["echo"] = Command::register_cmd(); + cmds["exit"] = Command::register_cmd(); + cmds["touch"] = Command::register_cmd(); + cmds["debug"] = Command::register_cmd(); + } - std::unordered_map > cmds; + std::unordered_map > cmds; + }; -}; - - -} \ No newline at end of file +} // namespace fsh \ No newline at end of file diff --git a/include/cmd/cmd_base.hpp b/include/cmd/cmd_base.hpp index a02bb8b..a28a677 100644 --- a/include/cmd/cmd_base.hpp +++ b/include/cmd/cmd_base.hpp @@ -9,33 +9,30 @@ #include "ast/ast.hpp" #include "cmd/args/arg.hpp" -namespace fsh{ +namespace fsh { -class Command { + class Command { - using FlagNode = std::optional&; - using ArgNodes = std::vector >; + using FlagNode = std::optional&; + using ArgNodes = std::vector >; - public: - void execute(FlagNode flag, ArgNodes& vec, std::istream& in, std::ostream& out); + public: + void execute(FlagNode flag, ArgNodes& vec, std::istream& in, std::ostream& out); - template - static std::unique_ptr register_cmd() { - std::unique_ptr cmd = std::make_unique(); - cmd->register_flags(); - return cmd; - } + template + static std::unique_ptr register_cmd() { + std::unique_ptr cmd = std::make_unique(); + cmd->register_flags(); + return cmd; + } - protected: - - virtual void register_flags() {}; - virtual void run(std::istream& in, std::ostream& out, ArgManager& args) {}; - - ArgFactory& get_factory() {return arg_factory;} - - private: - ArgFactory arg_factory; -}; + protected: + virtual void register_flags() {}; + virtual void run(std::istream& in, std::ostream& out, ArgManager& args) {}; + ArgFactory& get_factory() { return arg_factory; } + private: + ArgFactory arg_factory; + }; } \ No newline at end of file diff --git a/include/cmd/cmd_date.hpp b/include/cmd/cmd_date.hpp index ebe4f9a..885b4b8 100644 --- a/include/cmd/cmd_date.hpp +++ b/include/cmd/cmd_date.hpp @@ -5,17 +5,15 @@ #include "cmd/cmd_base.hpp" - namespace fsh { -class CmdDate : public Command { - - protected: - virtual void run(std::istream& in, std::ostream& out, ArgManager& args) override { - std::time_t time = std::time(nullptr); - out << std::asctime(std::localtime(&time)); - } + class CmdDate : public Command { -}; + protected: + virtual void run(std::istream& in, std::ostream& out, ArgManager& args) override { + std::time_t time = std::time(nullptr); + out << std::asctime(std::localtime(&time)); + } + }; } \ No newline at end of file diff --git a/include/cmd/cmd_echo.hpp b/include/cmd/cmd_echo.hpp index a7d891f..93e8742 100644 --- a/include/cmd/cmd_echo.hpp +++ b/include/cmd/cmd_echo.hpp @@ -4,23 +4,20 @@ namespace fsh { -class CmdEcho : public Command { - - protected: - virtual void register_flags() override { - ArgFactory& factory = get_factory(); - factory.add_input_rule(); - } + class CmdEcho : public Command { - virtual void run(std::istream& in, std::ostream& out, ArgManager& args) override { - std::string s; - std::string o; - while(getline(in, s)) { - o += s + "\n"; - } - out << o; - } + protected: + virtual void register_flags() override { + ArgFactory& factory = get_factory(); + factory.add_input_rule(); + } -}; + virtual void run(std::istream& in, std::ostream& out, ArgManager& args) override { + std::string s; + std::string o; + while (getline(in, s)) { o += s + "\n"; } + out << o; + } + }; } \ No newline at end of file diff --git a/include/cmd/cmd_misc.hpp b/include/cmd/cmd_misc.hpp index f3adf94..1c5e386 100644 --- a/include/cmd/cmd_misc.hpp +++ b/include/cmd/cmd_misc.hpp @@ -6,38 +6,33 @@ #include "fsh.hpp" #include "ast/ast.hpp" - namespace fsh { -class CmdExit : public Command { - - protected: - virtual void run(std::istream& in, std::ostream& out, ArgManager& args) override { - fsh::instance().environment["EXITING"] = "1"; - } + class CmdExit : public Command { -}; + protected: + virtual void run(std::istream& in, std::ostream& out, ArgManager& args) override { + fsh::instance().environment["EXITING"] = "1"; + } + }; -class CmdPrintTree : public Command { - - protected: + class CmdPrintTree : public Command { - virtual void register_flags() override { - auto& factory = get_factory(); - factory.add_input_rule(); - } + protected: + virtual void register_flags() override { + auto& factory = get_factory(); + factory.add_input_rule(); + } + virtual void run(std::istream& in, std::ostream& out, ArgManager& args) override { + std::string line; + std::getline(in, line); - virtual void run(std::istream& in, std::ostream& out, ArgManager& args) override { - std::string line; - std::getline(in, line); + auto tokens = Lexer::process(line); + auto ast = AstFactory::generate_ast(tokens); - auto tokens = Lexer::process(line); - auto ast = AstFactory::generate_ast(tokens); - - ast->print(0); - } - -}; + ast->print(0); + } + }; } \ No newline at end of file diff --git a/include/cmd/cmd_time.hpp b/include/cmd/cmd_time.hpp index dc55310..521696f 100644 --- a/include/cmd/cmd_time.hpp +++ b/include/cmd/cmd_time.hpp @@ -7,14 +7,13 @@ namespace fsh { -class CmdTime : public Command { - - protected: - virtual void run(std::istream& in, std::ostream& out, ArgManager& args) override { - std::time_t time = std::time(nullptr); - out << std::put_time(std::localtime(&time), "%T%n"); - } + class CmdTime : public Command { -}; + protected: + virtual void run(std::istream& in, std::ostream& out, ArgManager& args) override { + std::time_t time = std::time(nullptr); + out << std::put_time(std::localtime(&time), "%T%n"); + } + }; } \ No newline at end of file diff --git a/include/cmd/cmd_touch.hpp b/include/cmd/cmd_touch.hpp index 4c9ad03..0b06322 100644 --- a/include/cmd/cmd_touch.hpp +++ b/include/cmd/cmd_touch.hpp @@ -4,21 +4,20 @@ namespace fsh { -class CmdTouch : public Command { - - protected: - virtual void register_flags() override { - ArgFactory& factory = get_factory(); - factory.add_rule(true); - } + class CmdTouch : public Command { - 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"); + protected: + virtual void register_flags() override { + ArgFactory& factory = get_factory(); + factory.add_rule(true); } - } -}; + 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"); + } + } + }; } \ No newline at end of file diff --git a/include/cmd/cmd_wc.hpp b/include/cmd/cmd_wc.hpp index 38f92a4..19ecd73 100644 --- a/include/cmd/cmd_wc.hpp +++ b/include/cmd/cmd_wc.hpp @@ -4,45 +4,43 @@ namespace fsh { -class CmdWc : public Command { - - protected: - virtual void register_flags() override { - ArgFactory& factory = get_factory(); - factory.add_input_rule(); - factory.add_rule("-c"); - factory.add_rule("-w"); - } + class CmdWc : public Command { - int count_chars(std::istream& in) { - int i = 0; - char c; - while(in.get(c)) i++; - return i; - } - - int count_words(std::istream& in) { - int i = 0; - char c; - bool prev_space = true; - while(in.get(c)) { - i+= prev_space & !std::isspace(c); - prev_space = std::isspace(c); + protected: + virtual void register_flags() override { + ArgFactory& factory = get_factory(); + factory.add_input_rule(); + factory.add_rule("-c"); + factory.add_rule("-w"); } - return i; - } - virtual void run(std::istream& in, std::ostream& out, ArgManager& args) override { - bool prev_space = true; - if(args.get("-c")) { - out << count_chars(in) << "\n"; - } else if (args.get("-w")) { - out << count_words(in) << "\n"; - } else { - throw std::invalid_argument("Didn't provide flag"); + int count_chars(std::istream& in) { + int i = 0; + char c; + while (in.get(c)) i++; + return i; } - } -}; + int count_words(std::istream& in) { + int i = 0; + char c; + bool prev_space = true; + while (in.get(c)) { + i += prev_space & !std::isspace(c); + prev_space = std::isspace(c); + } + return i; + } + + virtual void run(std::istream& in, std::ostream& out, ArgManager& args) override { + if (args.get("-c")) { + out << count_chars(in) << "\n"; + } else if (args.get("-w")) { + out << count_words(in) << "\n"; + } else { + throw std::invalid_argument("Didn't provide flag"); + } + } + }; } \ No newline at end of file diff --git a/include/fsh.hpp b/include/fsh.hpp index 5ad36df..61546a5 100644 --- a/include/fsh.hpp +++ b/include/fsh.hpp @@ -6,28 +6,25 @@ #include "ast/ast.hpp" #include "util/input.hpp" -namespace fsh -{ - -class fsh { -public: - std::unordered_map environment; +namespace fsh { - static fsh& instance() { - static fsh f; - return f; - } + class fsh { + public: + std::unordered_map environment; - void run_line(std::string &line, std::istream &in = util::cin, std::ostream &out = std::cout); - void run(); + static fsh& instance() { + static fsh f; + return f; + } -private: - fsh(){ - environment["PROMPT"] = "$"; - environment["QUIT"] = ""; - } - -}; + void run_line(std::string& line, std::istream& in = util::cin, std::ostream& out = std::cout); + void run(); + private: + fsh() { + environment["PROMPT"] = "$"; + environment["QUIT"] = ""; + } + }; } \ No newline at end of file diff --git a/include/lexer.hpp b/include/lexer.hpp index ee1b00b..5dcfb46 100644 --- a/include/lexer.hpp +++ b/include/lexer.hpp @@ -1,92 +1,90 @@ #pragma once -#include #include +#include #include namespace fsh { -class Lexer { -public: - enum TokenType { - WHITESPACE, - WORD, - STRING_LITERAL, - OPT, - FLAG, - LREDIRECTION, - RREDIRECTION, - PIPE, - END_OF_STREAM + class Lexer { + public: + enum TokenType { + WHITESPACE, + WORD, + STRING_LITERAL, + OPT, + FLAG, + LREDIRECTION, + RREDIRECTION, + PIPE, + + END_OF_STREAM + }; + + using Token = std::pair; + + int peek(); + int consume(); + Token next_token(); + + static std::list process(std::string line); + + private: + std::string workspace; + unsigned int idx; + + Lexer(std::string str) : workspace(str), idx(0) {} + }; + using Token = Lexer::Token; + + class LexerStateMachine { + friend class Lexer; + + private: + enum State { + START, + REDIRECT, + FLAG_OPT, + POTENTIAL_WORD, + STRING_LITERAL, + STATE_COUNT, + + END + }; + + using StateHandler = std::function; + + Lexer& parser; + std::string token; + Lexer::TokenType type; + std::vector state_handlers; + + LexerStateMachine(Lexer& parser) : + parser(parser), token(""), type(Lexer::TokenType::WHITESPACE), state_handlers(STATE_COUNT) { + state_handlers[State::START] = std::bind(&LexerStateMachine::handle_start, this); + state_handlers[State::REDIRECT] = std::bind(&LexerStateMachine::handle_redirect, this); + state_handlers[State::FLAG_OPT] = std::bind(&LexerStateMachine::handle_flag_opt, this); + state_handlers[State::POTENTIAL_WORD] = std::bind(&LexerStateMachine::handle_potential_word, this); + state_handlers[State::STRING_LITERAL] = std::bind(&LexerStateMachine::handle_string_literal, this); + } + + State set_type(Lexer::TokenType type, State state); + + Token run(); + + State handle_start(); + State handle_redirect(); + State handle_flag_opt(); + State handle_potential_word(); + State handle_string_literal(); + + State word_like_handler(Lexer::TokenType type, State state); }; - using Token = std::pair; - - int peek(); - int consume(); - Token next_token(); - - static std::list process(std::string line); - -private: - std::string workspace; - unsigned int idx; - - Lexer(std::string str) : workspace(str), idx(0) {} - -}; -using Token = Lexer::Token; - - - -class LexerStateMachine { - friend class Lexer; -private: - enum State { - START, - REDIRECT, - FLAG_OPT, - POTENTIAL_WORD, - STRING_LITERAL, - STATE_COUNT, - - END - }; - - using StateHandler = std::function; - - Lexer& parser; - std::string token; - Lexer::TokenType type; - std::vector state_handlers; - - LexerStateMachine(Lexer& parser) : parser(parser), token(""), type(Lexer::TokenType::WHITESPACE), state_handlers(STATE_COUNT) { - state_handlers[State::START] = std::bind(&LexerStateMachine::handle_start , this); - state_handlers[State::REDIRECT ] = std::bind(&LexerStateMachine::handle_redirect , this); - state_handlers[State::FLAG_OPT ] = std::bind(&LexerStateMachine::handle_flag_opt , this); - state_handlers[State::POTENTIAL_WORD] = std::bind(&LexerStateMachine::handle_potential_word, this); - state_handlers[State::STRING_LITERAL] = std::bind(&LexerStateMachine::handle_string_literal, this); - } - - State set_type(Lexer::TokenType type, State state); - - Token run(); - - State handle_start(); - State handle_redirect(); - State handle_flag_opt(); - State handle_potential_word(); - State handle_string_literal(); - - State word_like_handler(Lexer::TokenType type, State state); -}; - - inline LexerStateMachine::State LexerStateMachine::set_type( - Lexer::TokenType type = Lexer::TokenType::WHITESPACE, - State state = State::END - ){ + inline LexerStateMachine::State LexerStateMachine::set_type(Lexer::TokenType type = Lexer::TokenType::WHITESPACE, + State state = State::END) { this->type = type; return state; } -} \ No newline at end of file +} // namespace fsh \ No newline at end of file diff --git a/include/util/input.hpp b/include/util/input.hpp index 971e250..fbedca7 100644 --- a/include/util/input.hpp +++ b/include/util/input.hpp @@ -7,63 +7,59 @@ #include #endif +namespace fsh::util { -namespace fsh::util -{ - -/** + /** * Applies some settings to terminals to fix some inconsistencies between * windows and linux terminals * * * **Windows** : it enables using ansii characters * * **Linux** : it enables [Noncanonical Mode](https://www.gnu.org/software/libc/manual/html_node/Noncanonical-Input.html) */ -void prepTerminal(); + void prepTerminal(); #ifndef _WIN32 -class CursorIStreamBuffer : public std::streambuf { -public: - enum EscapeSequence { - FAILED, - POSSIBLE, - LEFT_ARROW, - RIGHT_ARROW + class CursorIStreamBuffer : public std::streambuf { + public: + enum EscapeSequence { + FAILED, + POSSIBLE, + LEFT_ARROW, + RIGHT_ARROW + }; + + CursorIStreamBuffer() : valid(0), read_chars(0), cursor_pos(0), buffer() { setg(buffer, buffer, buffer); } + + protected: + int underflow() override; + // int uflow() override; + // std::streamsize xsgetn(char* s, std::streamsize n) override; + + private: + int valid; + EscapeSequence testForEscapeCodes(char chr); + + int readInputToNewLine(); + std::string input; + int read_chars; + int cursor_pos; + char buffer[32]; }; - CursorIStreamBuffer() : valid(0), read_chars(0), cursor_pos(0), buffer() { - setg(buffer,buffer,buffer); - } -protected: - int underflow() override; - // int uflow() override; - // std::streamsize xsgetn(char* s, std::streamsize n) override; + class CursorIStream : public std::istream { + public: + CursorIStream() : std::istream(&buffer) {} -private: - int valid; - EscapeSequence testForEscapeCodes(char chr); - - int readInputToNewLine(); - std::string input; - int read_chars; - int cursor_pos; - char buffer[32]; -}; - -class CursorIStream : public std::istream { -public: - CursorIStream() : std::istream(&buffer) {} -private: - CursorIStreamBuffer buffer; -}; -extern CursorIStream cin; + private: + CursorIStreamBuffer buffer; + }; + extern CursorIStream cin; #else -extern std::istream& cin; - -#endif - + extern std::istream& cin; +#endif } \ No newline at end of file diff --git a/include/util/text.hpp b/include/util/text.hpp index d6b58af..907c471 100644 --- a/include/util/text.hpp +++ b/include/util/text.hpp @@ -7,49 +7,37 @@ namespace fsh::util { + extern const char* tokens[]; -extern const char* tokens[]; - -template -static inline std::optional mk_optional(T t){ - if(t){ - return t->gvalue(); + template + static inline std::optional mk_optional(T t) { + if (t) { return t->gvalue(); } + return std::optional(); } - return std::optional(); -} -static inline bool contains(char x, std::string text) { - for (const auto& chr : text){ - if(chr == x) return 1; + static inline bool contains(char x, std::string text) { + for (const auto& chr : text) { + if (chr == x) return 1; + } + return 0; + }; + + 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"); } + return in; } - return 0; -}; - -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"); + 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"); } + return out; } - 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"); + 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"); } + return out; } - 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"); - } - return out; -} } \ No newline at end of file diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 859fd53..f4ef16f 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -2,91 +2,95 @@ namespace fsh { -template -std::shared_ptr AstNode::Optional(std::list::iterator& it, std::shared_ptr& node_ptr) { - auto it_ = it; - try{ - node_ptr = T::build(it); - return node_ptr; - } catch(AstBuildError &e){ - it = it_; + template + std::shared_ptr AstNode::Optional(std::list::iterator& it, std::shared_ptr& node_ptr) { + auto it_ = it; + try { + node_ptr = T::build(it); + return node_ptr; + } catch (AstBuildError& e) { + it = it_; + return nullptr; + } + } + + template + typename TokenNode::shared TokenNode::build(std::list::iterator& it) { + auto& [token_type, value] = *it; + if (T == token_type) { + it++; + return std::shared_ptr>(new TokenNode(value)); + } else { + throw AstBuildError(std::string("Got unexcpected: ") + util::tokens[token_type]); + } return nullptr; } -} -template -typename TokenNode::shared TokenNode::build(std::list::iterator& it){ - auto& [token_type,value] = *it; - if(T == token_type) { - it++; - return std::shared_ptr>(new TokenNode(value)); - } else { - throw AstBuildError(std::string("Got unexcpected: ") + util::tokens[token_type]); + std::shared_ptr CommandArgumentNode::build(std::list::iterator& it) { + auto word = Optional>(it); + + if (word) return std::shared_ptr(new CommandArgumentNode(word)); + + auto sl = Mandatory>(it); + return std::shared_ptr(new CommandArgumentNode(sl)); } - return nullptr; -} -std::shared_ptr CommandArgumentNode::build(std::list::iterator& it) { - auto word = Optional >(it); - if(word) { - return std::shared_ptr(new CommandArgumentNode(word)); + std::shared_ptr RRedirectNode::build(std::list::iterator& it) { + auto Rr = Mandatory>(it); + auto output = Mandatory>(it); + + return std::shared_ptr(new RRedirectNode(Rr, output)); } - auto sl = Mandatory >(it); - return std::shared_ptr(new CommandArgumentNode(sl)); -} -std::shared_ptr RRedirectNode::build(std::list::iterator& it) { - auto Rr = Mandatory >(it); - auto output = Mandatory >(it); - return std::shared_ptr(new RRedirectNode(Rr, output)); -} + std::shared_ptr LRedirectNode::build(std::list::iterator& it) { + auto Lr = Mandatory>(it); + auto output = Mandatory>(it); -std::shared_ptr LRedirectNode::build(std::list::iterator& it) { - auto Lr = Mandatory >(it); - auto output = Mandatory >(it); - return std::shared_ptr(new LRedirectNode(Lr, output)); -} - -std::shared_ptr RedirectsNode::build(std::list::iterator& it) { - TokenNode::shared input,output; - auto Lr = Optional(it); - auto Rr = Optional(it); - if(!Lr) Lr = Optional(it); - if(!Lr && !Rr) throw AstBuildError("No redirects where provided"); - return std::shared_ptr(new RedirectsNode(Lr,Rr)); -} - -std::shared_ptr CommandNode::build(std::list::iterator& it) { - auto cmd_name = Mandatory >(it); - auto cmd_opts = Optional >(it); - ArgumentVec argv; - auto cmd_argument = Optional(it); - while(cmd_argument){ - argv.push_back(cmd_argument); - cmd_argument = Optional(it); + return std::shared_ptr(new LRedirectNode(Lr, output)); } - auto cmd_redr = Optional(it); - return std::shared_ptr(new CommandNode(cmd_name,cmd_opts, argv, cmd_redr)); -} + std::shared_ptr RedirectsNode::build(std::list::iterator& it) { + TokenNode::shared input, output; -std::shared_ptr PipeLineNode::build(std::list::iterator& it) { - Mandatory >(it); - auto l_cmd = Mandatory(it); - if(Optional >(it)) { - return std::shared_ptr(new PipeLineNode(l_cmd, nullptr)); + auto Lr = Optional(it); + auto Rr = Optional(it); + + if (!Lr) Lr = Optional(it); + if (!Lr && !Rr) throw AstBuildError("No redirects where provided"); + return std::shared_ptr(new RedirectsNode(Lr, Rr)); } - auto r_cmd = Mandatory(it); - return std::shared_ptr(new PipeLineNode(l_cmd, r_cmd)); -} -std::shared_ptr CommandLineNode::build(std::list::iterator& it) { - auto cmd = Mandatory(it); - if(Optional >(it)){ - return std::shared_ptr(new CommandLineNode(cmd)); + std::shared_ptr CommandNode::build(std::list::iterator& it) { + auto cmd_name = Mandatory>(it); + auto cmd_opts = Optional>(it); + auto cmd_argument = Optional(it); + ArgumentVec argv; + + while (cmd_argument) { + argv.push_back(cmd_argument); + cmd_argument = Optional(it); + } + auto cmd_redr = Optional(it); + return std::shared_ptr(new CommandNode(cmd_name, cmd_opts, argv, cmd_redr)); + } + + std::shared_ptr PipeLineNode::build(std::list::iterator& it) { + Mandatory>(it); + auto l_cmd = Mandatory(it); + if (Optional>(it)) { + return std::shared_ptr(new PipeLineNode(l_cmd, nullptr)); + } + auto r_cmd = Mandatory(it); + return std::shared_ptr(new PipeLineNode(l_cmd, r_cmd)); + } + + std::shared_ptr CommandLineNode::build(std::list::iterator& it) { + auto cmd = Mandatory(it); + if (Optional>(it)) { + return std::shared_ptr(new CommandLineNode(cmd)); + } + auto pipe = Mandatory(it); + return std::shared_ptr(new CommandLineNode(cmd, pipe)); } - auto pipe = Mandatory(it); - return std::shared_ptr(new CommandLineNode(cmd, pipe)); -} } \ No newline at end of file diff --git a/src/ast/ast_exec.cpp b/src/ast/ast_exec.cpp index 62aa7ee..8bf7345 100644 --- a/src/ast/ast_exec.cpp +++ b/src/ast/ast_exec.cpp @@ -5,51 +5,48 @@ namespace fsh { -void CommandNode::execute(std::istream& in, std::ostream& out) { - std::istream* i = ∈ - std::ostream* o = &out; - std::ifstream f_i; - std::ofstream f_o; - if(redirects){ - if(redirects->goutput()) { - if(redirects->gappend()) - f_o = util::output_append(*(redirects->goutput())); - else - f_o = util::output(*(redirects->goutput())); - o = &f_o; + void CommandNode::execute(std::istream& in, std::ostream& out) { + std::istream* i = ∈ + std::ostream* o = &out; + std::ifstream f_i; + std::ofstream f_o; + if (redirects) { + if (redirects->goutput()) { + if (redirects->gappend()) f_o = util::output_append(*(redirects->goutput())); + else + f_o = util::output(*(redirects->goutput())); + o = &f_o; + } + if (redirects->ginput()) { + f_i = util::input(*(redirects->ginput())); + i = &f_i; + } } - if(redirects->ginput()) { - f_i = util::input(*(redirects->ginput())); - i = &f_i; + + /// TODO: Error when input/output was overwritten and in and out weren't cin/cout + + auto& cmd_registry = CommandRegistry::instance(); + cmd_registry[cmd_name].execute(flag, args, *i, *o); + } + + void PipeLineNode::execute(std::istream& in, std::ostream& out) { + std::stringstream buffer; + if (r_command) { + l_command->execute(in, buffer); + r_command->execute(buffer, out); + } else { + l_command->execute(in, out); } } - - /// TODO: Error when input/output was overwritten and in and out weren't cin/cout - - auto& cmd_registry = CommandRegistry::instance(); - cmd_registry[cmd_name].execute(flag, args, *i, *o); -} - - -void PipeLineNode::execute(std::istream& in, std::ostream& out) { - std::stringstream buffer; - if(r_command){ - l_command->execute(in, buffer); - r_command->execute(buffer, out); - } else { - l_command->execute(in,out); + void CommandLineNode::execute(std::istream& in, std::ostream& out) { + std::stringstream buffer; + if (pipe) { + command->execute(in, buffer); + pipe->execute(buffer, out); + } else { + command->execute(in, out); + } } -} - -void CommandLineNode::execute(std::istream& in, std::ostream& out) { - std::stringstream buffer; - if(pipe){ - command->execute(in, buffer); - pipe->execute(buffer, out); - } else { - command->execute(in,out); - } -} } \ No newline at end of file diff --git a/src/ast/ast_print.cpp b/src/ast/ast_print.cpp index bc55c24..dc1ef41 100644 --- a/src/ast/ast_print.cpp +++ b/src/ast/ast_print.cpp @@ -3,44 +3,38 @@ namespace fsh { -void CommandNode::print(int indent) { - std::string t = ""; - for(int i=0; igvalue() << " " << util::tokens[x->gtoken_type()] << "\n"; + if (redirects) { + auto out = redirects->goutput(); + auto in = redirects->ginput(); + if (in) std::cout << t << "\tinput = " << *in << "\n"; + if (out) std::cout << t << "\toutput = " << *out << " " << redirects->gappend() << "\n"; + } } - std::cout<gvalue() <<" "<< util::tokens[x->gtoken_type()]<< "\n"; - if(redirects) { - auto out = redirects->goutput(); - auto in = redirects->ginput(); - if(in) std::cout<gappend() << "\n"; - } -} -void PipeLineNode::print(int indent) { - std::string t = ""; - for(int i=0; iprint(indent + 1); + std::cout << "right_cmd:\n"; + if (r_command) r_command->print(indent + 1); } - std::cout<print(indent + 1); - std::cout<<"right_cmd:\n"; - if(r_command) r_command->print(indent + 1); -} -void CommandLineNode::print(int indent) { - std::string t = ""; - for(int i=0; iprint(indent + 1); + if (pipe) pipe->print(indent + 1); } - std::cout<print(indent + 1); - if(pipe) pipe->print(indent + 1); -} } \ No newline at end of file diff --git a/src/cmd/arg.cpp b/src/cmd/arg.cpp index ea17c18..87b0ded 100644 --- a/src/cmd/arg.cpp +++ b/src/cmd/arg.cpp @@ -2,71 +2,62 @@ namespace fsh { -void ArgInput::svalue(const std::string& val, const Lexer::TokenType& type) { - const std::string& txt = val; - if(type == Lexer::TokenType::STRING_LITERAL) { - str = std::stringstream(txt); - } else { - file = std::ifstream(txt, std::ios::in); - if(!*file) { - throw std::runtime_error("Failed to open file"); + void ArgInput::svalue(const std::string& val, const Lexer::TokenType& type) { + const std::string& txt = val; + if (type == Lexer::TokenType::STRING_LITERAL) { + str = std::stringstream(txt); + } else { + file = std::ifstream(txt, std::ios::in); + if (!*file) { throw std::runtime_error("Failed to open file"); } } } -} - -std::istream& ArgInput::gvalue(){ - if(str) - return *str; - return *file; -} - - - -void ArgFactory::parse(ArgManager& manager, ArgNodes& vec, FlagNode flag) { - if(flag) { - parse_flag(manager, flag); + std::istream& ArgInput::gvalue() { + if (str) return *str; + return *file; } - unsigned int i = 0; - for(const auto& arg : vec) { - std::shared_ptr<_Argument> a; - if(i >= pos_arg_rules.size()) throw std::invalid_argument("More arguments then excpected"); - manager.push_arg(build_arg(pos_arg_rules[i].build, arg)); - if(!pos_arg_rules[i].extends) i++; - } -} - -void ArgFactory::parse_flag(ArgManager& manager, FlagNode flag) { - const std::string f = *flag; - int f_sz = f.size(); - for(const auto& [key, rule] : flag_rules) { - const std::string_view k(key); - if(k == f) { - manager.push_flag(f); - return; - } - - if(rule.capturing && f == k.substr(0, f_sz)) { - auto arg = build_arg(rule.build, (std::string) k.substr(f_sz)); - manager.push_flag(f, arg); - return; + void ArgFactory::parse(ArgManager& manager, ArgNodes& vec, FlagNode flag) { + if (flag) { parse_flag(manager, flag); } + unsigned int i = 0; + for (const auto& arg : vec) { + std::shared_ptr<_Argument> a; + if (i >= pos_arg_rules.size()) throw std::invalid_argument("More arguments then excpected"); + manager.push_arg(build_arg(pos_arg_rules[i].build, arg)); + if (!pos_arg_rules[i].extends) i++; } } - throw std::invalid_argument("Invalid flag"); -} + void ArgFactory::parse_flag(ArgManager& manager, FlagNode flag) { + const std::string f = *flag; + int f_sz = f.size(); + for (const auto& [key, rule] : flag_rules) { + const std::string_view k(key); + if (k == f) { + manager.push_flag(f); + return; + } - std::shared_ptr<_Argument> ArgFactory::build_arg(BuildFunc build_func, const std::string& str) { - auto arg = build_func(); - arg->svalue(str); - return arg; -} + if (rule.capturing && f == k.substr(0, f_sz)) { + auto arg = build_arg(rule.build, (std::string)k.substr(f_sz)); + manager.push_flag(f, arg); + return; + } + } + throw std::invalid_argument("Invalid flag"); + } -std::shared_ptr<_Argument> ArgFactory::build_arg(BuildFunc build_func, std::shared_ptr cmd_arg) { - auto arg = build_func(); - arg->svalue(cmd_arg); - return arg; -} + std::shared_ptr<_Argument> ArgFactory::build_arg(BuildFunc build_func, const std::string& str) { + auto arg = build_func(); + arg->svalue(str); + return arg; + } + + std::shared_ptr<_Argument> ArgFactory::build_arg(BuildFunc build_func, + std::shared_ptr cmd_arg) { + auto arg = build_func(); + arg->svalue(cmd_arg); + return arg; + } } \ No newline at end of file diff --git a/src/cmd/cmd_base.cpp b/src/cmd/cmd_base.cpp index 43c726e..976deaa 100644 --- a/src/cmd/cmd_base.cpp +++ b/src/cmd/cmd_base.cpp @@ -6,20 +6,17 @@ namespace fsh { void Command::execute(FlagNode flag, ArgNodes& vec, std::istream& in, std::ostream& out) { ArgManager args; arg_factory.parse(args, vec, flag); - if(arg_factory.ghas_input()) { - - if(&args.get_input(0) != &util::cin && &in!= &util::cin) { + 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"); } - if(&args.get_input(0) != &util::cin) - run(args.get_input(0), out, args); + if (&args.get_input(0) != &util::cin) run(args.get_input(0), out, args); else run(in, out, args); - } - else { + } else { run(in, out, args); } in.clear(); } -} \ No newline at end of file +} // namespace fsh \ No newline at end of file diff --git a/src/fsh.cpp b/src/fsh.cpp index 3e0f637..a125264 100644 --- a/src/fsh.cpp +++ b/src/fsh.cpp @@ -12,20 +12,18 @@ namespace fsh { void fsh::run_line(std::string& line, std::istream& in, std::ostream& out) { auto tokens = Lexer::process(line); - auto ast = AstFactory::generate_ast(tokens); + auto ast = AstFactory::generate_ast(tokens); ast->execute(in, out); } void fsh::run() { - while(environment["EXITING"] == "") { + while (environment["EXITING"] == "") { try { std::cout << environment["PROMPT"] << " "; std::string line; std::getline(util::cin, line); run_line(line, util::cin, std::cout); - } catch(const std::exception& e) { - std::cerr << e.what() << "\n"; - } + } catch (const std::exception& e) { std::cerr << e.what() << "\n"; } } } } \ No newline at end of file diff --git a/src/lexer.cpp b/src/lexer.cpp index 38e2350..cd2f8b1 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -7,112 +7,98 @@ namespace fsh { -Token LexerStateMachine::run() { - State state = State::START; - while (state != State::END) { - state = state_handlers[state](); - } - return {type, token}; -} - - - -LexerStateMachine::State LexerStateMachine::handle_start() { - const int c = parser.peek(); - - if(c == std::char_traits::eof()) { - return set_type(Lexer::TokenType::END_OF_STREAM); - } else if(util::contains(c, " \t")) { - parser.consume(); - return State::START; - } else if(c == '\"') { - parser.consume(); - return set_type(Lexer::TokenType::STRING_LITERAL, State::STRING_LITERAL); + Token LexerStateMachine::run() { + State state = State::START; + while (state != State::END) { state = state_handlers[state](); } + return { type, token }; } - token = parser.consume(); - if(c == '|') { - return set_type(Lexer::TokenType::PIPE); - } else if(c == '<') { - return set_type(Lexer::TokenType::LREDIRECTION); - } else if(c == '>') { - return set_type(Lexer::TokenType::RREDIRECTION, State::REDIRECT ); - } else if(c == '-') { - return set_type(Lexer::TokenType::FLAG , State::FLAG_OPT ); - } else { - return set_type(Lexer::TokenType::WORD , State::POTENTIAL_WORD ); + LexerStateMachine::State LexerStateMachine::handle_start() { + const int c = parser.peek(); + + if (c == std::char_traits::eof()) { + return set_type(Lexer::TokenType::END_OF_STREAM); + } else if (util::contains(c, " \t")) { + parser.consume(); + return State::START; + } else if (c == '\"') { + parser.consume(); + return set_type(Lexer::TokenType::STRING_LITERAL, State::STRING_LITERAL); + } + + token = parser.consume(); + if (c == '|') { + return set_type(Lexer::TokenType::PIPE); + } else if (c == '<') { + return set_type(Lexer::TokenType::LREDIRECTION); + } else if (c == '>') { + return set_type(Lexer::TokenType::RREDIRECTION, State::REDIRECT); + } else if (c == '-') { + return set_type(Lexer::TokenType::FLAG, State::FLAG_OPT); + } else { + return set_type(Lexer::TokenType::WORD, State::POTENTIAL_WORD); + } } -} -LexerStateMachine::State LexerStateMachine::handle_flag_opt() { - return word_like_handler(Lexer::TokenType::FLAG, State::FLAG_OPT); -} - - - -LexerStateMachine::State LexerStateMachine::handle_redirect() { - const int c = parser.peek(); - if(c == '>') { - token += parser.consume(); + LexerStateMachine::State LexerStateMachine::handle_flag_opt() { + return word_like_handler(Lexer::TokenType::FLAG, State::FLAG_OPT); } - return set_type(Lexer::TokenType::RREDIRECTION); -} -LexerStateMachine::State LexerStateMachine::handle_potential_word() { - return word_like_handler(Lexer::TokenType::WORD, State::POTENTIAL_WORD); -} - - -LexerStateMachine::State LexerStateMachine::handle_string_literal() { - const int c = parser.peek(); - if( c == std::char_traits::eof() ) { - throw std::runtime_error("Excpected \" before \\n"); - } else if(c == '\"') { - parser.consume(); - return set_type(Lexer::TokenType::STRING_LITERAL); - } else { - token += parser.consume(); - return State::STRING_LITERAL; + LexerStateMachine::State LexerStateMachine::handle_redirect() { + const int c = parser.peek(); + if (c == '>') { token += parser.consume(); } + return set_type(Lexer::TokenType::RREDIRECTION); } -} + LexerStateMachine::State LexerStateMachine::handle_potential_word() { + return word_like_handler(Lexer::TokenType::WORD, State::POTENTIAL_WORD); + } -LexerStateMachine::State LexerStateMachine::word_like_handler(Lexer::TokenType type, State state) { - const int c = parser.peek(); - if(util::contains(c, "\t><| ") || c == std::char_traits::eof()) { - return set_type(type); - } else if(c == '\"') { - throw std::runtime_error("Invalid \""); - } else { - token += parser.consume(); - return state; - } -} + LexerStateMachine::State LexerStateMachine::handle_string_literal() { + const int c = parser.peek(); + if (c == std::char_traits::eof()) { + throw std::runtime_error("Excpected \" before \\n"); + } else if (c == '\"') { + parser.consume(); + return set_type(Lexer::TokenType::STRING_LITERAL); + } else { + token += parser.consume(); + return State::STRING_LITERAL; + } + } -std::list Lexer::process(std::string line) { - Lexer parser(line); - std::list tokens; - tokens.push_back(parser.next_token()); - while(tokens.back().first != TokenType::END_OF_STREAM) { + LexerStateMachine::State LexerStateMachine::word_like_handler(Lexer::TokenType type, State state) { + const int c = parser.peek(); + if (util::contains(c, "\t><| ") || c == std::char_traits::eof()) { + return set_type(type); + } else if (c == '\"') { + throw std::runtime_error("Invalid \""); + } else { + token += parser.consume(); + return state; + } + } + + std::list Lexer::process(std::string line) { + Lexer parser(line); + std::list tokens; tokens.push_back(parser.next_token()); - } - return tokens; -} + while (tokens.back().first != TokenType::END_OF_STREAM) { tokens.push_back(parser.next_token()); } + return tokens; + } -int Lexer::peek() { - if(idx < workspace.size()) - return workspace[idx]; - return std::char_traits::eof(); -} -int Lexer::consume() { - if(idx < workspace.size()) - return workspace[idx++]; - return std::char_traits::eof(); -} + int Lexer::peek() { + if (idx < workspace.size()) return workspace[idx]; + return std::char_traits::eof(); + } + int Lexer::consume() { + if (idx < workspace.size()) return workspace[idx++]; + return std::char_traits::eof(); + } -Token Lexer::next_token() { - LexerStateMachine psm(*this); - return psm.run(); -} + Token Lexer::next_token() { + LexerStateMachine psm(*this); + return psm.run(); + } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 1acf1a5..32fd251 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,8 +5,7 @@ #include "fsh.hpp" #include "util/input.hpp" - -int main(){ +int main() { fsh::util::prepTerminal(); fsh::fsh::instance().run(); return 0; diff --git a/src/util/input.cpp b/src/util/input.cpp index 880930c..8227b9e 100644 --- a/src/util/input.cpp +++ b/src/util/input.cpp @@ -11,139 +11,118 @@ #endif - #include #include "util/input.hpp" - -namespace fsh::util -{ +namespace fsh::util { #ifndef _WIN32 -struct termios prev_attr; -void resetTerminal() { - tcsetattr(STDIN_FILENO, TCSANOW, &prev_attr); -} + struct termios prev_attr; + void resetTerminal() { tcsetattr(STDIN_FILENO, TCSANOW, &prev_attr); } #endif - -void prepTerminal() { + void prepTerminal() { #ifdef _WIN32 - /* Ovaj deo koda je namenjen da omoguci koriscenje neke ansi-escape sekvence na windows platformi */ + /* Ovaj deo koda je namenjen da omoguci koriscenje neke ansi-escape sekvence na windows platformi */ #else - /* Ova porcija koda je preuzata sa https://www.gnu.org/software/libc/manual/html_node/Noncanon-Example.html + /* Ova porcija koda je preuzata sa https://www.gnu.org/software/libc/manual/html_node/Noncanon-Example.html Omogucava da karaktere primim kako su unjeti, sto kasnije koristim da omogucim pomeranje kursora pomocu strelica pod UNIX sistemima */ - struct termios tattr; - if(!isatty(STDIN_FILENO)) { - return; - } + struct termios tattr; + if (!isatty(STDIN_FILENO)) { return; } - tcgetattr(STDIN_FILENO, &prev_attr); - atexit(resetTerminal); + tcgetattr(STDIN_FILENO, &prev_attr); + atexit(resetTerminal); - tcgetattr(STDIN_FILENO, &tattr); - tattr.c_lflag &= ~(ICANON|ECHO); - tattr.c_cc[VMIN] = 1; - tattr.c_cc[VTIME] = 0; - tcsetattr(STDIN_FILENO, TCSAFLUSH ,&tattr); - std::cout << "\e[?25h"; + tcgetattr(STDIN_FILENO, &tattr); + tattr.c_lflag &= ~(ICANON | ECHO); + tattr.c_cc[VMIN] = 1; + tattr.c_cc[VTIME] = 0; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr); + std::cout << "\e[?25h"; #endif - -} + } #ifndef _WIN32 -int CursorIStreamBuffer::underflow() { - if(gptr() < egptr()) return traits_type::to_int_type(*gptr()); + int CursorIStreamBuffer::underflow() { + if (gptr() < egptr()) return traits_type::to_int_type(*gptr()); - if(read_chars >= input.size()) { - readInputToNewLine(); + if (read_chars >= input.size()) { readInputToNewLine(); } + + int n = input.copy(buffer, sizeof(buffer) - 1, read_chars); + read_chars += n; + + setg(buffer, buffer, buffer + n); + return traits_type::to_int_type(*gptr()); } - int n = input.copy(buffer, sizeof(buffer)-1, read_chars); - read_chars+=n; - - setg(buffer,buffer,buffer+n); - return traits_type::to_int_type(*gptr()); -} - -CursorIStreamBuffer::EscapeSequence CursorIStreamBuffer::testForEscapeCodes(char chr) { - if(valid==2) { + CursorIStreamBuffer::EscapeSequence CursorIStreamBuffer::testForEscapeCodes(char chr) { + if (valid == 2) { + valid = 0; + switch (chr) { + case 'C': return RIGHT_ARROW; + case 'D': return LEFT_ARROW; + } + } + if (chr == '\033') { + valid = 1; + return POSSIBLE; + } + if (valid == 1 && chr == '[') { + valid = 2; + return POSSIBLE; + } valid = 0; - switch (chr) - { - case 'C': - return RIGHT_ARROW; - case 'D': - return LEFT_ARROW; - } + return FAILED; } - if(chr == '\033') { - valid=1; - return POSSIBLE; - } - if(valid==1 && chr == '[') { - valid=2; - return POSSIBLE; - } - valid = 0; - return FAILED; -} - -int CursorIStreamBuffer::readInputToNewLine(){ - read_chars = 0; - cursor_pos = 0; - input = ""; - char c; - while(std::cin.get(c)) { - //std::cout << (int)c; - EscapeSequence e = testForEscapeCodes(c); - if(c == '\r') continue; // Skip CR to keep sanity - else if(c == 127 || c == 8) { - if(cursor_pos > 0) { - std::cout<<"\e[D\e[P"; - input.erase(--cursor_pos,1); - } - } - else if(e>0) { - if(e==RIGHT_ARROW) { - if(cursor_pos 0) { + std::cout << "\e[D\e[P"; + input.erase(--cursor_pos, 1); } - } - else if(e==LEFT_ARROW) { - if(cursor_pos>0) { - cursor_pos--; - std::cout << "\e[D"; + } else if (e > 0) { + if (e == RIGHT_ARROW) { + if (cursor_pos < input.size()) { + cursor_pos++; + std::cout << "\e[C"; + } + } else if (e == LEFT_ARROW) { + if (cursor_pos > 0) { + cursor_pos--; + std::cout << "\e[D"; + } } + continue; + } else if (c == '\n') { + std::cout << '\n'; + input.push_back(c); + return 0; + } else { + input.insert(cursor_pos++, 1, c); + std::cout << "\033[@" << c; } - continue; } - else if(c == '\n') { - std::cout<<'\n'; - input.push_back(c); - return 0; - } - else{ - input.insert(cursor_pos++,1,c); - std::cout<<"\033[@"<