mirror of
https://github.com/ksherlock/mpw-shell.git
synced 2025-01-15 03:30:00 +00:00
support for Loop ... End, Break, and Continue.
This commit is contained in:
parent
edd80fc3c5
commit
322a32af65
204
command.cpp
204
command.cpp
@ -30,6 +30,79 @@ namespace ToolBox {
|
|||||||
std::string UnixToMac(const std::string path);
|
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) {
|
fs::path which(const Environment &env, const std::string &name) {
|
||||||
std::error_code ec;
|
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 or_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
@ -367,51 +485,61 @@ int begin_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
|||||||
return env.status(rv);
|
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);
|
||||||
|
|
||||||
|
|
||||||
/*
|
env.set("command", "end");
|
||||||
* returns:
|
|
||||||
* <0 -> error
|
|
||||||
* 0 -> false
|
|
||||||
* >0 -> true
|
|
||||||
*/
|
|
||||||
|
|
||||||
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);
|
// check for extra tokens...
|
||||||
std::reverse(tokens.begin(), tokens.end());
|
auto bt = tokenize(b, true);
|
||||||
|
if (bt.size() != 1) {
|
||||||
int32_t e;
|
fprintf(stderr, "### Loop - Too many parameters were specified.\n");
|
||||||
|
fprintf(stderr, "Usage - Loop\n");
|
||||||
switch(type) {
|
return env.status(-3);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
* the entire command prints even if there is an error with the expression.
|
||||||
*
|
*
|
||||||
|
42
command.h
42
command.h
@ -22,7 +22,7 @@ struct command {
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
virtual bool terminal() const noexcept {
|
virtual bool terminal() const noexcept {
|
||||||
return type == EVALUATE || type == COMMAND;
|
return type == EVALUATE || type == COMMAND || type == BREAK || type == CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int type = 0;
|
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;
|
virtual int execute(Environment &e, const fdmask &fds, bool throwup = true) final override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct break_command : public command {
|
||||||
|
|
||||||
|
template<class S>
|
||||||
|
break_command(S &&s) : command(BREAK), text(std::forward<S>(s))
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string text;
|
||||||
|
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct continue_command : public command {
|
||||||
|
|
||||||
|
template<class S>
|
||||||
|
continue_command(S &&s) : command(CONTINUE), text(std::forward<S>(s))
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string text;
|
||||||
|
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct binary_command : public command {
|
struct binary_command : public command {
|
||||||
|
|
||||||
binary_command(int t, command_ptr &&a, command_ptr &&b) :
|
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;
|
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct loop_command : public vector_command {
|
||||||
|
|
||||||
|
template<class S1, class S2>
|
||||||
|
loop_command(int t, command_ptr_vector &&v, S1 &&b, S2 &&e) :
|
||||||
|
vector_command(t, std::move(v)), begin(std::forward<S1>(b)), end(std::forward<S2>(e))
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string begin;
|
||||||
|
std::string end;
|
||||||
|
|
||||||
|
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
|
||||||
|
};
|
||||||
|
|
||||||
typedef std::unique_ptr<struct if_else_clause> if_else_clause_ptr;
|
typedef std::unique_ptr<struct if_else_clause> if_else_clause_ptr;
|
||||||
struct if_command : public command {
|
struct if_command : public command {
|
||||||
|
|
||||||
@ -151,4 +187,6 @@ struct if_else_clause : public vector_command {
|
|||||||
//bool evaluate(const Environment &e);
|
//bool evaluate(const Environment &e);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
@ -98,12 +98,23 @@ public:
|
|||||||
catch (...) { _indent = i; throw; }
|
catch (...) { _indent = i; throw; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class FX>
|
||||||
|
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:
|
private:
|
||||||
// magic variables.
|
// magic variables.
|
||||||
|
|
||||||
friend class indent_helper;
|
friend class indent_helper;
|
||||||
|
|
||||||
int _indent = 0;
|
int _indent = 0;
|
||||||
|
int _loop = 0;
|
||||||
|
|
||||||
bool _exit = false;
|
bool _exit = false;
|
||||||
bool _test = false;
|
bool _test = false;
|
||||||
|
35
error.h
35
error.h
@ -4,14 +4,41 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
|
||||||
class execution_of_input_terminated : public std::runtime_error {
|
class mpw_error : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
execution_of_input_terminated(int status = -9) :
|
mpw_error(int status, const std::string &s) : std::runtime_error(s), _status(status)
|
||||||
std::runtime_error("MPW Shell - Execution of input Terminated."), _status(status)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
mpw_error(int status, const char *s) : std::runtime_error(s), _status(status)
|
||||||
|
{}
|
||||||
|
|
||||||
constexpr int status() const noexcept { return _status; }
|
constexpr int status() const noexcept { return _status; }
|
||||||
private:
|
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
|
#endif
|
@ -27,6 +27,7 @@ bool phase2_parser::continuation() const {
|
|||||||
if (e.major == IF) return true;
|
if (e.major == IF) return true;
|
||||||
if (e.major == AMP_AMP) return true;
|
if (e.major == AMP_AMP) return true;
|
||||||
if (e.major == PIPE_PIPE) return true;
|
if (e.major == PIPE_PIPE) return true;
|
||||||
|
if (e.major == LOOP) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -100,9 +101,12 @@ command(RV) ::= term(T). { RV = std::move(T); }
|
|||||||
|
|
||||||
term(RV) ::= COMMAND(C). { RV = std::make_unique<simple_command>(std::move(C)); }
|
term(RV) ::= COMMAND(C). { RV = std::make_unique<simple_command>(std::move(C)); }
|
||||||
term(RV) ::= EVALUATE(C). { RV = std::make_unique<evaluate_command>(std::move(C)); }
|
term(RV) ::= EVALUATE(C). { RV = std::make_unique<evaluate_command>(std::move(C)); }
|
||||||
|
term(RV) ::= BREAK(C). { RV = std::make_unique<break_command>(std::move(C)); }
|
||||||
|
term(RV) ::= CONTINUE(C). { RV = std::make_unique<continue_command>(std::move(C)); }
|
||||||
term(RV) ::= if_command(C). { RV = std::move(C); }
|
term(RV) ::= if_command(C). { RV = std::move(C); }
|
||||||
term(RV) ::= begin_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) ::= 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.
|
* 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<begin_command>(@T, std::move(L), std::move(T), std::move(E));
|
RV = std::make_unique<begin_command>(@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<loop_command>(@T, std::move(L), std::move(T), std::move(E));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if_command(RV) ::= IF(I) sep compound_list(L) END(E). {
|
if_command(RV) ::= IF(I) sep compound_list(L) END(E). {
|
||||||
|
|
||||||
if_command::clause_vector_type v;
|
if_command::clause_vector_type v;
|
||||||
|
17
phase2.rl
17
phase2.rl
@ -120,6 +120,9 @@
|
|||||||
END = /end/i;
|
END = /end/i;
|
||||||
BEGIN = /begin/i;
|
BEGIN = /begin/i;
|
||||||
EVALUATE = /evaluate/i;
|
EVALUATE = /evaluate/i;
|
||||||
|
LOOP = /loop/i;
|
||||||
|
BREAK = /break/i;
|
||||||
|
CONTINUE = /continue/i;
|
||||||
|
|
||||||
|
|
||||||
main := |*
|
main := |*
|
||||||
@ -129,8 +132,8 @@
|
|||||||
ELSE %eof{ type = ELSE; return; };
|
ELSE %eof{ type = ELSE; return; };
|
||||||
ELSE ws => { type = ELSE; return; };
|
ELSE ws => { type = ELSE; return; };
|
||||||
|
|
||||||
ELSE ws+ IF %eof{ type = ELSE_IF; return; };
|
#ELSE ws+ IF %eof{ type = ELSE_IF; return; };
|
||||||
ELSE ws+ IF ws => { type = ELSE_IF; return; };
|
#ELSE ws+ IF ws => { type = ELSE_IF; return; };
|
||||||
|
|
||||||
EVALUATE %eof{ type = EVALUATE; return; };
|
EVALUATE %eof{ type = EVALUATE; return; };
|
||||||
EVALUATE ws => { type = EVALUATE; return; };
|
EVALUATE ws => { type = EVALUATE; return; };
|
||||||
@ -141,6 +144,16 @@
|
|||||||
BEGIN %eof{ type = BEGIN; return; };
|
BEGIN %eof{ type = BEGIN; return; };
|
||||||
BEGIN ws => { 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; };
|
'(' => { type = LPAREN; return; };
|
||||||
*|;
|
*|;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user