implement delayed heredocument parsing

This commit is contained in:
zawz 2021-05-21 16:36:24 +02:00
parent 3e21098d95
commit 15ac04f505
6 changed files with 292 additions and 281 deletions

View file

@ -6,6 +6,7 @@
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <tuple>
#define SPACES " \t" #define SPACES " \t"
#define SEPARATORS " \t\n" #define SEPARATORS " \t\n"
@ -33,9 +34,13 @@
// structs // structs
void parse_error(std::string const& message, parse_context& ctx); struct list_parse_options {
char end_char=0;
bool word_mode=false;
std::vector<std::string> end_words={};
const char* expecting=NULL;
};
void parse_error(std::string const& message, parse_context& ctx, uint64_t i);
// globals // globals
extern const std::vector<std::string> posix_cmdvar; extern const std::vector<std::string> posix_cmdvar;
@ -48,12 +53,17 @@ std::pair<shmain*, parse_context> parse_text(std::string const& in, std::string
inline std::pair<shmain*, parse_context> parse(std::string const& file) { return parse_text(import_file(file), file); } inline std::pair<shmain*, parse_context> parse(std::string const& file) { return parse_text(import_file(file), file); }
// tools // tools
parse_context make_context(std::string const& in, std::string const& filename="", bool bash=false); parse_context make_context(std::string const& in, std::string const& filename="", bool bash=false);
parse_context make_context(parse_context ctx, std::string const& in="", std::string const& filename="", bool bash=false); parse_context make_context(parse_context ctx, std::string const& in="", std::string const& filename="", bool bash=false);
parse_context make_context(parse_context ctx, uint64_t i); parse_context make_context(parse_context ctx, uint64_t i);
parse_context operator+(parse_context ctx, int64_t a); parse_context operator+(parse_context ctx, int64_t a);
parse_context operator-(parse_context ctx, int64_t a); parse_context operator-(parse_context ctx, int64_t a);
// error handlers
void parse_error(std::string const& message, parse_context& ctx);
void parse_error(std::string const& message, parse_context& ctx, uint64_t i);
// ** unit parsers ** // // ** unit parsers ** //
/* util parsers */ /* util parsers */
@ -76,9 +86,10 @@ inline uint32_t skip_unread(parse_context const& ct) {
} }
// list // list
std::pair<list*, parse_context> parse_list_until(parse_context ct, char end_c, const char* expecting=NULL); // std::pair<list*, parse_context> parse_list_until(parse_context ct, char end_c, const char* expecting=NULL);
std::pair<list*, parse_context> parse_list_until(parse_context ct, std::string const& end_word); // std::pair<list*, parse_context> parse_list_until(parse_context ct, std::string const& end_word);
std::tuple<list*, parse_context, std::string> parse_list_until(parse_context ct, std::vector<std::string> const& end_words, const char* expecting=NULL); // std::tuple<list*, parse_context, std::string> parse_list_until(parse_context ct, std::vector<std::string> const& end_words, const char* expecting=NULL);
std::tuple<list*, parse_context, std::string> parse_list_until(parse_context ct, list_parse_options opts={});
// name // name
std::pair<variable*, parse_context> parse_var(parse_context ct, bool specialvars=true, bool array=false); std::pair<variable*, parse_context> parse_var(parse_context ct, bool specialvars=true, bool array=false);

View file

@ -62,6 +62,21 @@ subarg: can be one of
*/ */
// pre-definitions
#define AND_OP false
#define OR_OP true
class condlist;
class block;
class pipeline;
class arg;
class subarg;
class cmd;
class redirect;
// structs
struct parse_context { struct parse_context {
const char* data=NULL; const char* data=NULL;
uint64_t size=0; uint64_t size=0;
@ -73,9 +88,16 @@ struct parse_context {
const char* here_doc=""; const char* here_doc="";
const char operator[](uint64_t a) { return data[a]; } const char operator[](uint64_t a) { return data[a]; }
bool has_errored=false; bool has_errored=false;
redirect* here_document=nullptr;
char* here_delimitor=NULL;
};
struct generate_context {
arg* here_document=nullptr;
}; };
// exceptions // exceptions
class format_error : public std::exception class format_error : public std::exception
{ {
public: public:
@ -97,20 +119,8 @@ private:
std::string sdat; std::string sdat;
}; };
// objects // objects
#define AND_OP false
#define OR_OP true
class condlist;
class block;
class pipeline;
class arg;
class subarg;
class cmd;
// type pack of condlist // type pack of condlist
typedef std::vector<arg*> arglist_t; typedef std::vector<arg*> arglist_t;
@ -229,15 +239,17 @@ public:
class redirect : public _obj class redirect : public _obj
{ {
public: public:
redirect(std::string strop="") { type=_obj::_redirect; op=strop; target=nullptr; } redirect(std::string strop="") { type=_obj::_redirect; op=strop; target=nullptr; here_document=nullptr; }
redirect(arg* in) { type=_obj::_redirect; target=in; } redirect(arg* in) { type=_obj::_redirect; target=in; here_document=nullptr; }
redirect(std::string strop, arg* in) { type=_obj::_redirect; op=strop; target=in; } redirect(std::string strop, arg* in) { type=_obj::_redirect; op=strop; target=in; here_document=nullptr; }
redirect(std::string strop, arg* in, arg* doc) { type=_obj::_redirect; op=strop; target=in; here_document=doc; }
~redirect() { if(target != nullptr) delete target; } ~redirect() { if(target != nullptr) delete target; }
std::string generate(int ind); std::string generate(int ind);
std::string op; std::string op;
arg* target; arg* target;
arg* here_document;
}; };
// Meta block // Meta block
@ -252,9 +264,9 @@ public:
// subshell: return the containing cmd, if it is a single command // subshell: return the containing cmd, if it is a single command
cmd* single_cmd(); cmd* single_cmd();
std::string generate_redirs(int ind, std::string const& _str); std::string generate_redirs(int ind, std::string const& _str, generate_context* ctx);
virtual std::string generate(int ind)=0; virtual std::string generate(int ind, generate_context* ctx)=0;
}; };
// PL // PL
@ -269,7 +281,8 @@ public:
bool negated; // negated return value (! at start) bool negated; // negated return value (! at start)
std::string generate(int ind); std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); };
}; };
// CL // CL
@ -370,7 +383,8 @@ public:
arglist* args; arglist* args;
std::string generate(int ind); std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
}; };
class shmain : public block class shmain : public block
@ -388,7 +402,8 @@ public:
list* lst; list* lst;
std::string generate(bool print_shebang=true, int ind=0); std::string generate(bool print_shebang=true, int ind=0);
std::string generate(int ind); std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
}; };
class subshell : public block class subshell : public block
@ -404,7 +419,8 @@ public:
list* lst; list* lst;
std::string generate(int ind); std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
}; };
class brace : public block class brace : public block
@ -419,7 +435,8 @@ public:
list* lst; list* lst;
std::string generate(int ind); std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
}; };
class function : public block class function : public block
@ -433,7 +450,8 @@ public:
std::string name; std::string name;
list* lst; list* lst;
std::string generate(int ind); std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
}; };
class case_block : public block class case_block : public block
@ -453,7 +471,8 @@ public:
arg* carg; arg* carg;
std::vector< std::pair<std::vector<arg*>, list*> > cases; std::vector< std::pair<std::vector<arg*>, list*> > cases;
std::string generate(int ind); std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
}; };
class if_block : public block class if_block : public block
@ -473,7 +492,8 @@ public:
list* else_lst; list* else_lst;
std::string generate(int ind); std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
}; };
class for_block : public block class for_block : public block
@ -491,7 +511,8 @@ public:
arglist* iter; arglist* iter;
list* ops; list* ops;
std::string generate(int ind); std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
}; };
class while_block : public block class while_block : public block
@ -508,7 +529,8 @@ public:
list* cond; list* cond;
list* ops; list* ops;
std::string generate(int ind); std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
}; };
// Subarg subtypes // // Subarg subtypes //

View file

@ -152,7 +152,7 @@ void parse_exec(FILE* fd, parse_context ctx)
ctx=pp.second; ctx=pp.second;
if(ctx.has_errored) if(ctx.has_errored)
{ {
parse_list_until(ctx, 0); parse_list_until(ctx);
throw std::runtime_error("Aborting due to previous errors"); throw std::runtime_error("Aborting due to previous errors");
} }
t_lst->add(pp.first); t_lst->add(pp.first);

View file

@ -44,7 +44,7 @@ std::string arglist::generate(int ind)
return ret; return ret;
} }
std::string pipeline::generate(int ind) std::string pipeline::generate(int ind, generate_context* ctx)
{ {
std::string ret; std::string ret;
@ -53,11 +53,11 @@ std::string pipeline::generate(int ind)
if(negated) if(negated)
ret += "! "; ret += "! ";
ret += cmds[0]->generate(ind); ret += cmds[0]->generate(ind, ctx);
for(uint32_t i=1 ; i<cmds.size() ; i++) for(uint32_t i=1 ; i<cmds.size() ; i++)
{ {
ret += opt_minify ? "|" : " | " ; ret += opt_minify ? "|" : " | " ;
ret += cmds[i]->generate(ind); ret += cmds[i]->generate(ind, ctx);
} }
return ret; return ret;
@ -68,18 +68,27 @@ std::string condlist::generate(int ind)
std::string ret; std::string ret;
if(pls.size() <= 0) if(pls.size() <= 0)
return ""; return "";
ret += pls[0]->generate(ind); generate_context ctx;
ret += pls[0]->generate(ind, &ctx);
for(uint32_t i=0 ; i<pls.size()-1 ; i++) for(uint32_t i=0 ; i<pls.size()-1 ; i++)
{ {
if(or_ops[i]) if(or_ops[i])
ret += opt_minify ? "||" : " || "; ret += opt_minify ? "||" : " || ";
else else
ret += opt_minify ? "&&" : " && "; ret += opt_minify ? "&&" : " && ";
ret += pls[i+1]->generate(ind); ret += pls[i+1]->generate(ind, &ctx);
} }
if(ret=="") if(ret=="")
return ""; return "";
if(parallel) if(ctx.here_document != nullptr)
{
if(parallel)
ret += '&';
ret += '\n';
ret += ctx.here_document->generate(0);
ret += '\n';
}
else if(parallel)
{ {
ret += opt_minify ? "&" : " &\n"; ret += opt_minify ? "&" : " &\n";
} }
@ -123,12 +132,18 @@ std::string redirect::generate(int ind)
// BLOCK // BLOCK
std::string block::generate_redirs(int ind, std::string const& _str) std::string block::generate_redirs(int ind, std::string const& _str, generate_context* ctx=nullptr)
{ {
std::string ret=" "; std::string ret=" ";
bool previous_isnt_num = _str.size()>0 && !is_num(_str[_str.size()-1]); bool previous_isnt_num = _str.size()>0 && !is_num(_str[_str.size()-1]);
for(auto it: redirs) for(auto it: redirs)
{ {
if(ctx != nullptr && it->here_document != nullptr)
{
if(ctx->here_document != nullptr)
throw std::runtime_error("Unsupported generation of concurrent here documents");
ctx->here_document = it->here_document;
}
std::string _r = it->generate(0); std::string _r = it->generate(0);
if(opt_minify && _r.size() > 0 && !is_num(_r[0]) && previous_isnt_num) if(opt_minify && _r.size() > 0 && !is_num(_r[0]) && previous_isnt_num)
ret.pop_back(); // remove one space if possible ret.pop_back(); // remove one space if possible
@ -139,7 +154,7 @@ std::string block::generate_redirs(int ind, std::string const& _str)
return ret; return ret;
} }
std::string if_block::generate(int ind) std::string if_block::generate(int ind, generate_context* ctx)
{ {
std::string ret; std::string ret;
@ -169,11 +184,11 @@ std::string if_block::generate(int ind)
ret += indented("fi", ind); ret += indented("fi", ind);
ret += generate_redirs(ind, ret); ret += generate_redirs(ind, ret, ctx);
return ret; return ret;
} }
std::string for_block::generate(int ind) std::string for_block::generate(int ind, generate_context* ctx)
{ {
std::string ret; std::string ret;
@ -187,11 +202,11 @@ std::string for_block::generate(int ind)
if(opt_minify && ret.size()>1 && !is_alpha(ret[ret.size()-2])) if(opt_minify && ret.size()>1 && !is_alpha(ret[ret.size()-2]))
ret.pop_back(); ret.pop_back();
ret += generate_redirs(ind, ret); ret += generate_redirs(ind, ret, ctx);
return ret; return ret;
} }
std::string while_block::generate(int ind) std::string while_block::generate(int ind, generate_context* ctx)
{ {
std::string ret; std::string ret;
@ -207,11 +222,11 @@ std::string while_block::generate(int ind)
if(opt_minify && ret.size()>1 && !is_alpha(ret[ret.size()-2])) if(opt_minify && ret.size()>1 && !is_alpha(ret[ret.size()-2]))
ret.pop_back(); ret.pop_back();
ret += generate_redirs(ind, ret); ret += generate_redirs(ind, ret, ctx);
return ret; return ret;
} }
std::string subshell::generate(int ind) std::string subshell::generate(int ind, generate_context* ctx)
{ {
std::string ret; std::string ret;
// open subshell // open subshell
@ -224,11 +239,11 @@ std::string subshell::generate(int ind)
// close subshell // close subshell
ret += indented(")", ind); ret += indented(")", ind);
ret += generate_redirs(ind, ret); ret += generate_redirs(ind, ret, ctx);
return ret; return ret;
} }
std::string shmain::generate(int ind) std::string shmain::generate(int ind, generate_context* ctx)
{ {
return this->generate(false, ind); return this->generate(false, ind);
} }
@ -241,11 +256,10 @@ std::string shmain::generate(bool print_shebang, int ind)
if( opt_minify && ret[ret.size()-1] == '\n') if( opt_minify && ret[ret.size()-1] == '\n')
ret.pop_back(); ret.pop_back();
ret += generate_redirs(ind, ret);
return ret; return ret;
} }
std::string brace::generate(int ind) std::string brace::generate(int ind, generate_context* ctx)
{ {
std::string ret; std::string ret;
@ -253,11 +267,11 @@ std::string brace::generate(int ind)
ret += lst->generate(ind+1); ret += lst->generate(ind+1);
ret += indented("}", ind); ret += indented("}", ind);
ret += generate_redirs(ind, ret); ret += generate_redirs(ind, ret, ctx);
return ret; return ret;
} }
std::string function::generate(int ind) std::string function::generate(int ind, generate_context* ctx)
{ {
std::string ret; std::string ret;
// function definition // function definition
@ -268,11 +282,11 @@ std::string function::generate(int ind)
ret += lst->generate(ind+1); ret += lst->generate(ind+1);
ret += indented("}", ind); ret += indented("}", ind);
ret += generate_redirs(ind, ret); ret += generate_redirs(ind, ret, ctx);
return ret; return ret;
} }
std::string case_block::generate(int ind) std::string case_block::generate(int ind, generate_context* ctx)
{ {
std::string ret; std::string ret;
ret += "case " + carg->generate(ind) + " in\n"; ret += "case " + carg->generate(ind) + " in\n";
@ -307,14 +321,15 @@ std::string case_block::generate(int ind)
ind--; ind--;
ret += indented("esac", ind); ret += indented("esac", ind);
ret += generate_redirs(ind, ret); ret += generate_redirs(ind, ret, ctx);
return ret; return ret;
} }
std::string cmd::generate(int ind) std::string cmd::generate(int ind, generate_context* ctx)
{ {
std::string ret; std::string ret;
// var assigns
// is a varassign cmd
if(is_cmdvar) if(is_cmdvar)
{ {
ret += args->generate(ind) + ' '; ret += args->generate(ind) + ' ';
@ -330,6 +345,7 @@ std::string cmd::generate(int ind)
return ret; return ret;
} }
// pre-cmd var assigns
for(auto it: var_assigns) for(auto it: var_assigns)
{ {
if(it.first != nullptr) if(it.first != nullptr)
@ -339,6 +355,7 @@ std::string cmd::generate(int ind)
ret += ' '; ret += ' ';
} }
// cmd itself
if(args!=nullptr && args->size()>0) if(args!=nullptr && args->size()>0)
{ {
// command // command
@ -353,7 +370,7 @@ std::string cmd::generate(int ind)
ret.pop_back(); ret.pop_back();
} }
ret += generate_redirs(ind, ret); ret += generate_redirs(ind, ret, ctx);
return ret; return ret;
} }

View file

@ -25,7 +25,6 @@ const std::vector<std::string> out_reserved_words = { "then", "else", "fi", "esa
// stuff // stuff
std::string unexpected_token(char c) std::string unexpected_token(char c)
{ {
std::string print; std::string print;
@ -442,9 +441,9 @@ parse_context do_one_subarg_step(arg* ret, parse_context ctx, uint32_t& j, bool
// get subshell // get subshell
parse_context newct = ctx; parse_context newct = ctx;
ctx.size=k; ctx.size=k;
auto r=parse_list_until(newct, 0); auto r=parse_list_until(newct);
ret->add(new subshell_subarg(new subshell(r.first), is_quoted)); ret->add(new subshell_subarg(new subshell(std::get<0>(r)), is_quoted));
ctx = r.second; ctx = std::get<1>(r);
ctx.i++; ctx.i++;
j = ctx.i; j = ctx.i;
} }
@ -617,6 +616,35 @@ std::pair<arg*, parse_context> parse_arg(parse_context ctx, const char* end, con
return std::make_pair(ret, ctx); return std::make_pair(ret, ctx);
} }
parse_context parse_heredocument(parse_context ctx)
{
if(ctx.here_document == nullptr)
return ctx;
uint32_t j=ctx.i;
char* tc=NULL;
std::string delimitor=ctx.here_delimitor;
tc = (char*) strstr(ctx.data+ctx.i, std::string("\n"+delimitor+"\n").c_str()); // find delimitor
if(tc!=NULL) // delimitor was found
{
ctx.i = (tc-ctx.data)+delimitor.size()+1;
}
else
{
ctx.i = ctx.size;
}
// std::string tmpparse=std::string(ctx.data+j, ctx.i-j);
auto pval = parse_arg({ .data=ctx.data, .size=ctx.i, .i=j, .bash=ctx.bash} , NULL);
ctx.here_document->here_document = pval.first;
//
ctx.here_document=nullptr;
free(ctx.here_delimitor);
ctx.here_delimitor=NULL;
return ctx;
}
std::pair<redirect*, parse_context> parse_redirect(parse_context ctx) std::pair<redirect*, parse_context> parse_redirect(parse_context ctx)
{ {
bool is_redirect=false; bool is_redirect=false;
@ -717,14 +745,20 @@ std::pair<redirect*, parse_context> parse_redirect(parse_context ctx)
ctx.i = skip_chars(ctx, SPACES); ctx.i = skip_chars(ctx, SPACES);
if(ret->op == "<<") if(ret->op == "<<")
{ {
if(ctx.here_document != nullptr)
{
parse_error("unsupported multiple here documents at the same time", ctx);
return std::make_pair(ret, ctx);
}
else
ctx.here_document=ret;
auto pa = parse_arg(ctx); auto pa = parse_arg(ctx);
std::string delimitor = pa.first->string(); std::string delimitor = pa.first->string();
delete pa.first;
pa.first = nullptr;
if(delimitor == "") if(delimitor == "")
{ {
parse_error("Non-static or empty text input delimitor", ctx); parse_error("non-static or empty here document delimitor", ctx);
} }
if(delimitor.find('"') != std::string::npos || delimitor.find('\'') != std::string::npos || delimitor.find('\\') != std::string::npos) if(delimitor.find('"') != std::string::npos || delimitor.find('\'') != std::string::npos || delimitor.find('\\') != std::string::npos)
@ -732,34 +766,11 @@ std::pair<redirect*, parse_context> parse_redirect(parse_context ctx)
delimitor = ztd::sh("echo "+delimitor); // shell resolve the delimitor delimitor = ztd::sh("echo "+delimitor); // shell resolve the delimitor
delimitor.pop_back(); // remove \n delimitor.pop_back(); // remove \n
} }
ret->target = pa.first;
ctx = pa.second; ctx = pa.second;
ctx.i = skip_chars(ctx, SPACES); // skip spaces // copy delimitor
ctx.here_delimitor = (char*) malloc(delimitor.length()+1);
if(ctx[ctx.i] == '#') // skip comment strcpy(ctx.here_delimitor, delimitor.c_str());
ctx.i = skip_until(ctx, "\n"); //skip to endline
if(ctx[ctx.i] != '\n') // has another arg
{
parse_error("Additionnal argument after text input delimitor", ctx);
return std::make_pair(ret, ctx);
}
ctx.i++;
uint32_t j=ctx.i;
char* tc=NULL;
tc = (char*) strstr(ctx.data+ctx.i, std::string("\n"+delimitor+"\n").c_str()); // find delimitor
if(tc!=NULL) // delimitor was found
{
ctx.i = (tc-ctx.data)+delimitor.size()+1;
}
else
{
ctx.i = ctx.size;
}
std::string tmpparse=std::string(ctx.data+j, ctx.i-j);
auto pval = parse_arg({ .data=tmpparse.c_str(), .size=tmpparse.size(), .i=0, .bash=ctx.bash}, NULL);
ret->target = pval.first;
ret->target->insert(0, delimitor+"\n");
} }
else else
{ {
@ -1017,67 +1028,7 @@ std::pair<condlist*, parse_context> parse_condlist(parse_context ctx)
return std::make_pair(ret, ctx); return std::make_pair(ret, ctx);
} }
std::pair<list*, parse_context> parse_list_until(parse_context ctx, char end_c, const char* expecting) std::tuple<list*, parse_context, std::string> parse_list_until(parse_context ctx, list_parse_options opts)
{
list* ret = new list;
ctx.i=skip_unread(ctx);
#ifndef NO_PARSE_CATCH
try
{
#endif
;
const char* old_expect=ctx.expecting;
if(ctx.expecting!=NULL)
ctx.expecting = expecting;
while(ctx[ctx.i] != end_c)
{
auto pp=parse_condlist(ctx);
ret->add(pp.first);
ctx=pp.second;
if(ctx.i < ctx.size)
{
if(ctx[ctx.i] == end_c) // end char, stop here
break;
else if(ctx[ctx.i] == '#')
; // skip here
else if(is_in(ctx[ctx.i], COMMAND_SEPARATOR))
ctx.i++; // skip on next char
else if(is_in(ctx[ctx.i], CONTROL_END))
{
parse_error( unexpected_token(ctx[ctx.i]), ctx);
return std::make_pair(ret, ctx);
}
ctx.i = skip_unread(ctx);
}
if(ctx.i>=ctx.size)
{
if(end_c != 0)
{
parse_error(strf("Expecting '%s'", expecting), ctx);
return std::make_pair(ret, ctx);
}
else
break;
}
}
ctx.expecting = old_expect;
#ifndef NO_PARSE_CATCH
}
catch(format_error& e)
{
delete ret;
throw e;
}
#endif
return std::make_pair(ret, ctx);
}
std::pair<list*, parse_context> parse_list_until(parse_context ctx, std::string const& end_word)
{ {
list* ret = new list; list* ret = new list;
ctx.i=skip_unread(ctx); ctx.i=skip_unread(ctx);
@ -1088,114 +1039,114 @@ std::pair<list*, parse_context> parse_list_until(parse_context ctx, std::string
{ {
#endif #endif
; ;
char& end_c = opts.end_char;
std::vector<std::string>& end_words = opts.end_words;
const char* old_expect=ctx.expecting; const char* old_expect=ctx.expecting;
ctx.expecting=end_word.c_str(); if(opts.expecting!=NULL)
ctx.expecting=opts.expecting;
while(true) else if(opts.word_mode)
{
// check word
auto wp=get_word(ctx, ARG_END);
if(wp.first == end_word)
{
ctx.i=wp.second;
break;
}
// do a parse
auto pp=parse_condlist(ctx);
ret->add(pp.first);
ctx=pp.second;
if(ctx.i<ctx.size)
{
if(ctx[ctx.i] == '#')
; // skip here
else if(is_in(ctx[ctx.i], COMMAND_SEPARATOR))
ctx.i++;
else if(is_in(ctx[ctx.i], CONTROL_END))
{
parse_error(unexpected_token(ctx[ctx.i]), ctx);
return std::make_pair(ret, ctx);
}
ctx.i = skip_unread(ctx);
}
// word wasn't found
if(ctx.i>=ctx.size)
{
parse_error(strf("Expecting '%s'", ctx.expecting), ctx);
return std::make_pair(ret, ctx);
}
}
ctx.expecting=old_expect;
#ifndef NO_PARSE_CATCH
}
catch(format_error& e)
{
delete ret;
throw e;
}
#endif
return std::make_pair(ret, ctx);
}
std::tuple<list*, parse_context, std::string> parse_list_until(parse_context ctx, std::vector<std::string> const& end_words, const char* expecting)
{
list* ret = new list;
ctx.i=skip_unread(ctx);
std::string found_end_word;
#ifndef NO_PARSE_CATCH
try
{
#endif
;
const char* old_expect=ctx.expecting;
if(expecting!=NULL)
ctx.expecting=expecting;
else
ctx.expecting=end_words[0].c_str(); ctx.expecting=end_words[0].c_str();
else
ctx.expecting=std::string(&end_c, 1).c_str();
bool stop=false; bool stop=false;
while(true) while(true)
{ {
// check words if(opts.word_mode)
auto wp=get_word(ctx, ARG_END);
for(auto it: end_words)
{ {
if(it == ";" && ctx[ctx.i] == ';') // check words
auto wp=get_word(ctx, ARG_END);
for(auto it: end_words)
{ {
found_end_word=";"; if(it == ";" && ctx[ctx.i] == ';')
ctx.i++; {
stop=true; found_end_word=";";
break; ctx.i++;
stop=true;
break;
}
if(wp.first == it)
{
found_end_word=it;
ctx.i=wp.second;
stop=true;
break;
}
} }
if(wp.first == it) if(stop)
{
found_end_word=it;
ctx.i=wp.second;
stop=true;
break; break;
}
} }
if(stop) else if(ctx[ctx.i] == end_c)
{
break; break;
}
// do a parse // do a parse
auto pp=parse_condlist(ctx); auto pp=parse_condlist(ctx);
ret->add(pp.first); ret->add(pp.first);
ctx=pp.second; ctx=pp.second;
if(ctx[ctx.i] == '#')
if(!opts.word_mode && ctx[ctx.i] == end_c)
break; // reached end char: stop here
else if(ctx[ctx.i] == '\n')
{
if(ctx.here_document != nullptr)
ctx = parse_heredocument(ctx+1);
// do here document parse
}
else if(ctx[ctx.i] == '#')
; // skip here ; // skip here
else if(is_in(ctx[ctx.i], COMMAND_SEPARATOR)) else if(is_in(ctx[ctx.i], COMMAND_SEPARATOR))
ctx.i++; // skip on next ; // skip on next
else if(is_in(ctx[ctx.i], CONTROL_END))
{
// control end: unexpected
parse_error( unexpected_token(ctx[ctx.i]), ctx);
break;
}
if(ctx.here_document != nullptr)
{
uint8_t do_twice=2;
// case of : cat << EOF ;
while(do_twice>0)
{
if(ctx[ctx.i] == '\n')
{
ctx = parse_heredocument(ctx+1);
break;
}
else if(ctx[ctx.i] == '#')
{
ctx.i = skip_until(ctx, "\n"); //skip to endline
ctx = parse_heredocument(ctx+1);
break;
}
skip_chars(ctx, SPACES);
do_twice--;
}
// case of : cat << EOF ; ;
if(do_twice==0 && is_in(ctx[ctx.i], COMMAND_SEPARATOR))
parse_error( unexpected_token(ctx[ctx.i]), ctx);
}
if(is_in(ctx[ctx.i], COMMAND_SEPARATOR))
ctx.i++;
ctx.i = skip_unread(ctx); ctx.i = skip_unread(ctx);
// word wasn't found // word wasn't found
if(ctx.i>=ctx.size) if(ctx.i>=ctx.size)
{ {
parse_error(strf("Expecting '%s'", ctx.expecting), ctx); if(opts.word_mode || opts.end_char != 0)
return std::make_tuple(ret, ctx, ""); {
parse_error(strf("Expecting '%s'", ctx.expecting), ctx);
return std::make_tuple(ret, ctx, "");
}
else
break;
} }
} }
ctx.expecting=old_expect; ctx.expecting=old_expect;
@ -1224,9 +1175,9 @@ std::pair<subshell*, parse_context> parse_subshell(parse_context ctx)
{ {
#endif #endif
; ;
auto pp=parse_list_until(ctx, ')', ")"); auto pp=parse_list_until(ctx, {.end_char=')', .expecting=")"} );
ret->lst=pp.first; ret->lst=std::get<0>(pp);
ctx=pp.second; ctx=std::get<1>(pp);
if(ret->lst->size()<=0) if(ret->lst->size()<=0)
{ {
parse_error("Subshell is empty", ctx, start-1); parse_error("Subshell is empty", ctx, start-1);
@ -1258,9 +1209,9 @@ std::pair<brace*, parse_context> parse_brace(parse_context ctx)
{ {
#endif #endif
; ;
auto pp=parse_list_until(ctx, '}', "}"); auto pp=parse_list_until(ctx, {.end_char='}', .expecting="}"});
ret->lst=pp.first; ret->lst=std::get<0>(pp);
ctx=pp.second; ctx=std::get<1>(pp);
if(ret->lst->size()<=0) if(ret->lst->size()<=0)
{ {
parse_error("Brace block is empty", ctx, start-1); parse_error("Brace block is empty", ctx, start-1);
@ -1299,16 +1250,16 @@ std::pair<function*, parse_context> parse_function(parse_context ctx, const char
} }
ctx.i++; ctx.i++;
auto pp=parse_list_until(ctx, '}', "}"); auto pp=parse_list_until(ctx, {.end_char='}', .expecting="}"} );
if(pp.first->size()<=0) ret->lst=std::get<0>(pp);
if(ret->lst->size()<=0)
{ {
parse_error("Function is empty", ctx); parse_error("Function is empty", ctx);
ctx.i=pp.second.i+1; ctx.i=std::get<1>(pp).i+1;
return std::make_pair(ret, ctx); return std::make_pair(ret, ctx);
} }
ret->lst=pp.first; ctx=std::get<1>(pp);
ctx=pp.second;
ctx.i++; ctx.i++;
#ifndef NO_PARSE_CATCH #ifndef NO_PARSE_CATCH
} }
@ -1549,7 +1500,7 @@ std::pair<case_block*, parse_context> parse_case(parse_context ctx)
ctx.i++; ctx.i++;
// until ;; // until ;;
auto tp = parse_list_until(ctx, {";", "esac"}, ";;"); auto tp = parse_list_until(ctx, { .word_mode=true, .end_words={";", "esac"}, .expecting=";;" });
cc->second = std::get<0>(tp); cc->second = std::get<0>(tp);
ctx = std::get<1>(tp); ctx = std::get<1>(tp);
std::string word = std::get<2>(tp); std::string word = std::get<2>(tp);
@ -1600,20 +1551,21 @@ std::pair<if_block*, parse_context> parse_if(parse_context ctx)
while(true) while(true)
{ {
std::string word; std::string word;
parse_context oldctx = ctx;
ret->blocks.push_back(std::make_pair(nullptr, nullptr)); ret->blocks.push_back(std::make_pair(nullptr, nullptr));
auto ll = ret->blocks.end()-1; auto ll = ret->blocks.end()-1;
auto pp=parse_list_until(ctx, "then"); auto pp=parse_list_until(ctx, {.word_mode=true, .end_words={"then"}});
ll->first = pp.first; ll->first = std::get<0>(pp);
ctx = std::get<1>(pp);
if(ll->first->size()<=0) if(ll->first->size()<=0)
{ {
parse_error("Condition is empty", ctx); parse_error("Condition is empty", oldctx);
pp.second.has_errored=true; ctx.has_errored=true;
} }
ctx = pp.second;
auto tp=parse_list_until(ctx, {"fi", "elif", "else"}); auto tp=parse_list_until(ctx, {.word_mode=true, .end_words={"fi", "elif", "else"}} );
ll->second = std::get<0>(tp); ll->second = std::get<0>(tp);
parse_context newctx = std::get<1>(tp); parse_context newctx = std::get<1>(tp);
word = std::get<2>(tp); word = std::get<2>(tp);
@ -1628,14 +1580,17 @@ std::pair<if_block*, parse_context> parse_if(parse_context ctx)
break; break;
if(word == "else") if(word == "else")
{ {
auto pp=parse_list_until(ctx, "fi"); auto pp=parse_list_until(ctx, {.word_mode=true, .end_words={"fi"}});
if(pp.first->size()<=0) ret->else_lst=std::get<0>(pp);
if(ret->else_lst->size()<=0)
{ {
parse_error("else block is empty", ctx); parse_error("else block is empty", ctx);
pp.second.has_errored=true; ctx=std::get<1>(pp);
ctx.has_errored=true;
} }
ret->else_lst=pp.first; else
ctx=pp.second; ctx=std::get<1>(pp);
break; break;
} }
@ -1714,9 +1669,9 @@ std::pair<for_block*, parse_context> parse_for(parse_context ctx)
} }
// ops // ops
auto lp = parse_list_until(ctx, "done"); auto lp = parse_list_until(ctx, {.word_mode=true, .end_words={"done"}} );
ret->ops=lp.first; ret->ops=std::get<0>(lp);
ctx=lp.second; ctx=std::get<1>(lp);
#ifndef NO_PARSE_CATCH #ifndef NO_PARSE_CATCH
} }
catch(format_error& e) catch(format_error& e)
@ -1739,24 +1694,28 @@ std::pair<while_block*, parse_context> parse_while(parse_context ctx)
#endif #endif
; ;
// cond // cond
auto pp=parse_list_until(ctx, "do"); parse_context oldctx = ctx;
if(pp.first->size() <= 0) auto pp=parse_list_until(ctx, {.word_mode=true, .end_words={"do"}});
ret->cond = std::get<0>(pp);
ctx = std::get<1>(pp);
if(ret->cond->size() <= 0)
{ {
parse_error("condition is empty", ctx); parse_error("condition is empty", oldctx);
pp.second.has_errored=true; ctx.has_errored=true;
} }
ret->cond = pp.first;
ctx = pp.second;
// ops // ops
auto lp = parse_list_until(ctx, "done"); oldctx = ctx;
if(lp.first->size() <= 0) auto lp = parse_list_until(ctx, {.word_mode=true, .end_words={"done"}} );
ret->ops=std::get<0>(lp);
ctx = std::get<1>(lp);
if(ret->ops->size() <= 0)
{ {
parse_error("while is empty", ctx); parse_error("while is empty", oldctx);
lp.second.has_errored=true; ctx.has_errored=true;
} }
ret->ops=lp.first;
ctx = lp.second;
#ifndef NO_PARSE_CATCH #ifndef NO_PARSE_CATCH
} }
catch(format_error& e) catch(format_error& e)
@ -1936,13 +1895,14 @@ std::pair<shmain*, parse_context> parse_text(parse_context ctx)
if(!ctx.bash) if(!ctx.bash)
ctx.bash = (binshebang == "bash" || binshebang == "lxsh"); ctx.bash = (binshebang == "bash" || binshebang == "lxsh");
// parse all commands // parse all commands
auto pp=parse_list_until(ctx, 0); auto pp=parse_list_until(ctx);
ret->lst=pp.first; ret->lst=std::get<0>(pp);
ctx = std::get<1>(pp);
if(pp.second.has_errored) if(ctx.has_errored)
throw std::runtime_error("Aborted due to previous errors"); throw std::runtime_error("Aborted due to previous errors");
return std::make_pair(ret, pp.second); return std::make_pair(ret, ctx);
} }
std::pair<shmain*, parse_context> parse_text(std::string const& in, std::string const& filename) std::pair<shmain*, parse_context> parse_text(std::string const& in, std::string const& filename)

View file

@ -58,7 +58,8 @@ condlist* make_condlist(std::string const& in)
list* make_list(std::string const& in) list* make_list(std::string const& in)
{ {
return parse_list_until(make_context(in), 0).first; auto t = parse_list_until(make_context(in));
return std::get<0>(t);
} }
block* make_block(std::string const& in) block* make_block(std::string const& in)