mirror of
https://github.com/ksherlock/mpw-shell.git
synced 2025-01-09 10:31:46 +00:00
183 lines
3.0 KiB
Ragel
183 lines
3.0 KiB
Ragel
/*
|
|
* phase2 -- parse a line into major control structures (begin/end/if/etc)
|
|
* input is a full line -- comments have been removed, escape-nl handled, trailing newline stripped.
|
|
*
|
|
*/
|
|
|
|
#include "mpw-shell-grammar.h"
|
|
#include "phase2.h"
|
|
#include "command.h"
|
|
|
|
%%{
|
|
machine main;
|
|
alphtype unsigned char;
|
|
|
|
action not_special { !special() }
|
|
|
|
ws = [ \t];
|
|
|
|
main := |*
|
|
'||' when not_special => {
|
|
flush();
|
|
parse(PIPE_PIPE, std::string(ts, te));
|
|
};
|
|
|
|
'&&' when not_special => {
|
|
flush();
|
|
parse(AMP_AMP, std::string(ts, te));
|
|
};
|
|
|
|
'(' when not_special => {
|
|
flush();
|
|
parse(LPAREN, std::string(ts, te));
|
|
};
|
|
|
|
# ) may include redirection so start a new token but don't parse it yet.
|
|
')' when not_special => {
|
|
flush();
|
|
scratch.push_back(fc);
|
|
type = RPAREN;
|
|
};
|
|
|
|
# todo -- also add in strings and escapes.
|
|
|
|
';' => { flush(); parse(SEMI, ";"); };
|
|
ws => { if (!scratch.empty()) scratch.push_back(fc); };
|
|
any => { scratch.push_back(fc); };
|
|
*|;
|
|
}%%
|
|
|
|
|
|
%%{
|
|
machine classify;
|
|
alphtype unsigned char;
|
|
|
|
ws = [ \t];
|
|
|
|
IF = /if/i;
|
|
ELSE = /else/i;
|
|
END = /end/i;
|
|
BEGIN = /begin/i;
|
|
EVALUATE = /evaluate/i;
|
|
|
|
|
|
main := |*
|
|
IF %eof{ type = IF; return; };
|
|
IF ws => { type = IF; return; };
|
|
|
|
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; };
|
|
|
|
EVALUATE %eof{ type = EVALUATE; return; };
|
|
EVALUATE ws => { type = EVALUATE; return; };
|
|
|
|
END %eof{ type = END; return; };
|
|
END ws => { type = END; return; };
|
|
|
|
BEGIN %eof{ type = BEGIN; return; };
|
|
BEGIN ws => { type = BEGIN; return; };
|
|
|
|
')' => { type = LPAREN; return; };
|
|
*|;
|
|
|
|
}%%
|
|
|
|
|
|
namespace {
|
|
%% machine classify;
|
|
%% write data;
|
|
%% machine main;
|
|
%% write data;
|
|
}
|
|
|
|
void phase2::flush() {
|
|
// remove white space...
|
|
while (!scratch.empty() && isspace(scratch.back())) scratch.pop_back();
|
|
|
|
|
|
if (!scratch.empty()) {
|
|
if (!type) classify();
|
|
parse(type, std::move(scratch));
|
|
}
|
|
|
|
type = 0;
|
|
scratch.clear();
|
|
}
|
|
|
|
bool phase2::special() {
|
|
if (!type) classify();
|
|
|
|
switch (type) {
|
|
case IF:
|
|
case ELSE:
|
|
case ELSE_IF:
|
|
case EVALUATE:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void phase2::classify() {
|
|
if (type) return;
|
|
if (scratch.empty()) return;
|
|
|
|
int cs;
|
|
int act;
|
|
const unsigned char *p = (const unsigned char *)scratch.data();
|
|
const unsigned char *pe = p + scratch.size();
|
|
const unsigned char *eof = pe;
|
|
const unsigned char *te, *ts;
|
|
|
|
type = COMMAND;
|
|
|
|
%% machine classify;
|
|
%% write init;
|
|
%% write exec;
|
|
}
|
|
|
|
void phase2::process(const std::string &line) {
|
|
|
|
|
|
int cs;
|
|
int act;
|
|
const unsigned char *p = (const unsigned char *)line.data();
|
|
const unsigned char *pe = p + line.size();
|
|
const unsigned char *eof = pe;
|
|
const unsigned char *te, *ts;
|
|
|
|
scratch.clear();
|
|
type = 0;
|
|
|
|
%% machine main;
|
|
%% write init;
|
|
%% write exec;
|
|
|
|
flush();
|
|
parse(NL, "");
|
|
|
|
exec();
|
|
}
|
|
|
|
void phase2::finish() {
|
|
parse(0, "");
|
|
exec();
|
|
}
|
|
|
|
void phase2::exec() {
|
|
|
|
if (pipe_to) {
|
|
for (auto &p : command_queue) {
|
|
if (p) {
|
|
pipe_to(std::move(p));
|
|
}
|
|
}
|
|
command_queue.clear();
|
|
}
|
|
|
|
}
|
|
|