+#include <msp/core/raii.h>
#include <msp/strings/format.h>
#include <msp/strings/regex.h>
#include <msp/strings/utils.h>
delete module;
}
-Module &Parser::parse(const string &s, const string &n, unsigned i)
+Module &Parser::parse(const string &s, const string &n, int i)
{
source = s;
parse_source(n, i);
return *module;
}
-Module &Parser::parse(IO::Base &io, const string &n, unsigned i)
+Module &Parser::parse(IO::Base &io, const string &n, int i)
{
source = string();
while(!io.eof())
return *module;
}
-void Parser::parse_source(const string &name, unsigned index)
+void Parser::parse_source(const string &name, int index)
{
delete module;
module = new Module;
cur_stage = &module->shared;
base_index = index;
source_index = index;
- source_reference(1, name);
+ if(index>=0)
+ source_reference(1, name);
tokenizer.begin(name, source);
- while(RefPtr<Statement> statement = parse_global_declaration())
- cur_stage->content.body.push_back(statement);
+ allow_stage_change = true;
+ while(!tokenizer.peek_token().empty())
+ if(RefPtr<Statement> 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)
void Parser::source_reference(unsigned index, const string &name)
{
- if(index<1)
+ if(index<1 || base_index<0)
throw invalid_shader_source(tokenizer.get_location(), "Invalid source reference");
module->source_map.set_name(base_index+index-1, name);
return re.match(token);
}
+template<typename T>
+RefPtr<T> Parser::parse_with_recovery(RefPtr<T> (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<T>();
+}
+
RefPtr<Statement> 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();
}
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<ExpressionStatement> expr = new ExpressionStatement;
qual.name = token;
if((qual.has_value = check("=")))
- qual.value = expect_integer();
+ {
+ if(qual.name=="constant_id" && tokenizer.peek_token()=="auto")
+ {
+ qual.value = -1;
+ tokenizer.parse_token();
+ }
+ else
+ qual.value = expect_integer();
+ }
if(tokenizer.peek_token()==")")
break;
if(have_braces)
{
while(tokenizer.peek_token()!="}")
- block.body.push_back((this->*parse_content)());
+ if(RefPtr<Statement> node = parse_with_recovery(parse_content))
+ block.body.push_back(node);
}
else
block.body.push_back((this->*parse_content)());