+#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