]> git.tdb.fi Git - libs/gl.git/blob - source/glsl/parser.cpp
Make the GLSL parser resilient against common errors
[libs/gl.git] / source / glsl / parser.cpp
1 #include <msp/core/raii.h>
2 #include <msp/strings/format.h>
3 #include <msp/strings/regex.h>
4 #include <msp/strings/utils.h>
5 #include "glsl_error.h"
6 #include "parser.h"
7
8 #undef interface
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GL {
14 namespace SL {
15
16 Parser::Parser():
17         preprocessor(tokenizer),
18         module(0)
19 {
20         tokenizer.signal_preprocess.connect(sigc::mem_fun(&preprocessor, &Preprocessor::preprocess));
21         preprocessor.signal_version.connect(sigc::mem_fun(this, &Parser::set_required_version));
22         preprocessor.signal_source.connect(sigc::mem_fun(this, &Parser::source_reference));
23         preprocessor.signal_stage_change.connect(sigc::mem_fun(this, &Parser::stage_change));
24         preprocessor.signal_line.connect(sigc::mem_fun(this, &Parser::line_change));
25 }
26
27 Parser::~Parser()
28 {
29         delete module;
30 }
31
32 Module &Parser::parse(const string &s, const string &n, unsigned i)
33 {
34         source = s;
35         parse_source(n, i);
36         return *module;
37 }
38
39 Module &Parser::parse(IO::Base &io, const string &n, unsigned i)
40 {
41         source = string();
42         while(!io.eof())
43         {
44                 char buffer[4096];
45                 unsigned len = io.read(buffer, sizeof(buffer));
46                 source.append(buffer, len);
47         }
48         parse_source(n, i);
49         return *module;
50 }
51
52 void Parser::parse_source(const string &name, unsigned index)
53 {
54         delete module;
55         module = new Module;
56         cur_stage = &module->shared;
57         base_index = index;
58         source_index = index;
59         source_reference(1, name);
60         tokenizer.begin(name, source);
61         allow_stage_change = true;
62         while(!tokenizer.peek_token().empty())
63                 if(RefPtr<Statement> statement = parse_with_recovery(&Parser::parse_global_declaration))
64                         cur_stage->content.body.push_back(statement);
65
66         if(!errors.empty())
67                 throw invalid_shader_source(join(errors.begin(), errors.end(), "\n"));
68 }
69
70 void Parser::set_required_version(const Version &ver)
71 {
72         cur_stage->required_features.glsl_version = ver;
73 }
74
75 void Parser::source_reference(unsigned index, const string &name)
76 {
77         if(index<1)
78                 throw invalid_shader_source(tokenizer.get_location(), "Invalid source reference");
79
80         module->source_map.set_name(base_index+index-1, name);
81 }
82
83 void Parser::stage_change(Stage::Type stage)
84 {
85         if(!allow_stage_change)
86                 throw invalid_shader_source(tokenizer.get_location(), "Changing stage not allowed here");
87         else if(stage<=cur_stage->type)
88                 throw invalid_shader_source(tokenizer.get_location(), "Stage '%s' not allowed here", Stage::get_stage_name(stage));
89
90         module->stages.push_back(stage);
91
92         if(cur_stage->type!=Stage::SHARED)
93                 module->stages.back().previous = cur_stage;
94         cur_stage = &module->stages.back();
95 }
96
97 void Parser::line_change(int index, unsigned line)
98 {
99         if(index>0)
100                 source_index = base_index+index-1;
101         else if(index==0)
102                 source_index = 0;
103         else
104                 index = source_index;
105
106         string name = module->source_map.get_name(index);
107         if(name.empty())
108                 name = format("<%d>", index);
109         tokenizer.set_location(Location(name, line));
110 }
111
112 string Parser::expect_type()
113 {
114         string token = tokenizer.parse_token();
115         if(!is_type(token))
116                 throw parse_error(tokenizer.get_location(), token, "a type");
117         return token;
118 }
119
120 string Parser::expect_identifier()
121 {
122         string token = tokenizer.parse_token();
123         if(!is_identifier(token))
124                 throw parse_error(tokenizer.get_location(), token, "an identifier");
125         return token;
126 }
127
128 int Parser::expect_integer()
129 {
130         string token = tokenizer.parse_token();
131         if(!isnumrc(token))
132                 throw parse_error(tokenizer.get_location(), token, "an integer literal");
133         return lexical_cast<int>(token);
134 }
135
136 bool Parser::check(const string &token)
137 {
138         bool result = (tokenizer.peek_token()==token);
139         if(result)
140                 tokenizer.parse_token();
141         return result;
142 }
143
144 bool Parser::is_interface_qualifier(const string &token)
145 {
146         return (token=="uniform" || token=="in" || token=="out");
147 }
148
149 bool Parser::is_sampling_qualifier(const string &token)
150 {
151         return (token=="centroid" || token=="sample");
152 }
153
154 bool Parser::is_interpolation_qualifier(const string &token)
155 {
156         return (token=="smooth" || token=="flat" || token=="noperspective");
157 }
158
159 bool Parser::is_precision_qualifier(const string &token)
160 {
161         return (token=="highp" || token=="mediump" || token=="lowp");
162 }
163
164 bool Parser::is_qualifier(const string &token)
165 {
166         return (token=="const" ||
167                 is_interface_qualifier(token) ||
168                 is_sampling_qualifier(token) ||
169                 is_interpolation_qualifier(token) ||
170                 is_precision_qualifier(token));
171 }
172
173 bool Parser::is_builtin_type(const string &token)
174 {
175         static Regex re("^(void|float|int|bool|[ib]?vec[234]|mat[234](x[234])?|sampler((1D|2D|Cube)(Array)?(Shadow)?|3D))$");
176         return re.match(token);
177 }
178
179 bool Parser::is_type(const string &token)
180 {
181         return is_builtin_type(token) || declared_types.count(token);
182 }
183
184 bool Parser::is_identifier(const string &token)
185 {
186         static Regex re("^[a-zA-Z_][a-zA-Z0-9_]*$");
187         return re.match(token);
188 }
189
190 template<typename T>
191 RefPtr<T> Parser::parse_with_recovery(RefPtr<T> (Parser::*parse_func)())
192 {
193         tokenizer.clear_progress_mark();
194         try
195         {
196                 return (this->*parse_func)();
197         }
198         catch(const invalid_shader_source &exc)
199         {
200                 errors.push_back(exc.what());
201         }
202
203         if(tokenizer.get_last_token()!=";" || !tokenizer.get_progress_mark())
204         {
205                 unsigned scope_level = 0;
206                 while(1)
207                 {
208                         if(tokenizer.peek_token()=="}" && scope_level==0)
209                         {
210                                 if(!tokenizer.get_progress_mark())
211                                         tokenizer.parse_token();
212                                 break;
213                         }
214
215                         string token = tokenizer.parse_token();
216                         if(token=="}")
217                         {
218                                 --scope_level;
219                                 if(scope_level==0)
220                                         break;
221                         }
222                         else if(token=="{")
223                                 ++scope_level;
224                         else if(token==";" && scope_level==0)
225                                 break;
226                         else if(token.empty())
227                                 break;
228                 }
229         }
230
231         return RefPtr<T>();
232 }
233
234 RefPtr<Statement> Parser::parse_global_declaration()
235 {
236         string token = tokenizer.peek_token();
237         SetFlag disallow(allow_stage_change, false);
238
239         if(token=="import")
240                 return parse_import();
241         else if(token=="precision")
242                 return parse_precision();
243         else if(token=="layout")
244         {
245                 RefPtr<Layout> layout = parse_layout();
246                 token = tokenizer.peek_token();
247                 if(is_interface_qualifier(token) && tokenizer.peek_token(1)==";")
248                 {
249                         RefPtr<InterfaceLayout> iface_lo = new InterfaceLayout;
250                         iface_lo->source = source_index;
251                         iface_lo->line = tokenizer.get_location().line;
252                         iface_lo->layout.qualifiers = layout->qualifiers;
253                         iface_lo->interface = tokenizer.parse_token();
254                         tokenizer.expect(";");
255                         return iface_lo;
256                 }
257                 else
258                 {
259                         RefPtr<VariableDeclaration> var = parse_variable_declaration();
260                         var->layout = layout;
261                         return var;
262                 }
263         }
264         else if(token=="struct")
265                 return parse_struct_declaration();
266         else if(is_interface_qualifier(token))
267         {
268                 string next = tokenizer.peek_token(1);
269                 if(is_type(next) || is_qualifier(next))
270                         return parse_variable_declaration();
271                 else
272                         return parse_interface_block();
273         }
274         else if(is_qualifier(token))
275                 return parse_variable_declaration();
276         else if(is_type(token))
277         {
278                 if(tokenizer.peek_token(2)=="(")
279                         return parse_function_declaration();
280                 else
281                         return parse_variable_declaration();
282         }
283         else if(token.empty())
284                 return 0;
285         else
286                 throw parse_error(tokenizer.get_location(), token, "a global declaration");
287 }
288
289 RefPtr<Statement> Parser::parse_statement()
290 {
291         string token = tokenizer.peek_token();
292         if(token=="if")
293                 return parse_conditional();
294         else if(token=="for")
295                 return parse_for();
296         else if(token=="while")
297                 return parse_while();
298         else if(token=="passthrough")
299                 return parse_passthrough();
300         else if(token=="return")
301                 return parse_return();
302         else if(token=="break" || token=="continue" || token=="discard")
303         {
304                 RefPtr<Jump> jump = new Jump;
305                 jump->source = source_index;
306                 jump->line = tokenizer.get_location().line;
307                 jump->keyword = tokenizer.parse_token();
308                 tokenizer.expect(";");
309
310                 return jump;
311         }
312         else if(is_qualifier(token) || is_type(token))
313                 return parse_variable_declaration();
314         else if(token==";")
315         {
316                 tokenizer.parse_token();
317                 throw invalid_shader_source(tokenizer.get_location(), "Empty statement not allowed");
318         }
319         else if(!token.empty())
320         {
321                 RefPtr<ExpressionStatement> expr = new ExpressionStatement;
322                 expr->source = source_index;
323                 expr->line = tokenizer.get_location().line;
324                 expr->expression = parse_expression();
325                 tokenizer.expect(";");
326
327                 return expr;
328         }
329         else
330                 throw parse_error(tokenizer.get_location(), token, "a statement");
331 }
332
333 RefPtr<Import> Parser::parse_import()
334 {
335         if(cur_stage->type!=Stage::SHARED)
336                 throw invalid_shader_source(tokenizer.get_location(), "Imports are only allowed in the shared section");
337
338         tokenizer.expect("import");
339         RefPtr<Import> import = new Import;
340         import->source = source_index;
341         import->line = tokenizer.get_location().line;
342         import->module = expect_identifier();
343         tokenizer.expect(";");
344         return import;
345 }
346
347 RefPtr<Precision> Parser::parse_precision()
348 {
349         tokenizer.expect("precision");
350         RefPtr<Precision> precision = new Precision;
351         precision->source = source_index;
352         precision->line = tokenizer.get_location().line;
353
354         precision->precision = tokenizer.parse_token();
355         if(!is_precision_qualifier(precision->precision))
356                 throw parse_error(tokenizer.get_location(), precision->precision, "a precision qualifier");
357
358         precision->type = tokenizer.parse_token();
359         // Not entirely accurate; only float, int and sampler types are allowed
360         if(!is_builtin_type(precision->type))
361                 throw parse_error(tokenizer.get_location(), precision->type, "a builtin type");
362
363         tokenizer.expect(";");
364
365         return precision;
366 }
367
368 RefPtr<Layout> Parser::parse_layout()
369 {
370         tokenizer.expect("layout");
371         tokenizer.expect("(");
372         RefPtr<Layout> layout = new Layout;
373         while(1)
374         {
375                 string token = tokenizer.parse_token();
376                 if(token==")")
377                         throw parse_error(tokenizer.get_location(), token, "a layout qualifier name");
378
379                 layout->qualifiers.push_back(Layout::Qualifier());
380                 Layout::Qualifier &qual = layout->qualifiers.back();
381                 qual.name = token;
382
383                 if((qual.has_value = check("=")))
384                 {
385                         if(qual.name=="constant_id" && tokenizer.peek_token()=="auto")
386                         {
387                                 qual.value = -1;
388                                 tokenizer.parse_token();
389                         }
390                         else
391                                 qual.value = expect_integer();
392                 }
393
394                 if(tokenizer.peek_token()==")")
395                         break;
396
397                 tokenizer.expect(",");
398         }
399         tokenizer.expect(")");
400
401         return layout;
402 }
403
404 template<typename T>
405 void Parser::parse_block(Block &block, bool require_braces, RefPtr<T> (Parser::*parse_content)())
406 {
407         bool have_braces = (require_braces || tokenizer.peek_token()=="{");
408         if(have_braces)
409                 tokenizer.expect("{");
410
411         if(have_braces)
412         {
413                 while(tokenizer.peek_token()!="}")
414                         if(RefPtr<Statement> node = parse_with_recovery(parse_content))
415                                 block.body.push_back(node);
416         }
417         else
418                 block.body.push_back((this->*parse_content)());
419
420         block.use_braces = (require_braces || block.body.size()!=1);
421
422         if(have_braces)
423                 tokenizer.expect("}");
424 }
425
426 RefPtr<Expression> Parser::parse_expression(unsigned precedence)
427 {
428         RefPtr<Expression> left;
429         VariableReference *left_var = 0;
430         while(1)
431         {
432                 string token = tokenizer.peek_token();
433
434                 const Operator *oper = 0;
435                 for(const Operator *i=Operator::operators; (!oper && i->type); ++i)
436                         if(token==i->token && (!left || i->type!=Operator::PREFIX) && (left || i->type!=Operator::POSTFIX))
437                                 oper = i;
438
439                 if(token==";" || token==")" || token=="]" || token=="," || (oper && precedence && oper->precedence>=precedence))
440                 {
441                         if(left)
442                                 return left;
443                         else
444                                 throw parse_error(tokenizer.get_location(), token, "an expression");
445                 }
446                 else if(left)
447                 {
448                         if(token=="(")
449                         {
450                                 if(!left_var)
451                                         throw invalid_shader_source(tokenizer.get_location(), "Syntax error before '(': function name must be an identifier");
452                                 left = parse_function_call(*left_var);
453                         }
454                         else if(token==".")
455                         {
456                                 RefPtr<MemberAccess> memacc = new MemberAccess;
457                                 memacc->left = left;
458                                 tokenizer.parse_token();
459                                 memacc->member = expect_identifier();
460                                 left = memacc;
461                         }
462                         else if(oper && oper->type==Operator::POSTFIX)
463                         {
464                                 RefPtr<UnaryExpression> unary = new UnaryExpression;
465                                 unary->oper = tokenizer.parse_token();
466                                 unary->prefix = false;
467                                 unary->expression = left;
468                                 left = unary;
469                         }
470                         else if(oper && oper->type==Operator::BINARY)
471                                 left = parse_binary(left, oper);
472                         else
473                                 throw parse_error(tokenizer.get_location(), token, "an operator");
474                         left_var = 0;
475                 }
476                 else
477                 {
478                         if(token=="(")
479                         {
480                                 tokenizer.parse_token();
481                                 RefPtr<ParenthesizedExpression> parexpr = new ParenthesizedExpression;
482                                 parexpr->expression = parse_expression();
483                                 tokenizer.expect(")");
484                                 left = parexpr;
485                         }
486                         else if(isdigit(token[0]) || token=="true" || token=="false")
487                         {
488                                 RefPtr<Literal> literal = new Literal;
489                                 literal->token = tokenizer.parse_token();
490                                 left = literal;
491                         }
492                         else if(is_identifier(token))
493                         {
494                                 RefPtr<VariableReference> var = new VariableReference;
495                                 var->name = expect_identifier();
496                                 left = var;
497                                 left_var = var.get();
498                         }
499                         else if(oper && oper->type==Operator::PREFIX)
500                         {
501                                 RefPtr<UnaryExpression> unary = new UnaryExpression;
502                                 unary->oper = tokenizer.parse_token();
503                                 unary->prefix = true;
504                                 unary->expression = parse_expression(oper->precedence);
505                                 left = unary;
506                         }
507                         else
508                                 throw parse_error(tokenizer.get_location(), token, "an expression");
509                 }
510         }
511 }
512
513 RefPtr<BinaryExpression> Parser::parse_binary(const RefPtr<Expression> &left, const Operator *oper)
514 {
515         RefPtr<BinaryExpression> binary = (oper->precedence==16 ? new Assignment : new BinaryExpression);
516         binary->left = left;
517         binary->oper = tokenizer.parse_token();
518         if(binary->oper=="[")
519         {
520                 binary->right = parse_expression();
521                 tokenizer.expect("]");
522                 binary->after = "]";
523         }
524         else
525                 binary->right = parse_expression(oper->precedence+(oper->assoc==Operator::RIGHT_TO_LEFT));
526         return binary;
527 }
528
529 RefPtr<FunctionCall> Parser::parse_function_call(const VariableReference &var)
530 {
531         RefPtr<FunctionCall> call = new FunctionCall;
532         call->name = var.name;
533         call->constructor = is_type(call->name);
534         tokenizer.expect("(");
535         while(tokenizer.peek_token()!=")")
536         {
537                 if(!call->arguments.empty())
538                         tokenizer.expect(",");
539                 call->arguments.push_back(parse_expression());
540         }
541         tokenizer.expect(")");
542         return call;
543 }
544
545 RefPtr<StructDeclaration> Parser::parse_struct_declaration()
546 {
547         tokenizer.expect("struct");
548         RefPtr<StructDeclaration> strct = new StructDeclaration;
549         strct->source = source_index;
550         strct->line = tokenizer.get_location().line;
551
552         strct->name = expect_identifier();
553         parse_block(strct->members, true, &Parser::parse_variable_declaration);
554         tokenizer.expect(";");
555
556         declared_types.insert(strct->name);
557         return strct;
558 }
559
560 RefPtr<VariableDeclaration> Parser::parse_variable_declaration()
561 {
562         RefPtr<VariableDeclaration> var = new VariableDeclaration;
563         var->source = source_index;
564         var->line = tokenizer.get_location().line;
565
566         string token = tokenizer.peek_token();
567         while(is_qualifier(token))
568         {
569                 tokenizer.parse_token();
570                 if(is_interface_qualifier(token))
571                         var->interface = token;
572                 else if(is_sampling_qualifier(token))
573                         var->sampling = token;
574                 else if(is_interpolation_qualifier(token))
575                         var->interpolation = token;
576                 else if(is_precision_qualifier(token))
577                         var->precision = token;
578                 else if(token=="const")
579                         var->constant = true;
580                 token = tokenizer.peek_token();
581         }
582
583         var->type = expect_type();
584         var->name = expect_identifier();
585
586         if(check("["))
587         {
588                 var->array = true;
589                 if(!check("]"))
590                 {
591                         var->array_size = parse_expression();
592                         tokenizer.expect("]");
593                 }
594         }
595
596         if(check("="))
597                 var->init_expression = parse_expression();
598
599         tokenizer.expect(";");
600         return var;
601 }
602
603 RefPtr<VariableDeclaration> Parser::parse_variable_declaration_with_layout()
604 {
605         RefPtr<Layout> layout;
606         if(tokenizer.peek_token()=="layout")
607                 layout = parse_layout();
608
609         RefPtr<VariableDeclaration> var = parse_variable_declaration();
610         var->layout = layout;
611
612         return var;
613 }
614
615 RefPtr<FunctionDeclaration> Parser::parse_function_declaration()
616 {
617         RefPtr<FunctionDeclaration> func = new FunctionDeclaration;
618         func->source = source_index;
619         func->line = tokenizer.get_location().line;
620
621         func->return_type = expect_type();
622         func->name = expect_identifier();
623         tokenizer.expect("(");
624         while(tokenizer.peek_token()!=")")
625         {
626                 if(!func->parameters.empty())
627                         tokenizer.expect(",");
628
629                 RefPtr<VariableDeclaration> var = new VariableDeclaration;
630                 string token = tokenizer.peek_token();
631                 if(token=="in" || token=="out" || token=="inout")
632                         var->interface = tokenizer.parse_token();
633                 var->type = expect_type();
634                 var->name = expect_identifier();
635                 func->parameters.push_back(var);
636         }
637         tokenizer.expect(")");
638
639         string token = tokenizer.peek_token();
640         if(token=="{")
641         {
642                 func->definition = func.get();
643                 parse_block(func->body, true, &Parser::parse_statement);
644         }
645         else if(token==";")
646                 tokenizer.parse_token();
647         else
648                 throw parse_error(tokenizer.get_location(), token, "'{' or ';'");
649
650         return func;
651 }
652
653 RefPtr<InterfaceBlock> Parser::parse_interface_block()
654 {
655         RefPtr<InterfaceBlock> iface = new InterfaceBlock;
656         iface->source = source_index;
657         iface->line = tokenizer.get_location().line;
658
659         iface->interface = tokenizer.parse_token();
660         if(!is_interface_qualifier(iface->interface))
661                 throw parse_error(tokenizer.get_location(), iface->interface, "an interface qualifier");
662
663         iface->name = expect_identifier();
664         parse_block(iface->members, true, &Parser::parse_variable_declaration_with_layout);
665         if(!check(";"))
666         {
667                 iface->instance_name = expect_identifier();
668                 if(check("["))
669                 {
670                         iface->array = true;
671                         tokenizer.expect("]");
672                 }
673                 tokenizer.expect(";");
674         }
675
676         return iface;
677 }
678
679 RefPtr<Conditional> Parser::parse_conditional()
680 {
681         tokenizer.expect("if");
682         RefPtr<Conditional> cond = new Conditional;
683         cond->source = source_index;
684         cond->line = tokenizer.get_location().line;
685         tokenizer.expect("(");
686         cond->condition = parse_expression();
687         tokenizer.expect(")");
688
689         parse_block(cond->body, false, &Parser::parse_statement);
690
691         string token = tokenizer.peek_token();
692         if(token=="else")
693         {
694                 tokenizer.parse_token();
695                 parse_block(cond->else_body, false, &Parser::parse_statement);
696         }
697
698         return cond;
699 }
700
701 RefPtr<Iteration> Parser::parse_for()
702 {
703         tokenizer.expect("for");
704         RefPtr<Iteration> loop = new Iteration;
705         loop->source = source_index;
706         loop->line = tokenizer.get_location().line;
707         tokenizer.expect("(");
708         string token = tokenizer.peek_token();
709         if(is_type(token))
710                 loop->init_statement = parse_statement();
711         else
712         {
713                 if(token!=";")
714                 {
715                         RefPtr<ExpressionStatement> expr = new ExpressionStatement;
716                         expr->expression = parse_expression();
717                         loop->init_statement = expr;
718                 }
719                 tokenizer.expect(";");
720         }
721         if(tokenizer.peek_token()!=";")
722                 loop->condition = parse_expression();
723         tokenizer.expect(";");
724         if(tokenizer.peek_token()!=")")
725                 loop->loop_expression = parse_expression();
726         tokenizer.expect(")");
727
728         parse_block(loop->body, false, &Parser::parse_statement);
729
730         return loop;
731 }
732
733 RefPtr<Iteration> Parser::parse_while()
734 {
735         tokenizer.expect("while");
736         RefPtr<Iteration> loop = new Iteration;
737         loop->source = source_index;
738         loop->line = tokenizer.get_location().line;
739         tokenizer.expect("(");
740         loop->condition = parse_expression();
741         tokenizer.expect(")");
742
743         parse_block(loop->body, false, &Parser::parse_statement);
744
745         return loop;
746 }
747
748 RefPtr<Passthrough> Parser::parse_passthrough()
749 {
750         tokenizer.expect("passthrough");
751         RefPtr<Passthrough> pass = new Passthrough;
752         pass->source = source_index;
753         pass->line = tokenizer.get_location().line;
754         if(cur_stage->type==Stage::GEOMETRY)
755         {
756                 tokenizer.expect("[");
757                 pass->subscript = parse_expression();
758                 tokenizer.expect("]");
759         }
760         tokenizer.expect(";");
761         return pass;
762 }
763
764 RefPtr<Return> Parser::parse_return()
765 {
766         tokenizer.expect("return");
767         RefPtr<Return> ret = new Return;
768         ret->source = source_index;
769         ret->line = tokenizer.get_location().line;
770         if(tokenizer.peek_token()!=";")
771                 ret->expression = parse_expression();
772         tokenizer.expect(";");
773         return ret;
774 }
775
776 } // namespace SL
777 } // namespace GL
778 } // namespace Msp