From 577a1aef949b8c6383763240acf88c89e4a9a225 Mon Sep 17 00:00:00 2001 From: Mateo Feron Date: Tue, 1 Sep 2020 17:11:21 +0200 Subject: [PATCH] Improve parsing errors --- Makefile | 2 +- include/util.hpp | 5 +++ src/generate.cpp | 24 ++++++++++----- src/main.cpp | 6 ++-- src/options.cpp | 3 +- src/parse.cpp | 80 +++++++++++++++++++++++++++++++++--------------- src/util.cpp | 46 ++++++++++++++++++++++++++++ 7 files changed, 129 insertions(+), 37 deletions(-) diff --git a/Makefile b/Makefile index 3d50c54..8942ce2 100644 --- a/Makefile +++ b/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++ diff --git a/include/util.hpp b/include/util.hpp index e86e45a..f4f6362 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -7,6 +7,8 @@ #include #include +#include + #define INDENT indent(ind) extern std::string indenting_string; @@ -36,4 +38,7 @@ void _exec(std::string const& bin, std::vector 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 diff --git a/src/generate.cpp b/src/generate.cpp index be56a6f..e07e83d 100644 --- a/src/generate.cpp +++ b/src/generate.cpp @@ -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 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 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 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 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 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; diff --git a/src/main.cpp b/src/main.cpp index 2c940e3..cb43f9b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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; } diff --git a/src/options.cpp b/src/options.cpp index a3a6ce2..33b09d9 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -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; } diff --git a/src/parse.cpp b/src/parse.cpp index 8af1df0..9151784 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -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 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") && in[i+1]=='&') // special case for <& and >& @@ -93,15 +106,13 @@ std::pair parse_arg(const char* in, uint32_t size, uint32_t start } else if(in[i] == '"') // start double quote { + q=i; i++; - while(i=size) - break; - i++; + i+=2; } else if( word_eq("$(", in, size, i) ) // substitution { @@ -115,18 +126,20 @@ std::pair 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) - 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 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 parse_arglist(const char* in, uint32_t size, uint32_t start) +std::pair 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 parse_block(const char* in, uint32_t size, uint32_t s // ends at either &;\n#) std::pair 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 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 parse_subshell(const char* in, uint32_t size, uint32_ { uint32_t i = skip_unread(in, size, start); block ret(block::subshell); - while(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 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) + 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 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 parse_cmd(const char* in, uint32_t size, uint32_t sta std::pair 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 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 parse_case(const char* in, uint32_t size, uint32_t st // toto) std::pair 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 parse_case(const char* in, uint32_t size, uint32_t st std::pair 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 ret; if(in[i] == '{') // brace block { @@ -431,7 +461,7 @@ std::pair 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; } diff --git a/src/util.cpp b/src/util.cpp index 5560a44..76197a7 100644 --- a/src/util.cpp +++ b/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= 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; + } + } +} +