03268258df718681454405d84e02b66751dd1d7f
[supertux.git] / external / 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         ADD_KEYWORD(static,TK_STATIC);\r
69         ADD_KEYWORD(enum,TK_ENUM);\r
70         ADD_KEYWORD(const,TK_CONST);\r
71 \r
72         _readf = rg;\r
73         _up = up;\r
74         _lasttokenline = _currentline = 1;\r
75         _currentcolumn = 0;\r
76         _prevtoken = -1;\r
77         Next();\r
78 }\r
79 \r
80 void SQLexer::Error(const SQChar *err)\r
81 {\r
82         _errfunc(_errtarget,err);\r
83 }\r
84 \r
85 void SQLexer::Next()\r
86 {\r
87         SQInteger t = _readf(_up);\r
88         if(t > MAX_CHAR) Error(_SC("Invalid character"));\r
89         if(t != 0) {\r
90                 _currdata = (LexChar)t;\r
91                 return;\r
92         }\r
93         _currdata = SQUIRREL_EOB;\r
94 }\r
95 \r
96 const SQChar *SQLexer::Tok2Str(SQInteger tok)\r
97 {\r
98         SQObjectPtr itr, key, val;\r
99         SQInteger nitr;\r
100         while((nitr = _keywords->Next(false,itr, key, val)) != -1) {\r
101                 itr = (SQInteger)nitr;\r
102                 if(((SQInteger)_integer(val)) == tok)\r
103                         return _stringval(key);\r
104         }\r
105         return NULL;\r
106 }\r
107 \r
108 void SQLexer::LexBlockComment()\r
109 {\r
110         bool done = false;\r
111         while(!done) {\r
112                 switch(CUR_CHAR) {\r
113                         case _SC('*'): { NEXT(); if(CUR_CHAR == _SC('/')) { done = true; NEXT(); }}; continue;\r
114                         case _SC('\n'): _currentline++; NEXT(); continue;\r
115                         case SQUIRREL_EOB: Error(_SC("missing \"*/\" in comment"));\r
116                         default: NEXT();\r
117                 }\r
118         }\r
119 }\r
120 \r
121 SQInteger SQLexer::Lex()\r
122 {\r
123         _lasttokenline = _currentline;\r
124         while(CUR_CHAR != SQUIRREL_EOB) {\r
125                 switch(CUR_CHAR){\r
126                 case _SC('\t'): case _SC('\r'): case _SC(' '): NEXT(); continue;\r
127                 case _SC('\n'):\r
128                         _currentline++;\r
129                         _prevtoken=_curtoken;\r
130                         _curtoken=_SC('\n');\r
131                         NEXT();\r
132                         _currentcolumn=1;\r
133                         continue;\r
134                 case _SC('/'):\r
135                         NEXT();\r
136                         switch(CUR_CHAR){\r
137                         case _SC('*'):\r
138                                 NEXT();\r
139                                 LexBlockComment();\r
140                                 continue;       \r
141                         case _SC('/'):\r
142                                 do { NEXT(); } while (CUR_CHAR != _SC('\n') && (!IS_EOB()));\r
143                                 continue;\r
144                         case _SC('='):\r
145                                 NEXT();\r
146                                 RETURN_TOKEN(TK_DIVEQ);\r
147                                 continue;\r
148                         case _SC('>'):\r
149                                 NEXT();\r
150                                 RETURN_TOKEN(TK_ATTR_CLOSE);\r
151                                 continue;\r
152                         default:\r
153                                 RETURN_TOKEN('/');\r
154                         }\r
155                 case _SC('='):\r
156                         NEXT();\r
157                         if (CUR_CHAR != _SC('=')){ RETURN_TOKEN('=') }\r
158                         else { NEXT(); RETURN_TOKEN(TK_EQ); }\r
159                 case _SC('<'):\r
160                         NEXT();\r
161                         if ( CUR_CHAR == _SC('=') ) { NEXT(); RETURN_TOKEN(TK_LE) }\r
162                         else if ( CUR_CHAR == _SC('-') ) { NEXT(); RETURN_TOKEN(TK_NEWSLOT); }\r
163                         else if ( CUR_CHAR == _SC('<') ) { NEXT(); RETURN_TOKEN(TK_SHIFTL); }\r
164                         else if ( CUR_CHAR == _SC('/') ) { NEXT(); RETURN_TOKEN(TK_ATTR_OPEN); }\r
165                         //else if ( CUR_CHAR == _SC('[') ) { NEXT(); ReadMultilineString(); RETURN_TOKEN(TK_STRING_LITERAL); }\r
166                         else { RETURN_TOKEN('<') }\r
167                 case _SC('>'):\r
168                         NEXT();\r
169                         if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_GE);}\r
170                         else if(CUR_CHAR == _SC('>')){ \r
171                                 NEXT(); \r
172                                 if(CUR_CHAR == _SC('>')){\r
173                                         NEXT();\r
174                                         RETURN_TOKEN(TK_USHIFTR);\r
175                                 }\r
176                                 RETURN_TOKEN(TK_SHIFTR);\r
177                         }\r
178                         else { RETURN_TOKEN('>') }\r
179                 case _SC('!'):\r
180                         NEXT();\r
181                         if (CUR_CHAR != _SC('=')){ RETURN_TOKEN('!')}\r
182                         else { NEXT(); RETURN_TOKEN(TK_NE); }\r
183                 case _SC('@'): {\r
184                         SQInteger stype;\r
185                         NEXT(); \r
186                         if(CUR_CHAR != _SC('"'))\r
187                                 Error(_SC("string expected"));\r
188                         if((stype=ReadString('"',true))!=-1) {\r
189                                 RETURN_TOKEN(stype);\r
190                         }\r
191                         Error(_SC("error parsing the string"));\r
192                                            }\r
193                 case _SC('"'):\r
194                 case _SC('\''): {\r
195                         SQInteger stype;\r
196                         if((stype=ReadString(CUR_CHAR,false))!=-1){\r
197                                 RETURN_TOKEN(stype);\r
198                         }\r
199                         Error(_SC("error parsing the string"));\r
200                         }\r
201                 case _SC('{'): case _SC('}'): case _SC('('): case _SC(')'): case _SC('['): case _SC(']'):\r
202                 case _SC(';'): case _SC(','): case _SC('?'): case _SC('^'): case _SC('~'):\r
203                         {SQInteger ret = CUR_CHAR;\r
204                         NEXT(); RETURN_TOKEN(ret); }\r
205                 case _SC('.'):\r
206                         NEXT();\r
207                         if (CUR_CHAR != _SC('.')){ RETURN_TOKEN('.') }\r
208                         NEXT();\r
209                         if (CUR_CHAR != _SC('.')){ Error(_SC("invalid token '..'")); }\r
210                         NEXT();\r
211                         RETURN_TOKEN(TK_VARPARAMS);\r
212                 case _SC('&'):\r
213                         NEXT();\r
214                         if (CUR_CHAR != _SC('&')){ RETURN_TOKEN('&') }\r
215                         else { NEXT(); RETURN_TOKEN(TK_AND); }\r
216                 case _SC('|'):\r
217                         NEXT();\r
218                         if (CUR_CHAR != _SC('|')){ RETURN_TOKEN('|') }\r
219                         else { NEXT(); RETURN_TOKEN(TK_OR); }\r
220                 case _SC(':'):\r
221                         NEXT();\r
222                         if (CUR_CHAR != _SC(':')){ RETURN_TOKEN(':') }\r
223                         else { NEXT(); RETURN_TOKEN(TK_DOUBLE_COLON); }\r
224                 case _SC('*'):\r
225                         NEXT();\r
226                         if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MULEQ);}\r
227                         else RETURN_TOKEN('*');\r
228                 case _SC('%'):\r
229                         NEXT();\r
230                         if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MODEQ);}\r
231                         else RETURN_TOKEN('%');\r
232                 case _SC('-'):\r
233                         NEXT();\r
234                         if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MINUSEQ);}\r
235                         else if  (CUR_CHAR == _SC('-')){ NEXT(); RETURN_TOKEN(TK_MINUSMINUS);}\r
236                         else RETURN_TOKEN('-');\r
237                 case _SC('+'):\r
238                         NEXT();\r
239                         if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_PLUSEQ);}\r
240                         else if (CUR_CHAR == _SC('+')){ NEXT(); RETURN_TOKEN(TK_PLUSPLUS);}\r
241                         else RETURN_TOKEN('+');\r
242                 case SQUIRREL_EOB:\r
243                         return 0;\r
244                 default:{\r
245                                 if (scisdigit(CUR_CHAR)) {\r
246                                         SQInteger ret = ReadNumber();\r
247                                         RETURN_TOKEN(ret);\r
248                                 }\r
249                                 else if (scisalpha(CUR_CHAR) || CUR_CHAR == _SC('_')) {\r
250                                         SQInteger t = ReadID();\r
251                                         RETURN_TOKEN(t);\r
252                                 }\r
253                                 else {\r
254                                         SQInteger c = CUR_CHAR;\r
255                                         if (sciscntrl((int)c)) Error(_SC("unexpected character(control)"));\r
256                                         NEXT();\r
257                                         RETURN_TOKEN(c);  \r
258                                 }\r
259                                 RETURN_TOKEN(0);\r
260                         }\r
261                 }\r
262         }\r
263         return 0;    \r
264 }\r
265         \r
266 SQInteger SQLexer::GetIDType(SQChar *s)\r
267 {\r
268         SQObjectPtr t;\r
269         if(_keywords->Get(SQString::Create(_sharedstate, s), t)) {\r
270                 return SQInteger(_integer(t));\r
271         }\r
272         return TK_IDENTIFIER;\r
273 }\r
274 \r
275 \r
276 SQInteger SQLexer::ReadString(SQInteger ndelim,bool verbatim)\r
277 {\r
278         INIT_TEMP_STRING();\r
279         NEXT();\r
280         if(IS_EOB()) return -1;\r
281         for(;;) {\r
282                 while(CUR_CHAR != ndelim) {\r
283                         switch(CUR_CHAR) {\r
284                         case SQUIRREL_EOB:\r
285                                 Error(_SC("unfinished string"));\r
286                                 return -1;\r
287                         case _SC('\n'): \r
288                                 if(!verbatim) Error(_SC("newline in a constant")); \r
289                                 APPEND_CHAR(CUR_CHAR); NEXT(); \r
290                                 _currentline++;\r
291                                 break;\r
292                         case _SC('\\'):\r
293                                 if(verbatim) {\r
294                                         APPEND_CHAR('\\'); NEXT(); \r
295                                 }\r
296                                 else {\r
297                                         NEXT();\r
298                                         switch(CUR_CHAR) {\r
299                                         case _SC('x'): NEXT(); {\r
300                                                 if(!isxdigit(CUR_CHAR)) Error(_SC("hexadecimal number expected")); \r
301                                                 const SQInteger maxdigits = 4;\r
302                                                 SQChar temp[maxdigits+1];\r
303                                                 SQInteger n = 0;\r
304                                                 while(isxdigit(CUR_CHAR) && n < maxdigits) {\r
305                                                         temp[n] = CUR_CHAR;\r
306                                                         n++;\r
307                                                         NEXT();\r
308                                                 }\r
309                                                 temp[n] = 0;\r
310                                                 SQChar *sTemp;\r
311                                                 APPEND_CHAR((SQChar)scstrtoul(temp,&sTemp,16));\r
312                                         }\r
313                                     break;\r
314                                         case _SC('t'): APPEND_CHAR(_SC('\t')); NEXT(); break;\r
315                                         case _SC('a'): APPEND_CHAR(_SC('\a')); NEXT(); break;\r
316                                         case _SC('b'): APPEND_CHAR(_SC('\b')); NEXT(); break;\r
317                                         case _SC('n'): APPEND_CHAR(_SC('\n')); NEXT(); break;\r
318                                         case _SC('r'): APPEND_CHAR(_SC('\r')); NEXT(); break;\r
319                                         case _SC('v'): APPEND_CHAR(_SC('\v')); NEXT(); break;\r
320                                         case _SC('f'): APPEND_CHAR(_SC('\f')); NEXT(); break;\r
321                                         case _SC('0'): APPEND_CHAR(_SC('\0')); NEXT(); break;\r
322                                         case _SC('\\'): APPEND_CHAR(_SC('\\')); NEXT(); break;\r
323                                         case _SC('"'): APPEND_CHAR(_SC('"')); NEXT(); break;\r
324                                         case _SC('\''): APPEND_CHAR(_SC('\'')); NEXT(); break;\r
325                                         default:\r
326                                                 Error(_SC("unrecognised escaper char"));\r
327                                         break;\r
328                                         }\r
329                                 }\r
330                                 break;\r
331                         default:\r
332                                 APPEND_CHAR(CUR_CHAR);\r
333                                 NEXT();\r
334                         }\r
335                 }\r
336                 NEXT();\r
337                 if(verbatim && CUR_CHAR == '"') { //double quotation\r
338                         APPEND_CHAR(CUR_CHAR);\r
339                         NEXT();\r
340                 }\r
341                 else {\r
342                         break;\r
343                 }\r
344         }\r
345         TERMINATE_BUFFER();\r
346         SQInteger len = _longstr.size()-1;\r
347         if(ndelim == _SC('\'')) {\r
348                 if(len == 0) Error(_SC("empty constant"));\r
349                 if(len > 1) Error(_SC("constant too long"));\r
350                 _nvalue = _longstr[0];\r
351                 return TK_INTEGER;\r
352         }\r
353         _svalue = &_longstr[0];\r
354         return TK_STRING_LITERAL;\r
355 }\r
356 \r
357 void LexHexadecimal(const SQChar *s,SQUnsignedInteger *res)\r
358 {\r
359         *res = 0;\r
360         while(*s != 0)\r
361         {\r
362                 if(scisdigit(*s)) *res = (*res)*16+((*s++)-'0');\r
363                 else if(scisxdigit(*s)) *res = (*res)*16+(toupper(*s++)-'A'+10);\r
364                 else { assert(0); }\r
365         }\r
366 }\r
367 \r
368 void LexInteger(const SQChar *s,SQUnsignedInteger *res)\r
369 {\r
370         *res = 0;\r
371         while(*s != 0)\r
372         {\r
373                 *res = (*res)*10+((*s++)-'0');\r
374         }\r
375 }\r
376 \r
377 SQInteger scisodigit(SQInteger c) { return c >= _SC('0') && c <= _SC('7'); }\r
378 \r
379 void LexOctal(const SQChar *s,SQUnsignedInteger *res)\r
380 {\r
381         *res = 0;\r
382         while(*s != 0)\r
383         {\r
384                 if(scisodigit(*s)) *res = (*res)*8+((*s++)-'0');\r
385                 else { assert(0); }\r
386         }\r
387 }\r
388 \r
389 SQInteger isexponent(SQInteger c) { return c == 'e' || c=='E'; }\r
390 \r
391 \r
392 #define MAX_HEX_DIGITS (sizeof(SQInteger)*2)\r
393 SQInteger SQLexer::ReadNumber()\r
394 {\r
395 #define TINT 1\r
396 #define TFLOAT 2\r
397 #define THEX 3\r
398 #define TSCIENTIFIC 4\r
399 #define TOCTAL 5\r
400         SQInteger type = TINT, firstchar = CUR_CHAR;\r
401         SQChar *sTemp;\r
402         INIT_TEMP_STRING();\r
403         NEXT();\r
404         if(firstchar == _SC('0') && (toupper(CUR_CHAR) == _SC('X') || scisodigit(CUR_CHAR)) ) {\r
405                 if(scisodigit(CUR_CHAR)) {\r
406                         type = TOCTAL;\r
407                         while(scisodigit(CUR_CHAR)) {\r
408                                 APPEND_CHAR(CUR_CHAR);\r
409                                 NEXT();\r
410                         }\r
411                         if(scisdigit(CUR_CHAR)) Error(_SC("invalid octal number"));\r
412                 }\r
413                 else {\r
414                         NEXT();\r
415                         type = THEX;\r
416                         while(isxdigit(CUR_CHAR)) {\r
417                                 APPEND_CHAR(CUR_CHAR);\r
418                                 NEXT();\r
419                         }\r
420                         if(_longstr.size() > MAX_HEX_DIGITS) Error(_SC("too many digits for an Hex number"));\r
421                 }\r
422         }\r
423         else {\r
424                 APPEND_CHAR((int)firstchar);\r
425                 while (CUR_CHAR == _SC('.') || scisdigit(CUR_CHAR) || isexponent(CUR_CHAR)) {\r
426             if(CUR_CHAR == _SC('.') || isexponent(CUR_CHAR)) type = TFLOAT;\r
427                         if(isexponent(CUR_CHAR)) {\r
428                                 if(type != TFLOAT) Error(_SC("invalid numeric format"));\r
429                                 type = TSCIENTIFIC;\r
430                                 APPEND_CHAR(CUR_CHAR);\r
431                                 NEXT();\r
432                                 if(CUR_CHAR == '+' || CUR_CHAR == '-'){\r
433                                         APPEND_CHAR(CUR_CHAR);\r
434                                         NEXT();\r
435                                 }\r
436                                 if(!scisdigit(CUR_CHAR)) Error(_SC("exponent expected"));\r
437                         }\r
438                         \r
439                         APPEND_CHAR(CUR_CHAR);\r
440                         NEXT();\r
441                 }\r
442         }\r
443         TERMINATE_BUFFER();\r
444         switch(type) {\r
445         case TSCIENTIFIC:\r
446         case TFLOAT:\r
447                 _fvalue = (SQFloat)scstrtod(&_longstr[0],&sTemp);\r
448                 return TK_FLOAT;\r
449         case TINT:\r
450                 LexInteger(&_longstr[0],(SQUnsignedInteger *)&_nvalue);\r
451                 return TK_INTEGER;\r
452         case THEX:\r
453                 LexHexadecimal(&_longstr[0],(SQUnsignedInteger *)&_nvalue);\r
454                 return TK_INTEGER;\r
455         case TOCTAL:\r
456                 LexOctal(&_longstr[0],(SQUnsignedInteger *)&_nvalue);\r
457                 return TK_INTEGER;\r
458         }\r
459         return 0;\r
460 }\r
461 \r
462 SQInteger SQLexer::ReadID()\r
463 {\r
464         SQInteger res;\r
465         INIT_TEMP_STRING();\r
466         do {\r
467                 APPEND_CHAR(CUR_CHAR);\r
468                 NEXT();\r
469         } while(scisalnum(CUR_CHAR) || CUR_CHAR == _SC('_'));\r
470         TERMINATE_BUFFER();\r
471         res = GetIDType(&_longstr[0]);\r
472         if(res == TK_IDENTIFIER || res == TK_CONSTRUCTOR) {\r
473                 _svalue = &_longstr[0];\r
474         }\r
475         return res;\r
476 }\r