X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=blobdiff_plain;f=source%2Fprogramparser.cpp;h=f38f39097615c94185c0d4bf7504d70e0dc7d646;hp=11c9a9fe4060acd391b390cbb36749b82f998d6f;hb=bfeb6c6404659fffb1222e084b0bd08cccb4e67d;hpb=5945ad9b63bbc55c3ed21f0c023d17f73aaac370 diff --git a/source/programparser.cpp b/source/programparser.cpp index 11c9a9fe..f38f3909 100644 --- a/source/programparser.cpp +++ b/source/programparser.cpp @@ -1,3 +1,4 @@ +#include #include #include #include "programparser.h" @@ -67,16 +68,18 @@ ProgramParser::~ProgramParser() delete module; } -Module &ProgramParser::parse(const string &s) +Module &ProgramParser::parse(const string &s, const string &n) { source = s; + source_name = n; parse_source(); return *module; } -Module &ProgramParser::parse(IO::Base &io) +Module &ProgramParser::parse(IO::Base &io, const string &n) { source = string(); + source_name = n; while(!io.eof()) { char buffer[4096]; @@ -89,72 +92,84 @@ Module &ProgramParser::parse(IO::Base &io) void ProgramParser::parse_source() { + while(1) + { + string::size_type slashes = source.find("//////"); + if(slashes==string::npos) + break; + + string::size_type newline = source.find('\n', slashes); + string pragma = format("#pragma MSP stage(%s)", source.substr(slashes+6, newline-slashes-6)); + source.replace(slashes, newline-slashes, pragma); + } + delete module; module = new Module; cur_stage = &module->shared; iter = source.begin(); - while(1) - { - while(Node *statement = parse_global_declaration()) - cur_stage->content.body.push_back(statement); - - parse_token(); - string token = parse_token(); - if(token.empty()) - break; - else if(token=="vertex") - module->stages.push_back(VERTEX); - else if(token=="geometry") - module->stages.push_back(GEOMETRY); - else if(token=="fragment") - module->stages.push_back(FRAGMENT); - else - throw runtime_error(format("Parse error at '%s': expected stage identifier", token)); + source_end = source.end(); + current_line = 1; + allow_preprocess = true; + while(RefPtr statement = parse_global_declaration()) + cur_stage->content.body.push_back(statement); +} - if(cur_stage->type!=SHARED) - module->stages.back().previous = cur_stage; - cur_stage = &module->stages.back(); +string ProgramParser::format_error(const std::string &message) +{ + string location = format("%s:%d: ", source_name, current_line); + return location+message; +} - for(; (iter!=source.end() && *iter!='\n'); ++iter) ; - } +string ProgramParser::format_syntax_error(const std::string &expected) +{ + return format_error(format("Syntax error at '%s': expected %s", last_token, expected)); } const string &ProgramParser::peek_token(unsigned index) { while(next_tokens.size()<=index) next_tokens.push_back(parse_token_()); - return next_tokens[index]; + return (last_token = next_tokens[index]); } -string ProgramParser::parse_token() +const string &ProgramParser::parse_token() { if(!next_tokens.empty()) { - string token = next_tokens.front(); + last_token = next_tokens.front(); next_tokens.pop_front(); - return token; + return last_token; } - return parse_token_(); + return (last_token = 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(); + while(1) + { + skip_comment_and_whitespace(); + if(iter==source_end) + return string(); + else if(allow_preprocess && *iter=='#') + { + allow_preprocess = false; + SetForScope > clear_tokens(next_tokens, deque()); + preprocess(); + } + else 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()) + while(iter!=source_end) { if(isalnum(*iter) || *iter=='_') ident += *iter++; @@ -169,7 +184,7 @@ string ProgramParser::parse_number() { bool accept_sign = false; string number; - while(iter!=source.end()) + while(iter!=source_end) { if(isdigit(*iter) || *iter=='.') number += *iter++; @@ -189,11 +204,11 @@ string ProgramParser::parse_number() string ProgramParser::parse_other() { - if(iter==source.end()) + if(iter==source_end) return string(); string token(1, *iter++); - for(unsigned i=1; (i<3 && iter!=source.end()); ++i) + for(unsigned i=1; (i<3 && iter!=source_end); ++i) { bool matched = false; for(const Operator *j=operators; (!matched && j->type); ++j) @@ -212,11 +227,10 @@ string ProgramParser::parse_other() return token; } -bool ProgramParser::skip_comment_and_whitespace() +void ProgramParser::skip_comment_and_whitespace() { unsigned comment = 0; - unsigned slashes = 0; - while(iter!=source.end()) + while(iter!=source_end) { if(comment==0) { @@ -228,10 +242,7 @@ bool ProgramParser::skip_comment_and_whitespace() else if(comment==1) { if(*iter=='/') - { comment = 2; - slashes = 2; - } else if(*iter=='*') comment = 3; else @@ -245,10 +256,6 @@ bool ProgramParser::skip_comment_and_whitespace() { if(*iter=='\n') comment = 0; - else if(*iter=='/') - ++slashes; - else if(!isspace(*iter) && slashes>=6) - return false; } else if(comment==3 && *iter=='*') comment = 4; @@ -260,24 +267,28 @@ bool ProgramParser::skip_comment_and_whitespace() comment = 3; } + if(*iter=='\n') + { + ++current_line; + allow_preprocess = (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)); + throw runtime_error(format_syntax_error(format("'%s'", 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)); + throw runtime_error(format_syntax_error("a type")); return token; } @@ -285,7 +296,7 @@ string ProgramParser::expect_identifier() { string token = parse_token(); if(!is_identifier(token)) - throw runtime_error(format("Parse error at '%s': expected an identifier", token)); + throw runtime_error(format_syntax_error("an identifier")); return token; } @@ -307,9 +318,14 @@ bool ProgramParser::is_sampling_qualifier(const string &token) return token=="centroid"; } +bool ProgramParser::is_precision_qualifier(const string &token) +{ + return (token=="highp" || token=="mediump" || token=="lowp"); +} + bool ProgramParser::is_qualifier(const string &token) { - return (token=="const" || is_interface_qualifier(token) || is_sampling_qualifier(token)); + return (token=="const" || is_interface_qualifier(token) || is_sampling_qualifier(token) || is_precision_qualifier(token)); } bool ProgramParser::is_builtin_type(const string &token) @@ -329,22 +345,117 @@ bool ProgramParser::is_identifier(const string &token) return re.match(token); } -Node *ProgramParser::parse_global_declaration() +void ProgramParser::preprocess() +{ + expect("#"); + + string::const_iterator line_end = iter; + for(; (line_end!=source_end && *line_end!='\n'); ++line_end) ; + SetForScope stop_at_line_end(source_end, line_end); + + string token = peek_token(); + if(token=="pragma") + preprocess_pragma(); + else if(token=="define" || token=="undef" || token=="if" || token=="ifdef" || token=="ifndef" || token=="else" || + token=="elif" || token=="endif" || token=="error" || token=="extension" || token=="version" || token=="line") + throw runtime_error(format_error(format("Unsupported preprocessor directive '%s'", token))); + else if(!token.empty()) + throw runtime_error(format_syntax_error("a preprocessor directive")); + + iter = line_end; +} + +void ProgramParser::preprocess_pragma() +{ + expect("pragma"); + string token = parse_token(); + if(token=="MSP") + preprocess_pragma_msp(); +} + +void ProgramParser::preprocess_pragma_msp() { string token = peek_token(); - if(token=="layout") - return parse_layout(); + if(token=="stage") + preprocess_stage(); + else + throw runtime_error(format_error(format("Unrecognized MSP pragma '%s'", token))); + + token = parse_token(); + if(!token.empty()) + throw runtime_error(format_syntax_error("end of line")); +} + +void ProgramParser::preprocess_stage() +{ + if(!allow_stage_change) + throw runtime_error(format_error("Changing stage not allowed here")); + + expect("stage"); + expect("("); + string token = expect_identifier(); + StageType stage = SHARED; + if(token=="vertex") + stage = VERTEX; + else if(token=="geometry") + stage = GEOMETRY; + else if(token=="fragment") + stage = FRAGMENT; + else + throw runtime_error(format_syntax_error("stage identifier")); + expect(")"); + + if(stage<=cur_stage->type) + throw runtime_error(format_error(format("Stage '%s' not allowed here", token))); + + module->stages.push_back(stage); + + if(cur_stage->type!=SHARED) + module->stages.back().previous = cur_stage; + cur_stage = &module->stages.back(); +} + +RefPtr ProgramParser::parse_global_declaration() +{ + allow_stage_change = true; + string token = peek_token(); + allow_stage_change = false; + + if(token=="import") + return parse_import(); + else if(token=="precision") + return parse_precision(); + else if(token=="layout") + { + RefPtr layout = parse_layout(); + token = peek_token(); + if(is_interface_qualifier(token) && peek_token(1)==";") + { + RefPtr iface_lo = new InterfaceLayout; + iface_lo->layout.qualifiers = layout->qualifiers; + iface_lo->interface = parse_token(); + expect(";"); + return iface_lo; + } + else + { + RefPtr var = parse_variable_declaration(); + var->layout = layout; + return var; + } + } 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))) + string next = peek_token(1); + if(is_type(next) || is_precision_qualifier(next)) return parse_variable_declaration(); else return parse_interface_block(); } + else if(is_qualifier(token)) + return parse_variable_declaration(); else if(is_type(token)) { if(peek_token(2)=="(") @@ -355,10 +466,10 @@ Node *ProgramParser::parse_global_declaration() else if(token.empty()) return 0; else - throw runtime_error(format("Syntax error at '%s': expected a global declaration", token)); + throw runtime_error(format_syntax_error("a global declaration")); } -Node *ProgramParser::parse_statement() +RefPtr ProgramParser::parse_statement() { string token = peek_token(); if(token=="if") @@ -369,6 +480,14 @@ Node *ProgramParser::parse_statement() return parse_passthrough(); else if(token=="return") return parse_return(); + else if(token=="break" || token=="continue" || token=="discard") + { + RefPtr jump = new Jump; + jump->keyword = parse_token(); + expect(";"); + + return jump; + } else if(is_qualifier(token) || is_type(token)) return parse_variable_declaration(); else if(!token.empty()) @@ -377,13 +496,44 @@ Node *ProgramParser::parse_statement() expr->expression = parse_expression(); expect(";"); - return expr.release(); + return expr; } else - throw runtime_error(format("Syntax error at '%s': expected a statement", token)); + throw runtime_error(format_syntax_error("a statement")); } -Layout *ProgramParser::parse_layout() +RefPtr ProgramParser::parse_import() +{ + if(cur_stage->type!=SHARED) + throw runtime_error(format_error("Imports are only allowed in the shared section")); + + expect("import"); + RefPtr import = new Import; + import->module = parse_token(); + expect(";"); + return import; +} + +RefPtr ProgramParser::parse_precision() +{ + expect("precision"); + RefPtr precision = new Precision; + + precision->precision = parse_token(); + if(!is_precision_qualifier(precision->precision)) + throw runtime_error(format_syntax_error("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")); + + expect(";"); + + return precision; +} + +RefPtr ProgramParser::parse_layout() { expect("layout"); expect("("); @@ -392,7 +542,7 @@ Layout *ProgramParser::parse_layout() { string token = parse_token(); if(token==")") - throw runtime_error(format("Parse error at '%s': expected layout qualifier id", token)); + throw runtime_error(format_syntax_error("a layout qualifier name")); layout->qualifiers.push_back(Layout::Qualifier()); Layout::Qualifier &qual = layout->qualifiers.back(); @@ -407,10 +557,8 @@ Layout *ProgramParser::parse_layout() expect(","); } expect(")"); - layout->interface = parse_token(); - expect(";"); - return layout.release(); + return layout; } void ProgramParser::parse_block(Block &block, bool require_braces) @@ -433,7 +581,7 @@ void ProgramParser::parse_block(Block &block, bool require_braces) expect("}"); } -Expression *ProgramParser::parse_expression(unsigned precedence) +RefPtr ProgramParser::parse_expression(unsigned precedence) { RefPtr left; VariableReference *left_var = 0; @@ -449,22 +597,22 @@ Expression *ProgramParser::parse_expression(unsigned precedence) if(token==";" || token==")" || token=="]" || token=="," || (oper && precedence && oper->precedence>=precedence)) { if(left) - return left.release(); + return left; else - throw runtime_error(format("Parse error at '%s': expected an expression", token)); + throw runtime_error(format_syntax_error("an expression")); } else if(left) { if(token=="(") { if(!left_var) - throw runtime_error(format("Parse error at '%s': function name must be an identifier", token)); - left = parse_function_call(left_var); + throw runtime_error(format_error("Syntax error before '(': function name must be an identifier")); + left = parse_function_call(*left_var); } else if(token==".") { RefPtr memacc = new MemberAccess; - memacc->left = left.release(); + memacc->left = left; parse_token(); memacc->member = expect_identifier(); left = memacc; @@ -474,13 +622,13 @@ Expression *ProgramParser::parse_expression(unsigned precedence) RefPtr unary = new UnaryExpression; unary->oper = parse_token(); unary->prefix = false; - unary->expression = left.release(); + unary->expression = left; left = unary; } else if(oper && oper->type==BINARY) - left = parse_binary(left.release(), oper); + left = parse_binary(left, oper); else - throw runtime_error(format("Parse error at '%s': expected an operator", token)); + throw runtime_error(format_syntax_error("an operator")); left_var = 0; } else @@ -493,6 +641,12 @@ Expression *ProgramParser::parse_expression(unsigned precedence) expect(")"); left = parexpr; } + else if(isdigit(token[0]) || token=="true" || token=="false") + { + RefPtr literal = new Literal; + literal->token = parse_token(); + left = literal; + } else if(is_identifier(token)) { RefPtr var = new VariableReference; @@ -508,21 +662,15 @@ Expression *ProgramParser::parse_expression(unsigned precedence) unary->expression = parse_expression(oper->precedence); left = unary; } - else if(isdigit(token[0])) - { - RefPtr literal = new Literal; - literal->token = parse_token(); - left = literal; - } else - throw runtime_error(format("Parse error at '%s': expected an expression", token)); + throw runtime_error(format_syntax_error("an expression")); } } } -BinaryExpression *ProgramParser::parse_binary(Expression *left, const Operator *oper) +RefPtr ProgramParser::parse_binary(const RefPtr &left, const Operator *oper) { - RefPtr binary = new BinaryExpression; + RefPtr binary = (oper->precedence==16 ? new Assignment : new BinaryExpression); binary->left = left; binary->oper = parse_token(); if(binary->oper=="[") @@ -533,14 +681,13 @@ BinaryExpression *ProgramParser::parse_binary(Expression *left, const Operator * } else binary->right = parse_expression(oper->precedence+(oper->assoc==RIGHT_TO_LEFT)); - binary->assignment = (oper->precedence==16); - return binary.release(); + return binary; } -FunctionCall *ProgramParser::parse_function_call(VariableReference *var) +RefPtr ProgramParser::parse_function_call(const VariableReference &var) { RefPtr call = new FunctionCall; - call->name = var->name; + call->name = var.name; call->constructor = is_type(call->name); expect("("); while(peek_token()!=")") @@ -550,10 +697,10 @@ FunctionCall *ProgramParser::parse_function_call(VariableReference *var) call->arguments.push_back(parse_expression()); } expect(")"); - return call.release(); + return call; } -StructDeclaration *ProgramParser::parse_struct_declaration() +RefPtr ProgramParser::parse_struct_declaration() { expect("struct"); RefPtr strct = new StructDeclaration; @@ -563,10 +710,10 @@ StructDeclaration *ProgramParser::parse_struct_declaration() expect(";"); declared_types.insert(strct->name); - return strct.release(); + return strct; } -VariableDeclaration *ProgramParser::parse_variable_declaration() +RefPtr ProgramParser::parse_variable_declaration() { RefPtr var = new VariableDeclaration; @@ -576,7 +723,7 @@ VariableDeclaration *ProgramParser::parse_variable_declaration() 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)); + throw runtime_error(format_syntax_error("an interface qualifier")); } if(is_interface_qualifier(token)) @@ -587,6 +734,9 @@ VariableDeclaration *ProgramParser::parse_variable_declaration() parse_token(); } + if(is_precision_qualifier(token)) + var->precision = parse_token(); + var->type = expect_type(); var->name = expect_identifier(); @@ -604,10 +754,10 @@ VariableDeclaration *ProgramParser::parse_variable_declaration() var->init_expression = parse_expression(); expect(";"); - return var.release(); + return var; } -FunctionDeclaration *ProgramParser::parse_function_declaration() +RefPtr ProgramParser::parse_function_declaration() { RefPtr func = new FunctionDeclaration; @@ -620,33 +770,36 @@ FunctionDeclaration *ProgramParser::parse_function_declaration() expect(","); RefPtr var = new VariableDeclaration; + string token = peek_token(); + if(token=="in" || token=="out" || token=="inout") + var->interface = parse_token(); var->type = expect_type(); var->name = expect_identifier(); - func->parameters.push_back(var.release()); + func->parameters.push_back(var); } expect(")"); string token = peek_token(); if(token=="{") { - func->definition = true; + func->definition = func.get(); parse_block(func->body, true); } else if(token==";") parse_token(); else - throw runtime_error(format("Parse error at '%s': expected '{' or ';'", token)); + throw runtime_error(format_syntax_error("'{' or ';'")); - return func.release(); + return func; } -InterfaceBlock *ProgramParser::parse_interface_block() +RefPtr ProgramParser::parse_interface_block() { RefPtr 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)); + throw runtime_error(format_syntax_error("an interface qualifier")); iface->name = expect_identifier(); parse_block(iface->members, true); @@ -661,10 +814,10 @@ InterfaceBlock *ProgramParser::parse_interface_block() expect(";"); } - return iface.release(); + return iface; } -Conditional *ProgramParser::parse_conditional() +RefPtr ProgramParser::parse_conditional() { expect("if"); expect("("); @@ -681,10 +834,10 @@ Conditional *ProgramParser::parse_conditional() parse_block(cond->else_body, false); } - return cond.release(); + return cond; } -Iteration *ProgramParser::parse_iteration() +RefPtr ProgramParser::parse_iteration() { expect("for"); expect("("); @@ -697,7 +850,7 @@ Iteration *ProgramParser::parse_iteration() RefPtr expr = new ExpressionStatement; expr->expression = parse_expression(); expect(";"); - loop->init_statement = expr.release(); + loop->init_statement = expr; } loop->condition = parse_expression(); expect(";"); @@ -706,10 +859,10 @@ Iteration *ProgramParser::parse_iteration() parse_block(loop->body, false); - return loop.release(); + return loop; } -Passthrough *ProgramParser::parse_passthrough() +RefPtr ProgramParser::parse_passthrough() { expect("passthrough"); RefPtr pass = new Passthrough; @@ -720,17 +873,17 @@ Passthrough *ProgramParser::parse_passthrough() expect("]"); } expect(";"); - return pass.release(); + return pass; } -Return *ProgramParser::parse_return() +RefPtr ProgramParser::parse_return() { expect("return"); RefPtr ret = new Return; if(peek_token()!=";") ret->expression = parse_expression(); expect(";"); - return ret.release(); + return ret; } } // namespace GL