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