From: Mikko Rasa Date: Sun, 14 Feb 2021 14:57:32 +0000 (+0200) Subject: Refactor error reporting in SL::Parser X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=commitdiff_plain;h=47286086863b6009192ffdb3d3471c83211ee943 Refactor error reporting in SL::Parser There's now dedicated exception classes which take care of the formatting. --- diff --git a/source/glsl/glsl_error.cpp b/source/glsl/glsl_error.cpp new file mode 100644 index 00000000..ea8e9306 --- /dev/null +++ b/source/glsl/glsl_error.cpp @@ -0,0 +1,20 @@ +#include "glsl_error.h" +#include "parser.h" + +using namespace std; + +namespace Msp { +namespace GL { +namespace SL { + +invalid_shader_source::invalid_shader_source(const Location &loc, const string &message): + runtime_error(format("%s:%d: %s", loc.name, loc.line, message)) +{ } + +parse_error::parse_error(const Location &loc, const string &token, const string &expected): + invalid_shader_source(loc, "Parse error at '%s': expected %s", token, expected) +{ } + +} // namespace SL +} // namespace GL +} // namespace Msp diff --git a/source/glsl/glsl_error.h b/source/glsl/glsl_error.h new file mode 100644 index 00000000..63c49bbd --- /dev/null +++ b/source/glsl/glsl_error.h @@ -0,0 +1,37 @@ +#ifndef MSP_GL_SL_ERROR_H_ +#define MSP_GL_SL_ERROR_H_ + +#include +#include + +namespace Msp { +namespace GL { +namespace SL { + +struct Location; + +class invalid_shader_source: public std::runtime_error +{ +public: + invalid_shader_source(const Location &, const std::string &); +#if __cplusplus>=201103L + template + invalid_shader_source(const Location &loc, const std::string & fmt, Args... args): + invalid_shader_source(loc, format(fmt, args...)) + { } +#endif + virtual ~invalid_shader_source() throw() { } +}; + +class parse_error: public invalid_shader_source +{ +public: + parse_error(const Location &, const std::string &, const std::string &); + virtual ~parse_error() throw() { } +}; + +} // namespace SL +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/glsl/parser.cpp b/source/glsl/parser.cpp index d55cdab3..3126fae6 100644 --- a/source/glsl/parser.cpp +++ b/source/glsl/parser.cpp @@ -1,6 +1,7 @@ #include #include #include +#include "glsl_error.h" #include "parser.h" #undef interface @@ -72,7 +73,7 @@ Parser::~Parser() Module &Parser::parse(const string &s, const string &n, unsigned i) { source = s; - source_name = n; + location.name = n; source_index = i; parse_source(); return *module; @@ -81,7 +82,7 @@ Module &Parser::parse(const string &s, const string &n, unsigned i) Module &Parser::parse(IO::Base &io, const string &n, unsigned i) { source = string(); - source_name = n; + location.name = n; source_index = i; while(!io.eof()) { @@ -111,23 +112,12 @@ void Parser::parse_source() cur_stage = &module->shared; iter = source.begin(); source_end = source.end(); - current_line = 1; + location.line = 1; allow_preprocess = true; while(RefPtr statement = parse_global_declaration()) cur_stage->content.body.push_back(statement); } -string Parser::format_error(const std::string &message) -{ - string location = format("%s:%d: ", source_name, current_line); - return location+message; -} - -string Parser::format_syntax_error(const std::string &expected) -{ - return format_error(format("Syntax error at '%s': expected %s", last_token, expected)); -} - const string &Parser::peek_token(unsigned index) { while(next_tokens.size()<=index) @@ -272,7 +262,7 @@ void Parser::skip_comment_and_whitespace() if(*iter=='\n') { - ++current_line; + ++location.line; allow_preprocess = (comment<3); } @@ -284,14 +274,14 @@ void Parser::expect(const string &token) { string parsed = parse_token(); if(parsed!=token) - throw runtime_error(format_syntax_error(format("'%s'", token))); + throw parse_error(location, parsed, format("'%s'", token)); } string Parser::expect_type() { string token = parse_token(); if(!is_type(token)) - throw runtime_error(format_syntax_error("a type")); + throw parse_error(location, token, "a type"); return token; } @@ -299,7 +289,7 @@ string Parser::expect_identifier() { string token = parse_token(); if(!is_identifier(token)) - throw runtime_error(format_syntax_error("an identifier")); + throw parse_error(location, token, "an identifier"); return token; } @@ -372,9 +362,9 @@ void Parser::preprocess() preprocess_version(); else if(token=="define" || token=="undef" || token=="if" || token=="ifdef" || token=="ifndef" || token=="else" || token=="elif" || token=="endif" || token=="error" || token=="extension" || token=="line") - throw runtime_error(format_error(format("Unsupported preprocessor directive '%s'", token))); + throw invalid_shader_source(location, "Unsupported preprocessor directive '%s'", token); else if(!token.empty()) - throw runtime_error(format_syntax_error("a preprocessor directive")); + throw parse_error(location, token, "a preprocessor directive"); iter = line_end; } @@ -388,7 +378,7 @@ void Parser::preprocess_version() token = parse_token(); if(!token.empty()) - throw runtime_error(format_syntax_error("end of line")); + throw parse_error(location, token, "end of line"); } void Parser::preprocess_pragma() @@ -405,17 +395,17 @@ void Parser::preprocess_pragma_msp() if(token=="stage") preprocess_stage(); else - throw runtime_error(format_error(format("Unrecognized MSP pragma '%s'", token))); + throw invalid_shader_source(location, "Unrecognized MSP pragma '%s'", token); token = parse_token(); if(!token.empty()) - throw runtime_error(format_syntax_error("end of line")); + throw parse_error(location, token, "end of line"); } void Parser::preprocess_stage() { if(!allow_stage_change) - throw runtime_error(format_error("Changing stage not allowed here")); + throw invalid_shader_source(location, "Changing stage not allowed here"); expect("stage"); expect("("); @@ -428,11 +418,11 @@ void Parser::preprocess_stage() else if(token=="fragment") stage = FRAGMENT; else - throw runtime_error(format_syntax_error("stage identifier")); + throw parse_error(location, token, "stage identifier"); expect(")"); if(stage<=cur_stage->type) - throw runtime_error(format_error(format("Stage '%s' not allowed here", token))); + throw invalid_shader_source(location, "Stage '%s' not allowed here", token); module->stages.push_back(stage); @@ -459,7 +449,7 @@ RefPtr Parser::parse_global_declaration() { RefPtr iface_lo = new InterfaceLayout; iface_lo->source = source_index; - iface_lo->line = current_line; + iface_lo->line = location.line; iface_lo->layout.qualifiers = layout->qualifiers; iface_lo->interface = parse_token(); expect(";"); @@ -494,7 +484,7 @@ RefPtr Parser::parse_global_declaration() else if(token.empty()) return 0; else - throw runtime_error(format_syntax_error("a global declaration")); + throw parse_error(location, token, "a global declaration"); } RefPtr Parser::parse_statement() @@ -514,7 +504,7 @@ RefPtr Parser::parse_statement() { RefPtr jump = new Jump; jump->source = source_index; - jump->line = current_line; + jump->line = location.line; jump->keyword = parse_token(); expect(";"); @@ -526,25 +516,25 @@ RefPtr Parser::parse_statement() { RefPtr expr = new ExpressionStatement; expr->source = source_index; - expr->line = current_line; + expr->line = location.line; expr->expression = parse_expression(); expect(";"); return expr; } else - throw runtime_error(format_syntax_error("a statement")); + throw parse_error(location, token, "a statement"); } RefPtr Parser::parse_import() { if(cur_stage->type!=SHARED) - throw runtime_error(format_error("Imports are only allowed in the shared section")); + throw invalid_shader_source(location, "Imports are only allowed in the shared section"); expect("import"); RefPtr import = new Import; import->source = source_index; - import->line = current_line; + import->line = location.line; import->module = expect_identifier(); expect(";"); return import; @@ -555,16 +545,16 @@ RefPtr Parser::parse_precision() expect("precision"); RefPtr precision = new Precision; precision->source = source_index; - precision->line = current_line; + precision->line = location.line; precision->precision = parse_token(); if(!is_precision_qualifier(precision->precision)) - throw runtime_error(format_syntax_error("a precision qualifier")); + throw parse_error(location, precision->precision, "a precision qualifier"); precision->type = parse_token(); // Not entirely accurate; only float, int and sampler types are allowed if(!is_builtin_type(precision->type)) - throw runtime_error(format_syntax_error("a builtin type")); + throw parse_error(location, precision->type, "a builtin type"); expect(";"); @@ -580,7 +570,7 @@ RefPtr Parser::parse_layout() { string token = parse_token(); if(token==")") - throw runtime_error(format_syntax_error("a layout qualifier name")); + throw parse_error(location, token, "a layout qualifier name"); layout->qualifiers.push_back(Layout::Qualifier()); Layout::Qualifier &qual = layout->qualifiers.back(); @@ -637,14 +627,14 @@ RefPtr Parser::parse_expression(unsigned precedence) if(left) return left; else - throw runtime_error(format_syntax_error("an expression")); + throw parse_error(location, token, "an expression"); } else if(left) { if(token=="(") { if(!left_var) - throw runtime_error(format_error("Syntax error before '(': function name must be an identifier")); + throw invalid_shader_source(location, "Syntax error before '(': function name must be an identifier"); left = parse_function_call(*left_var); } else if(token==".") @@ -666,7 +656,7 @@ RefPtr Parser::parse_expression(unsigned precedence) else if(oper && oper->type==BINARY) left = parse_binary(left, oper); else - throw runtime_error(format_syntax_error("an operator")); + throw parse_error(location, token, "an operator"); left_var = 0; } else @@ -701,7 +691,7 @@ RefPtr Parser::parse_expression(unsigned precedence) left = unary; } else - throw runtime_error(format_syntax_error("an expression")); + throw parse_error(location, token, "an expression"); } } } @@ -743,7 +733,7 @@ RefPtr Parser::parse_struct_declaration() expect("struct"); RefPtr strct = new StructDeclaration; strct->source = source_index; - strct->line = current_line; + strct->line = location.line; strct->name = expect_identifier(); parse_block(strct->members, true); @@ -757,7 +747,7 @@ RefPtr Parser::parse_variable_declaration() { RefPtr var = new VariableDeclaration; var->source = source_index; - var->line = current_line; + var->line = location.line; string token = peek_token(); while(is_qualifier(token)) @@ -800,7 +790,7 @@ RefPtr Parser::parse_function_declaration() { RefPtr func = new FunctionDeclaration; func->source = source_index; - func->line = current_line; + func->line = location.line; func->return_type = expect_type(); func->name = expect_identifier(); @@ -829,7 +819,7 @@ RefPtr Parser::parse_function_declaration() else if(token==";") parse_token(); else - throw runtime_error(format_syntax_error("'{' or ';'")); + throw parse_error(location, token, "'{' or ';'"); return func; } @@ -838,11 +828,11 @@ RefPtr Parser::parse_interface_block() { RefPtr iface = new InterfaceBlock; iface->source = source_index; - iface->line = current_line; + iface->line = location.line; iface->interface = parse_token(); if(!is_interface_qualifier(iface->interface)) - throw runtime_error(format_syntax_error("an interface qualifier")); + throw parse_error(location, iface->interface, "an interface qualifier"); iface->name = expect_identifier(); parse_block(iface->members, true); @@ -865,7 +855,7 @@ RefPtr Parser::parse_conditional() expect("if"); RefPtr cond = new Conditional; cond->source = source_index; - cond->line = current_line; + cond->line = location.line; expect("("); cond->condition = parse_expression(); expect(")"); @@ -887,7 +877,7 @@ RefPtr Parser::parse_for() expect("for"); RefPtr loop = new Iteration; loop->source = source_index; - loop->line = current_line; + loop->line = location.line; expect("("); string token = peek_token(); if(is_type(token)) @@ -919,7 +909,7 @@ RefPtr Parser::parse_while() expect("while"); RefPtr loop = new Iteration; loop->source = source_index; - loop->line = current_line; + loop->line = location.line; expect("("); loop->condition = parse_expression(); expect(")"); @@ -934,7 +924,7 @@ RefPtr Parser::parse_passthrough() expect("passthrough"); RefPtr pass = new Passthrough; pass->source = source_index; - pass->line = current_line; + pass->line = location.line; if(cur_stage->type==GEOMETRY) { expect("["); @@ -950,7 +940,7 @@ RefPtr Parser::parse_return() expect("return"); RefPtr ret = new Return; ret->source = source_index; - ret->line = current_line; + ret->line = location.line; if(peek_token()!=";") ret->expression = parse_expression(); expect(";"); diff --git a/source/glsl/parser.h b/source/glsl/parser.h index c9588bfb..743149d8 100644 --- a/source/glsl/parser.h +++ b/source/glsl/parser.h @@ -12,6 +12,12 @@ namespace Msp { namespace GL { namespace SL { +struct Location +{ + std::string name; + unsigned line; +}; + class Parser { private: @@ -38,9 +44,8 @@ private: }; std::string source; - std::string source_name; unsigned source_index; - unsigned current_line; + Location location; std::string::const_iterator iter; std::string::const_iterator source_end; bool allow_preprocess; @@ -63,9 +68,6 @@ public: private: void parse_source(); - std::string format_error(const std::string &); - std::string format_syntax_error(const std::string &); - const std::string &peek_token(unsigned = 0); const std::string &parse_token(); std::string parse_token_();