From: Mikko Rasa Date: Sun, 28 Feb 2021 00:12:48 +0000 (+0200) Subject: Make the GLSL parser resilient against common errors X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=commitdiff_plain;h=7b73b63df12b3ace4231842aa291d6e1d7b3f948 Make the GLSL parser resilient against common errors It will now attempt to continue parsing the source, with the intent of reporting all syntax errors at once. --- diff --git a/source/glsl/glsl_error.h b/source/glsl/glsl_error.h index 46ca49be..1b005e0f 100644 --- a/source/glsl/glsl_error.h +++ b/source/glsl/glsl_error.h @@ -13,6 +13,7 @@ struct Location; class invalid_shader_source: public std::runtime_error { public: + invalid_shader_source(const std::string &w): runtime_error(w) { } invalid_shader_source(const Location &, const std::string &); #if __cplusplus>=201103L template diff --git a/source/glsl/parser.cpp b/source/glsl/parser.cpp index 5ffc2a9b..d9fdbc4e 100644 --- a/source/glsl/parser.cpp +++ b/source/glsl/parser.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -57,8 +58,13 @@ void Parser::parse_source(const string &name, unsigned index) source_index = index; source_reference(1, name); tokenizer.begin(name, source); - while(RefPtr statement = parse_global_declaration()) - cur_stage->content.body.push_back(statement); + allow_stage_change = true; + while(!tokenizer.peek_token().empty()) + if(RefPtr statement = parse_with_recovery(&Parser::parse_global_declaration)) + cur_stage->content.body.push_back(statement); + + if(!errors.empty()) + throw invalid_shader_source(join(errors.begin(), errors.end(), "\n")); } void Parser::set_required_version(const Version &ver) @@ -181,11 +187,54 @@ bool Parser::is_identifier(const string &token) return re.match(token); } +template +RefPtr Parser::parse_with_recovery(RefPtr (Parser::*parse_func)()) +{ + tokenizer.clear_progress_mark(); + try + { + return (this->*parse_func)(); + } + catch(const invalid_shader_source &exc) + { + errors.push_back(exc.what()); + } + + if(tokenizer.get_last_token()!=";" || !tokenizer.get_progress_mark()) + { + unsigned scope_level = 0; + while(1) + { + if(tokenizer.peek_token()=="}" && scope_level==0) + { + if(!tokenizer.get_progress_mark()) + tokenizer.parse_token(); + break; + } + + string token = tokenizer.parse_token(); + if(token=="}") + { + --scope_level; + if(scope_level==0) + break; + } + else if(token=="{") + ++scope_level; + else if(token==";" && scope_level==0) + break; + else if(token.empty()) + break; + } + } + + return RefPtr(); +} + RefPtr Parser::parse_global_declaration() { - allow_stage_change = true; string token = tokenizer.peek_token(); - allow_stage_change = false; + SetFlag disallow(allow_stage_change, false); if(token=="import") return parse_import(); @@ -262,6 +311,11 @@ RefPtr Parser::parse_statement() } else if(is_qualifier(token) || is_type(token)) return parse_variable_declaration(); + else if(token==";") + { + tokenizer.parse_token(); + throw invalid_shader_source(tokenizer.get_location(), "Empty statement not allowed"); + } else if(!token.empty()) { RefPtr expr = new ExpressionStatement; @@ -357,7 +411,8 @@ void Parser::parse_block(Block &block, bool require_braces, RefPtr (Parser::* if(have_braces) { while(tokenizer.peek_token()!="}") - block.body.push_back((this->*parse_content)()); + if(RefPtr node = parse_with_recovery(parse_content)) + block.body.push_back(node); } else block.body.push_back((this->*parse_content)()); diff --git a/source/glsl/parser.h b/source/glsl/parser.h index 9ab1232a..acfb7d9e 100644 --- a/source/glsl/parser.h +++ b/source/glsl/parser.h @@ -24,6 +24,7 @@ private: Module *module; Stage *cur_stage; std::set declared_types; + std::vector errors; public: Parser(); @@ -60,6 +61,8 @@ private: void preprocess_stage(); RefPtr parse_global_declaration(); + template + RefPtr parse_with_recovery(RefPtr (Parser::*)()); RefPtr parse_statement(); RefPtr parse_import(); RefPtr parse_precision(); diff --git a/source/glsl/tokenizer.cpp b/source/glsl/tokenizer.cpp index b7efb8f8..6ba139ff 100644 --- a/source/glsl/tokenizer.cpp +++ b/source/glsl/tokenizer.cpp @@ -34,11 +34,13 @@ const string &Tokenizer::peek_token(unsigned index) { while(next_tokens.size()<=index) next_tokens.push_back(parse_token_()); - return (last_token = next_tokens[index]); + return next_tokens[index]; } const string &Tokenizer::parse_token() { + progress_mark = true; + if(!next_tokens.empty()) { last_token = next_tokens.front(); diff --git a/source/glsl/tokenizer.h b/source/glsl/tokenizer.h index 20ea756a..46aa62fb 100644 --- a/source/glsl/tokenizer.h +++ b/source/glsl/tokenizer.h @@ -29,6 +29,7 @@ private: std::string::const_iterator iter; std::string::const_iterator source_end; Location location; + bool progress_mark; bool allow_preprocess; bool suppress_line_advance; std::string last_token; @@ -40,8 +41,11 @@ public: void begin(const std::string &, const std::string &); const std::string &peek_token(unsigned = 0); const std::string &parse_token(); + const std::string &get_last_token() const { return last_token; } void expect(const std::string &); void set_location(const Location &); + void clear_progress_mark() { progress_mark = false; } + bool get_progress_mark() const { return progress_mark; } const Location &get_location() const { return location; } private: std::string parse_token_();