From 322a32af6504c6df0bb88a7fc8a33c66f7d429bc Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 15 Jun 2016 23:01:03 -0400 Subject: [PATCH] support for Loop ... End, Break, and Continue. --- command.cpp | 204 +++++++++++++++++++++++++++++++++++--------- command.h | 42 ++++++++- environment.h | 11 +++ error.h | 35 +++++++- phase2-parser.lemon | 10 +++ phase2.rl | 17 +++- 6 files changed, 273 insertions(+), 46 deletions(-) diff --git a/command.cpp b/command.cpp index adf2ccf..c99d7cb 100644 --- a/command.cpp +++ b/command.cpp @@ -30,6 +30,79 @@ namespace ToolBox { std::string UnixToMac(const std::string path); } + + + + +namespace { + + struct break_command_t {}; + struct continue_command_t {}; + + /* + * returns: + * <0 -> error + * 0 -> false + * >0 -> true + */ + + int bad_if(const char *name) { + fprintf(stderr, "### %s - Missing if keyword.\n", name); + fprintf(stderr, "# Usage - %s [if expression...]\n", name); + return -3; + } + int evaluate(int type, const std::string &s, Environment &env) { + + auto tokens = tokenize(s, true); + std::reverse(tokens.begin(), tokens.end()); + + int32_t e; + + switch(type) { + default: return 0; + + case BREAK: + case CONTINUE: + case ELSE: + tokens.pop_back(); + + if (tokens.empty()) return 1; + + if (strcasecmp(tokens.back().string.c_str(), "if") != 0) { + const char *name = ""; + switch(type) { + case BREAK: name = "Break"; break; + case CONTINUE: name = "Continue"; break; + case ELSE: name = "Else"; break; + } + return bad_if(name); + } + // fall through. + case IF: + tokens.pop_back(); + try { + e = evaluate_expression("If", std::move(tokens)); + } + catch (std::exception &ex) { + fprintf(stderr, "%s\n", ex.what()); + return -5; + } + break; + } + return !!e; + } +} + + + + + + + + + + + fs::path which(const Environment &env, const std::string &name) { std::error_code ec; @@ -243,6 +316,51 @@ int evaluate_command::execute(Environment &env, const fdmask &fds, bool throwup) } +int break_command::execute(Environment &env, const fdmask &fds, bool throwup) { + + if (control_c) throw execution_of_input_terminated(); + + std::string s = expand_vars(text, env); + + env.set("command", "break"); + + env.echo("%s", s.c_str()); + if (!env.loop()) { + fputs("MPW Shell - Break must be within for or loop.\n", stderr); + return env.status(-3, throwup); + } + + // check/evaluate if clause. + int status = evaluate(BREAK, s, env); + if (status > 0) + throw break_command_t(); + return env.status(status, throwup); + +} + +int continue_command::execute(Environment &env, const fdmask &fds, bool throwup) { + + if (control_c) throw execution_of_input_terminated(); + + std::string s = expand_vars(text, env); + + env.set("command", "continue"); + + env.echo("%s", s.c_str()); + if (!env.loop()) { + fputs("MPW Shell - Continue must be within for or loop.\n", stderr); + return env.status(-3, throwup); + } + + + // check/evaluate if clause. + int status = evaluate(CONTINUE, s, env); + if (status > 0) + throw continue_command_t(); + return env.status(status, throwup); + +} + int or_command::execute(Environment &e, const fdmask &fds, bool throwup) { int rv = 0; @@ -367,51 +485,61 @@ int begin_command::execute(Environment &env, const fdmask &fds, bool throwup) { return env.status(rv); } -namespace { + +int loop_command::execute(Environment &env, const fdmask &fds, bool throwup) { + // todo -- parse end for redirection. + + std::string b = expand_vars(begin, env); + std::string e = expand_vars(end, env); - /* - * returns: - * <0 -> error - * 0 -> false - * >0 -> true - */ + env.set("command", "end"); - int evaluate(int type, const std::string &s, Environment &env) { + // echo! + env.echo("%s ... %s", + b.c_str(), + e.c_str() + ); - auto tokens = tokenize(s, true); - std::reverse(tokens.begin(), tokens.end()); - - int32_t e; - - switch(type) { - default: return 0; - - case ELSE_IF: - tokens.pop_back(); - case IF: - tokens.pop_back(); - try { - e = evaluate_expression("If", std::move(tokens)); - } - catch (std::exception &ex) { - fprintf(stderr, "%s\n", ex.what()); - return -5; - } - break; - - case ELSE: - e = 1; - if (tokens.size() > 1) { - fprintf(stderr, "### Else - Missing if keyword.\n"); - fprintf(stderr, "# Usage - Else [if expression...]\n"); - return -3; - } - } - return !!e; + // check for extra tokens... + auto bt = tokenize(b, true); + if (bt.size() != 1) { + fprintf(stderr, "### Loop - Too many parameters were specified.\n"); + fprintf(stderr, "Usage - Loop\n"); + return env.status(-3); } + + fdmask newfds; + int status = check_ends(e, newfds); + newfds |= fds; + if (status) return env.status(status); + + int rv = 0; + for(;; env.echo("end") ) { + + if (control_c) throw execution_of_input_terminated(); + + try { + env.loop_indent_and([&]{ + rv = vector_command::execute(env, newfds); + }); + } + catch (continue_command_t &ex) { + continue; + } + catch (break_command_t &ex) { + break; + } + + } + env.echo("end"); + + return env.status(rv); } + + + /* * the entire command prints even if there is an error with the expression. * diff --git a/command.h b/command.h index 57161f2..ee706d0 100644 --- a/command.h +++ b/command.h @@ -22,7 +22,7 @@ struct command { {} virtual bool terminal() const noexcept { - return type == EVALUATE || type == COMMAND; + return type == EVALUATE || type == COMMAND || type == BREAK || type == CONTINUE; } int type = 0; @@ -60,6 +60,29 @@ struct evaluate_command : public command { virtual int execute(Environment &e, const fdmask &fds, bool throwup = true) final override; }; + +struct break_command : public command { + + template + break_command(S &&s) : command(BREAK), text(std::forward(s)) + {} + + std::string text; + virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override; +}; + +struct continue_command : public command { + + template + continue_command(S &&s) : command(CONTINUE), text(std::forward(s)) + {} + + std::string text; + virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override; +}; + + + struct binary_command : public command { binary_command(int t, command_ptr &&a, command_ptr &&b) : @@ -122,6 +145,19 @@ struct begin_command : public vector_command { virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override; }; +struct loop_command : public vector_command { + + template + loop_command(int t, command_ptr_vector &&v, S1 &&b, S2 &&e) : + vector_command(t, std::move(v)), begin(std::forward(b)), end(std::forward(e)) + {} + + std::string begin; + std::string end; + + virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override; +}; + typedef std::unique_ptr if_else_clause_ptr; struct if_command : public command { @@ -151,4 +187,6 @@ struct if_else_clause : public vector_command { //bool evaluate(const Environment &e); }; -#endif \ No newline at end of file + + +#endif diff --git a/environment.h b/environment.h index 41545d9..792584d 100644 --- a/environment.h +++ b/environment.h @@ -98,12 +98,23 @@ public: catch (...) { _indent = i; throw; } } + template + void loop_indent_and(FX &&fx) { + int i = _indent++; + int j = _loop++; + try { fx(); _indent = i; _loop = j; } + catch (...) { _indent = i; _loop = j; throw; } + } + + constexpr bool loop() const noexcept { return _loop; } + private: // magic variables. friend class indent_helper; int _indent = 0; + int _loop = 0; bool _exit = false; bool _test = false; diff --git a/error.h b/error.h index bf7afcb..712b204 100644 --- a/error.h +++ b/error.h @@ -4,14 +4,41 @@ #include #include -class execution_of_input_terminated : public std::runtime_error { +class mpw_error : public std::runtime_error { public: - execution_of_input_terminated(int status = -9) : - std::runtime_error("MPW Shell - Execution of input Terminated."), _status(status) + mpw_error(int status, const std::string &s) : std::runtime_error(s), _status(status) {} + + mpw_error(int status, const char *s) : std::runtime_error(s), _status(status) + {} + constexpr int status() const noexcept { return _status; } private: - int _status; + int _status; }; +class execution_of_input_terminated : public mpw_error { +public: + execution_of_input_terminated(int status = -9) : + mpw_error(status, "MPW Shell - Execution of input Terminated.") + {} +}; + + +class break_error : public mpw_error { +public: + break_error(int status = -3) : + mpw_error(status, "MPW Shell - Break must be within for or loop.") + {} +}; + +class continue_error : public mpw_error { +public: + continue_error(int status = -3) : + mpw_error(status, "MPW Shell - Continue must be within for or loop.") + {} +}; + + + #endif \ No newline at end of file diff --git a/phase2-parser.lemon b/phase2-parser.lemon index ae3668a..943d3d3 100644 --- a/phase2-parser.lemon +++ b/phase2-parser.lemon @@ -27,6 +27,7 @@ bool phase2_parser::continuation() const { if (e.major == IF) return true; if (e.major == AMP_AMP) return true; if (e.major == PIPE_PIPE) return true; + if (e.major == LOOP) return true; } return false; } @@ -100,9 +101,12 @@ command(RV) ::= term(T). { RV = std::move(T); } term(RV) ::= COMMAND(C). { RV = std::make_unique(std::move(C)); } term(RV) ::= EVALUATE(C). { RV = std::make_unique(std::move(C)); } +term(RV) ::= BREAK(C). { RV = std::make_unique(std::move(C)); } +term(RV) ::= CONTINUE(C). { RV = std::make_unique(std::move(C)); } term(RV) ::= if_command(C). { RV = std::move(C); } term(RV) ::= begin_command(C). { RV = std::move(C); } term(RV) ::= paren_command(C). { RV = std::move(C); } +term(RV) ::= loop_command(C). { RV = std::move(C); } /* * fall back to an end error. w/o fallback, it will cause a parse conflict. @@ -137,6 +141,12 @@ begin_command(RV) ::= BEGIN(T) sep compound_list(L) END(E). { RV = std::make_unique(@T, std::move(L), std::move(T), std::move(E)); } + +loop_command(RV) ::= LOOP(T) sep compound_list(L) END(E). { + RV = std::make_unique(@T, std::move(L), std::move(T), std::move(E)); +} + + if_command(RV) ::= IF(I) sep compound_list(L) END(E). { if_command::clause_vector_type v; diff --git a/phase2.rl b/phase2.rl index 3247738..145c536 100644 --- a/phase2.rl +++ b/phase2.rl @@ -120,6 +120,9 @@ END = /end/i; BEGIN = /begin/i; EVALUATE = /evaluate/i; + LOOP = /loop/i; + BREAK = /break/i; + CONTINUE = /continue/i; main := |* @@ -129,8 +132,8 @@ ELSE %eof{ type = ELSE; return; }; ELSE ws => { type = ELSE; return; }; - ELSE ws+ IF %eof{ type = ELSE_IF; return; }; - ELSE ws+ IF ws => { type = ELSE_IF; return; }; + #ELSE ws+ IF %eof{ type = ELSE_IF; return; }; + #ELSE ws+ IF ws => { type = ELSE_IF; return; }; EVALUATE %eof{ type = EVALUATE; return; }; EVALUATE ws => { type = EVALUATE; return; }; @@ -141,6 +144,16 @@ BEGIN %eof{ type = BEGIN; return; }; BEGIN ws => { type = BEGIN; return; }; + LOOP %eof{ type = LOOP; return; }; + LOOP ws => { type = LOOP; return; }; + + BREAK %eof{ type = BREAK; return; }; + BREAK ws => { type = BREAK; return; }; + + CONTINUE %eof{ type = CONTINUE; return; }; + CONTINUE ws => { type = CONTINUE; return; }; + + '(' => { type = LPAREN; return; }; *|;