mpw-shell/phase2.rl

319 lines
5.5 KiB
Plaintext
Raw Normal View History

2016-01-30 03:23:14 +00:00
/*
* 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.
*
*/
2016-01-30 17:44:42 +00:00
#include "phase2-parser.h"
2016-01-30 03:23:14 +00:00
#include "phase2.h"
#include "command.h"
%%{
machine main;
alphtype unsigned char;
action not_special { !special() }
2016-01-31 05:41:02 +00:00
escape = 0xb6;
2016-01-30 03:23:14 +00:00
ws = [ \t];
2016-01-31 05:41:02 +00:00
escape_seq =
escape any
;
sstring =
[']
( (any-[']) )*
[']
$err{
throw std::runtime_error("### MPW Shell - 's must occur in pairs.");
}
;
vstring =
[{]
2016-02-02 01:38:29 +00:00
( (any-[}]) )*
2016-01-31 05:41:02 +00:00
[}]
$err{
2016-02-02 01:38:29 +00:00
throw std::runtime_error("### MPW Shell - {s must occur in pairs.");
2016-01-31 05:41:02 +00:00
}
;
# double-quoted string.
dstring =
["]
(
escape_seq
|
(any-escape-["])
)*
["]
$err{
throw std::runtime_error("### MPW Shell - \"s must occur in pairs.");
}
;
2016-01-30 03:23:14 +00:00
main := |*
2016-01-31 05:41:02 +00:00
2016-01-30 03:23:14 +00:00
'||' when not_special => {
flush();
parse(PIPE_PIPE, std::string(ts, te));
};
'&&' when not_special => {
flush();
parse(AMP_AMP, std::string(ts, te));
};
2016-01-31 05:41:02 +00:00
# ( evaluate (1+2) ) is lparen, eval, rparen.
# need to balance parens here and terminate a special token when it goes negative.
'(' => {
if (special()) {
pcount++;
scratch.push_back(fc);
} else {
2016-01-30 03:23:14 +00:00
flush();
parse(LPAREN, std::string(ts, te));
2016-01-31 05:41:02 +00:00
}
2016-01-30 03:23:14 +00:00
};
2016-01-31 05:41:02 +00:00
')' => {
if (special() && pcount-- > 0) scratch.push_back(fc);
else {
2016-01-30 03:23:14 +00:00
flush();
scratch.push_back(fc);
type = RPAREN;
2016-01-31 05:41:02 +00:00
}
2016-01-30 03:23:14 +00:00
};
';' => { flush(); parse(SEMI, ";"); };
2016-01-31 05:41:02 +00:00
2016-01-30 03:23:14 +00:00
ws => { if (!scratch.empty()) scratch.push_back(fc); };
2016-01-31 05:41:02 +00:00
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); };
2016-01-30 03:23:14 +00:00
*|;
}%%
%%{
machine classify;
alphtype unsigned char;
ws = [ \t];
IF = /if/i;
ELSE = /else/i;
END = /end/i;
BEGIN = /begin/i;
EVALUATE = /evaluate/i;
LOOP = /loop/i;
2016-06-16 04:04:29 +00:00
FOR = /for/i;
BREAK = /break/i;
CONTINUE = /continue/i;
2016-01-30 03:23:14 +00:00
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; };
2016-01-30 03:23:14 +00:00
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; };
LOOP %eof{ type = LOOP; return; };
LOOP ws => { type = LOOP; return; };
2016-06-16 04:04:29 +00:00
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; };
2016-01-31 05:41:02 +00:00
'(' => { type = LPAREN; return; };
2016-01-30 03:23:14 +00:00
*|;
}%%
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();
}
2016-01-31 05:41:02 +00:00
/* slightly wrong since whitespace is needed for it to be special. */
2016-01-30 03:23:14 +00:00
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) {
if (line.empty()) { finish(); return; }
2016-01-31 05:41:02 +00:00
int pcount = 0; // special form parens cannot cross lines.
2016-01-30 03:23:14 +00:00
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();
2016-01-31 05:41:02 +00:00
// 2 NLs to make the stack reduce. harmless if in a multi-line constuct.
parse(NL, "");
2016-01-30 03:23:14 +00:00
parse(NL, "");
exec();
}
void phase2::finish() {
parse(0, "");
exec();
}
2016-01-30 17:44:42 +00:00
void phase2::parse(int token, std::string &&s) {
if (parser) parser->parse(token, std::move(s));
}
2016-01-30 03:23:14 +00:00
void phase2::exec() {
2016-01-30 17:44:42 +00:00
if (pipe_to && parser) {
command_ptr_vector tmp;
2016-01-30 17:44:42 +00:00
for (auto &p : parser->command_queue) {
if (p) tmp.emplace_back(std::move(p));
2016-01-30 03:23:14 +00:00
}
2016-01-30 17:44:42 +00:00
parser->command_queue.clear();
if (!tmp.empty()) pipe_to(std::move(tmp));
2016-01-30 03:23:14 +00:00
}
}
2016-01-30 17:44:42 +00:00
phase2::phase2() {
2016-02-05 17:42:22 +00:00
parser = phase2_parser::make();
2016-01-31 05:41:02 +00:00
//parser->trace(stdout, " ] ");
2016-01-30 17:44:42 +00:00
}
2016-02-05 17:42:22 +00:00
void phase2::abort() {
parser = nullptr;
parser = phase2_parser::make();
}
2016-01-30 17:44:42 +00:00
#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) {
2016-02-02 01:38:29 +00:00
/*
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:
2016-01-30 17:44:42 +00:00
fprintf(stderr, "### Parse error near %s\n", yyminor.c_str());
2016-02-02 01:38:29 +00:00
break;
}
*/
2016-01-30 17:44:42 +00:00
2016-02-02 01:38:29 +00:00
fprintf(stderr, "### MPW Shell - Parse error near %s\n", yymajor ? yyminor.c_str() : "EOF");
2016-01-30 17:44:42 +00:00
error = true;
}
2016-02-05 17:42:22 +00:00