mirror of
https://github.com/ksherlock/mpw-shell.git
synced 2024-12-26 11:30:27 +00:00
update the parser/lexer to make it more reusable. For example, subshells are now handled.
This commit is contained in:
parent
548880a517
commit
33dd3228f8
@ -85,17 +85,17 @@ RAGEL_TARGET(value value.rl value.cpp COMPILE_FLAGS "-p -G2")
|
||||
|
||||
# need to copy all OUTPUT file to the build dir
|
||||
add_custom_command(
|
||||
OUTPUT phase2-parser.cpp phase2-parser.h phase2-parser.out
|
||||
COMMAND lemon++ phase2-parser.lemon
|
||||
COMMAND mv -f phase2-parser.cpp phase2-parser.h phase2-parser.out ${CMAKE_CURRENT_BINARY_DIR}/
|
||||
MAIN_DEPENDENCY phase2-parser.lemon
|
||||
OUTPUT phase3.cpp phase3.h phase3.out
|
||||
COMMAND lemon++ phase3.lemon
|
||||
COMMAND mv -f phase3.cpp phase3.h phase3.out ${CMAKE_CURRENT_BINARY_DIR}/
|
||||
MAIN_DEPENDENCY phase3.lemon
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
|
||||
add_executable(mpw-shell mpw-shell.cpp mpw-shell-token.cpp mpw-shell-expand.cpp
|
||||
mpw-shell-parser.cpp value.cpp mpw-shell-quote.cpp
|
||||
phase1.cpp phase2.cpp phase2-parser.cpp command.cpp environment.cpp builtins.cpp
|
||||
mpw-shell-parser.cpp mpw_parser.cpp value.cpp mpw-shell-quote.cpp
|
||||
phase1.cpp phase2.cpp phase3.cpp command.cpp environment.cpp builtins.cpp
|
||||
pathnames.cpp
|
||||
macroman.cpp
|
||||
cxx/mapped_file.cpp
|
||||
|
23
builtins.cpp
23
builtins.cpp
@ -855,12 +855,15 @@ int builtin_which(Environment &env, const std::vector<std::string> &tokens, cons
|
||||
"catenate",
|
||||
"directory",
|
||||
"echo",
|
||||
"execute",
|
||||
"exists",
|
||||
"export",
|
||||
"false", // not in MPW
|
||||
"parameters",
|
||||
"quote",
|
||||
"set",
|
||||
"shift",
|
||||
"true", // not in MPW
|
||||
"unalias",
|
||||
"unexport",
|
||||
"unset",
|
||||
@ -1034,3 +1037,23 @@ int builtin_true(Environment &, const std::vector<std::string> &, const fdmask &
|
||||
int builtin_false(Environment &, const std::vector<std::string> &, const fdmask &) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int builtin_execute(Environment &e, const std::vector<std::string> &tokens, const fdmask &fds) {
|
||||
|
||||
// runs argv[1] in the current environment. unlike MPW, argv[1] must be a script.
|
||||
// special enhancement -- '-' means execute stdin.
|
||||
|
||||
if (tokens.size() < 2) return 0;
|
||||
std::string filename = tokens[1];
|
||||
if (filename == "-") {
|
||||
// since we're parsing stdin, don't let any children read it. [???]
|
||||
fdset new_fds;
|
||||
int fd = open("/dev/null", O_RDONLY);
|
||||
new_fds.set(0, fd);
|
||||
|
||||
return read_fd(e, fds[0], new_fds | fds);
|
||||
}
|
||||
|
||||
return read_file(e, filename, fds);
|
||||
}
|
||||
|
@ -25,6 +25,12 @@ int builtin_which(Environment &e, const std::vector<std::string> &, const fdmask
|
||||
int builtin_alias(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
int builtin_unalias(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
|
||||
int builtin_execute(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
int builtin_true(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
int builtin_false(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
int builtin_execute(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
|
||||
|
||||
int builtin_evaluate(Environment &e, std::vector<token> &&, const fdmask &);
|
||||
|
||||
#endif
|
287
command.cpp
287
command.cpp
@ -1,5 +1,5 @@
|
||||
#include "command.h"
|
||||
#include "phase2-parser.h"
|
||||
#include "phase3.h"
|
||||
#include "environment.h"
|
||||
#include "fdset.h"
|
||||
#include "builtins.h"
|
||||
@ -198,6 +198,7 @@ namespace {
|
||||
{"catenate", builtin_catenate},
|
||||
{"directory", builtin_directory},
|
||||
{"echo", builtin_echo},
|
||||
{"execute", builtin_execute},
|
||||
{"exists", builtin_exists},
|
||||
{"export", builtin_export},
|
||||
{"parameters", builtin_parameters},
|
||||
@ -209,6 +210,10 @@ namespace {
|
||||
{"unset", builtin_unset},
|
||||
{"version", builtin_version},
|
||||
{"which", builtin_which},
|
||||
|
||||
// not in MPW.
|
||||
{"true", builtin_true},
|
||||
{"false", builtin_false},
|
||||
};
|
||||
|
||||
|
||||
@ -278,9 +283,6 @@ namespace {
|
||||
}
|
||||
|
||||
|
||||
|
||||
//std::string expand_vars(const std::string &s, const class Environment &);
|
||||
|
||||
command::~command()
|
||||
{}
|
||||
|
||||
@ -293,7 +295,7 @@ command::~command()
|
||||
|
||||
|
||||
template<class F>
|
||||
int exec(std::string command, Environment &env, bool throwup, F &&fx) {
|
||||
int exec(std::string command, Environment &env, const fdmask &fds, bool throwup, F &&fx) {
|
||||
|
||||
bool echo = true;
|
||||
int rv = 0;
|
||||
@ -302,7 +304,7 @@ int exec(std::string command, Environment &env, bool throwup, F &&fx) {
|
||||
|
||||
try {
|
||||
process p;
|
||||
command = expand_vars(command, env);
|
||||
command = expand_vars(command, env, fds);
|
||||
auto tokens = tokenize(command, false);
|
||||
if (tokens.empty()) return 0;
|
||||
parse_tokens(std::move(tokens), p);
|
||||
@ -330,7 +332,7 @@ int exec(std::string command, Environment &env, bool throwup, F &&fx) {
|
||||
int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
|
||||
return exec(text, env, throwup, [&](process &p){
|
||||
return exec(text, env, fds, throwup, [&](process &p){
|
||||
|
||||
fdmask newfds = p.fds | fds;
|
||||
|
||||
@ -367,7 +369,7 @@ int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
}
|
||||
|
||||
template<class F>
|
||||
int eval_exec(std::string command, Environment &env, bool throwup, F &&fx){
|
||||
int eval_exec(std::string command, Environment &env, const fdmask &fds, bool throwup, F &&fx){
|
||||
|
||||
std::string name;
|
||||
bool echo = true;
|
||||
@ -376,7 +378,7 @@ int eval_exec(std::string command, Environment &env, bool throwup, F &&fx){
|
||||
if (control_c) throw execution_of_input_terminated();
|
||||
|
||||
try {
|
||||
command = expand_vars(command, env);
|
||||
command = expand_vars(command, env, fds);
|
||||
auto tokens = tokenize(command, true);
|
||||
|
||||
if (tokens.empty()) return 0;
|
||||
@ -406,29 +408,8 @@ int eval_exec(std::string command, Environment &env, bool throwup, F &&fx){
|
||||
|
||||
int evaluate_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
#if 0
|
||||
if (control_c) throw execution_of_input_terminated();
|
||||
|
||||
std::string s = expand_vars(text, env);
|
||||
|
||||
env.set("command", "evaluate");
|
||||
|
||||
env.echo("%s", s.c_str());
|
||||
|
||||
try {
|
||||
auto tokens = tokenize(s, true);
|
||||
if (tokens.empty()) return 0;
|
||||
|
||||
int status = builtin_evaluate(env, std::move(tokens), fds);
|
||||
|
||||
return env.status(status, throwup);
|
||||
} catch (std::exception &e) {
|
||||
fprintf(stderr, "%s\n", e.what());
|
||||
return env.status(1, throwup);
|
||||
}
|
||||
#endif
|
||||
|
||||
return eval_exec(text, env, throwup, [&](token_vector &tokens){
|
||||
return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){
|
||||
env.set("command", "evaluate");
|
||||
return builtin_evaluate(env, std::move(tokens), fds);
|
||||
});
|
||||
@ -439,7 +420,7 @@ int evaluate_command::execute(Environment &env, const fdmask &fds, bool throwup)
|
||||
|
||||
int break_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return eval_exec(text, env, throwup, [&](token_vector &tokens){
|
||||
return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){
|
||||
env.set("command", "break");
|
||||
if (!env.loop()) throw break_error();
|
||||
int status = evaluate(BREAK, std::move(tokens), env);
|
||||
@ -447,30 +428,11 @@ int break_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
return status;
|
||||
});
|
||||
|
||||
#if 0
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
int continue_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return eval_exec(text, env, throwup, [&](token_vector &tokens){
|
||||
return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){
|
||||
env.set("command", "continue");
|
||||
if (!env.loop()) throw continue_error();
|
||||
int status = evaluate(CONTINUE, std::move(tokens), env);
|
||||
@ -478,26 +440,6 @@ int continue_command::execute(Environment &env, const fdmask &fds, bool throwup)
|
||||
return status;
|
||||
});
|
||||
|
||||
#if 0
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
int or_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||
@ -577,7 +519,7 @@ int error_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||
return e.status(-3);
|
||||
}
|
||||
|
||||
std::string s = expand_vars(text, e);
|
||||
std::string s = expand_vars(text, e, fds);
|
||||
|
||||
e.echo("%s", s.c_str());
|
||||
|
||||
@ -601,7 +543,7 @@ int error_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||
}
|
||||
|
||||
template<class F>
|
||||
int begin_end_exec(std::string begin, std::string end, Environment &env, bool throwup, F &&fx) {
|
||||
int begin_end_exec(std::string begin, std::string end, Environment &env, const fdmask &fds, bool throwup, F &&fx) {
|
||||
|
||||
|
||||
bool echo = true;
|
||||
@ -611,8 +553,8 @@ int begin_end_exec(std::string begin, std::string end, Environment &env, bool th
|
||||
|
||||
try {
|
||||
process p;
|
||||
begin = expand_vars(begin, env);
|
||||
end = expand_vars(end, env);
|
||||
begin = expand_vars(begin, env, fds);
|
||||
end = expand_vars(end, env, fds);
|
||||
|
||||
auto b = tokenize(begin, true);
|
||||
auto e = tokenize(end, false);
|
||||
@ -639,12 +581,11 @@ int begin_end_exec(std::string begin, std::string end, Environment &env, bool th
|
||||
}
|
||||
|
||||
return env.status(rv, throwup);
|
||||
|
||||
}
|
||||
|
||||
int begin_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return begin_end_exec(begin, end, env, throwup, [&](token_vector &b, process &p){
|
||||
return begin_end_exec(begin, end, env, fds, throwup, [&](token_vector &b, process &p){
|
||||
|
||||
env.set("command", type == BEGIN ? "end" : ")");
|
||||
if (b.size() != 1) {
|
||||
@ -664,53 +605,12 @@ int begin_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return rv;
|
||||
});
|
||||
|
||||
#if 0
|
||||
|
||||
std::string b = expand_vars(begin, env);
|
||||
std::string e = expand_vars(end, env);
|
||||
|
||||
|
||||
env.set("command", type == BEGIN ? "end" : ")");
|
||||
|
||||
// echo!
|
||||
env.echo("%s ... %s",
|
||||
b.c_str(),
|
||||
e.c_str()
|
||||
);
|
||||
|
||||
|
||||
|
||||
// tokenize the begin and end commands.
|
||||
// begin may not have extra arguments. end may have redirection.
|
||||
|
||||
auto bt = tokenize(b, true);
|
||||
if (bt.size() != 1) {
|
||||
fprintf(stderr, "### Begin - Too many parameters were specified.\n");
|
||||
fprintf(stderr, "Usage - Begin\n");
|
||||
return env.status(-3);
|
||||
}
|
||||
|
||||
fdmask newfds;
|
||||
int status = check_ends(e, newfds);
|
||||
newfds |= fds;
|
||||
if (status) return env.status(status);
|
||||
|
||||
int rv;
|
||||
env.indent_and([&]{
|
||||
rv = vector_command::execute(env, newfds);
|
||||
});
|
||||
|
||||
env.echo("%s", type == BEGIN ? "end" : ")");
|
||||
|
||||
return env.status(rv);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int loop_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return begin_end_exec(begin, end, env, throwup, [&](token_vector &b, process &p){
|
||||
return begin_end_exec(begin, end, env, fds, throwup, [&](token_vector &b, process &p){
|
||||
|
||||
env.set("command", "end");
|
||||
if (b.size() != 1) {
|
||||
@ -742,58 +642,11 @@ int loop_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return rv;
|
||||
});
|
||||
|
||||
|
||||
#if 0
|
||||
std::string b = expand_vars(begin, env);
|
||||
std::string e = expand_vars(end, env);
|
||||
|
||||
|
||||
env.set("command", "end");
|
||||
|
||||
// echo!
|
||||
env.echo("%s ... %s",
|
||||
b.c_str(),
|
||||
e.c_str()
|
||||
);
|
||||
|
||||
// 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(;;) {
|
||||
|
||||
if (control_c) throw execution_of_input_terminated();
|
||||
|
||||
try {
|
||||
env.loop_indent_and([&]{
|
||||
rv = vector_command::execute(env, newfds);
|
||||
});
|
||||
}
|
||||
catch (break_command_t &ex) {
|
||||
env.echo("end");
|
||||
break;
|
||||
}
|
||||
catch (continue_command_t &ex) {}
|
||||
env.echo("end");
|
||||
}
|
||||
return env.status(rv);
|
||||
#endif
|
||||
}
|
||||
|
||||
int for_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return begin_end_exec(begin, end, env, throwup, [&](token_vector &b, process &p){
|
||||
return begin_end_exec(begin, end, env, fds, throwup, [&](token_vector &b, process &p){
|
||||
|
||||
env.set("command", "end");
|
||||
|
||||
@ -827,56 +680,6 @@ int for_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return rv;
|
||||
});
|
||||
|
||||
#if 0
|
||||
std::string b = expand_vars(begin, env);
|
||||
std::string e = expand_vars(end, env);
|
||||
|
||||
|
||||
env.set("command", "end");
|
||||
|
||||
// echo!
|
||||
env.echo("%s ... %s",
|
||||
b.c_str(),
|
||||
e.c_str()
|
||||
);
|
||||
|
||||
// check for extra tokens...
|
||||
auto bt = tokenize(b, true);
|
||||
if (bt.size() < 3 || strcasecmp(bt[2].string.c_str(), "in")) {
|
||||
fprintf(stderr, "### For - Missing in keyword.\n");
|
||||
fprintf(stderr, "Usage - For name in [word...]\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 (int i = 3; i < bt.size(); ++i ) {
|
||||
|
||||
if (control_c) throw execution_of_input_terminated();
|
||||
|
||||
env.set(bt[1].string, bt[i].string);
|
||||
|
||||
try {
|
||||
env.loop_indent_and([&]{
|
||||
rv = vector_command::execute(env, newfds);
|
||||
});
|
||||
}
|
||||
catch (break_command_t &ex) {
|
||||
env.echo("end");
|
||||
break;
|
||||
}
|
||||
catch (continue_command_t &ex) {}
|
||||
env.echo("end");
|
||||
}
|
||||
|
||||
return env.status(rv);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -901,7 +704,7 @@ int if_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
int tmp;
|
||||
if (first) {
|
||||
first = false;
|
||||
tmp = begin_end_exec(c->clause, end, env, false, [&](token_vector &b, process &p){
|
||||
tmp = begin_end_exec(c->clause, end, env, fds, false, [&](token_vector &b, process &p){
|
||||
|
||||
newfds = p.fds | fds;
|
||||
|
||||
@ -923,7 +726,7 @@ int if_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
}
|
||||
else {
|
||||
// second...
|
||||
tmp = eval_exec(c->clause, env, false, [&](token_vector &b){
|
||||
tmp = eval_exec(c->clause, env, fds, false, [&](token_vector &b){
|
||||
if (skip || error) return 0;
|
||||
|
||||
int status = evaluate(c->type, std::move(b), env);
|
||||
@ -946,49 +749,5 @@ int if_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
env.echo("end");
|
||||
if (error) return env.status(error, throwup);
|
||||
return env.status(rv, throwup);
|
||||
|
||||
#if 0
|
||||
|
||||
|
||||
|
||||
|
||||
int rv = 0;
|
||||
bool ok = false;
|
||||
|
||||
std::string e = expand_vars(end, env);
|
||||
|
||||
env.set("command", "end");
|
||||
|
||||
// parse end for indirection.
|
||||
fdmask newfds;
|
||||
int status = check_ends(e, newfds);
|
||||
newfds |= fds;
|
||||
if (status) {
|
||||
rv = status;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
for (auto &c : clauses) {
|
||||
std::string s = expand_vars(c->clause, env);
|
||||
|
||||
if (c->type == IF)
|
||||
env.echo("%s ... %s", s.c_str(), e.c_str());
|
||||
else
|
||||
env.echo("%s", s.c_str());
|
||||
|
||||
if (ok) continue;
|
||||
|
||||
int tmp = evaluate(c->type, s, env);
|
||||
if (tmp < 0) { ok = true; rv = tmp; continue; }
|
||||
if (tmp == 0) continue;
|
||||
|
||||
env.indent_and([&](){
|
||||
rv = c->execute(env, newfds);
|
||||
});
|
||||
}
|
||||
|
||||
env.echo("end");
|
||||
return env.status(rv);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include "phase2-parser.h"
|
||||
#include "phase3.h"
|
||||
|
||||
typedef std::unique_ptr<struct command> command_ptr;
|
||||
typedef std::vector<command_ptr> command_ptr_vector;
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <stdexcept>
|
||||
#include <system_error>
|
||||
#include <algorithm>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
@ -42,16 +44,29 @@
|
||||
fgoto *xcs;
|
||||
}
|
||||
|
||||
action einit{ /* einit */ ev.clear(); xcs = fcurs; fgoto estring_state; }
|
||||
action epush{ /* epush */ ev.push_back(fc); }
|
||||
action efinish1{
|
||||
action einit { /* einit */ ev.clear(); xcs = fcurs; fgoto estring_state; }
|
||||
action epush { /* epush */ ev.push_back(fc); }
|
||||
action efinish1 {
|
||||
/* efinish1 */
|
||||
|
||||
/*
|
||||
throw std::runtime_error("MPW Shell - `...` not yet supported.");
|
||||
*/
|
||||
|
||||
std::string s = subshell(ev, env, fds);
|
||||
scratch.append(s);
|
||||
|
||||
fgoto *xcs;
|
||||
}
|
||||
action efinish2{
|
||||
action efinish2 {
|
||||
/* efinish2 */
|
||||
throw std::runtime_error("MPW Shell - `...` not yet supported.");
|
||||
|
||||
std::string s = subshell(ev, env, fds);
|
||||
for (auto c : s) {
|
||||
if (c == '\'' || c == '"' ) scratch.push_back(escape);
|
||||
scratch.push_back(c);
|
||||
}
|
||||
|
||||
fgoto *xcs;
|
||||
}
|
||||
|
||||
@ -110,9 +125,48 @@
|
||||
|
||||
namespace {
|
||||
%% write data;
|
||||
|
||||
|
||||
std::string subshell(const std::string &s, Environment &env, const fdmask &fds) {
|
||||
|
||||
|
||||
char temp[32] = "/tmp/mpw-shell-XXXXXXXX";
|
||||
|
||||
int fd = mkstemp(temp);
|
||||
unlink(temp);
|
||||
|
||||
fdset new_fds;
|
||||
new_fds.set(1, fd);
|
||||
|
||||
int rv = 0;
|
||||
env.indent_and([&](){
|
||||
|
||||
rv = read_string(env, s, new_fds | fds);
|
||||
|
||||
});
|
||||
|
||||
std::string tmp;
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
for(;;) {
|
||||
uint8_t buffer[1024];
|
||||
ssize_t len = read(fd, buffer, sizeof(buffer));
|
||||
if (len == 0) break;
|
||||
if (len < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
throw std::system_error(errno, std::system_category(), "read");
|
||||
}
|
||||
tmp.append(buffer, buffer + len);
|
||||
}
|
||||
std::transform(tmp.begin(), tmp.end(), tmp.begin(), [](uint8_t x){
|
||||
if (x == '\r' || x == '\n') x = ' ';
|
||||
return x;
|
||||
});
|
||||
return tmp;
|
||||
}
|
||||
|
||||
std::string expand_vars(const std::string &s, const Environment &env) {
|
||||
}
|
||||
|
||||
std::string expand_vars(const std::string &s, Environment &env, const fdmask &fds) {
|
||||
if (s.find_first_of("{`", 0, 2) == s.npos) return s;
|
||||
|
||||
int cs;
|
||||
|
155
mpw-shell.cpp
155
mpw-shell.cpp
@ -12,14 +12,12 @@
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "mpw-shell.h"
|
||||
#include "mpw_parser.h"
|
||||
|
||||
#include "fdset.h"
|
||||
|
||||
#include "macroman.h"
|
||||
|
||||
#include "phase1.h"
|
||||
#include "phase2.h"
|
||||
#include "command.h"
|
||||
|
||||
#include "cxx/mapped_file.h"
|
||||
#include "cxx/filesystem.h"
|
||||
#include "cxx/string_splitter.h"
|
||||
@ -80,48 +78,60 @@ void init(Environment &env) {
|
||||
}
|
||||
|
||||
|
||||
int read_file(phase1 &p, const std::string &file) {
|
||||
|
||||
int read_file(Environment &e, const std::string &file, const fdmask &fds) {
|
||||
std::error_code ec;
|
||||
const mapped_file mf(file, mapped_file::readonly, ec);
|
||||
if (ec) {
|
||||
fprintf(stderr, "# Error reading %s: %s\n", file.c_str(), ec.message().c_str());
|
||||
return 0;
|
||||
return e.status(-1, false);
|
||||
}
|
||||
|
||||
p.process(mf.begin(), mf.end(), false);
|
||||
|
||||
mpw_parser p(e, fds);
|
||||
e.status(0, false);
|
||||
|
||||
p.parse(mf.begin(), mf.end());
|
||||
p.finish();
|
||||
return 0;
|
||||
return e.status();
|
||||
}
|
||||
|
||||
int read_fd(phase1 &p, int fd) {
|
||||
|
||||
int read_string(Environment &e, const std::string &s, const fdmask &fds) {
|
||||
mpw_parser p(e, fds);
|
||||
e.status(0, false);
|
||||
p.parse(s);
|
||||
p.finish();
|
||||
return e.status();
|
||||
}
|
||||
|
||||
|
||||
int read_fd(Environment &e, int fd, const fdmask &fds) {
|
||||
|
||||
unsigned char buffer[2048];
|
||||
ssize_t size;
|
||||
|
||||
mpw_parser p(e, fds);
|
||||
e.status(0, false);
|
||||
|
||||
for (;;) {
|
||||
size = read(fd, buffer, sizeof(buffer));
|
||||
if (size < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
perror("read: ");
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
if (size == 0) p.finish();
|
||||
else p.process(buffer, buffer + size);
|
||||
} catch(std::exception &ex) {
|
||||
fprintf(stderr, "### %s\n", ex.what());
|
||||
p.reset();
|
||||
perror("read");
|
||||
e.status(-1, false);
|
||||
}
|
||||
if (size == 0) break;
|
||||
p.parse(buffer, buffer + size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
p.finish();
|
||||
return e.status();
|
||||
}
|
||||
|
||||
void launch_mpw(const Environment &env, const std::vector<std::string> &argv, const fdmask &fds);
|
||||
fs::path which(const Environment &env, const std::string &name);
|
||||
|
||||
int read_make(phase1 &p1, phase2 &p2, Environment &env, const std::vector<std::string> &argv) {
|
||||
int read_make(Environment &env, const std::vector<std::string> &argv) {
|
||||
|
||||
int out[2];
|
||||
int ok;
|
||||
@ -153,10 +163,9 @@ int read_make(phase1 &p1, phase2 &p2, Environment &env, const std::vector<std::s
|
||||
}
|
||||
|
||||
close(out[1]);
|
||||
int rv = read_fd(p1, out[0]);
|
||||
int rv = read_fd(env, out[0]);
|
||||
close(out[0]);
|
||||
p1.finish();
|
||||
p2.finish();
|
||||
|
||||
|
||||
// check for make errors.
|
||||
for(;;) {
|
||||
@ -182,7 +191,7 @@ int read_make(phase1 &p1, phase2 &p2, Environment &env, const std::vector<std::s
|
||||
exit(EX_OSERR);
|
||||
}
|
||||
|
||||
return env.status();
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::atomic<int> control_c{0};
|
||||
@ -196,7 +205,7 @@ void control_c_handler(int signal, siginfo_t *sinfo, void *context) {
|
||||
}
|
||||
|
||||
|
||||
int interactive(Environment &env, phase1 &p1, phase2& p2) {
|
||||
int interactive(Environment &env) {
|
||||
|
||||
std::string history_file = root();
|
||||
history_file += ".history";
|
||||
@ -213,18 +222,18 @@ int interactive(Environment &env, phase1 &p1, phase2& p2) {
|
||||
|
||||
sigaction(SIGINT, &act, &old_act);
|
||||
|
||||
mpw_parser p(env, true);
|
||||
|
||||
|
||||
for(;;) {
|
||||
const char *prompt = "# ";
|
||||
if (p1.continuation() || p2.continuation()) prompt = "> ";
|
||||
if (p.continuation()) prompt = "> ";
|
||||
char *cp = readline(prompt);
|
||||
if (!cp) {
|
||||
if (control_c) {
|
||||
control_c = 0;
|
||||
fprintf(stdout, "\n");
|
||||
p1.abort();
|
||||
p2.abort();
|
||||
p.abort();
|
||||
env.status(-9, false);
|
||||
continue;
|
||||
}
|
||||
@ -246,29 +255,17 @@ int interactive(Environment &env, phase1 &p1, phase2& p2) {
|
||||
s = utf8_to_macroman(s);
|
||||
|
||||
s.push_back('\n');
|
||||
try {
|
||||
p1.process(s);
|
||||
|
||||
} catch(std::exception &ex) {
|
||||
fprintf(stderr, "### %s\n", ex.what());
|
||||
p1.reset();
|
||||
}
|
||||
p.parse(s);
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
p1.finish();
|
||||
} catch(std::exception &ex) {
|
||||
fprintf(stderr, "### %s\n", ex.what());
|
||||
p1.reset();
|
||||
}
|
||||
p.finish();
|
||||
|
||||
sigaction(SIGINT, &old_act, nullptr);
|
||||
|
||||
write_history(history_file.c_str());
|
||||
fprintf(stdout, "\n");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -361,34 +358,8 @@ int make(int argc, char **argv) {
|
||||
|
||||
}
|
||||
|
||||
phase1 p1;
|
||||
phase2 p2;
|
||||
|
||||
p1 >>= [&p2](std::string &&s) {
|
||||
if (s.empty()) p2.finish();
|
||||
else p2(std::move(s));
|
||||
};
|
||||
|
||||
p2 >>= [&e](command_ptr_vector &&v) {
|
||||
|
||||
for (auto iter = v.begin(); iter != v.end(); ++iter) {
|
||||
auto &ptr = *iter;
|
||||
fdmask fds;
|
||||
try {
|
||||
ptr->execute(e, fds);
|
||||
} catch (execution_of_input_terminated &ex) {
|
||||
control_c = 0;
|
||||
fprintf(stderr, "### %s\n", ex.what());
|
||||
if (e.exit())
|
||||
exit(ex.status());
|
||||
e.status(ex.status(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
e.startup(true);
|
||||
read_file(p1, root() / "Startup");
|
||||
read_file(e, root() / "Startup");
|
||||
e.startup(false);
|
||||
|
||||
auto path = which(e, "Make");
|
||||
@ -399,7 +370,7 @@ int make(int argc, char **argv) {
|
||||
e.set("command", path);
|
||||
args[0] = path;
|
||||
|
||||
return read_make(p1, p2, e, args);
|
||||
return read_make(e, args);
|
||||
|
||||
}
|
||||
|
||||
@ -517,40 +488,14 @@ int main(int argc, char **argv) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
phase1 p1;
|
||||
phase2 p2;
|
||||
|
||||
p1 >>= [&p2](std::string &&s) {
|
||||
if (s.empty()) p2.finish();
|
||||
else p2(std::move(s));
|
||||
};
|
||||
|
||||
p2 >>= [&e](command_ptr_vector &&v) {
|
||||
|
||||
for (auto iter = v.begin(); iter != v.end(); ++iter) {
|
||||
auto &ptr = *iter;
|
||||
fdmask fds;
|
||||
try {
|
||||
ptr->execute(e, fds);
|
||||
} catch (execution_of_input_terminated &ex) {
|
||||
control_c = 0;
|
||||
if (!(ptr->terminal() && ++iter == v.end())) {
|
||||
fprintf(stderr, "### %s\n", ex.what());
|
||||
}
|
||||
e.status(ex.status(), false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!cflag) fprintf(stdout, "MPW Shell " VERSION "\n");
|
||||
if (!fflag) {
|
||||
fs::path startup = root() / "Startup";
|
||||
e.startup(true);
|
||||
mpw_parser p(e);
|
||||
|
||||
try {
|
||||
read_file(p1, startup);
|
||||
read_file(e, startup);
|
||||
} catch (const std::system_error &ex) {
|
||||
fprintf(stderr, "### %s: %s\n", startup.c_str(), ex.what());
|
||||
}
|
||||
@ -558,18 +503,14 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
if (cflag) {
|
||||
std::string s(cflag);
|
||||
s.push_back('\n');
|
||||
p1.process(s, true);
|
||||
p2.finish();
|
||||
read_string(e, cflag);
|
||||
exit(e.status());
|
||||
}
|
||||
|
||||
if (isatty(STDIN_FILENO))
|
||||
interactive(e, p1, p2);
|
||||
interactive(e);
|
||||
else
|
||||
read_fd(p1, STDIN_FILENO);
|
||||
p2.finish();
|
||||
read_fd(e, STDIN_FILENO);
|
||||
|
||||
exit(e.status());
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "environment.h"
|
||||
#include "fdset.h"
|
||||
|
||||
const unsigned char escape = 0xb6;
|
||||
|
||||
@ -49,7 +50,7 @@ public:
|
||||
|
||||
|
||||
std::vector<token> tokenize(std::string &s, bool eval = false);
|
||||
std::string expand_vars(const std::string &s, const class Environment &);
|
||||
std::string expand_vars(const std::string &s, class Environment &, const fdmask &fds = fdmask());
|
||||
|
||||
//std::string quote(std::string &&s);
|
||||
std::string quote(const std::string &s);
|
||||
@ -67,4 +68,9 @@ int32_t evaluate_expression(const std::string &name, std::vector<token> &&tokens
|
||||
|
||||
|
||||
|
||||
int read_file(Environment &e, const std::string &file, const fdmask &fds = fdmask());
|
||||
int read_string(Environment &e, const std::string &s, const fdmask &fds = fdmask());
|
||||
int read_fd(Environment &e, int fd, const fdmask &fds = fdmask());
|
||||
|
||||
|
||||
#endif
|
||||
|
104
mpw_parser.cpp
Normal file
104
mpw_parser.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
|
||||
#include "mpw_parser.h"
|
||||
#include "phase3_parser.h"
|
||||
|
||||
#include "command.h"
|
||||
#include "error.h"
|
||||
|
||||
mpw_parser::mpw_parser(Environment &e, fdmask fds, bool interactive) : _env(e), _fds(fds), _interactive(interactive)
|
||||
{
|
||||
|
||||
_p3 = phase3::make();
|
||||
_p2.set_next([this](int type, std::string &&s){
|
||||
_p3->parse(type, std::move(s));
|
||||
});
|
||||
|
||||
_p1.set_next([this](std::string &&s){
|
||||
_p2.parse(std::move(s));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
mpw_parser::~mpw_parser() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool mpw_parser::continuation() const {
|
||||
if (_p1.continuation()) return true;
|
||||
if (_p2.continuation()) return true;
|
||||
if (_p3->continuation()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void mpw_parser::finish() {
|
||||
_p1.finish();
|
||||
_p2.finish();
|
||||
_p3->parse(0, "");
|
||||
|
||||
// and now execute the commands...
|
||||
execute();
|
||||
}
|
||||
|
||||
|
||||
void mpw_parser::reset() {
|
||||
_p1.reset();
|
||||
_p2.reset();
|
||||
_p3->reset();
|
||||
_abort = false;
|
||||
}
|
||||
|
||||
void mpw_parser::abort() {
|
||||
_p1.abort();
|
||||
_p2.abort();
|
||||
//_p3->abort();
|
||||
_p3->reset();
|
||||
_abort = true;
|
||||
}
|
||||
|
||||
|
||||
void mpw_parser::parse(const void *begin, const void *end) {
|
||||
if (_abort) return;
|
||||
_p1.parse((const unsigned char *)begin, (const unsigned char *)end);
|
||||
|
||||
// and execute...
|
||||
execute();
|
||||
}
|
||||
|
||||
|
||||
void mpw_parser::execute() {
|
||||
if (_abort) {
|
||||
_p3->command_queue.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
auto commands = std::move(_p3->command_queue);
|
||||
_p3->command_queue.clear();
|
||||
|
||||
|
||||
std::reverse(commands.begin(), commands.end());
|
||||
|
||||
command_ptr cmd;
|
||||
|
||||
try {
|
||||
while (!commands.empty()) {
|
||||
cmd = std::move(commands.back());
|
||||
commands.pop_back();
|
||||
cmd->execute(_env, _fds);
|
||||
}
|
||||
|
||||
} catch (execution_of_input_terminated &ex) {
|
||||
|
||||
_env.status(ex.status(), false);
|
||||
|
||||
if (_interactive) {
|
||||
if (!cmd->terminal() || !commands.empty()) {
|
||||
fprintf(stderr, "### %s\n", ex.what());
|
||||
}
|
||||
return;
|
||||
}
|
||||
_abort = true;
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
59
mpw_parser.h
Normal file
59
mpw_parser.h
Normal file
@ -0,0 +1,59 @@
|
||||
#ifndef __mpw_parser__
|
||||
#define __mpw_parser__
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "Environment.h"
|
||||
#include "fdset.h"
|
||||
|
||||
#include "phase1.h"
|
||||
#include "phase2.h"
|
||||
|
||||
class mpw_parser {
|
||||
|
||||
public:
|
||||
|
||||
mpw_parser(Environment &e, fdmask fds = fdmask(), bool interactive = false);
|
||||
mpw_parser(Environment &e, bool interactive) : mpw_parser(e, fdmask(), interactive)
|
||||
{}
|
||||
|
||||
~mpw_parser();
|
||||
|
||||
void parse(const void *begin, const void *end);
|
||||
void parse(const std::string &s) {
|
||||
parse(s.data(), s.data() + s.size());
|
||||
}
|
||||
void parse(const void *begin, size_t length) {
|
||||
parse(begin, (const char *)begin + length);
|
||||
}
|
||||
|
||||
void reset();
|
||||
void abort();
|
||||
void finish();
|
||||
|
||||
bool continuation() const;
|
||||
|
||||
private:
|
||||
|
||||
mpw_parser& operator=(const mpw_parser &) = delete;
|
||||
mpw_parser& operator=(mpw_parser &&) = delete;
|
||||
|
||||
mpw_parser(const mpw_parser &) = delete;
|
||||
mpw_parser(mpw_parser &&) = delete;
|
||||
|
||||
void execute();
|
||||
|
||||
Environment &_env;
|
||||
fdmask _fds;
|
||||
bool _interactive = false;
|
||||
bool _abort = false;
|
||||
|
||||
phase1 _p1;
|
||||
phase2 _p2;
|
||||
std::unique_ptr<class phase3> _p3;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
14
phase1.cpp
14
phase1.cpp
@ -150,19 +150,10 @@ text:
|
||||
}
|
||||
|
||||
|
||||
void phase1::process(const std::string &s, bool final) {
|
||||
|
||||
for (auto c : s) {
|
||||
cs = process(c, cs);
|
||||
}
|
||||
if (final) finish();
|
||||
}
|
||||
|
||||
void phase1::process(const unsigned char *begin, const unsigned char *end, bool final) {
|
||||
void phase1::parse(const unsigned char *begin, const unsigned char *end) {
|
||||
while (begin != end) {
|
||||
cs = process(*begin++, cs);
|
||||
}
|
||||
if (final) finish();
|
||||
}
|
||||
|
||||
void phase1::finish() {
|
||||
@ -181,7 +172,6 @@ void phase1::reset() {
|
||||
void phase1::flush() {
|
||||
multiline = false;
|
||||
if (scratch.empty()) return;
|
||||
// strip trailing whitespace?
|
||||
if (pipe_to) pipe_to(std::move(scratch));
|
||||
if (_then) _then(std::move(scratch));
|
||||
scratch.clear();
|
||||
}
|
||||
|
32
phase1.h
32
phase1.h
@ -8,47 +8,33 @@
|
||||
class phase1 {
|
||||
|
||||
public:
|
||||
typedef std::function<void(std::string &&)> pipe_function;
|
||||
|
||||
typedef std::function<void(std::string &&)> next_function_type;
|
||||
|
||||
phase1() = default;
|
||||
|
||||
void process(const unsigned char *begin, const unsigned char *end, bool final = false);
|
||||
|
||||
void process(const char *begin, const char *end, bool final = false) {
|
||||
process((const unsigned char *)begin, (const unsigned char *)end, final);
|
||||
}
|
||||
|
||||
void process(const std::string &s, bool final = false);// { process(s.data(), s.data() + s.size(), final); }
|
||||
|
||||
void finish();// { const char *tmp = "\n"; process(tmp, tmp+1, true); }
|
||||
void parse(const unsigned char *begin, const unsigned char *end);
|
||||
void parse(const std::string &s) { parse((const unsigned char *)s.data(), (const unsigned char *)s.data() + s.size()); }
|
||||
void finish();
|
||||
|
||||
void reset();
|
||||
void abort() { reset(); }
|
||||
|
||||
|
||||
phase1 &operator >>= (pipe_function f) { pipe_to = f; return *this; }
|
||||
|
||||
|
||||
template<class F>
|
||||
phase1 &operator >>= (F &f) {
|
||||
using std::placeholders::_1;
|
||||
pipe_to = std::bind(&F::operator(), &f, _1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
bool continuation() const { return multiline; }
|
||||
|
||||
void set_next(next_function_type &&fx) { _then = std::move(fx); }
|
||||
|
||||
private:
|
||||
|
||||
int process(unsigned char, int);
|
||||
void flush();
|
||||
|
||||
std::string scratch;
|
||||
pipe_function pipe_to;
|
||||
int line = 1;
|
||||
int cs = 0;
|
||||
bool multiline = false;
|
||||
|
||||
next_function_type _then;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
72
phase2.h
72
phase2.h
@ -3,95 +3,41 @@
|
||||
#define __phase2_h__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "command.h"
|
||||
#include <lemon_base.h>
|
||||
|
||||
//typedef std::unique_ptr<struct command> command_ptr;
|
||||
//typedef std::vector<command_ptr> command_ptr_vector;
|
||||
|
||||
class phase2_parser : public lemon_base<std::string> {
|
||||
|
||||
public:
|
||||
static std::unique_ptr<phase2_parser> make();
|
||||
|
||||
virtual void syntax_error(int yymajor, std::string &yyminor) override final;
|
||||
virtual void parse_accept() override final;
|
||||
virtual void parse_failure() override final;
|
||||
|
||||
bool continuation() const;
|
||||
|
||||
private:
|
||||
friend class phase2;
|
||||
|
||||
phase2_parser(const phase2_parser &) = delete;
|
||||
phase2_parser(phase2_parser &&) = delete;
|
||||
|
||||
phase2_parser& operator=(const phase2_parser &) = delete;
|
||||
phase2_parser& operator=(phase2_parser &&) = delete;
|
||||
|
||||
protected:
|
||||
// these need to be accessible to the lemon-generated parser.
|
||||
phase2_parser() = default;
|
||||
|
||||
command_ptr_vector command_queue;
|
||||
bool error = false;
|
||||
};
|
||||
|
||||
|
||||
class phase2 {
|
||||
|
||||
public:
|
||||
typedef std::function<void(command_ptr_vector &&)> pipe_function;
|
||||
|
||||
phase2();
|
||||
phase2(const phase2 &) = delete;
|
||||
phase2(phase2 &&) = default;
|
||||
typedef std::function<void(int, std::string &&)> next_function_type;
|
||||
|
||||
phase2 & operator=(const phase2 &) = delete;
|
||||
phase2 & operator=(phase2 &&) = default;
|
||||
phase2() = default;
|
||||
|
||||
void operator()(const std::string &line) { process(line); }
|
||||
void process(const std::string &line);
|
||||
void parse(std::string &&);
|
||||
void finish();
|
||||
|
||||
phase2 &operator >>=(pipe_function f) { pipe_to = f; return *this; }
|
||||
void reset();
|
||||
void abort() { reset(); }
|
||||
|
||||
template<class F>
|
||||
phase2 &operator >>= (F &f) {
|
||||
using std::placeholders::_1;
|
||||
pipe_to = std::bind(&F::operator(), &f, _1);
|
||||
return *this;
|
||||
}
|
||||
bool continuation() const { return false; }
|
||||
|
||||
bool continuation() const {
|
||||
return parser ? parser->continuation() : false;
|
||||
}
|
||||
|
||||
void abort();
|
||||
void set_next(next_function_type &&fx) { _then = std::move(fx); }
|
||||
|
||||
private:
|
||||
|
||||
void parse(int, std::string &&);
|
||||
|
||||
|
||||
std::unique_ptr<phase2_parser> parser;
|
||||
void parse(int type, std::string &&s);
|
||||
|
||||
std::string scratch;
|
||||
int type = 0;
|
||||
int pcount = 0;
|
||||
|
||||
pipe_function pipe_to;
|
||||
|
||||
void flush();
|
||||
bool special();
|
||||
int classify();
|
||||
void exec();
|
||||
|
||||
|
||||
next_function_type _then;
|
||||
};
|
||||
|
||||
|
||||
|
97
phase2.rl
97
phase2.rl
@ -4,9 +4,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "phase2-parser.h"
|
||||
#include "phase2.h"
|
||||
#include "command.h"
|
||||
#include "phase3.h"
|
||||
|
||||
%%{
|
||||
machine main;
|
||||
@ -217,9 +216,7 @@ void phase2::flush() {
|
||||
while (!scratch.empty() && isspace(scratch.back())) scratch.pop_back();
|
||||
|
||||
|
||||
if (!scratch.empty()) {
|
||||
parse(classify(), std::move(scratch));
|
||||
}
|
||||
if (!scratch.empty()) parse(classify(), std::move(scratch));
|
||||
|
||||
type = 0;
|
||||
pcount = 0;
|
||||
@ -242,14 +239,14 @@ bool phase2::special() {
|
||||
}
|
||||
}
|
||||
|
||||
void phase2::process(const std::string &line) {
|
||||
void phase2::parse(int type, std::string &&s) {
|
||||
if (_then) _then(type, std::move(s));
|
||||
}
|
||||
|
||||
void phase2::parse(std::string &&line) {
|
||||
|
||||
//fprintf(stderr, "-> %s\n", line.c_str());
|
||||
|
||||
// still needed?
|
||||
if (line.empty()) { finish(); return; }
|
||||
|
||||
|
||||
int cs;
|
||||
const unsigned char *p = (const unsigned char *)line.data();
|
||||
const unsigned char *pe = p + line.size();
|
||||
@ -264,81 +261,19 @@ void phase2::process(const std::string &line) {
|
||||
%% write exec;
|
||||
|
||||
flush();
|
||||
// 2 NLs to make the stack reduce. harmless if in a multi-line constuct.
|
||||
parse(NL, "");
|
||||
parse(NL, "");
|
||||
|
||||
exec();
|
||||
if (_then) {
|
||||
_then(NL, "");
|
||||
_then(NL, "");
|
||||
}
|
||||
}
|
||||
|
||||
void phase2::finish() {
|
||||
parse(0, "");
|
||||
exec();
|
||||
}
|
||||
|
||||
void phase2::parse(int token, std::string &&s) {
|
||||
if (parser) parser->parse(token, std::move(s));
|
||||
}
|
||||
|
||||
void phase2::exec() {
|
||||
|
||||
if (pipe_to && parser) {
|
||||
command_ptr_vector tmp;
|
||||
for (auto &p : parser->command_queue) {
|
||||
if (p) tmp.emplace_back(std::move(p));
|
||||
}
|
||||
parser->command_queue.clear();
|
||||
|
||||
if (!tmp.empty()) pipe_to(std::move(tmp));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
phase2::phase2() {
|
||||
parser = phase2_parser::make();
|
||||
//parser->trace(stdout, " ] ");
|
||||
|
||||
}
|
||||
|
||||
void phase2::abort() {
|
||||
parser = nullptr;
|
||||
parser = phase2_parser::make();
|
||||
}
|
||||
|
||||
#pragma mark - phase2_parser
|
||||
|
||||
void phase2_parser::parse_accept() {
|
||||
error = false;
|
||||
}
|
||||
|
||||
void phase2_parser::parse_failure() {
|
||||
error = false;
|
||||
}
|
||||
|
||||
void phase2_parser::syntax_error(int yymajor, std::string &yyminor) {
|
||||
/*
|
||||
switch (yymajor) {
|
||||
case END:
|
||||
fprintf(stderr, "### MPW Shell - Extra END command.\n");
|
||||
break;
|
||||
|
||||
case RPAREN:
|
||||
fprintf(stderr, "### MPW Shell - Extra ) command.\n");
|
||||
break;
|
||||
|
||||
case ELSE:
|
||||
case ELSE_IF:
|
||||
fprintf(stderr, "### MPW Shell - ELSE must be within IF ... END.\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "### Parse error near %s\n", yyminor.c_str());
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
fprintf(stderr, "### MPW Shell - Parse error near %s\n", yymajor ? yyminor.c_str() : "EOF");
|
||||
error = true;
|
||||
void phase2::reset() {
|
||||
|
||||
type = 0;
|
||||
pcount = 0;
|
||||
scratch.clear();
|
||||
}
|
||||
|
||||
|
@ -6,19 +6,19 @@
|
||||
|
||||
%include {
|
||||
|
||||
#include "phase2.h"
|
||||
#include "phase3_parser.h"
|
||||
#include "command.h"
|
||||
#define LEMON_SUPER phase2_parser
|
||||
#include "phase2-parser.h"
|
||||
#define LEMON_SUPER phase3
|
||||
}
|
||||
|
||||
|
||||
%code {
|
||||
|
||||
std::unique_ptr<phase2_parser> phase2_parser::make() {
|
||||
std::unique_ptr<phase3> phase3::make() {
|
||||
return std::make_unique<yypParser>();
|
||||
}
|
||||
|
||||
bool phase2_parser::continuation() const {
|
||||
bool phase3::continuation() const {
|
||||
yypParser *self = (yypParser *)this;
|
||||
|
||||
for (const auto &e : *self) {
|
||||
@ -36,6 +36,43 @@ bool phase2_parser::continuation() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void phase3::parse_accept() {
|
||||
error = false;
|
||||
}
|
||||
|
||||
void phase3::parse_failure() {
|
||||
error = false;
|
||||
}
|
||||
|
||||
void phase3::syntax_error(int yymajor, std::string &yyminor) {
|
||||
/*
|
||||
switch (yymajor) {
|
||||
case END:
|
||||
fprintf(stderr, "### MPW Shell - Extra END command.\n");
|
||||
break;
|
||||
|
||||
case RPAREN:
|
||||
fprintf(stderr, "### MPW Shell - Extra ) command.\n");
|
||||
break;
|
||||
|
||||
case ELSE:
|
||||
case ELSE_IF:
|
||||
fprintf(stderr, "### MPW Shell - ELSE must be within IF ... END.\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "### Parse error near %s\n", yyminor.c_str());
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
fprintf(stderr, "### MPW Shell - Parse error near %s\n", yymajor ? yyminor.c_str() : "EOF");
|
||||
error = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
%left PIPE_PIPE AMP_AMP.
|
43
phase3_parser.h
Normal file
43
phase3_parser.h
Normal file
@ -0,0 +1,43 @@
|
||||
|
||||
#ifndef __phase3_h__
|
||||
#define __phase3_h__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "command.h"
|
||||
#include <lemon_base.h>
|
||||
|
||||
class phase3 : public lemon_base<std::string> {
|
||||
|
||||
public:
|
||||
static std::unique_ptr<phase3> make();
|
||||
|
||||
virtual void syntax_error(int yymajor, std::string &yyminor) override final;
|
||||
virtual void parse_accept() override final;
|
||||
virtual void parse_failure() override final;
|
||||
|
||||
bool continuation() const;
|
||||
|
||||
//void parse(int type, std::string &&s) override final;
|
||||
|
||||
private:
|
||||
|
||||
phase3(const phase3 &) = delete;
|
||||
phase3(phase3 &&) = delete;
|
||||
|
||||
phase3& operator=(const phase3 &) = delete;
|
||||
phase3& operator=(phase3 &&) = delete;
|
||||
|
||||
protected:
|
||||
// these need to be accessible to the lemon-generated parser.
|
||||
phase3() = default;
|
||||
|
||||
command_ptr_vector command_queue;
|
||||
bool error = false;
|
||||
|
||||
friend class mpw_parser;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user