]> git.tdb.fi Git - libs/gl.git/blob - source/programparser.cpp
Begin implementing a new shader program generator system
[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 Module &ProgramParser::parse(const string &s)
13 {
14         source = s;
15         parse_source(main_module);
16         return main_module;
17 }
18
19 Module &ProgramParser::parse(IO::Base &io)
20 {
21         source = string();
22         while(!io.eof())
23         {
24                 char buffer[4096];
25                 unsigned len = io.read(buffer, sizeof(buffer));
26                 source.append(buffer, len);
27         }
28         parse_source(main_module);
29         return main_module;
30 }
31
32 void ProgramParser::parse_source(Module &module)
33 {
34         cur_module = &module;
35         iter = source.begin();
36         Context *cur_context = &module.global_context;
37         while(1)
38         {
39                 while(Node *statement = parse_global_declaration())
40                         cur_context->content.body.push_back(statement);
41                 cur_context->present = !cur_context->content.body.empty();
42
43                 parse_token();
44                 string token = parse_token();
45                 if(token.empty())
46                         break;
47                 else if(token=="global")
48                         cur_context = &module.global_context;
49                 else if(token=="vertex")
50                         cur_context = &module.vertex_context;
51                 else if(token=="geometry")
52                         cur_context = &module.geometry_context;
53                 else if(token=="fragment")
54                         cur_context = &module.fragment_context;
55                 else
56                         throw runtime_error(format("Parse error at '%s': expected context identifier", token));
57
58                 for(; (iter!=source.end() && *iter!='\n'); ++iter) ;
59         }
60 }
61
62 const string &ProgramParser::peek_token(unsigned index)
63 {
64         while(next_tokens.size()<=index)
65                 next_tokens.push_back(parse_token_());
66         return next_tokens[index];
67 }
68
69 string ProgramParser::parse_token()
70 {
71         if(!next_tokens.empty())
72         {
73                 string token = next_tokens.front();
74                 next_tokens.pop_front();
75                 return token;
76         }
77
78         return parse_token_();
79 }
80
81 string ProgramParser::parse_token_()
82 {
83         if(!skip_comment_and_whitespace())
84                 return string();
85
86         if(isalpha(*iter) || *iter=='_')
87                 return parse_identifier();
88         else if(isdigit(*iter))
89                 return parse_number();
90         else
91                 return parse_other();
92 }
93
94 string ProgramParser::parse_identifier()
95 {
96         string ident;
97         while(iter!=source.end())
98         {
99                 if(isalnum(*iter) || *iter=='_')
100                         ident += *iter++;
101                 else
102                         break;
103         }
104
105         return ident;
106 }
107
108 string ProgramParser::parse_number()
109 {
110         bool accept_sign = false;
111         string number;
112         while(iter!=source.end())
113         {
114                 if(isdigit(*iter) || *iter=='.')
115                         number += *iter++;
116                 else if(*iter=='e' || *iter=='E')
117                 {
118                         number += *iter++;
119                         accept_sign = true;
120                 }
121                 else if(accept_sign && (*iter=='+' || *iter=='-'))
122                         number += *iter++;
123                 else
124                         break;
125         }
126
127         return number;
128 }
129
130 string ProgramParser::parse_other()
131 {
132         string token;
133         while(iter!=source.end())
134         {
135                 if(isalnum(*iter) || *iter=='_' || isspace(*iter))
136                         break;
137                 token += *iter++;
138                 if(*iter==';' || *iter=='(' || *iter==')' || *iter=='[' || *iter==']')
139                         break;
140         }
141
142         return token;
143 }
144
145 bool ProgramParser::skip_comment_and_whitespace()
146 {
147         unsigned comment = 0;
148         unsigned slashes = 0;
149         while(iter!=source.end())
150         {
151                 //IO::print("%d '%c'\n", comment, *iter);
152                 if(comment==0)
153                 {
154                         if(*iter=='/')
155                                 comment = 1;
156                         else if(!isspace(*iter))
157                                 break;
158                 }
159                 else if(comment==1)
160                 {
161                         if(*iter=='/')
162                         {
163                                 comment = 2;
164                                 slashes = 2;
165                         }
166                         else if(*iter=='*')
167                                 comment = 3;
168                         else
169                         {
170                                 comment = 0;
171                                 --iter;
172                                 break;
173                         }
174                 }
175                 else if(comment==2)
176                 {
177                         if(*iter=='\n')
178                                 comment = 0;
179                         else if(*iter=='/')
180                                 ++slashes;
181                         else if(!isspace(*iter) && slashes>=6)
182                                 return false;
183                 }
184                 else if(comment==3 && *iter=='*')
185                         comment = 4;
186                 else if(comment==4)
187                 {
188                         if(*iter=='/')
189                                 comment = 0;
190                         else
191                                 comment = 3;
192                 }
193
194                 ++iter;
195         }
196
197         return iter!=source.end();
198 }
199
200 void ProgramParser::expect(const string &token)
201 {
202         string parsed = parse_token();
203         if(parsed!=token)
204                 throw runtime_error(format("Parse error at '%s': expected '%s'", parsed, token));
205 }
206
207 string ProgramParser::expect_type()
208 {
209         string token = parse_token();
210         if(!is_type(token))
211                 throw runtime_error(format("Parse error at '%s': expected a type", token));
212         return token;
213 }
214
215 string ProgramParser::expect_identifier()
216 {
217         static Regex re("^[a-zA-Z_][a-zA-Z0-9_]*$");
218         string token = parse_token();
219         if(!re.match(token))
220                 throw runtime_error(format("Parse error at '%s': expected an identifier", token));
221         return token;
222 }
223
224 bool ProgramParser::check(const string &token)
225 {
226         bool result = (peek_token()==token);
227         if(result)
228                 parse_token();
229         return result;
230 }
231
232 bool ProgramParser::is_interface_qualifier(const string &token)
233 {
234         return (token=="uniform" || token=="in" || token=="out");
235 }
236
237 bool ProgramParser::is_sampling_qualifier(const string &token)
238 {
239         return token=="centroid";
240 }
241
242 bool ProgramParser::is_qualifier(const string &token)
243 {
244         return (token=="const" || is_interface_qualifier(token) || is_sampling_qualifier(token));
245 }
246
247 bool ProgramParser::is_builtin_type(const string &token)
248 {
249         static Regex re("^(void|float|int|bool|[ib]?vec[234]|mat[234](x[234])?|sampler((1D|2D)(Array)?(Shadow)?|Cube(Shadow)?|3D))$");
250         return re.match(token);
251 }
252
253 bool ProgramParser::is_type(const string &token)
254 {
255         return is_builtin_type(token) || cur_module->structs.count(token);
256 }
257
258 Node *ProgramParser::parse_global_declaration()
259 {
260         string token = peek_token();
261         if(token=="layout")
262                 return parse_layout();
263         else if(token=="struct")
264                 return parse_struct_declaration();
265         else if(is_sampling_qualifier(token) || token=="const")
266                 return parse_variable_declaration();
267         else if(is_interface_qualifier(token))
268         {
269                 if(is_type(peek_token(1)))
270                         return parse_variable_declaration();
271                 else
272                         return parse_interface_block();
273         }
274         else if(is_type(token))
275         {
276                 if(peek_token(2)=="(")
277                         return parse_function_declaration();
278                 else
279                         return parse_variable_declaration();
280         }
281         else if(token.empty())
282                 return 0;
283         else
284                 throw runtime_error(format("Syntax error at '%s': expected a global declaration", token));
285 }
286
287 Node *ProgramParser::parse_statement()
288 {
289         string token = peek_token();
290         if(token=="if")
291                 return parse_conditional();
292         else if(token=="for")
293                 return parse_iteration();
294         else if(token=="return")
295                 return parse_return();
296         else if(is_qualifier(token) || is_type(token))
297                 return parse_variable_declaration();
298         else if(!token.empty())
299         {
300                 RefPtr<ExpressionStatement> expr = new ExpressionStatement;
301                 parse_expression(expr->expression);
302                 expect(";");
303
304                 return expr.release();
305         }
306         else
307                 throw runtime_error(format("Syntax error at '%s': expected a statement", token));
308 }
309
310 Layout *ProgramParser::parse_layout()
311 {
312         expect("layout");
313         expect("(");
314         RefPtr<Layout> layout = new Layout;
315         while(1)
316         {
317                 string token = parse_token();
318                 if(token==")")
319                         throw runtime_error(format("Parse error at '%s': expected layout qualifier id", token));
320
321                 layout->qualifiers.push_back(Layout::Qualifier());
322                 Layout::Qualifier &qual = layout->qualifiers.back();
323                 qual.identifier = token;
324
325                 if(check("="))
326                         qual.value = parse_token();
327
328                 if(peek_token()==")")
329                         break;
330
331                 expect(",");
332         }
333         expect(")");
334         layout->interface = parse_token();
335         expect(";");
336
337         return layout.release();
338 }
339
340 void ProgramParser::parse_block(Block &block, bool require_braces)
341 {
342         bool have_braces = (require_braces || peek_token()=="{");
343         if(have_braces)
344                 expect("{");
345
346         while(1)
347         {
348                 string token = peek_token();
349                 if(token=="}")
350                         break;
351
352                 block.body.push_back(parse_statement());
353                 if(!have_braces)
354                         break;
355         }
356
357         block.use_braces = (require_braces || block.body.size()!=1);
358
359         if(have_braces)
360                 expect("}");
361 }
362
363 void ProgramParser::parse_expression(Expression &expr)
364 {
365         unsigned nesting_level = 0;
366         while(iter!=source.end())
367         {
368                 string token = peek_token();
369                 if(token=="(" || token=="[")
370                         ++nesting_level;
371                 else if(token==")" || token=="]")
372                 {
373                         if(!nesting_level)
374                                 break;
375                         --nesting_level;
376                 }
377                 else if(token==";")
378                         break;
379
380                 parse_token();
381                 expr.tokens.push_back(token);
382         }
383 }
384
385 StructDeclaration *ProgramParser::parse_struct_declaration()
386 {
387         expect("struct");
388         RefPtr<StructDeclaration> strct = new StructDeclaration;
389
390         strct->name = expect_identifier();
391         parse_block(strct->members, true);
392         expect(";");
393
394         cur_module->structs[strct->name] = strct.get();
395         return strct.release();
396 }
397
398 VariableDeclaration *ProgramParser::parse_variable_declaration()
399 {
400         RefPtr<VariableDeclaration> var = new VariableDeclaration;
401
402         string token = peek_token();
403         if(is_sampling_qualifier(token))
404         {
405                 var->sampling = parse_token();
406                 token = peek_token();
407                 if(!is_interface_qualifier(token))
408                         throw runtime_error(format("Parse error at '%s': expected an interface qualifier", token));
409         }
410
411         if(is_interface_qualifier(token))
412                 var->interface = parse_token();
413         else if(token=="const")
414         {
415                 var->constant = true;
416                 parse_token();
417         }
418
419         var->type = expect_type();
420         var->name = expect_identifier();
421
422         if(check("["))
423         {
424                 var->array = true;
425                 if(!check("]"))
426                 {
427                         parse_expression(var->array_size);
428                         expect("]");
429                 }
430         }
431
432         if(check("="))
433                 parse_expression(var->init_expression);
434
435         expect(";");
436         return var.release();
437 }
438
439 FunctionDeclaration *ProgramParser::parse_function_declaration()
440 {
441         RefPtr<FunctionDeclaration> func = new FunctionDeclaration;
442
443         func->return_type = expect_type();
444         func->name = expect_identifier();
445         parse_function_parameter_list(*func);
446
447         string token = peek_token();
448         if(token=="{")
449         {
450                 func->definition = true;
451                 parse_block(func->body, true);
452         }
453         else if(token==";")
454                 parse_token();
455         else
456                 throw runtime_error(format("Parse error at '%s': expected '{' or ';'", token));
457
458         return func.release();
459 }
460
461 void ProgramParser::parse_function_parameter_list(FunctionDeclaration &func)
462 {
463         expect("(");
464         while(1)
465         {
466                 string token = peek_token();
467                 if(token==")")
468                         break;
469                 else if(!func.parameters.empty())
470                         expect(",");
471
472                 RefPtr<VariableDeclaration> var = new VariableDeclaration;
473                 var->type = expect_type();
474                 var->name = expect_identifier();
475                 func.parameters.push_back(var.release());
476         }
477         expect(")");
478 }
479
480 InterfaceBlock *ProgramParser::parse_interface_block()
481 {
482         RefPtr<InterfaceBlock> iface = new InterfaceBlock;
483
484         iface->interface = parse_token();
485         if(!is_interface_qualifier(iface->interface))
486                 throw runtime_error(format("Parse error at '%s': expected an interface qualifier", iface->interface));
487
488         iface->name = expect_identifier();
489         parse_block(iface->members, true);
490         expect(";");
491
492         return iface.release();
493 }
494
495 Conditional *ProgramParser::parse_conditional()
496 {
497         expect("if");
498         expect("(");
499         RefPtr<Conditional> cond = new Conditional;
500         parse_expression(cond->condition);
501         expect(")");
502
503         parse_block(cond->body, false);
504
505         string token = peek_token();
506         if(token=="else")
507         {
508                 parse_token();
509                 parse_block(cond->else_body, false);
510         }
511
512         return cond.release();
513 }
514
515 Iteration *ProgramParser::parse_iteration()
516 {
517         expect("for");
518         expect("(");
519         RefPtr<Iteration> loop = new Iteration;
520         string token = peek_token();
521         if(is_type(token))
522                 loop->init_statement = parse_statement();
523         else
524         {
525                 RefPtr<ExpressionStatement> expr = new ExpressionStatement;
526                 parse_expression(expr->expression);
527                 expect(";");
528                 loop->init_statement = expr.release();
529         }
530         parse_expression(loop->condition);
531         expect(";");
532         parse_expression(loop->loop_expression);
533         expect(")");
534
535         parse_block(loop->body, false);
536
537         return loop.release();
538 }
539
540 Return *ProgramParser::parse_return()
541 {
542         expect("return");
543         RefPtr<Return> ret = new Return;
544         parse_expression(ret->expression);
545         expect(";");
546         return ret.release();
547 }
548
549 } // namespace GL
550 } // namespace Msp