mirror of
https://github.com/ksherlock/mpw-shell.git
synced 2024-06-12 21:29:51 +00:00
rewrite phase-2 lexical analysis. It splits on ; ( ) || && and I've deferred the invalid string checks until later.
This commit is contained in:
parent
31f33096cb
commit
c28cfb0710
2
phase2.h
2
phase2.h
|
@ -88,7 +88,7 @@ private:
|
||||||
|
|
||||||
void flush();
|
void flush();
|
||||||
bool special();
|
bool special();
|
||||||
void classify();
|
int classify();
|
||||||
void exec();
|
void exec();
|
||||||
|
|
||||||
|
|
||||||
|
|
307
phase2.rl
307
phase2.rl
|
@ -14,183 +14,188 @@
|
||||||
|
|
||||||
action not_special { !special() }
|
action not_special { !special() }
|
||||||
|
|
||||||
|
action parse_ws {
|
||||||
|
if (scratch.empty()) fgoto main;
|
||||||
|
}
|
||||||
|
action parse_semi {
|
||||||
|
flush();
|
||||||
|
parse(SEMI, ";");
|
||||||
|
fgoto main;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
action parse_amp_amp {
|
||||||
|
if (!special()) {
|
||||||
|
scratch.pop_back();
|
||||||
|
flush();
|
||||||
|
parse(AMP_AMP, "&&");
|
||||||
|
fgoto main;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
action parse_pipe_pipe {
|
||||||
|
if (!special()) {
|
||||||
|
scratch.pop_back();
|
||||||
|
flush();
|
||||||
|
parse(PIPE_PIPE, "||");
|
||||||
|
fgoto main;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
action parse_lparen {
|
||||||
|
if (scratch.empty()) {
|
||||||
|
parse(LPAREN, "(");
|
||||||
|
fgoto main;
|
||||||
|
}
|
||||||
|
pcount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
action parse_rparen {
|
||||||
|
if (pcount <= 0) {
|
||||||
|
flush();
|
||||||
|
parse(RPAREN, ")");
|
||||||
|
fgoto main;
|
||||||
|
}
|
||||||
|
--pcount;
|
||||||
|
}
|
||||||
|
|
||||||
escape = 0xb6;
|
escape = 0xb6;
|
||||||
ws = [ \t];
|
ws = [ \t];
|
||||||
|
|
||||||
|
|
||||||
escape_seq =
|
escape_seq = escape any ;
|
||||||
escape any
|
|
||||||
;
|
|
||||||
|
|
||||||
sstring =
|
schar = [^'];
|
||||||
[']
|
sstring = ['] schar** ['] ;
|
||||||
( (any-[']) )*
|
|
||||||
[']
|
|
||||||
$err{
|
|
||||||
throw std::runtime_error("### MPW Shell - 's must occur in pairs.");
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
vstring =
|
vchar = [^}];
|
||||||
[{]
|
vstring = [{] vchar** [}] ;
|
||||||
( (any-[}]) )*
|
|
||||||
[}]
|
|
||||||
$err{
|
|
||||||
throw std::runtime_error("### MPW Shell - {s must occur in pairs.");
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
# double-quoted string.
|
# double-quoted string.
|
||||||
dstring =
|
dchar = escape_seq | (any - escape - ["]) ;
|
||||||
["]
|
dstring = ["] dchar** ["];
|
||||||
(
|
|
||||||
escape_seq
|
|
||||||
|
|
|
||||||
(any-escape-["])
|
|
||||||
)*
|
|
||||||
["]
|
|
||||||
$err{
|
|
||||||
throw std::runtime_error("### MPW Shell - \"s must occur in pairs.");
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
|
echar = escape_seq | (any - escape - [`]) ;
|
||||||
|
estring1 = '`' echar** '`';
|
||||||
|
estring2 = '``' echar** '``';
|
||||||
|
estring = estring1 | estring2 ;
|
||||||
|
|
||||||
main := |*
|
# default action is to push character into scratch.
|
||||||
|
# fgoto main inhibits.
|
||||||
|
main := (
|
||||||
|
ws $parse_ws
|
||||||
|
| ';' $parse_semi
|
||||||
|
| '(' $parse_lparen
|
||||||
|
| ')' $parse_rparen
|
||||||
|
| '|' '|' $parse_pipe_pipe
|
||||||
|
| '&' '&' $parse_amp_amp
|
||||||
|
| escape_seq
|
||||||
|
| sstring
|
||||||
|
| dstring
|
||||||
|
| vstring
|
||||||
|
| estring
|
||||||
|
| any
|
||||||
|
)** ${ scratch.push_back(fc); };
|
||||||
|
|
||||||
'||' when not_special => {
|
|
||||||
flush();
|
|
||||||
parse(PIPE_PIPE, std::string(ts, te));
|
|
||||||
};
|
|
||||||
|
|
||||||
'&&' when not_special => {
|
|
||||||
flush();
|
|
||||||
parse(AMP_AMP, std::string(ts, te));
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
# ( evaluate (1+2) ) is lparen, eval, rparen.
|
|
||||||
# need to balance parens here and terminate a special token when it goes negative.
|
|
||||||
#
|
|
||||||
# counterpoint: '(
|
|
||||||
# echo (hello) == echo "(hello)"
|
|
||||||
# echo ; (echo) == echo ; LPAREN echo RPAREN
|
|
||||||
# ( echo ( ) ==> error (needs second closing rparen )
|
|
||||||
# ( echo ())) --> error (MPW Shell - Extra ) command.
|
|
||||||
|
|
||||||
'(' => {
|
|
||||||
|
|
||||||
// if type == 0, this is the start and LPAREN is a token.
|
|
||||||
// otherwise, LPAREN is a normal character (but is balanced)
|
|
||||||
classify();
|
|
||||||
if (type) {
|
|
||||||
pcount++;
|
|
||||||
scratch.push_back(fc);
|
|
||||||
} else {
|
|
||||||
// start of command.
|
|
||||||
flush();
|
|
||||||
parse(LPAREN, std::string(ts, te));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
')' => {
|
|
||||||
if (pcount) {
|
|
||||||
scratch.push_back(fc);
|
|
||||||
--pcount;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
flush();
|
|
||||||
scratch.push_back(fc);
|
|
||||||
type = RPAREN;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
';' => { flush(); parse(SEMI, ";"); };
|
|
||||||
|
|
||||||
ws => { if (!scratch.empty()) scratch.push_back(fc); };
|
|
||||||
|
|
||||||
sstring => { scratch.append(ts, te); };
|
|
||||||
dstring => { scratch.append(ts, te); };
|
|
||||||
vstring => { scratch.append(ts, te); };
|
|
||||||
escape_seq => { scratch.append(ts, te); };
|
|
||||||
|
|
||||||
|
|
||||||
(any-escape-['"{]) => { scratch.push_back(fc); };
|
|
||||||
*|;
|
|
||||||
}%%
|
}%%
|
||||||
|
|
||||||
|
|
||||||
%%{
|
%%{
|
||||||
machine classify;
|
machine argv0;
|
||||||
alphtype unsigned char;
|
alphtype unsigned char;
|
||||||
|
|
||||||
|
action push { argv0.push_back(tolower(fc)); }
|
||||||
|
|
||||||
|
|
||||||
|
escape = 0xb6;
|
||||||
ws = [ \t];
|
ws = [ \t];
|
||||||
|
|
||||||
IF = /if/i;
|
|
||||||
ELSE = /else/i;
|
# ` and { not supported here.
|
||||||
END = /end/i;
|
|
||||||
BEGIN = /begin/i;
|
|
||||||
EVALUATE = /evaluate/i;
|
|
||||||
LOOP = /loop/i;
|
|
||||||
FOR = /for/i;
|
|
||||||
BREAK = /break/i;
|
|
||||||
CONTINUE = /continue/i;
|
|
||||||
|
|
||||||
|
|
||||||
main := |*
|
# hmmm ... only push the converted char - escape n = \n, for example.
|
||||||
IF %eof{ type = IF; return; };
|
esc_seq =
|
||||||
IF ws => { type = IF; return; };
|
escape (
|
||||||
|
'f' ${argv0.push_back('\f'); } |
|
||||||
|
'n' ${argv0.push_back('\n'); } |
|
||||||
|
't' ${argv0.push_back('\t'); } |
|
||||||
|
[^fnt] $push
|
||||||
|
);
|
||||||
|
|
||||||
ELSE %eof{ type = ELSE; return; };
|
schar = [^'] $push;
|
||||||
ELSE ws => { type = ELSE; return; };
|
sstring = ['] schar** ['];
|
||||||
|
|
||||||
#ELSE ws+ IF %eof{ type = ELSE_IF; return; };
|
dchar = esc_seq | (any-escape-["]) $push;
|
||||||
#ELSE ws+ IF ws => { type = ELSE_IF; return; };
|
dstring = ["] dchar** ["];
|
||||||
|
|
||||||
EVALUATE %eof{ type = EVALUATE; return; };
|
main := (
|
||||||
EVALUATE ws => { type = EVALUATE; return; };
|
ws ${ fbreak; }
|
||||||
|
| [{`] ${ argv0.clear(); fbreak; }
|
||||||
END %eof{ type = END; return; };
|
| sstring
|
||||||
END ws => { type = END; return; };
|
| dstring
|
||||||
|
| esc_seq
|
||||||
BEGIN %eof{ type = BEGIN; return; };
|
| (any-escape-['"]) $push
|
||||||
BEGIN ws => { type = BEGIN; return; };
|
)**;
|
||||||
|
|
||||||
LOOP %eof{ type = LOOP; return; };
|
|
||||||
LOOP ws => { type = LOOP; return; };
|
|
||||||
|
|
||||||
FOR %eof{ type = FOR; return; };
|
|
||||||
FOR ws => { type = FOR; return; };
|
|
||||||
|
|
||||||
BREAK %eof{ type = BREAK; return; };
|
|
||||||
BREAK ws => { type = BREAK; return; };
|
|
||||||
|
|
||||||
CONTINUE %eof{ type = CONTINUE; return; };
|
|
||||||
CONTINUE ws => { type = CONTINUE; return; };
|
|
||||||
|
|
||||||
|
|
||||||
'(' => { type = LPAREN; return; };
|
|
||||||
*|;
|
|
||||||
|
|
||||||
}%%
|
}%%
|
||||||
|
|
||||||
namespace {
|
|
||||||
%% machine classify;
|
int phase2::classify() {
|
||||||
|
|
||||||
|
%%machine argv0;
|
||||||
%%write data;
|
%%write data;
|
||||||
|
|
||||||
|
if (type) return type;
|
||||||
|
std::string argv0;
|
||||||
|
|
||||||
|
const unsigned char *p = (const unsigned char *)scratch.data();
|
||||||
|
const unsigned char *pe = p + scratch.size();
|
||||||
|
int cs;
|
||||||
|
|
||||||
|
type = COMMAND;
|
||||||
|
|
||||||
|
%%write init;
|
||||||
|
%%write exec;
|
||||||
|
|
||||||
|
// fprintf(stderr, "%s -> %s\n", scratch.c_str(), argv0.c_str());
|
||||||
|
#undef _
|
||||||
|
#define _(a,b) if (argv0 == a) { type = b; return type; }
|
||||||
|
|
||||||
|
// expand aliases?
|
||||||
|
|
||||||
|
_("begin", BEGIN)
|
||||||
|
_("break", BREAK)
|
||||||
|
_("continue", CONTINUE)
|
||||||
|
_("else", ELSE)
|
||||||
|
_("end", END)
|
||||||
|
_("evaluate", EVALUATE)
|
||||||
|
_("for", FOR)
|
||||||
|
_("if", IF)
|
||||||
|
_("loop", LOOP)
|
||||||
|
|
||||||
|
#undef _
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
%% machine argv0;
|
||||||
|
%% write data;
|
||||||
|
|
||||||
%% machine main;
|
%% machine main;
|
||||||
%% write data;
|
%% write data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void phase2::flush() {
|
void phase2::flush() {
|
||||||
|
//fprintf(stderr, "flush: %s\n", scratch.c_str());
|
||||||
// remove white space...
|
// remove white space...
|
||||||
while (!scratch.empty() && isspace(scratch.back())) scratch.pop_back();
|
while (!scratch.empty() && isspace(scratch.back())) scratch.pop_back();
|
||||||
|
|
||||||
|
|
||||||
if (!scratch.empty()) {
|
if (!scratch.empty()) {
|
||||||
if (!type) classify();
|
parse(classify(), std::move(scratch));
|
||||||
parse(type, std::move(scratch));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type = 0;
|
type = 0;
|
||||||
|
@ -200,9 +205,8 @@ void phase2::flush() {
|
||||||
|
|
||||||
/* slightly wrong since whitespace is needed for it to be special. */
|
/* slightly wrong since whitespace is needed for it to be special. */
|
||||||
bool phase2::special() {
|
bool phase2::special() {
|
||||||
if (!type) classify();
|
|
||||||
|
|
||||||
switch (type) {
|
switch (classify()) {
|
||||||
case IF:
|
case IF:
|
||||||
case ELSE:
|
case ELSE:
|
||||||
case ELSE_IF:
|
case ELSE_IF:
|
||||||
|
@ -215,39 +219,22 @@ bool phase2::special() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
void phase2::process(const std::string &line) {
|
||||||
|
|
||||||
|
//fprintf(stderr, "-> %s\n", line.c_str());
|
||||||
|
|
||||||
|
// still needed?
|
||||||
if (line.empty()) { finish(); return; }
|
if (line.empty()) { finish(); return; }
|
||||||
|
|
||||||
//int pcount = 0; // special form parens cannot cross lines.
|
|
||||||
|
|
||||||
int cs;
|
int cs;
|
||||||
int act;
|
|
||||||
const unsigned char *p = (const unsigned char *)line.data();
|
const unsigned char *p = (const unsigned char *)line.data();
|
||||||
const unsigned char *pe = p + line.size();
|
const unsigned char *pe = p + line.size();
|
||||||
const unsigned char *eof = pe;
|
const unsigned char *eof = pe;
|
||||||
const unsigned char *te, *ts;
|
|
||||||
|
|
||||||
scratch.clear();
|
scratch.clear();
|
||||||
type = 0;
|
type = 0;
|
||||||
|
pcount = 0; // parenthesis balancing within command only.
|
||||||
|
|
||||||
%% machine main;
|
%% machine main;
|
||||||
%% write init;
|
%% write init;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user