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