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