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