]> git.tdb.fi Git - libs/gl.git/blobdiff - source/glsl/parser.cpp
Inject builtins into the module
[libs/gl.git] / source / glsl / parser.cpp
index b493c677d3d4007a4c9d8c285ba6d8c4b99fc9f3..1f0d712467f3b43c2d9990dfa277bb1c46d0ff42 100644 (file)
@@ -1,3 +1,4 @@
+#include <msp/core/raii.h>
 #include <msp/strings/format.h>
 #include <msp/strings/regex.h>
 #include <msp/strings/utils.h>
@@ -28,14 +29,14 @@ Parser::~Parser()
        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())
@@ -48,17 +49,23 @@ Module &Parser::parse(IO::Base &io, const string &n, unsigned i)
        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)
@@ -68,7 +75,7 @@ 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);
@@ -181,11 +188,54 @@ bool Parser::is_identifier(const string &token)
        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();
@@ -262,6 +312,11 @@ RefPtr<Statement> 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<ExpressionStatement> expr = new ExpressionStatement;
@@ -327,7 +382,15 @@ RefPtr<Layout> Parser::parse_layout()
                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;
@@ -349,7 +412,8 @@ void Parser::parse_block(Block &block, bool require_braces, RefPtr<T> (Parser::*
        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)());