Improve parsing errors
This commit is contained in:
parent
af1de6d8fb
commit
577a1aef94
7 changed files with 129 additions and 37 deletions
2
Makefile
2
Makefile
|
|
@ -9,7 +9,7 @@ BINDIR=.
|
|||
NAME = $(shell readlink -f . | xargs basename)
|
||||
|
||||
# global links
|
||||
LDFLAGS = -lpthread
|
||||
LDFLAGS = -Wl,--no-as-needed -lpthread
|
||||
|
||||
# compiler
|
||||
CC=g++
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <ztd/filedat.hpp>
|
||||
|
||||
#define INDENT indent(ind)
|
||||
|
||||
extern std::string indenting_string;
|
||||
|
|
@ -36,4 +38,7 @@ void _exec(std::string const& bin, std::vector<std::string> const& args);
|
|||
|
||||
std::string stringReplace(std::string subject, const std::string& search, const std::string& replace);
|
||||
|
||||
void printFormatError(ztd::format_error const& e, bool print_line=true);
|
||||
void printErrorIndex(const char* in, const int index, const std::string& message, const std::string& origin, bool print_line=true);
|
||||
|
||||
#endif //UTIL_HPP
|
||||
|
|
|
|||
|
|
@ -44,6 +44,9 @@ std::string pipeline::generate(int ind)
|
|||
{
|
||||
std::string ret;
|
||||
|
||||
if(cmds.size()<=0)
|
||||
return "";
|
||||
|
||||
ret += cmds[0].generate(ind);
|
||||
for(uint32_t i=1 ; i<cmds.size() ; i++)
|
||||
{
|
||||
|
|
@ -57,7 +60,10 @@ std::string pipeline::generate(int ind)
|
|||
std::string condlist::generate(int ind)
|
||||
{
|
||||
std::string ret;
|
||||
if(!opt_minimize) ret += INDENT;
|
||||
if(pls.size() <= 0)
|
||||
return "";
|
||||
if(!opt_minimize)
|
||||
ret += INDENT;
|
||||
ret += pls[0].generate(ind);
|
||||
for(uint32_t i=0 ; i<pls.size()-1 ; i++)
|
||||
{
|
||||
|
|
@ -114,7 +120,8 @@ std::string generate_resolve(std::vector<std::string> args, int ind)
|
|||
dir=pwd();
|
||||
std::string cddir=ztd::exec("dirname", g_origin).first;
|
||||
cddir.pop_back();
|
||||
chdir(cddir.c_str());
|
||||
if(chdir(cddir.c_str()) != 0)
|
||||
throw std::runtime_error("Cannot cd to '"+cddir+"'");
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -150,7 +157,8 @@ std::string generate_resolve(std::vector<std::string> args, int ind)
|
|||
}
|
||||
|
||||
if(!opts['C'] && !piped)
|
||||
chdir(dir.c_str());
|
||||
if(chdir(dir.c_str()) != 0)
|
||||
throw std::runtime_error("Cannot cd to '"+dir+"'");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -176,7 +184,8 @@ std::string generate_include(std::vector<std::string> args, int ind)
|
|||
dir=pwd();
|
||||
std::string cddir=ztd::exec("dirname", curfile).first;
|
||||
cddir.pop_back();
|
||||
chdir(cddir.c_str());
|
||||
if(chdir(cddir.c_str()) != 0)
|
||||
throw std::runtime_error("Cannot cd to '"+cddir+"'");
|
||||
}
|
||||
|
||||
// do shell resolution
|
||||
|
|
@ -199,7 +208,7 @@ std::string generate_include(std::vector<std::string> args, int ind)
|
|||
add_include(it) ) // not already included
|
||||
{
|
||||
file=import_file(it);
|
||||
if(opts['e'])
|
||||
if(opts['d'])
|
||||
file = stringReplace(file, "\"", "\\\"");
|
||||
if(opts['r'])
|
||||
ret += file;
|
||||
|
|
@ -226,7 +235,8 @@ std::string generate_include(std::vector<std::string> args, int ind)
|
|||
}
|
||||
}
|
||||
if(!opts['C'] && !piped)
|
||||
chdir(dir.c_str());
|
||||
if(chdir(dir.c_str()) != 0)
|
||||
throw std::runtime_error("Cannot cd to '"+dir+"'");
|
||||
g_origin=curfile;
|
||||
|
||||
if(!opts['r'])
|
||||
|
|
@ -321,7 +331,7 @@ std::string block::generate(int ind, bool print_shebang)
|
|||
// commands
|
||||
for(auto it: cls)
|
||||
ret += it.generate(ind+1);
|
||||
if(opt_minimize)
|
||||
if(opt_minimize && ret.size()>1)
|
||||
ret.pop_back(); // ) can be right after command
|
||||
else
|
||||
ret += INDENT;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ int main(int argc, char* argv[])
|
|||
try
|
||||
{
|
||||
block sh(parse(import_file(file)));
|
||||
if(options['E'])
|
||||
if(options['e'])
|
||||
{
|
||||
std::string data=sh.generate();
|
||||
// generate path
|
||||
|
|
@ -94,11 +94,13 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
catch(ztd::format_error& e)
|
||||
{
|
||||
printFormatException(e);
|
||||
printFormatError(e);
|
||||
return 100;
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,12 @@ ztd::option_set options = gen_options();
|
|||
bool opt_minimize;
|
||||
bool piped=false;
|
||||
|
||||
|
||||
ztd::option_set gen_options()
|
||||
{
|
||||
ztd::option_set ret;
|
||||
ret.add(ztd::option('h', "help", false, "Display this help message"));
|
||||
ret.add(ztd::option('m', "minimize", false, "Minimize code"));
|
||||
ret.add(ztd::option('E', "exec", false, "Directly exec instead of outputting"));
|
||||
ret.add(ztd::option('e', "exec", false, "Directly exec instead of outputting"));
|
||||
ret.add(ztd::option("help-commands", false, "Print help for linker commands"));
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ std::string g_origin;
|
|||
|
||||
inline bool is_in(char c, const char* set)
|
||||
{
|
||||
return index(set, c) != NULL;
|
||||
return strchr(set, c) != NULL;
|
||||
}
|
||||
|
||||
inline bool is_alphanum(char c)
|
||||
|
|
@ -38,6 +38,15 @@ bool word_eq(const char* word, const char* in, uint32_t size, uint32_t start, co
|
|||
return false;
|
||||
}
|
||||
|
||||
std::string get_word(const char* in, uint32_t size, uint32_t start, const char* end_set)
|
||||
{
|
||||
uint32_t i=start;
|
||||
while(i<size && !is_in(in[i], end_set))
|
||||
i++;
|
||||
|
||||
return std::string(in+start, i-start);
|
||||
}
|
||||
|
||||
uint32_t skip_chars(const char* in, uint32_t size, uint32_t start, const char* set)
|
||||
{
|
||||
for(uint32_t i=start; i<size ; i++)
|
||||
|
|
@ -77,7 +86,11 @@ std::pair<arg, uint32_t> parse_arg(const char* in, uint32_t size, uint32_t start
|
|||
{
|
||||
arg ret;
|
||||
// j : start of subarg
|
||||
uint32_t i=start,j=start;
|
||||
uint32_t i=start,j=start,q=start;
|
||||
|
||||
if(is_in(in[i], "&|;\n#()"))
|
||||
throw ztd::format_error( strf("Unexpected token '%c'", in[i]) , g_origin, in, i);
|
||||
|
||||
while(i<size && !is_in(in[i], " \t|&;\n()"))
|
||||
{
|
||||
if(i+1<size && is_in(in[i], "<>") && in[i+1]=='&') // special case for <& and >&
|
||||
|
|
@ -93,15 +106,13 @@ std::pair<arg, uint32_t> parse_arg(const char* in, uint32_t size, uint32_t start
|
|||
}
|
||||
else if(in[i] == '"') // start double quote
|
||||
{
|
||||
q=i;
|
||||
i++;
|
||||
while(i<size && in[i] != '"') // while inside quoted string
|
||||
while(in[i] != '"') // while inside quoted string
|
||||
{
|
||||
if(in[i] == '\\') // backslash: don't check next char
|
||||
{
|
||||
i++;
|
||||
if(i>=size)
|
||||
break;
|
||||
i++;
|
||||
i+=2;
|
||||
}
|
||||
else if( word_eq("$(", in, size, i) ) // substitution
|
||||
{
|
||||
|
|
@ -115,18 +126,20 @@ std::pair<arg, uint32_t> parse_arg(const char* in, uint32_t size, uint32_t start
|
|||
}
|
||||
else
|
||||
i++;
|
||||
|
||||
if(i>=size)
|
||||
throw ztd::format_error("Unterminated double quote", g_origin, in, q);
|
||||
}
|
||||
if(i>=size)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
else if(in[i] == '\'') // start single quote
|
||||
{
|
||||
q=i;
|
||||
i++;
|
||||
while(i<size && in[i]!='\'')
|
||||
i++;
|
||||
if(i>=size)
|
||||
break;
|
||||
throw ztd::format_error("Unterminated single quote", g_origin, in, q);
|
||||
i++;
|
||||
}
|
||||
else if( word_eq("$(", in, size, i) ) // substitution
|
||||
|
|
@ -155,10 +168,17 @@ std::pair<arg, uint32_t> parse_arg(const char* in, uint32_t size, uint32_t start
|
|||
// must start at a read char
|
||||
// first char has to be read
|
||||
// ends at either &|;\n#()
|
||||
std::pair<arglist, uint32_t> parse_arglist(const char* in, uint32_t size, uint32_t start)
|
||||
std::pair<arglist, uint32_t> parse_arglist(const char* in, uint32_t size, uint32_t start, bool hard_error=false)
|
||||
{
|
||||
uint32_t i=start;
|
||||
arglist ret;
|
||||
if(is_in(in[i], "&|;\n#(){}"))
|
||||
{
|
||||
if(hard_error)
|
||||
throw ztd::format_error( strf("Unexpected token '%c'", in[i]) , g_origin, in, i);
|
||||
else
|
||||
return std::make_pair(ret, i);
|
||||
}
|
||||
while(i<size)
|
||||
{
|
||||
auto pp=parse_arg(in, size, i);
|
||||
|
|
@ -178,7 +198,7 @@ std::pair<block, uint32_t> parse_block(const char* in, uint32_t size, uint32_t s
|
|||
// ends at either &;\n#)
|
||||
std::pair<pipeline, uint32_t> parse_pipeline(const char* in, uint32_t size, uint32_t start)
|
||||
{
|
||||
uint32_t i = skip_unread(in, size, start);
|
||||
uint32_t i=start;
|
||||
pipeline ret;
|
||||
while(i<size)
|
||||
{
|
||||
|
|
@ -237,6 +257,9 @@ std::pair<condlist, uint32_t> parse_condlist(const char* in, uint32_t size, uint
|
|||
throw ztd::format_error( strf("Unexpected token: '%c%c'", in[i], in[i+1]), g_origin, in, i);
|
||||
else // unknown
|
||||
throw ztd::format_error("Unknown error", g_origin, in, i);
|
||||
i = skip_unread(in, size, i);
|
||||
if(i>=size)
|
||||
throw ztd::format_error( "Unexpected end of file", g_origin, in, i );
|
||||
}
|
||||
return std::make_pair(ret, i);
|
||||
}
|
||||
|
|
@ -248,14 +271,16 @@ std::pair<block, uint32_t> parse_subshell(const char* in, uint32_t size, uint32_
|
|||
{
|
||||
uint32_t i = skip_unread(in, size, start);
|
||||
block ret(block::subshell);
|
||||
while(i<size && in[i] != ')')
|
||||
while(in[i] != ')')
|
||||
{
|
||||
auto pp=parse_condlist(in, size, i);
|
||||
ret.cls.push_back(pp.first);
|
||||
i = skip_unread(in, size, pp.second);
|
||||
if(i>=size)
|
||||
throw ztd::format_error("Expecting )", g_origin, in, start-1);
|
||||
}
|
||||
if(i>=size)
|
||||
throw ztd::format_error("Expecting )", g_origin, in, start-1);
|
||||
if(ret.cls.size()<=0)
|
||||
throw ztd::format_error("Subshell is empty", g_origin, in, start-1);
|
||||
i++;
|
||||
return std::make_pair(ret,i);
|
||||
}
|
||||
|
|
@ -267,17 +292,18 @@ std::pair<block, uint32_t> parse_brace(const char* in, uint32_t size, uint32_t s
|
|||
{
|
||||
uint32_t i = skip_unread(in, size, start);
|
||||
block ret(block::brace);
|
||||
while(i<size && in[i] != '}')
|
||||
while(in[i] != '}')
|
||||
{
|
||||
auto pp=parse_condlist(in, size, i);
|
||||
ret.cls.push_back(pp.first);
|
||||
i = skip_unread(in, size, pp.second);
|
||||
if(i>=size)
|
||||
throw ztd::format_error("Expecting }", g_origin, in, start-1);
|
||||
if(is_in(in[i], ")"))
|
||||
throw ztd::format_error( strf("Unexpected token: '%c'", in[i]) , g_origin, in, i );
|
||||
}
|
||||
if(i>=size)
|
||||
throw ztd::format_error("Expecting }", g_origin, in, start-1);
|
||||
|
||||
if(ret.cls.size()<=0)
|
||||
throw ztd::format_error("Brace block is empty", g_origin, in, start-1);
|
||||
i++;
|
||||
|
||||
return std::make_pair(ret,i);
|
||||
|
|
@ -321,7 +347,7 @@ std::pair<block, uint32_t> parse_cmd(const char* in, uint32_t size, uint32_t sta
|
|||
}
|
||||
else // is a command
|
||||
{
|
||||
auto pp=parse_arglist(in, size, start);
|
||||
auto pp=parse_arglist(in, size, start, true);
|
||||
ret.args = pp.first;
|
||||
i = pp.second;
|
||||
}
|
||||
|
|
@ -335,7 +361,7 @@ std::pair<block, uint32_t> parse_cmd(const char* in, uint32_t size, uint32_t sta
|
|||
std::pair<block, uint32_t> parse_case(const char* in, uint32_t size, uint32_t start)
|
||||
{
|
||||
block ret(block::case_block);
|
||||
uint32_t i=skip_unread(in, size, start);;
|
||||
uint32_t i=skip_chars(in, size, start, " \t");;
|
||||
|
||||
// get the treated argument
|
||||
auto pa = parse_arg(in, size, i);
|
||||
|
|
@ -343,10 +369,10 @@ std::pair<block, uint32_t> parse_case(const char* in, uint32_t size, uint32_t st
|
|||
i=skip_unread(in, size, pa.second);
|
||||
|
||||
// must be an 'in'
|
||||
if(!word_eq("in", in, size, i))
|
||||
if(!word_eq("in", in, size, i, " \t\n"))
|
||||
{
|
||||
auto pp=parse_arg(in, size, i);
|
||||
throw ztd::format_error("Unexpected word: '"+pp.first.raw+"', expecting 'in' after case", g_origin, in, i);
|
||||
std::string pp=get_word(in, size, i, " \t\n");
|
||||
throw ztd::format_error("Unexpected word: '"+pp+"', expecting 'in' after case", g_origin, in, i);
|
||||
}
|
||||
|
||||
i=skip_unread(in, size, i+2);
|
||||
|
|
@ -357,6 +383,8 @@ std::pair<block, uint32_t> parse_case(const char* in, uint32_t size, uint32_t st
|
|||
// toto)
|
||||
std::pair<arg, list_t> cc;
|
||||
pa = parse_arg(in, size, i);
|
||||
if(pa.first.raw == "")
|
||||
throw ztd::format_error("Empty case value", g_origin, in, i);
|
||||
cc.first = pa.first;
|
||||
i=skip_unread(in, size, pa.second);
|
||||
|
||||
|
|
@ -410,6 +438,8 @@ std::pair<block, uint32_t> parse_case(const char* in, uint32_t size, uint32_t st
|
|||
std::pair<block, uint32_t> parse_block(const char* in, uint32_t size, uint32_t start)
|
||||
{
|
||||
uint32_t i = skip_chars(in, size, start, " \n\t");
|
||||
if(i>=size)
|
||||
throw ztd::format_error("Unexpected end of file", g_origin, in, i);
|
||||
std::pair<block, uint32_t> ret;
|
||||
if(in[i] == '{') // brace block
|
||||
{
|
||||
|
|
@ -431,7 +461,7 @@ std::pair<block, uint32_t> parse_block(const char* in, uint32_t size, uint32_t s
|
|||
}
|
||||
if(ret.first.args.args.size()<=0)
|
||||
{
|
||||
auto pp=parse_arglist(in, size, ret.second); // in case of redirects
|
||||
auto pp=parse_arglist(in, size, ret.second, false); // in case of redirects
|
||||
ret.second=pp.second;
|
||||
ret.first.args=pp.first;
|
||||
}
|
||||
|
|
|
|||
46
src/util.cpp
46
src/util.cpp
|
|
@ -89,3 +89,49 @@ std::string stringReplace(std::string subject, const std::string& search, const
|
|||
}
|
||||
return subject;
|
||||
}
|
||||
|
||||
void printFormatError(ztd::format_error const& e, bool print_line)
|
||||
{
|
||||
printErrorIndex(e.data(), e.where(), e.what(), e.origin(), print_line);
|
||||
}
|
||||
|
||||
std::string repeatString(std::string const& str, uint32_t n)
|
||||
{
|
||||
std::string ret;
|
||||
for(uint32_t i=0; i<n; i++)
|
||||
ret += str;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void printErrorIndex(const char* in, const int index, const std::string& message, const std::string& origin, bool print_line)
|
||||
{
|
||||
int i=0, j=0; // j: last newline
|
||||
int line=1; //n: line #
|
||||
int in_size=strlen(in);
|
||||
if(index >= 0)
|
||||
{
|
||||
while(i < in_size && i < index)
|
||||
{
|
||||
if(in[i] == '\n')
|
||||
{
|
||||
line++;
|
||||
j=i+1;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
while(i < in_size && in[i]!='\n')
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if(origin != "")
|
||||
{
|
||||
fprintf(stderr, "%s:%u:%u: %s\n", origin.c_str(), line, index-j+1, message.c_str());
|
||||
if(print_line)
|
||||
{
|
||||
std::cerr << std::string(in+j, i-j) << std::endl;
|
||||
std::cerr << repeatString(" ", index-j) << '^' << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue