implement delayed heredocument parsing
This commit is contained in:
parent
3e21098d95
commit
15ac04f505
6 changed files with 292 additions and 281 deletions
|
|
@ -6,6 +6,7 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
||||
#define SPACES " \t"
|
||||
#define SEPARATORS " \t\n"
|
||||
|
|
@ -33,9 +34,13 @@
|
|||
|
||||
// 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
|
||||
|
||||
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); }
|
||||
|
||||
// tools
|
||||
|
||||
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, uint64_t i);
|
||||
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 ** //
|
||||
|
||||
/* util parsers */
|
||||
|
|
@ -76,9 +86,10 @@ inline uint32_t skip_unread(parse_context const& ct) {
|
|||
}
|
||||
|
||||
// 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, 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::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::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
|
||||
std::pair<variable*, parse_context> parse_var(parse_context ct, bool specialvars=true, bool array=false);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
const char* data=NULL;
|
||||
uint64_t size=0;
|
||||
|
|
@ -73,9 +88,16 @@ struct parse_context {
|
|||
const char* here_doc="";
|
||||
const char operator[](uint64_t a) { return data[a]; }
|
||||
bool has_errored=false;
|
||||
redirect* here_document=nullptr;
|
||||
char* here_delimitor=NULL;
|
||||
};
|
||||
|
||||
struct generate_context {
|
||||
arg* here_document=nullptr;
|
||||
};
|
||||
|
||||
// exceptions
|
||||
|
||||
class format_error : public std::exception
|
||||
{
|
||||
public:
|
||||
|
|
@ -97,20 +119,8 @@ private:
|
|||
std::string sdat;
|
||||
};
|
||||
|
||||
|
||||
// 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
|
||||
typedef std::vector<arg*> arglist_t;
|
||||
|
||||
|
|
@ -229,15 +239,17 @@ public:
|
|||
class redirect : public _obj
|
||||
{
|
||||
public:
|
||||
redirect(std::string strop="") { type=_obj::_redirect; op=strop; target=nullptr; }
|
||||
redirect(arg* in) { type=_obj::_redirect; target=in; }
|
||||
redirect(std::string strop, arg* in) { type=_obj::_redirect; op=strop; target=in; }
|
||||
redirect(std::string strop="") { type=_obj::_redirect; op=strop; target=nullptr; here_document=nullptr; }
|
||||
redirect(arg* in) { type=_obj::_redirect; target=in; here_document=nullptr; }
|
||||
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; }
|
||||
|
||||
std::string generate(int ind);
|
||||
|
||||
std::string op;
|
||||
arg* target;
|
||||
arg* here_document;
|
||||
};
|
||||
|
||||
// Meta block
|
||||
|
|
@ -252,9 +264,9 @@ public:
|
|||
// subshell: return the containing cmd, if it is a single command
|
||||
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
|
||||
|
|
@ -269,7 +281,8 @@ public:
|
|||
|
||||
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
|
||||
|
|
@ -370,7 +383,8 @@ public:
|
|||
|
||||
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
|
||||
|
|
@ -388,7 +402,8 @@ public:
|
|||
list* lst;
|
||||
|
||||
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
|
||||
|
|
@ -404,7 +419,8 @@ public:
|
|||
|
||||
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
|
||||
|
|
@ -419,7 +435,8 @@ public:
|
|||
|
||||
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
|
||||
|
|
@ -433,7 +450,8 @@ public:
|
|||
std::string name;
|
||||
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
|
||||
|
|
@ -453,7 +471,8 @@ public:
|
|||
arg* carg;
|
||||
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
|
||||
|
|
@ -473,7 +492,8 @@ public:
|
|||
|
||||
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
|
||||
|
|
@ -491,7 +511,8 @@ public:
|
|||
arglist* iter;
|
||||
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
|
||||
|
|
@ -508,7 +529,8 @@ public:
|
|||
list* cond;
|
||||
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 //
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ void parse_exec(FILE* fd, parse_context ctx)
|
|||
ctx=pp.second;
|
||||
if(ctx.has_errored)
|
||||
{
|
||||
parse_list_until(ctx, 0);
|
||||
parse_list_until(ctx);
|
||||
throw std::runtime_error("Aborting due to previous errors");
|
||||
}
|
||||
t_lst->add(pp.first);
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ std::string arglist::generate(int ind)
|
|||
return ret;
|
||||
}
|
||||
|
||||
std::string pipeline::generate(int ind)
|
||||
std::string pipeline::generate(int ind, generate_context* ctx)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
|
|
@ -53,11 +53,11 @@ std::string pipeline::generate(int ind)
|
|||
|
||||
if(negated)
|
||||
ret += "! ";
|
||||
ret += cmds[0]->generate(ind);
|
||||
ret += cmds[0]->generate(ind, ctx);
|
||||
for(uint32_t i=1 ; i<cmds.size() ; i++)
|
||||
{
|
||||
ret += opt_minify ? "|" : " | " ;
|
||||
ret += cmds[i]->generate(ind);
|
||||
ret += cmds[i]->generate(ind, ctx);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -68,18 +68,27 @@ std::string condlist::generate(int ind)
|
|||
std::string ret;
|
||||
if(pls.size() <= 0)
|
||||
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++)
|
||||
{
|
||||
if(or_ops[i])
|
||||
ret += opt_minify ? "||" : " || ";
|
||||
else
|
||||
ret += opt_minify ? "&&" : " && ";
|
||||
ret += pls[i+1]->generate(ind);
|
||||
ret += pls[i+1]->generate(ind, &ctx);
|
||||
}
|
||||
if(ret=="")
|
||||
return "";
|
||||
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";
|
||||
}
|
||||
|
|
@ -123,12 +132,18 @@ std::string redirect::generate(int ind)
|
|||
|
||||
// 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=" ";
|
||||
bool previous_isnt_num = _str.size()>0 && !is_num(_str[_str.size()-1]);
|
||||
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);
|
||||
if(opt_minify && _r.size() > 0 && !is_num(_r[0]) && previous_isnt_num)
|
||||
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;
|
||||
}
|
||||
|
||||
std::string if_block::generate(int ind)
|
||||
std::string if_block::generate(int ind, generate_context* ctx)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
|
|
@ -169,11 +184,11 @@ std::string if_block::generate(int ind)
|
|||
|
||||
ret += indented("fi", ind);
|
||||
|
||||
ret += generate_redirs(ind, ret);
|
||||
ret += generate_redirs(ind, ret, ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string for_block::generate(int ind)
|
||||
std::string for_block::generate(int ind, generate_context* ctx)
|
||||
{
|
||||
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]))
|
||||
ret.pop_back();
|
||||
ret += generate_redirs(ind, ret);
|
||||
ret += generate_redirs(ind, ret, ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string while_block::generate(int ind)
|
||||
std::string while_block::generate(int ind, generate_context* ctx)
|
||||
{
|
||||
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]))
|
||||
ret.pop_back();
|
||||
ret += generate_redirs(ind, ret);
|
||||
ret += generate_redirs(ind, ret, ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string subshell::generate(int ind)
|
||||
std::string subshell::generate(int ind, generate_context* ctx)
|
||||
{
|
||||
std::string ret;
|
||||
// open subshell
|
||||
|
|
@ -224,11 +239,11 @@ std::string subshell::generate(int ind)
|
|||
// close subshell
|
||||
ret += indented(")", ind);
|
||||
|
||||
ret += generate_redirs(ind, ret);
|
||||
ret += generate_redirs(ind, ret, ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string shmain::generate(int ind)
|
||||
std::string shmain::generate(int ind, generate_context* ctx)
|
||||
{
|
||||
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')
|
||||
ret.pop_back();
|
||||
|
||||
ret += generate_redirs(ind, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string brace::generate(int ind)
|
||||
std::string brace::generate(int ind, generate_context* ctx)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
|
|
@ -253,11 +267,11 @@ std::string brace::generate(int ind)
|
|||
ret += lst->generate(ind+1);
|
||||
ret += indented("}", ind);
|
||||
|
||||
ret += generate_redirs(ind, ret);
|
||||
ret += generate_redirs(ind, ret, ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string function::generate(int ind)
|
||||
std::string function::generate(int ind, generate_context* ctx)
|
||||
{
|
||||
std::string ret;
|
||||
// function definition
|
||||
|
|
@ -268,11 +282,11 @@ std::string function::generate(int ind)
|
|||
ret += lst->generate(ind+1);
|
||||
ret += indented("}", ind);
|
||||
|
||||
ret += generate_redirs(ind, ret);
|
||||
ret += generate_redirs(ind, ret, ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string case_block::generate(int ind)
|
||||
std::string case_block::generate(int ind, generate_context* ctx)
|
||||
{
|
||||
std::string ret;
|
||||
ret += "case " + carg->generate(ind) + " in\n";
|
||||
|
|
@ -307,14 +321,15 @@ std::string case_block::generate(int ind)
|
|||
ind--;
|
||||
ret += indented("esac", ind);
|
||||
|
||||
ret += generate_redirs(ind, ret);
|
||||
ret += generate_redirs(ind, ret, ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string cmd::generate(int ind)
|
||||
std::string cmd::generate(int ind, generate_context* ctx)
|
||||
{
|
||||
std::string ret;
|
||||
// var assigns
|
||||
|
||||
// is a varassign cmd
|
||||
if(is_cmdvar)
|
||||
{
|
||||
ret += args->generate(ind) + ' ';
|
||||
|
|
@ -330,6 +345,7 @@ std::string cmd::generate(int ind)
|
|||
return ret;
|
||||
}
|
||||
|
||||
// pre-cmd var assigns
|
||||
for(auto it: var_assigns)
|
||||
{
|
||||
if(it.first != nullptr)
|
||||
|
|
@ -339,6 +355,7 @@ std::string cmd::generate(int ind)
|
|||
ret += ' ';
|
||||
}
|
||||
|
||||
// cmd itself
|
||||
if(args!=nullptr && args->size()>0)
|
||||
{
|
||||
// command
|
||||
|
|
@ -353,7 +370,7 @@ std::string cmd::generate(int ind)
|
|||
ret.pop_back();
|
||||
}
|
||||
|
||||
ret += generate_redirs(ind, ret);
|
||||
ret += generate_redirs(ind, ret, ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
368
src/parse.cpp
368
src/parse.cpp
|
|
@ -25,7 +25,6 @@ const std::vector<std::string> out_reserved_words = { "then", "else", "fi", "esa
|
|||
|
||||
// stuff
|
||||
|
||||
|
||||
std::string unexpected_token(char c)
|
||||
{
|
||||
std::string print;
|
||||
|
|
@ -442,9 +441,9 @@ parse_context do_one_subarg_step(arg* ret, parse_context ctx, uint32_t& j, bool
|
|||
// get subshell
|
||||
parse_context newct = ctx;
|
||||
ctx.size=k;
|
||||
auto r=parse_list_until(newct, 0);
|
||||
ret->add(new subshell_subarg(new subshell(r.first), is_quoted));
|
||||
ctx = r.second;
|
||||
auto r=parse_list_until(newct);
|
||||
ret->add(new subshell_subarg(new subshell(std::get<0>(r)), is_quoted));
|
||||
ctx = std::get<1>(r);
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
bool is_redirect=false;
|
||||
|
|
@ -717,14 +745,20 @@ std::pair<redirect*, parse_context> parse_redirect(parse_context ctx)
|
|||
ctx.i = skip_chars(ctx, SPACES);
|
||||
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);
|
||||
std::string delimitor = pa.first->string();
|
||||
delete pa.first;
|
||||
pa.first = nullptr;
|
||||
|
||||
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)
|
||||
|
|
@ -732,34 +766,11 @@ std::pair<redirect*, parse_context> parse_redirect(parse_context ctx)
|
|||
delimitor = ztd::sh("echo "+delimitor); // shell resolve the delimitor
|
||||
delimitor.pop_back(); // remove \n
|
||||
}
|
||||
|
||||
ret->target = pa.first;
|
||||
ctx = pa.second;
|
||||
ctx.i = skip_chars(ctx, SPACES); // skip spaces
|
||||
|
||||
if(ctx[ctx.i] == '#') // skip comment
|
||||
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");
|
||||
// copy delimitor
|
||||
ctx.here_delimitor = (char*) malloc(delimitor.length()+1);
|
||||
strcpy(ctx.here_delimitor, delimitor.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1017,67 +1028,7 @@ std::pair<condlist*, parse_context> parse_condlist(parse_context ctx)
|
|||
return std::make_pair(ret, ctx);
|
||||
}
|
||||
|
||||
std::pair<list*, parse_context> parse_list_until(parse_context ctx, char end_c, const char* expecting)
|
||||
{
|
||||
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)
|
||||
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);
|
||||
|
|
@ -1088,77 +1039,23 @@ std::pair<list*, parse_context> parse_list_until(parse_context ctx, std::string
|
|||
{
|
||||
#endif
|
||||
;
|
||||
|
||||
char& end_c = opts.end_char;
|
||||
std::vector<std::string>& end_words = opts.end_words;
|
||||
|
||||
const char* old_expect=ctx.expecting;
|
||||
|
||||
ctx.expecting=end_word.c_str();
|
||||
|
||||
while(true)
|
||||
{
|
||||
// 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
|
||||
if(opts.expecting!=NULL)
|
||||
ctx.expecting=opts.expecting;
|
||||
else if(opts.word_mode)
|
||||
ctx.expecting=end_words[0].c_str();
|
||||
else
|
||||
ctx.expecting=std::string(&end_c, 1).c_str();
|
||||
|
||||
bool stop=false;
|
||||
while(true)
|
||||
{
|
||||
if(opts.word_mode)
|
||||
{
|
||||
// check words
|
||||
auto wp=get_word(ctx, ARG_END);
|
||||
|
|
@ -1181,22 +1078,76 @@ std::tuple<list*, parse_context, std::string> parse_list_until(parse_context ctx
|
|||
}
|
||||
if(stop)
|
||||
break;
|
||||
}
|
||||
else if(ctx[ctx.i] == end_c)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// do a parse
|
||||
auto pp=parse_condlist(ctx);
|
||||
ret->add(pp.first);
|
||||
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
|
||||
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);
|
||||
|
||||
// word wasn't found
|
||||
if(ctx.i>=ctx.size)
|
||||
{
|
||||
if(opts.word_mode || opts.end_char != 0)
|
||||
{
|
||||
parse_error(strf("Expecting '%s'", ctx.expecting), ctx);
|
||||
return std::make_tuple(ret, ctx, "");
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx.expecting=old_expect;
|
||||
#ifndef NO_PARSE_CATCH
|
||||
|
|
@ -1224,9 +1175,9 @@ std::pair<subshell*, parse_context> parse_subshell(parse_context ctx)
|
|||
{
|
||||
#endif
|
||||
;
|
||||
auto pp=parse_list_until(ctx, ')', ")");
|
||||
ret->lst=pp.first;
|
||||
ctx=pp.second;
|
||||
auto pp=parse_list_until(ctx, {.end_char=')', .expecting=")"} );
|
||||
ret->lst=std::get<0>(pp);
|
||||
ctx=std::get<1>(pp);
|
||||
if(ret->lst->size()<=0)
|
||||
{
|
||||
parse_error("Subshell is empty", ctx, start-1);
|
||||
|
|
@ -1258,9 +1209,9 @@ std::pair<brace*, parse_context> parse_brace(parse_context ctx)
|
|||
{
|
||||
#endif
|
||||
;
|
||||
auto pp=parse_list_until(ctx, '}', "}");
|
||||
ret->lst=pp.first;
|
||||
ctx=pp.second;
|
||||
auto pp=parse_list_until(ctx, {.end_char='}', .expecting="}"});
|
||||
ret->lst=std::get<0>(pp);
|
||||
ctx=std::get<1>(pp);
|
||||
if(ret->lst->size()<=0)
|
||||
{
|
||||
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++;
|
||||
|
||||
auto pp=parse_list_until(ctx, '}', "}");
|
||||
if(pp.first->size()<=0)
|
||||
auto pp=parse_list_until(ctx, {.end_char='}', .expecting="}"} );
|
||||
ret->lst=std::get<0>(pp);
|
||||
if(ret->lst->size()<=0)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
ret->lst=pp.first;
|
||||
ctx=pp.second;
|
||||
ctx=std::get<1>(pp);
|
||||
ctx.i++;
|
||||
#ifndef NO_PARSE_CATCH
|
||||
}
|
||||
|
|
@ -1549,7 +1500,7 @@ std::pair<case_block*, parse_context> parse_case(parse_context ctx)
|
|||
ctx.i++;
|
||||
|
||||
// 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);
|
||||
ctx = std::get<1>(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)
|
||||
{
|
||||
std::string word;
|
||||
parse_context oldctx = ctx;
|
||||
|
||||
ret->blocks.push_back(std::make_pair(nullptr, nullptr));
|
||||
auto ll = ret->blocks.end()-1;
|
||||
|
||||
auto pp=parse_list_until(ctx, "then");
|
||||
ll->first = pp.first;
|
||||
auto pp=parse_list_until(ctx, {.word_mode=true, .end_words={"then"}});
|
||||
ll->first = std::get<0>(pp);
|
||||
ctx = std::get<1>(pp);
|
||||
if(ll->first->size()<=0)
|
||||
{
|
||||
parse_error("Condition is empty", ctx);
|
||||
pp.second.has_errored=true;
|
||||
parse_error("Condition is empty", oldctx);
|
||||
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);
|
||||
parse_context newctx = std::get<1>(tp);
|
||||
word = std::get<2>(tp);
|
||||
|
|
@ -1628,14 +1580,17 @@ std::pair<if_block*, parse_context> parse_if(parse_context ctx)
|
|||
break;
|
||||
if(word == "else")
|
||||
{
|
||||
auto pp=parse_list_until(ctx, "fi");
|
||||
if(pp.first->size()<=0)
|
||||
auto pp=parse_list_until(ctx, {.word_mode=true, .end_words={"fi"}});
|
||||
ret->else_lst=std::get<0>(pp);
|
||||
if(ret->else_lst->size()<=0)
|
||||
{
|
||||
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;
|
||||
ctx=pp.second;
|
||||
else
|
||||
ctx=std::get<1>(pp);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1714,9 +1669,9 @@ std::pair<for_block*, parse_context> parse_for(parse_context ctx)
|
|||
}
|
||||
|
||||
// ops
|
||||
auto lp = parse_list_until(ctx, "done");
|
||||
ret->ops=lp.first;
|
||||
ctx=lp.second;
|
||||
auto lp = parse_list_until(ctx, {.word_mode=true, .end_words={"done"}} );
|
||||
ret->ops=std::get<0>(lp);
|
||||
ctx=std::get<1>(lp);
|
||||
#ifndef NO_PARSE_CATCH
|
||||
}
|
||||
catch(format_error& e)
|
||||
|
|
@ -1739,24 +1694,28 @@ std::pair<while_block*, parse_context> parse_while(parse_context ctx)
|
|||
#endif
|
||||
;
|
||||
// cond
|
||||
auto pp=parse_list_until(ctx, "do");
|
||||
if(pp.first->size() <= 0)
|
||||
parse_context oldctx = ctx;
|
||||
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);
|
||||
pp.second.has_errored=true;
|
||||
parse_error("condition is empty", oldctx);
|
||||
ctx.has_errored=true;
|
||||
}
|
||||
ret->cond = pp.first;
|
||||
ctx = pp.second;
|
||||
|
||||
// ops
|
||||
auto lp = parse_list_until(ctx, "done");
|
||||
if(lp.first->size() <= 0)
|
||||
oldctx = ctx;
|
||||
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);
|
||||
lp.second.has_errored=true;
|
||||
parse_error("while is empty", oldctx);
|
||||
ctx.has_errored=true;
|
||||
}
|
||||
ret->ops=lp.first;
|
||||
ctx = lp.second;
|
||||
#ifndef NO_PARSE_CATCH
|
||||
}
|
||||
catch(format_error& e)
|
||||
|
|
@ -1936,13 +1895,14 @@ std::pair<shmain*, parse_context> parse_text(parse_context ctx)
|
|||
if(!ctx.bash)
|
||||
ctx.bash = (binshebang == "bash" || binshebang == "lxsh");
|
||||
// parse all commands
|
||||
auto pp=parse_list_until(ctx, 0);
|
||||
ret->lst=pp.first;
|
||||
auto pp=parse_list_until(ctx);
|
||||
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");
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -58,7 +58,8 @@ condlist* make_condlist(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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue