Made Mr_Tree graphic smaller, from 99 to 85 pixels.
[supertux.git] / src / squirrel / squirrel / sqlexer.cpp
1 /*\r
2         see copyright notice in squirrel.h\r
3 */\r
4 #include "sqpcheader.h"\r
5 #include <ctype.h>\r
6 #include <stdlib.h>\r
7 #include "sqtable.h"\r
8 #include "sqstring.h"\r
9 #include "sqcompiler.h"\r
10 #include "sqlexer.h"\r
11 \r
12 #define CUR_CHAR (_currdata)\r
13 #define RETURN_TOKEN(t) { _prevtoken = _curtoken; _curtoken = t; return t;}\r
14 #define IS_EOB() (CUR_CHAR <= SQUIRREL_EOB)\r
15 #define NEXT() {Next();_currentcolumn++;}\r
16 #define INIT_TEMP_STRING() { _longstr.resize(0);}\r
17 #define APPEND_CHAR(c) { _longstr.push_back(c);}\r
18 #define TERMINATE_BUFFER() {_longstr.push_back(_SC('\0'));}\r
19 #define ADD_KEYWORD(key,id) _keywords->NewSlot( SQString::Create(ss, _SC(#key)) ,SQInteger(id))\r
20 \r
21 SQLexer::SQLexer(){}\r
22 SQLexer::~SQLexer()\r
23 {\r
24         _keywords->Release();\r
25 }\r
26 \r
27 void SQLexer::Init(SQSharedState *ss, SQLEXREADFUNC rg, SQUserPointer up,CompilerErrorFunc efunc,void *ed)\r
28 {\r
29         _errfunc = efunc;\r
30         _errtarget = ed;\r
31         _sharedstate = ss;\r
32         _keywords = SQTable::Create(ss, 26);\r
33         ADD_KEYWORD(while, TK_WHILE);\r
34         ADD_KEYWORD(do, TK_DO);\r
35         ADD_KEYWORD(if, TK_IF);\r
36         ADD_KEYWORD(else, TK_ELSE);\r
37         ADD_KEYWORD(break, TK_BREAK);\r
38         ADD_KEYWORD(continue, TK_CONTINUE);\r
39         ADD_KEYWORD(return, TK_RETURN);\r
40         ADD_KEYWORD(null, TK_NULL);\r
41         ADD_KEYWORD(function, TK_FUNCTION);\r
42         ADD_KEYWORD(local, TK_LOCAL);\r
43         ADD_KEYWORD(for, TK_FOR);\r
44         ADD_KEYWORD(foreach, TK_FOREACH);\r
45         ADD_KEYWORD(in, TK_IN);\r
46         ADD_KEYWORD(typeof, TK_TYPEOF);\r
47         ADD_KEYWORD(delegate, TK_DELEGATE);\r
48         ADD_KEYWORD(delete, TK_DELETE);\r
49         ADD_KEYWORD(try, TK_TRY);\r
50         ADD_KEYWORD(catch, TK_CATCH);\r
51         ADD_KEYWORD(throw, TK_THROW);\r
52         ADD_KEYWORD(clone, TK_CLONE);\r
53         ADD_KEYWORD(yield, TK_YIELD);\r
54         ADD_KEYWORD(resume, TK_RESUME);\r
55         ADD_KEYWORD(switch, TK_SWITCH);\r
56         ADD_KEYWORD(case, TK_CASE);\r
57         ADD_KEYWORD(default, TK_DEFAULT);\r
58         ADD_KEYWORD(this, TK_THIS);\r
59         ADD_KEYWORD(parent,TK_PARENT);\r
60         ADD_KEYWORD(class,TK_CLASS);\r
61         ADD_KEYWORD(extends,TK_EXTENDS);\r
62         ADD_KEYWORD(constructor,TK_CONSTRUCTOR);\r
63         ADD_KEYWORD(instanceof,TK_INSTANCEOF);\r
64         ADD_KEYWORD(vargc,TK_VARGC);\r
65         ADD_KEYWORD(vargv,TK_VARGV);\r
66         ADD_KEYWORD(true,TK_TRUE);\r
67         ADD_KEYWORD(false,TK_FALSE);\r
68 \r
69         _readf = rg;\r
70         _up = up;\r
71         _lasttokenline = _currentline = 1;\r
72         _currentcolumn = 0;\r
73         _prevtoken = -1;\r
74         Next();\r
75 }\r
76 \r
77 void SQLexer::Error(const SQChar *err)\r
78 {\r
79         _errfunc(_errtarget,err);\r
80 }\r
81 \r
82 void SQLexer::Next()\r
83 {\r
84         SQInteger t = _readf(_up);\r
85         if(t > MAX_CHAR) Error(_SC("Invalid character"));\r
86         if(t != 0) {\r
87                 _currdata = t;\r
88                 return;\r
89         }\r
90         _currdata = SQUIRREL_EOB;\r
91 }\r
92 \r
93 const SQChar *SQLexer::Tok2Str(SQInteger tok)\r
94 {\r
95         SQObjectPtr itr, key, val;\r
96         SQInteger nitr;\r
97         while((nitr = _keywords->Next(false,itr, key, val)) != -1) {\r
98                 itr = (SQInteger)nitr;\r
99                 if(((SQInteger)_integer(val)) == tok)\r
100                         return _stringval(key);\r
101         }\r
102         return NULL;\r
103 }\r
104 \r
105 void SQLexer::LexBlockComment()\r
106 {\r
107         bool done = false;\r
108         while(!done) {\r
109                 switch(CUR_CHAR) {\r
110                         case _SC('*'): { NEXT(); if(CUR_CHAR == _SC('/')) { done = true; NEXT(); }}; continue;\r
111                         case _SC('\n'): _currentline++; NEXT(); continue;\r
112                         case SQUIRREL_EOB: Error(_SC("missing \"*/\" in comment"));\r
113                         default: NEXT();\r
114                 }\r
115         }\r
116 }\r
117 \r
118 SQInteger SQLexer::Lex()\r
119 {\r
120         _lasttokenline = _currentline;\r
121         while(CUR_CHAR != SQUIRREL_EOB) {\r
122                 switch(CUR_CHAR){\r
123                 case _SC('\t'): case _SC('\r'): case _SC(' '): NEXT(); continue;\r
124                 case _SC('\n'):\r
125                         _currentline++;\r
126                         _prevtoken=_curtoken;\r
127                         _curtoken=_SC('\n');\r
128                         NEXT();\r
129                         _currentcolumn=1;\r
130                         continue;\r
131                 case _SC('/'):\r
132                         NEXT();\r
133                         switch(CUR_CHAR){\r
134                         case _SC('*'):\r
135                                 NEXT();\r
136                                 LexBlockComment();\r
137                                 continue;       \r
138                         case _SC('/'):\r
139                                 do { NEXT(); } while (CUR_CHAR != _SC('\n') && (!IS_EOB()));\r
140                                 continue;\r
141                         case _SC('='):\r
142                                 NEXT();\r
143                                 RETURN_TOKEN(TK_DIVEQ);\r
144                                 continue;\r
145                         case _SC('>'):\r
146                                 NEXT();\r
147                                 RETURN_TOKEN(TK_ATTR_CLOSE);\r
148                                 continue;\r
149                         default:\r
150                                 RETURN_TOKEN('/');\r
151                         }\r
152                 case _SC('='):\r
153                         NEXT();\r
154                         if (CUR_CHAR != _SC('=')){ RETURN_TOKEN('=') }\r
155                         else { NEXT(); RETURN_TOKEN(TK_EQ); }\r
156                 case _SC('<'):\r
157                         NEXT();\r
158                         if ( CUR_CHAR == _SC('=') ) { NEXT(); RETURN_TOKEN(TK_LE) }\r
159                         else if ( CUR_CHAR == _SC('-') ) { NEXT(); RETURN_TOKEN(TK_NEWSLOT); }\r
160                         else if ( CUR_CHAR == _SC('<') ) { NEXT(); RETURN_TOKEN(TK_SHIFTL); }\r
161                         else if ( CUR_CHAR == _SC('/') ) { NEXT(); RETURN_TOKEN(TK_ATTR_OPEN); }\r
162                         //else if ( CUR_CHAR == _SC('[') ) { NEXT(); ReadMultilineString(); RETURN_TOKEN(TK_STRING_LITERAL); }\r
163                         else { RETURN_TOKEN('<') }\r
164                 case _SC('>'):\r
165                         NEXT();\r
166                         if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_GE);}\r
167                         else if(CUR_CHAR == _SC('>')){ \r
168                                 NEXT(); \r
169                                 if(CUR_CHAR == _SC('>')){\r
170                                         NEXT();\r
171                                         RETURN_TOKEN(TK_USHIFTR);\r
172                                 }\r
173                                 RETURN_TOKEN(TK_SHIFTR);\r
174                         }\r
175                         else { RETURN_TOKEN('>') }\r
176                 case _SC('!'):\r
177                         NEXT();\r
178                         if (CUR_CHAR != _SC('=')){ RETURN_TOKEN('!')}\r
179                         else { NEXT(); RETURN_TOKEN(TK_NE); }\r
180                 case _SC('@'): {\r
181                         SQInteger stype;\r
182                         NEXT(); \r
183                         if(CUR_CHAR != _SC('"'))\r
184                                 Error(_SC("string expected"));\r
185                         if((stype=ReadString('"',true))!=-1) {\r
186                                 RETURN_TOKEN(stype);\r
187                         }\r
188                         Error(_SC("error parsing the string"));\r
189                                            }\r
190                 case _SC('"'):\r
191                 case _SC('\''): {\r
192                         SQInteger stype;\r
193                         if((stype=ReadString(CUR_CHAR,false))!=-1){\r
194                                 RETURN_TOKEN(stype);\r
195                         }\r
196                         Error(_SC("error parsing the string"));\r
197                         }\r
198                 case _SC('{'): case _SC('}'): case _SC('('): case _SC(')'): case _SC('['): case _SC(']'):\r
199                 case _SC(';'): case _SC(','): case _SC('?'): case _SC('^'): case _SC('~'):\r
200                         {SQInteger ret = CUR_CHAR;\r
201                         NEXT(); RETURN_TOKEN(ret); }\r
202                 case _SC('.'):\r
203                         NEXT();\r
204                         if (CUR_CHAR != _SC('.')){ RETURN_TOKEN('.') }\r
205                         NEXT();\r
206                         if (CUR_CHAR != _SC('.')){ Error(_SC("invalid token '..'")); }\r
207                         NEXT();\r
208                         RETURN_TOKEN(TK_VARPARAMS);\r
209                 case _SC('&'):\r
210                         NEXT();\r
211                         if (CUR_CHAR != _SC('&')){ RETURN_TOKEN('&') }\r
212                         else { NEXT(); RETURN_TOKEN(TK_AND); }\r
213                 case _SC('|'):\r
214                         NEXT();\r
215                         if (CUR_CHAR != _SC('|')){ RETURN_TOKEN('|') }\r
216                         else { NEXT(); RETURN_TOKEN(TK_OR); }\r
217                 case _SC(':'):\r
218                         NEXT();\r
219                         if (CUR_CHAR != _SC(':')){ RETURN_TOKEN(':') }\r
220                         else { NEXT(); RETURN_TOKEN(TK_DOUBLE_COLON); }\r
221                 case _SC('*'):\r
222                         NEXT();\r
223                         if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MULEQ);}\r
224                         else RETURN_TOKEN('*');\r
225                 case _SC('%'):\r
226                         NEXT();\r
227                         if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MODEQ);}\r
228                         else RETURN_TOKEN('%');\r
229                 case _SC('-'):\r
230                         NEXT();\r
231                         if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MINUSEQ);}\r
232                         else if  (CUR_CHAR == _SC('-')){ NEXT(); RETURN_TOKEN(TK_MINUSMINUS);}\r
233                         else RETURN_TOKEN('-');\r
234                 case _SC('+'):\r
235                         NEXT();\r
236                         if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_PLUSEQ);}\r
237                         else if (CUR_CHAR == _SC('+')){ NEXT(); RETURN_TOKEN(TK_PLUSPLUS);}\r
238                         else RETURN_TOKEN('+');\r
239                 case SQUIRREL_EOB:\r
240                         return 0;\r
241                 default:{\r
242                                 if (scisdigit(CUR_CHAR)) {\r
243                                         SQInteger ret = ReadNumber();\r
244                                         RETURN_TOKEN(ret);\r
245                                 }\r
246                                 else if (scisalpha(CUR_CHAR) || CUR_CHAR == _SC('_')) {\r
247                                         SQInteger t = ReadID();\r
248                                         RETURN_TOKEN(t);\r
249                                 }\r
250                                 else {\r
251                                         SQInteger c = CUR_CHAR;\r
252                                         if (sciscntrl(c)) Error(_SC("unexpected character(control)"));\r
253                                         NEXT();\r
254                                         RETURN_TOKEN(c);  \r
255                                 }\r
256                                 RETURN_TOKEN(0);\r
257                         }\r
258                 }\r
259         }\r
260         return 0;    \r
261 }\r
262         \r
263 SQInteger SQLexer::GetIDType(SQChar *s)\r
264 {\r
265         SQObjectPtr t;\r
266         if(_keywords->Get(SQString::Create(_sharedstate, s), t)) {\r
267                 return SQInteger(_integer(t));\r
268         }\r
269         return TK_IDENTIFIER;\r
270 }\r
271 \r
272 \r
273 SQInteger SQLexer::ReadString(SQInteger ndelim,bool verbatim)\r
274 {\r
275         INIT_TEMP_STRING();\r
276         NEXT();\r
277         if(IS_EOB()) return -1;\r
278         for(;;) {\r
279                 while(CUR_CHAR != ndelim) {\r
280                         switch(CUR_CHAR) {\r
281                         case SQUIRREL_EOB:\r
282                                 Error(_SC("unfinished string"));\r
283                                 return -1;\r
284                         case _SC('\n'): \r
285                                 if(!verbatim) Error(_SC("newline in a constant")); \r
286                                 APPEND_CHAR(CUR_CHAR); NEXT(); \r
287                                 _currentline++;\r
288                                 break;\r
289                         case _SC('\\'):\r
290                                 if(verbatim) {\r
291                                         APPEND_CHAR('\\'); NEXT(); \r
292                                 }\r
293                                 else {\r
294                                         NEXT();\r
295                                         switch(CUR_CHAR) {\r
296                                         case _SC('x'): NEXT(); {\r
297                                                 if(!isxdigit(CUR_CHAR)) Error(_SC("hexadecimal number expected")); \r
298                                                 const SQInteger maxdigits = 4;\r
299                                                 SQChar temp[maxdigits+1];\r
300                                                 SQInteger n = 0;\r
301                                                 while(isxdigit(CUR_CHAR) && n < maxdigits) {\r
302                                                         temp[n] = CUR_CHAR;\r
303                                                         n++;\r
304                                                         NEXT();\r
305                                                 }\r
306                                                 temp[n] = 0;\r
307                                                 SQChar *sTemp;\r
308                                                 APPEND_CHAR((SQChar)scstrtoul(temp,&sTemp,16));\r
309                                         }\r
310                                     break;\r
311                                         case _SC('t'): APPEND_CHAR(_SC('\t')); NEXT(); break;\r
312                                         case _SC('a'): APPEND_CHAR(_SC('\a')); NEXT(); break;\r
313                                         case _SC('b'): APPEND_CHAR(_SC('\b')); NEXT(); break;\r
314                                         case _SC('n'): APPEND_CHAR(_SC('\n')); NEXT(); break;\r
315                                         case _SC('r'): APPEND_CHAR(_SC('\r')); NEXT(); break;\r
316                                         case _SC('v'): APPEND_CHAR(_SC('\v')); NEXT(); break;\r
317                                         case _SC('f'): APPEND_CHAR(_SC('\f')); NEXT(); break;\r
318                                         case _SC('0'): APPEND_CHAR(_SC('\0')); NEXT(); break;\r
319                                         case _SC('\\'): APPEND_CHAR(_SC('\\')); NEXT(); break;\r
320                                         case _SC('"'): APPEND_CHAR(_SC('"')); NEXT(); break;\r
321                                         case _SC('\''): APPEND_CHAR(_SC('\'')); NEXT(); break;\r
322                                         default:\r
323                                                 Error(_SC("unrecognised escaper char"));\r
324                                         break;\r
325                                         }\r
326                                 }\r
327                                 break;\r
328                         default:\r
329                                 APPEND_CHAR(CUR_CHAR);\r
330                                 NEXT();\r
331                         }\r
332                 }\r
333                 NEXT();\r
334                 if(verbatim && CUR_CHAR == '"') { //double quotation\r
335                         APPEND_CHAR(CUR_CHAR);\r
336                         NEXT();\r
337                 }\r
338                 else {\r
339                         break;\r
340                 }\r
341         }\r
342         TERMINATE_BUFFER();\r
343         SQInteger len = _longstr.size()-1;\r
344         if(ndelim == _SC('\'')) {\r
345                 if(len == 0) Error(_SC("empty constant"));\r
346                 if(len > 1) Error(_SC("constant too long"));\r
347                 _nvalue = _longstr[0];\r
348                 return TK_INTEGER;\r
349         }\r
350         _svalue = &_longstr[0];\r
351         return TK_STRING_LITERAL;\r
352 }\r
353 \r
354 SQInteger isexponent(SQInteger c) { return c == 'e' || c=='E'; }\r
355 \r
356 SQInteger SQLexer::ReadNumber()\r
357 {\r
358 #define TINT 1\r
359 #define TFLOAT 2\r
360 #define THEX 3\r
361 #define TSCIENTIFIC 4\r
362         SQInteger type = TINT, firstchar = CUR_CHAR;\r
363         bool isfloat = false;\r
364         SQChar *sTemp;\r
365         INIT_TEMP_STRING();\r
366         NEXT();\r
367         if(firstchar == _SC('0') && toupper(CUR_CHAR) == _SC('X')) {\r
368                 NEXT();\r
369                 type = THEX;\r
370                 while(isxdigit(CUR_CHAR)) {\r
371                         APPEND_CHAR(CUR_CHAR);\r
372                         NEXT();\r
373                 }\r
374                 if(_longstr.size() > 8) Error(_SC("Hex number over 8 digits"));\r
375         }\r
376         else {\r
377                 APPEND_CHAR(firstchar);\r
378                 while (CUR_CHAR == _SC('.') || scisdigit(CUR_CHAR) || isexponent(CUR_CHAR)) {\r
379             if(CUR_CHAR == _SC('.')) type = TFLOAT;\r
380                         if(isexponent(CUR_CHAR)) {\r
381                                 if(type != TFLOAT) Error(_SC("invalid numeric format"));\r
382                                 type = TSCIENTIFIC;\r
383                                 APPEND_CHAR(CUR_CHAR);\r
384                                 NEXT();\r
385                                 if(CUR_CHAR == '+' || CUR_CHAR == '-'){\r
386                                         APPEND_CHAR(CUR_CHAR);\r
387                                         NEXT();\r
388                                 }\r
389                                 if(!scisdigit(CUR_CHAR)) Error(_SC("exponent expected"));\r
390                         }\r
391                         \r
392                         APPEND_CHAR(CUR_CHAR);\r
393                         NEXT();\r
394                 }\r
395         }\r
396         TERMINATE_BUFFER();\r
397         switch(type) {\r
398         case TSCIENTIFIC:\r
399         case TFLOAT:\r
400                 _fvalue = (SQFloat)scstrtod(&_longstr[0],&sTemp);\r
401                 return TK_FLOAT;\r
402         case TINT:\r
403                 _nvalue = (SQInteger)scstrtol(&_longstr[0],&sTemp,10);\r
404                 return TK_INTEGER;\r
405         case THEX:\r
406                 *((SQUnsignedInteger *)&_nvalue) = scstrtoul(&_longstr[0],&sTemp,16);\r
407                 return TK_INTEGER;\r
408         }\r
409         return 0;\r
410 }\r
411 \r
412 SQInteger SQLexer::ReadID()\r
413 {\r
414         SQInteger res, size = 0;\r
415         INIT_TEMP_STRING();\r
416         do {\r
417                 APPEND_CHAR(CUR_CHAR);\r
418                 NEXT();\r
419         } while(scisalnum(CUR_CHAR) || CUR_CHAR == _SC('_'));\r
420         TERMINATE_BUFFER();\r
421         res = GetIDType(&_longstr[0]);\r
422         if(res == TK_IDENTIFIER) {\r
423                 _svalue = &_longstr[0];\r
424         }\r
425         return res;\r
426 }\r