]> git.tdb.fi Git - libs/gl.git/commitdiff
Begin implementing a new shader program generator system
authorMikko Rasa <tdb@tdb.fi>
Mon, 7 Nov 2016 21:10:51 +0000 (23:10 +0200)
committerMikko Rasa <tdb@tdb.fi>
Tue, 8 Nov 2016 20:26:36 +0000 (22:26 +0200)
ProgramBuilder is good enough when working within its standard features,
but it's difficult to expand.  Custom variables are overly sensitive to
declration order.  Adding loops or conditionals (other than the operator
flavor) requires using an additional shader.  It's not possible to modify
a variable's expression without replicating it in its entirety.

All of the internal processing with coordinate spaces, interfaces and
indices can also be difficult to understand.  Not to mention the reverse
processing order which starts from the goal variables and works backwards
to figure out how they can be computed.

The new ProgramCompiler system, as its name implies, works more like a
compiler.  It's designed to take GLSL as input, apply transformations to
it and produce different GLSL as output.  Storing the progrem as an
abstract syntax tree allows much more comprehensive processing than the
old system which barely understood what an identifier is.

At the moment it doesn't do much yet.  The only transformation is to
split a single source file into vertex, geometry and fragment parts and
add a common section at the beginning of each of them.  More will be
added soon.

source/programcompiler.cpp [new file with mode: 0644]
source/programcompiler.h [new file with mode: 0644]
source/programparser.cpp [new file with mode: 0644]
source/programparser.h [new file with mode: 0644]
source/programsyntax.cpp [new file with mode: 0644]
source/programsyntax.h [new file with mode: 0644]

diff --git a/source/programcompiler.cpp b/source/programcompiler.cpp
new file mode 100644 (file)
index 0000000..b08f173
--- /dev/null
@@ -0,0 +1,199 @@
+#include <msp/strings/format.h>
+#include <msp/strings/utils.h>
+#include "error.h"
+#include "program.h"
+#include "programcompiler.h"
+#include "shader.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+using namespace ProgramSyntax;
+
+ProgramCompiler::ProgramCompiler():
+       module(0)
+{ }
+
+void ProgramCompiler::compile(const string &source)
+{
+       module = &parser.parse(source);
+}
+
+void ProgramCompiler::compile(IO::Base &io)
+{
+       module = &parser.parse(io);
+}
+
+void ProgramCompiler::add_shaders(Program &program)
+{
+       if(!module)
+               throw invalid_operation("ProgramCompiler::add_shaders");
+
+       string global_source = "#version 150\n"+format_context(module->global_context);
+       if(module->vertex_context.present)
+               program.attach_shader_owned(new VertexShader(global_source+"\n"+format_context(module->vertex_context)));
+       if(module->geometry_context.present)
+               program.attach_shader_owned(new GeometryShader(global_source+"\n"+format_context(module->geometry_context)));
+       if(module->fragment_context.present)
+               program.attach_shader_owned(new FragmentShader(global_source+"\n"+format_context(module->fragment_context)));
+
+       program.bind_attribute(VERTEX4, "vertex");
+       program.bind_attribute(NORMAL3, "normal");
+       program.bind_attribute(COLOR4_FLOAT, "color");
+       program.bind_attribute(TEXCOORD4, "texcoord");
+}
+
+string ProgramCompiler::format_context(Context &context)
+{
+       Formatter formatter;
+       context.content.visit(formatter);
+       return formatter.formatted;
+}
+
+
+ProgramCompiler::Formatter::Formatter():
+       indent(0),
+       parameter_list(false),
+       else_if(false)
+{ }
+
+string ProgramCompiler::Formatter::format_expression(Expression &expr)
+{
+       return join(expr.tokens.begin(), expr.tokens.end(), string());
+}
+
+void ProgramCompiler::Formatter::visit(ExpressionStatement &expr)
+{
+       formatted += format("%s;", format_expression(expr.expression));
+}
+
+void ProgramCompiler::Formatter::visit(Block &block)
+{
+       if(block.use_braces)
+       {
+               if(else_if)
+               {
+                       formatted += '\n';
+                       else_if = false;
+               }
+               formatted += format("%s{\n", string(indent*2, ' '));
+       }
+
+       bool change_indent = (!formatted.empty() && !else_if);
+       indent += change_indent;
+       string spaces(indent*2, ' ');
+       for(vector<Node *>::const_iterator i=block.body.begin(); i!=block.body.end(); ++i)
+       {
+               if(i!=block.body.begin())
+                       formatted += '\n';
+               if(!else_if)
+                       formatted += spaces;
+               (*i)->visit(*this);
+       }
+       indent -= change_indent;
+
+       if(block.use_braces)
+               formatted += format("\n%s}", string(indent*2, ' '));
+}
+
+void ProgramCompiler::Formatter::visit(Layout &layout)
+{
+       formatted += "layout(";
+       for(vector<Layout::Qualifier>::const_iterator i=layout.qualifiers.begin(); i!=layout.qualifiers.end(); ++i)
+       {
+               if(i!=layout.qualifiers.begin())
+                       formatted += ", ";
+               formatted += i->identifier;
+               if(!i->value.empty())
+                       formatted += format("=%s", i->value);
+       }
+       formatted += format(") %s;", layout.interface);
+}
+
+void ProgramCompiler::Formatter::visit(StructDeclaration &strct)
+{
+       formatted += format("struct %s\n", strct.name);
+       strct.members.visit(*this);
+       formatted += ';';
+}
+
+void ProgramCompiler::Formatter::visit(VariableDeclaration &var)
+{
+       if(var.constant)
+               formatted += "const ";
+       if(!var.sampling.empty())
+               formatted += format("%s ", var.sampling);
+       if(!var.interface.empty())
+               formatted += format("%s ", var.interface);
+       formatted += format("%s %s", var.type, var.name);
+       if(var.array)
+               formatted += format("[%s]", format_expression(var.array_size));
+       if(!var.init_expression.empty())
+               formatted += format(" = %s", format_expression(var.init_expression));
+       if(!parameter_list)
+               formatted += ';';
+}
+
+void ProgramCompiler::Formatter::visit(InterfaceBlock &iface)
+{
+       formatted += format("%s %s\n", iface.interface, iface.name);
+       iface.members.visit(*this);
+       formatted += ';';
+}
+
+void ProgramCompiler::Formatter::visit(FunctionDeclaration &func)
+{
+       formatted += format("%s %s(", func.return_type, func.name);
+       parameter_list = true;
+       for(vector<VariableDeclaration *>::const_iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
+       {
+               if(i!=func.parameters.begin())
+                       formatted += ", ";
+               (*i)->visit(*this);
+       }
+       parameter_list = false;
+       formatted += ')';
+       if(func.definition)
+       {
+               formatted += '\n';
+               func.body.visit(*this);
+       }
+       else
+               formatted += ';';
+}
+
+void ProgramCompiler::Formatter::visit(Conditional &cond)
+{
+       if(else_if)
+       {
+               formatted += ' ';
+               else_if = false;
+       }
+       formatted += format("if(%s)\n", format_expression(cond.condition));
+       cond.body.visit(*this);
+       if(!cond.else_body.body.empty())
+       {
+               formatted += format("\n%selse", string(indent*2, ' '));
+               else_if = true;
+               cond.else_body.visit(*this);
+               else_if = false;
+       }
+}
+
+void ProgramCompiler::Formatter::visit(Iteration &iter)
+{
+       formatted += "for(";
+       iter.init_statement->visit(*this);
+       formatted += format(" %s; %s)\n", format_expression(iter.condition), format_expression(iter.loop_expression));
+       iter.body.visit(*this);
+}
+
+void ProgramCompiler::Formatter::visit(Return &ret)
+{
+       formatted += format("return %s;", format_expression(ret.expression));
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/programcompiler.h b/source/programcompiler.h
new file mode 100644 (file)
index 0000000..9a82548
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef MSP_GL_PROGRAMCOMPILER_H_
+#define MSP_GL_PROGRAMCOMPILER_H_
+
+#include "programparser.h"
+#include "programsyntax.h"
+
+namespace Msp {
+namespace GL {
+
+class Program;
+
+class ProgramCompiler
+{
+public:
+       struct Formatter: ProgramSyntax::NodeVisitor
+       {
+               std::string formatted;
+               unsigned indent;
+               bool parameter_list;
+               bool else_if;
+
+               Formatter();
+
+               std::string format_expression(ProgramSyntax::Expression &);
+               virtual void visit(ProgramSyntax::Block &);
+               virtual void visit(ProgramSyntax::ExpressionStatement &);
+               virtual void visit(ProgramSyntax::Layout &);
+               virtual void visit(ProgramSyntax::StructDeclaration &);
+               virtual void visit(ProgramSyntax::VariableDeclaration &);
+               virtual void visit(ProgramSyntax::InterfaceBlock &);
+               virtual void visit(ProgramSyntax::FunctionDeclaration &);
+               virtual void visit(ProgramSyntax::Conditional &);
+               virtual void visit(ProgramSyntax::Iteration &);
+               virtual void visit(ProgramSyntax::Return &);
+       };
+
+private:
+       ProgramParser parser;
+       ProgramSyntax::Module *module;
+
+public:
+       ProgramCompiler();
+
+       void compile(const std::string &);
+       void compile(IO::Base &);
+       void add_shaders(Program &);
+
+private:
+       std::string format_context(ProgramSyntax::Context &);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/programparser.cpp b/source/programparser.cpp
new file mode 100644 (file)
index 0000000..1981f47
--- /dev/null
@@ -0,0 +1,550 @@
+#include <msp/strings/format.h>
+#include <msp/strings/regex.h>
+#include "programparser.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+using namespace ProgramSyntax;
+
+Module &ProgramParser::parse(const string &s)
+{
+       source = s;
+       parse_source(main_module);
+       return main_module;
+}
+
+Module &ProgramParser::parse(IO::Base &io)
+{
+       source = string();
+       while(!io.eof())
+       {
+               char buffer[4096];
+               unsigned len = io.read(buffer, sizeof(buffer));
+               source.append(buffer, len);
+       }
+       parse_source(main_module);
+       return main_module;
+}
+
+void ProgramParser::parse_source(Module &module)
+{
+       cur_module = &module;
+       iter = source.begin();
+       Context *cur_context = &module.global_context;
+       while(1)
+       {
+               while(Node *statement = parse_global_declaration())
+                       cur_context->content.body.push_back(statement);
+               cur_context->present = !cur_context->content.body.empty();
+
+               parse_token();
+               string token = parse_token();
+               if(token.empty())
+                       break;
+               else if(token=="global")
+                       cur_context = &module.global_context;
+               else if(token=="vertex")
+                       cur_context = &module.vertex_context;
+               else if(token=="geometry")
+                       cur_context = &module.geometry_context;
+               else if(token=="fragment")
+                       cur_context = &module.fragment_context;
+               else
+                       throw runtime_error(format("Parse error at '%s': expected context identifier", token));
+
+               for(; (iter!=source.end() && *iter!='\n'); ++iter) ;
+       }
+}
+
+const string &ProgramParser::peek_token(unsigned index)
+{
+       while(next_tokens.size()<=index)
+               next_tokens.push_back(parse_token_());
+       return next_tokens[index];
+}
+
+string ProgramParser::parse_token()
+{
+       if(!next_tokens.empty())
+       {
+               string token = next_tokens.front();
+               next_tokens.pop_front();
+               return token;
+       }
+
+       return parse_token_();
+}
+
+string ProgramParser::parse_token_()
+{
+       if(!skip_comment_and_whitespace())
+               return string();
+
+       if(isalpha(*iter) || *iter=='_')
+               return parse_identifier();
+       else if(isdigit(*iter))
+               return parse_number();
+       else
+               return parse_other();
+}
+
+string ProgramParser::parse_identifier()
+{
+       string ident;
+       while(iter!=source.end())
+       {
+               if(isalnum(*iter) || *iter=='_')
+                       ident += *iter++;
+               else
+                       break;
+       }
+
+       return ident;
+}
+
+string ProgramParser::parse_number()
+{
+       bool accept_sign = false;
+       string number;
+       while(iter!=source.end())
+       {
+               if(isdigit(*iter) || *iter=='.')
+                       number += *iter++;
+               else if(*iter=='e' || *iter=='E')
+               {
+                       number += *iter++;
+                       accept_sign = true;
+               }
+               else if(accept_sign && (*iter=='+' || *iter=='-'))
+                       number += *iter++;
+               else
+                       break;
+       }
+
+       return number;
+}
+
+string ProgramParser::parse_other()
+{
+       string token;
+       while(iter!=source.end())
+       {
+               if(isalnum(*iter) || *iter=='_' || isspace(*iter))
+                       break;
+               token += *iter++;
+               if(*iter==';' || *iter=='(' || *iter==')' || *iter=='[' || *iter==']')
+                       break;
+       }
+
+       return token;
+}
+
+bool ProgramParser::skip_comment_and_whitespace()
+{
+       unsigned comment = 0;
+       unsigned slashes = 0;
+       while(iter!=source.end())
+       {
+               //IO::print("%d '%c'\n", comment, *iter);
+               if(comment==0)
+               {
+                       if(*iter=='/')
+                               comment = 1;
+                       else if(!isspace(*iter))
+                               break;
+               }
+               else if(comment==1)
+               {
+                       if(*iter=='/')
+                       {
+                               comment = 2;
+                               slashes = 2;
+                       }
+                       else if(*iter=='*')
+                               comment = 3;
+                       else
+                       {
+                               comment = 0;
+                               --iter;
+                               break;
+                       }
+               }
+               else if(comment==2)
+               {
+                       if(*iter=='\n')
+                               comment = 0;
+                       else if(*iter=='/')
+                               ++slashes;
+                       else if(!isspace(*iter) && slashes>=6)
+                               return false;
+               }
+               else if(comment==3 && *iter=='*')
+                       comment = 4;
+               else if(comment==4)
+               {
+                       if(*iter=='/')
+                               comment = 0;
+                       else
+                               comment = 3;
+               }
+
+               ++iter;
+       }
+
+       return iter!=source.end();
+}
+
+void ProgramParser::expect(const string &token)
+{
+       string parsed = parse_token();
+       if(parsed!=token)
+               throw runtime_error(format("Parse error at '%s': expected '%s'", parsed, token));
+}
+
+string ProgramParser::expect_type()
+{
+       string token = parse_token();
+       if(!is_type(token))
+               throw runtime_error(format("Parse error at '%s': expected a type", token));
+       return token;
+}
+
+string ProgramParser::expect_identifier()
+{
+       static Regex re("^[a-zA-Z_][a-zA-Z0-9_]*$");
+       string token = parse_token();
+       if(!re.match(token))
+               throw runtime_error(format("Parse error at '%s': expected an identifier", token));
+       return token;
+}
+
+bool ProgramParser::check(const string &token)
+{
+       bool result = (peek_token()==token);
+       if(result)
+               parse_token();
+       return result;
+}
+
+bool ProgramParser::is_interface_qualifier(const string &token)
+{
+       return (token=="uniform" || token=="in" || token=="out");
+}
+
+bool ProgramParser::is_sampling_qualifier(const string &token)
+{
+       return token=="centroid";
+}
+
+bool ProgramParser::is_qualifier(const string &token)
+{
+       return (token=="const" || is_interface_qualifier(token) || is_sampling_qualifier(token));
+}
+
+bool ProgramParser::is_builtin_type(const string &token)
+{
+       static Regex re("^(void|float|int|bool|[ib]?vec[234]|mat[234](x[234])?|sampler((1D|2D)(Array)?(Shadow)?|Cube(Shadow)?|3D))$");
+       return re.match(token);
+}
+
+bool ProgramParser::is_type(const string &token)
+{
+       return is_builtin_type(token) || cur_module->structs.count(token);
+}
+
+Node *ProgramParser::parse_global_declaration()
+{
+       string token = peek_token();
+       if(token=="layout")
+               return parse_layout();
+       else if(token=="struct")
+               return parse_struct_declaration();
+       else if(is_sampling_qualifier(token) || token=="const")
+               return parse_variable_declaration();
+       else if(is_interface_qualifier(token))
+       {
+               if(is_type(peek_token(1)))
+                       return parse_variable_declaration();
+               else
+                       return parse_interface_block();
+       }
+       else if(is_type(token))
+       {
+               if(peek_token(2)=="(")
+                       return parse_function_declaration();
+               else
+                       return parse_variable_declaration();
+       }
+       else if(token.empty())
+               return 0;
+       else
+               throw runtime_error(format("Syntax error at '%s': expected a global declaration", token));
+}
+
+Node *ProgramParser::parse_statement()
+{
+       string token = peek_token();
+       if(token=="if")
+               return parse_conditional();
+       else if(token=="for")
+               return parse_iteration();
+       else if(token=="return")
+               return parse_return();
+       else if(is_qualifier(token) || is_type(token))
+               return parse_variable_declaration();
+       else if(!token.empty())
+       {
+               RefPtr<ExpressionStatement> expr = new ExpressionStatement;
+               parse_expression(expr->expression);
+               expect(";");
+
+               return expr.release();
+       }
+       else
+               throw runtime_error(format("Syntax error at '%s': expected a statement", token));
+}
+
+Layout *ProgramParser::parse_layout()
+{
+       expect("layout");
+       expect("(");
+       RefPtr<Layout> layout = new Layout;
+       while(1)
+       {
+               string token = parse_token();
+               if(token==")")
+                       throw runtime_error(format("Parse error at '%s': expected layout qualifier id", token));
+
+               layout->qualifiers.push_back(Layout::Qualifier());
+               Layout::Qualifier &qual = layout->qualifiers.back();
+               qual.identifier = token;
+
+               if(check("="))
+                       qual.value = parse_token();
+
+               if(peek_token()==")")
+                       break;
+
+               expect(",");
+       }
+       expect(")");
+       layout->interface = parse_token();
+       expect(";");
+
+       return layout.release();
+}
+
+void ProgramParser::parse_block(Block &block, bool require_braces)
+{
+       bool have_braces = (require_braces || peek_token()=="{");
+       if(have_braces)
+               expect("{");
+
+       while(1)
+       {
+               string token = peek_token();
+               if(token=="}")
+                       break;
+
+               block.body.push_back(parse_statement());
+               if(!have_braces)
+                       break;
+       }
+
+       block.use_braces = (require_braces || block.body.size()!=1);
+
+       if(have_braces)
+               expect("}");
+}
+
+void ProgramParser::parse_expression(Expression &expr)
+{
+       unsigned nesting_level = 0;
+       while(iter!=source.end())
+       {
+               string token = peek_token();
+               if(token=="(" || token=="[")
+                       ++nesting_level;
+               else if(token==")" || token=="]")
+               {
+                       if(!nesting_level)
+                               break;
+                       --nesting_level;
+               }
+               else if(token==";")
+                       break;
+
+               parse_token();
+               expr.tokens.push_back(token);
+       }
+}
+
+StructDeclaration *ProgramParser::parse_struct_declaration()
+{
+       expect("struct");
+       RefPtr<StructDeclaration> strct = new StructDeclaration;
+
+       strct->name = expect_identifier();
+       parse_block(strct->members, true);
+       expect(";");
+
+       cur_module->structs[strct->name] = strct.get();
+       return strct.release();
+}
+
+VariableDeclaration *ProgramParser::parse_variable_declaration()
+{
+       RefPtr<VariableDeclaration> var = new VariableDeclaration;
+
+       string token = peek_token();
+       if(is_sampling_qualifier(token))
+       {
+               var->sampling = parse_token();
+               token = peek_token();
+               if(!is_interface_qualifier(token))
+                       throw runtime_error(format("Parse error at '%s': expected an interface qualifier", token));
+       }
+
+       if(is_interface_qualifier(token))
+               var->interface = parse_token();
+       else if(token=="const")
+       {
+               var->constant = true;
+               parse_token();
+       }
+
+       var->type = expect_type();
+       var->name = expect_identifier();
+
+       if(check("["))
+       {
+               var->array = true;
+               if(!check("]"))
+               {
+                       parse_expression(var->array_size);
+                       expect("]");
+               }
+       }
+
+       if(check("="))
+               parse_expression(var->init_expression);
+
+       expect(";");
+       return var.release();
+}
+
+FunctionDeclaration *ProgramParser::parse_function_declaration()
+{
+       RefPtr<FunctionDeclaration> func = new FunctionDeclaration;
+
+       func->return_type = expect_type();
+       func->name = expect_identifier();
+       parse_function_parameter_list(*func);
+
+       string token = peek_token();
+       if(token=="{")
+       {
+               func->definition = true;
+               parse_block(func->body, true);
+       }
+       else if(token==";")
+               parse_token();
+       else
+               throw runtime_error(format("Parse error at '%s': expected '{' or ';'", token));
+
+       return func.release();
+}
+
+void ProgramParser::parse_function_parameter_list(FunctionDeclaration &func)
+{
+       expect("(");
+       while(1)
+       {
+               string token = peek_token();
+               if(token==")")
+                       break;
+               else if(!func.parameters.empty())
+                       expect(",");
+
+               RefPtr<VariableDeclaration> var = new VariableDeclaration;
+               var->type = expect_type();
+               var->name = expect_identifier();
+               func.parameters.push_back(var.release());
+       }
+       expect(")");
+}
+
+InterfaceBlock *ProgramParser::parse_interface_block()
+{
+       RefPtr<InterfaceBlock> iface = new InterfaceBlock;
+
+       iface->interface = parse_token();
+       if(!is_interface_qualifier(iface->interface))
+               throw runtime_error(format("Parse error at '%s': expected an interface qualifier", iface->interface));
+
+       iface->name = expect_identifier();
+       parse_block(iface->members, true);
+       expect(";");
+
+       return iface.release();
+}
+
+Conditional *ProgramParser::parse_conditional()
+{
+       expect("if");
+       expect("(");
+       RefPtr<Conditional> cond = new Conditional;
+       parse_expression(cond->condition);
+       expect(")");
+
+       parse_block(cond->body, false);
+
+       string token = peek_token();
+       if(token=="else")
+       {
+               parse_token();
+               parse_block(cond->else_body, false);
+       }
+
+       return cond.release();
+}
+
+Iteration *ProgramParser::parse_iteration()
+{
+       expect("for");
+       expect("(");
+       RefPtr<Iteration> loop = new Iteration;
+       string token = peek_token();
+       if(is_type(token))
+               loop->init_statement = parse_statement();
+       else
+       {
+               RefPtr<ExpressionStatement> expr = new ExpressionStatement;
+               parse_expression(expr->expression);
+               expect(";");
+               loop->init_statement = expr.release();
+       }
+       parse_expression(loop->condition);
+       expect(";");
+       parse_expression(loop->loop_expression);
+       expect(")");
+
+       parse_block(loop->body, false);
+
+       return loop.release();
+}
+
+Return *ProgramParser::parse_return()
+{
+       expect("return");
+       RefPtr<Return> ret = new Return;
+       parse_expression(ret->expression);
+       expect(";");
+       return ret.release();
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/programparser.h b/source/programparser.h
new file mode 100644 (file)
index 0000000..0a91c50
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef MSP_GL_PROGRAMPARSER_H_
+#define MSP_GL_PROGRAMPARSER_H_
+
+#include <deque>
+#include <map>
+#include <string>
+#include <msp/io/base.h>
+#include "programsyntax.h"
+
+namespace Msp {
+namespace GL {
+
+class ProgramParser
+{
+private:
+       std::string source;
+       std::string::const_iterator iter;
+       std::deque<std::string> next_tokens;
+       ProgramSyntax::Module main_module;
+       ProgramSyntax::Module *cur_module;
+
+public:
+       ProgramSyntax::Module &parse(const std::string &);
+       ProgramSyntax::Module &parse(IO::Base &);
+
+private:
+       void parse_source(ProgramSyntax::Module &);
+
+       const std::string &peek_token(unsigned = 0);
+       std::string parse_token();
+       std::string parse_token_();
+       std::string parse_identifier();
+       std::string parse_number();
+       std::string parse_other();
+       bool skip_comment_and_whitespace();
+       void expect(const std::string &);
+       std::string expect_type();
+       std::string expect_identifier();
+       bool check(const std::string &);
+
+       static bool is_interface_qualifier(const std::string &);
+       static bool is_sampling_qualifier(const std::string &);
+       static bool is_qualifier(const std::string &);
+       static bool is_builtin_type(const std::string &);
+       bool is_type(const std::string &);
+
+       ProgramSyntax::Node *parse_global_declaration();
+       ProgramSyntax::Node *parse_statement();
+       ProgramSyntax::Layout *parse_layout();
+       void parse_block(ProgramSyntax::Block &, bool);
+       void parse_expression(ProgramSyntax::Expression &);
+       ProgramSyntax::StructDeclaration *parse_struct_declaration();
+       ProgramSyntax::VariableDeclaration *parse_variable_declaration();
+       ProgramSyntax::FunctionDeclaration *parse_function_declaration();
+       void parse_function_parameter_list(ProgramSyntax::FunctionDeclaration &);
+       ProgramSyntax::InterfaceBlock *parse_interface_block();
+       ProgramSyntax::Conditional *parse_conditional();
+       ProgramSyntax::Iteration *parse_iteration();
+       ProgramSyntax::Return *parse_return();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/programsyntax.cpp b/source/programsyntax.cpp
new file mode 100644 (file)
index 0000000..09ba82c
--- /dev/null
@@ -0,0 +1,128 @@
+#include "programsyntax.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+namespace ProgramSyntax {
+
+Block::Block():
+       use_braces(false)
+{ }
+
+Block::~Block()
+{
+       for(vector<Node *>::iterator i=body.begin(); i!=body.end(); ++i)
+               delete *i;
+}
+
+void Block::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void ExpressionStatement::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void Layout::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+StructDeclaration::StructDeclaration()
+{
+       members.use_braces = true;
+}
+
+void StructDeclaration::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+VariableDeclaration::VariableDeclaration():
+       constant(false),
+       array(false)
+{ }
+
+void VariableDeclaration::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+InterfaceBlock::InterfaceBlock()
+{
+       members.use_braces = true;
+}
+
+void InterfaceBlock::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+FunctionDeclaration::FunctionDeclaration():
+       definition(false)
+{ }
+
+FunctionDeclaration::~FunctionDeclaration()
+{
+       for(vector<VariableDeclaration *>::iterator i=parameters.begin(); i!=parameters.end(); ++i)
+               delete *i;
+}
+
+void FunctionDeclaration::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void Conditional::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void Return::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+Iteration::Iteration():
+       init_statement(0)
+{ }
+
+Iteration::~Iteration()
+{
+       delete init_statement;
+}
+
+void Iteration::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+Context::Context(ContextType t):
+       type(t),
+       present(false)
+{ }
+
+
+Module::Module():
+       global_context(GLOBAL),
+       vertex_context(VERTEX),
+       geometry_context(GEOMETRY),
+       fragment_context(FRAGMENT)
+{ }
+
+} // namespace ProgramSyntax
+} // namespace GL
+} // namespace Msp
diff --git a/source/programsyntax.h b/source/programsyntax.h
new file mode 100644 (file)
index 0000000..0013d82
--- /dev/null
@@ -0,0 +1,188 @@
+#ifndef MSP_GL_PROGRAMSYNTAX_H_
+#define MSP_GL_PROGRAMSYNTAX_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace Msp {
+namespace GL {
+namespace ProgramSyntax {
+
+struct NodeVisitor;
+
+struct Node
+{
+       virtual ~Node() { }
+
+       virtual void visit(NodeVisitor &) = 0;
+};
+
+struct Block: Node
+{
+       std::vector<Node *> body;
+       bool use_braces;
+
+       Block();
+       virtual ~Block();
+
+       virtual void visit(NodeVisitor &);
+};
+
+struct Expression
+{
+       std::vector<std::string> tokens;
+
+       bool empty() const { return tokens.empty(); }
+};
+
+struct ExpressionStatement: Node
+{
+       Expression expression;
+
+       virtual void visit(NodeVisitor &);
+};
+
+struct Layout: Node
+{
+       struct Qualifier
+       {
+               std::string identifier;
+               std::string value;
+       };
+
+       std::vector<Qualifier> qualifiers;
+       std::string interface;
+
+       virtual void visit(NodeVisitor &);
+};
+
+struct StructDeclaration: Node
+{
+       std::string name;
+       Block members;
+
+       StructDeclaration();
+
+       virtual void visit(NodeVisitor &);
+};
+
+struct VariableDeclaration: Node
+{
+       bool constant;
+       std::string sampling;
+       std::string interface;
+       std::string type;
+       std::string name;
+       bool array;
+       Expression array_size;
+       Expression init_expression;
+
+       VariableDeclaration();
+
+       virtual void visit(NodeVisitor &);
+};
+
+struct InterfaceBlock: Node
+{
+       std::string interface;
+       std::string name;
+       Block members;
+
+       InterfaceBlock();
+
+       virtual void visit(NodeVisitor &);
+};
+
+struct FunctionDeclaration: Node
+{
+       std::string return_type;
+       std::string name;
+       std::vector<VariableDeclaration *> parameters;
+       bool definition;
+       Block body;
+
+       FunctionDeclaration();
+       ~FunctionDeclaration();
+
+       virtual void visit(NodeVisitor &);
+};
+
+struct Conditional: Node
+{
+       Expression condition;
+       Block body;
+       Block else_body;
+
+       virtual void visit(NodeVisitor &);
+};
+
+struct Iteration: Node
+{
+       Node *init_statement;
+       Expression condition;
+       Expression loop_expression;
+       Block body;
+
+       Iteration();
+       virtual ~Iteration();
+
+       virtual void visit(NodeVisitor &);
+};
+
+struct Return: Node
+{
+       Expression expression;
+
+       virtual void visit(NodeVisitor &);
+};
+
+struct NodeVisitor
+{
+       virtual ~NodeVisitor() { }
+
+       virtual void visit(Block &) { }
+       virtual void visit(ExpressionStatement &) { }
+       virtual void visit(Layout &) { }
+       virtual void visit(StructDeclaration &) { }
+       virtual void visit(VariableDeclaration &) { }
+       virtual void visit(InterfaceBlock &) { }
+       virtual void visit(FunctionDeclaration &) { }
+       virtual void visit(Conditional &) { }
+       virtual void visit(Iteration &) { }
+       virtual void visit(Return &) { }
+};
+
+enum ContextType
+{
+       GLOBAL,
+       VERTEX,
+       GEOMETRY,
+       FRAGMENT
+};
+
+struct Context
+{
+       ContextType type;
+       bool present;
+       ProgramSyntax::Block content;
+
+       Context(ContextType);
+};
+
+struct Module
+{
+       Context global_context;
+       Context vertex_context;
+       Context geometry_context;
+       Context fragment_context;
+       std::map<std::string, StructDeclaration *> structs;
+
+       Module();
+};
+
+} // namespace ProgramSyntax
+} // namespace GL
+} // namespace Msp
+
+#endif