]> git.tdb.fi Git - libs/gl.git/blob - source/glsl/tokenizer.cpp
595a97abc71e28402fef288be5f7c9636bc87e28
[libs/gl.git] / source / glsl / tokenizer.cpp
1 #include <msp/core/raii.h>
2 #include "glsl_error.h"
3 #include "preprocessor.h"
4 #include "syntax.h"
5 #include "tokenizer.h"
6
7 using namespace std;
8
9 namespace Msp {
10 namespace GL {
11 namespace SL {
12
13 Tokenizer::Tokenizer():
14         allow_preprocess(true),
15         suppress_line_advance(false)
16 {
17         static string empty;
18         iter = empty.begin();
19         source_end = empty.end();
20 }
21
22 void Tokenizer::begin(const string &src, const string &name)
23 {
24         iter = src.begin();
25         source_end = src.end();
26         location.name = name;
27         location.line = 1;
28         allow_preprocess = true;
29         last_token.clear();
30         next_tokens.clear();
31 }
32
33 const string &Tokenizer::peek_token(unsigned index)
34 {
35         while(next_tokens.size()<=index)
36                 next_tokens.push_back(parse_token_());
37         return next_tokens[index];
38 }
39
40 const string &Tokenizer::parse_token()
41 {
42         progress_mark = true;
43
44         if(!next_tokens.empty())
45         {
46                 last_token = next_tokens.front();
47                 next_tokens.pop_front();
48                 return last_token;
49         }
50
51         return (last_token = parse_token_());
52 }
53
54 void Tokenizer::expect(const string &token)
55 {
56         string parsed = parse_token();
57         if(parsed!=token)
58                 throw parse_error(location, parsed, format("'%s'", token));
59 }
60
61 void Tokenizer::set_location(const Location &loc)
62 {
63         location = loc;
64         suppress_line_advance = true;
65 }
66
67 string Tokenizer::parse_token_()
68 {
69         while(1)
70         {
71                 skip_comment_and_whitespace();
72                 bool allow_preproc = allow_preprocess;
73                 allow_preprocess = false;
74                 if(iter==source_end)
75                         return string();
76                 else if(allow_preproc && *iter=='#')
77                 {
78                         ++iter;
79                         preprocess();
80                 }
81                 else if(isalpha(*iter) || *iter=='_')
82                         return parse_identifier();
83                 else if(isdigit(*iter))
84                         return parse_number();
85                 else if(*iter=='"')
86                         return parse_string();
87                 else if(*iter=='#' || *iter=='$' || *iter=='\'' || *iter=='@' || *iter=='\\' || *iter=='`')
88                         throw syntax_error(location, string(1, *iter), "Invalid character in source");
89                 else
90                         return parse_other();
91         }
92 }
93
94 void Tokenizer::preprocess()
95 {
96         SetForScope<deque<string> > clear_tokens(next_tokens, deque<string>());
97
98         auto line_end = iter;
99         for(; (line_end!=source_end && *line_end!='\n'); ++line_end) ;
100         SetForScope<string::const_iterator> stop_at_line_end(source_end, line_end);
101
102         signal_preprocess.emit();
103
104         iter = line_end;
105 }
106
107 string Tokenizer::parse_identifier()
108 {
109         string ident;
110         while(iter!=source_end)
111         {
112                 if(isalnum(*iter) || *iter=='_')
113                         ident += *iter++;
114                 else
115                         break;
116         }
117
118         return ident;
119 }
120
121 string Tokenizer::parse_number()
122 {
123         bool got_fract = false;
124         string number;
125         while(iter!=source_end)
126         {
127                 if(isdigit(*iter))
128                         number += *iter++;
129                 else if(!got_fract && *iter=='.')
130                 {
131                         number += *iter++;
132                         got_fract = true;
133                 }
134                 else
135                         break;
136         }
137
138         bool require_digit = false;
139         if(iter==source_end)
140                 ;
141         else if(*iter=='e' || *iter=='E')
142         {
143                 number += *iter++;
144                 if(iter!=source_end && (*iter=='-' || *iter=='+'))
145                         number += *iter++;
146                 require_digit = true;
147                 while(iter!=source_end)
148                 {
149                         if(isdigit(*iter))
150                         {
151                                 number += *iter++;
152                                 require_digit = false;
153                         }
154                         else
155                                 break;
156                 }
157                 if(!require_digit && iter!=source_end && *iter=='f')
158                         number += *iter++;
159         }
160         else if(*iter=='u' || *iter=='f')
161                 number += *iter++;
162
163         if(require_digit)
164                 throw syntax_error(location, number, "Incomplete numeric literal");
165         else if(iter!=source_end && (isalnum(*iter) || *iter=='_'))
166                 throw syntax_error(location, number, "Garbage at end of numeric literal");
167
168         return number;
169 }
170
171 string Tokenizer::parse_string()
172 {
173         string str(1, *iter++);
174         bool escape = false;
175         while(iter!=source_end)
176         {
177                 char c = *iter++;
178                 str += c;
179                 if(c=='\\')
180                         escape = true;
181                 else if(c=='"' && !escape)
182                         break;
183                 else
184                         escape = false;
185         }
186
187         return str;
188 }
189
190 string Tokenizer::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=Operator::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 void Tokenizer::skip_comment_and_whitespace()
216 {
217         unsigned comment = 0;
218         while(iter!=source_end)
219         {
220                 if(comment==0)
221                 {
222                         if(*iter=='/')
223                                 comment = 1;
224                         else if(!isspace(*iter))
225                                 break;
226                 }
227                 else if(comment==1)
228                 {
229                         if(*iter=='/')
230                                 comment = 2;
231                         else if(*iter=='*')
232                                 comment = 3;
233                         else
234                         {
235                                 comment = 0;
236                                 --iter;
237                                 break;
238                         }
239                 }
240                 else if(comment==2)
241                 {
242                         if(*iter=='\n')
243                                 comment = 0;
244                 }
245                 else if(comment==3 && *iter=='*')
246                         comment = 4;
247                 else if(comment==4)
248                 {
249                         if(*iter=='/')
250                                 comment = 0;
251                         else if(*iter!='*')
252                                 comment = 3;
253                 }
254
255                 if(*iter=='\n')
256                 {
257                         if(!suppress_line_advance)
258                                 ++location.line;
259                         allow_preprocess = (comment<3);
260                 }
261
262                 ++iter;
263         }
264
265         suppress_line_advance = false;
266 }
267
268 } // namespace SL
269 } // namespace GL
270 } // namespace Msp