1/*
2  Copyright (c) 2010-2021, Intel Corporation
3  All rights reserved.
4
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are
7  met:
8
9    * Redistributions of source code must retain the above copyright
10      notice, this list of conditions and the following disclaimer.
11
12    * Redistributions in binary form must reproduce the above copyright
13      notice, this list of conditions and the following disclaimer in the
14      documentation and/or other materials provided with the distribution.
15
16    * Neither the name of Intel Corporation nor the names of its
17      contributors may be used to endorse or promote products derived from
18      this software without specific prior written permission.
19
20
21   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24   PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
25   OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32*/
33
34%{
35
36#include "ispc.h"
37#include "sym.h"
38#include "util.h"
39#include "module.h"
40#include "type.h"
41#include <stdlib.h>
42#include <stdint.h>
43
44using namespace ispc;
45#include "parse.hh"
46
47static uint64_t lParseBinary(const char *ptr, SourcePos pos, char **endPtr);
48static int lParseInteger(bool dotdotdot);
49static void lCComment(SourcePos *);
50static void lCppComment(SourcePos *);
51static void lNextValidChar(SourcePos *, char const*&);
52static void lPragmaIgnoreWarning(SourcePos *, std::string);
53static void lPragmaUnroll(YYSTYPE *, SourcePos *, std::string, bool);
54static bool lConsumePragma(YYSTYPE *, SourcePos *);
55static void lHandleCppHash(SourcePos *);
56static void lStringConst(YYSTYPE *, SourcePos *);
57static double lParseHexFloat(const char *ptr);
58extern void RegisterDependency(const std::string &fileName);
59
60#define YY_USER_ACTION \
61    yylloc.first_line = yylloc.last_line; \
62    yylloc.first_column = yylloc.last_column; \
63    yylloc.last_column += yyleng;
64
65#ifdef ISPC_HOST_IS_WINDOWS
66inline int isatty(int) { return 0; }
67#else
68#include <unistd.h>
69#endif // ISPC_HOST_IS_WINDOWS
70
71static int allTokens[] = {
72  TOKEN_ASSERT, TOKEN_BOOL, TOKEN_BREAK, TOKEN_CASE,
73  TOKEN_CDO, TOKEN_CFOR, TOKEN_CIF, TOKEN_CWHILE,
74  TOKEN_CONST, TOKEN_CONTINUE, TOKEN_DEFAULT, TOKEN_DO,
75  TOKEN_DELETE, TOKEN_DOUBLE, TOKEN_ELSE, TOKEN_ENUM,
76  TOKEN_EXPORT, TOKEN_EXTERN, TOKEN_FALSE, TOKEN_FLOAT, TOKEN_FOR,
77  TOKEN_FOREACH, TOKEN_FOREACH_ACTIVE, TOKEN_FOREACH_TILED,
78  TOKEN_FOREACH_UNIQUE, TOKEN_GOTO, TOKEN_IF, TOKEN_IN, TOKEN_INLINE,
79  TOKEN_INT, TOKEN_INT8, TOKEN_INT16, TOKEN_INT, TOKEN_INT64, TOKEN_LAUNCH,
80  TOKEN_UINT, TOKEN_UINT8, TOKEN_UINT16, TOKEN_UINT64,
81  TOKEN_NEW, TOKEN_NULL, TOKEN_PRINT, TOKEN_RETURN, TOKEN_SOA, TOKEN_SIGNED,
82  TOKEN_SIZEOF, TOKEN_ALLOCA, TOKEN_STATIC, TOKEN_STRUCT, TOKEN_SWITCH, TOKEN_SYNC,
83  TOKEN_TASK, TOKEN_TRUE, TOKEN_TYPEDEF, TOKEN_UNIFORM, TOKEN_UNMASKED,
84  TOKEN_UNSIGNED, TOKEN_VARYING, TOKEN_VOID, TOKEN_WHILE,
85  TOKEN_STRING_C_LITERAL, TOKEN_DOTDOTDOT,
86  TOKEN_FLOAT_CONSTANT, TOKEN_DOUBLE_CONSTANT,
87  TOKEN_INT8_CONSTANT, TOKEN_UINT8_CONSTANT,
88  TOKEN_INT16_CONSTANT, TOKEN_UINT16_CONSTANT,
89  TOKEN_INT32_CONSTANT, TOKEN_UINT32_CONSTANT,
90  TOKEN_INT64_CONSTANT, TOKEN_UINT64_CONSTANT,
91  TOKEN_INC_OP, TOKEN_DEC_OP, TOKEN_LEFT_OP, TOKEN_RIGHT_OP, TOKEN_LE_OP,
92  TOKEN_GE_OP, TOKEN_EQ_OP, TOKEN_NE_OP, TOKEN_AND_OP, TOKEN_OR_OP,
93  TOKEN_MUL_ASSIGN, TOKEN_DIV_ASSIGN, TOKEN_MOD_ASSIGN, TOKEN_ADD_ASSIGN,
94  TOKEN_SUB_ASSIGN, TOKEN_LEFT_ASSIGN, TOKEN_RIGHT_ASSIGN, TOKEN_AND_ASSIGN,
95  TOKEN_XOR_ASSIGN, TOKEN_OR_ASSIGN, TOKEN_PTR_OP, TOKEN_NOINLINE, TOKEN_VECTORCALL,
96  ';', '{', '}', ',', ':', '=', '(', ')', '[', ']', '.', '&', '!', '~', '-',
97  '+', '*', '/', '%', '<', '>', '^', '|', '?',
98};
99
100std::map<int, std::string> tokenToName;
101std::map<std::string, std::string> tokenNameRemap;
102
103void ParserInit() {
104    tokenToName[TOKEN_ASSERT] = "assert";
105    tokenToName[TOKEN_BOOL] = "bool";
106    tokenToName[TOKEN_BREAK] = "break";
107    tokenToName[TOKEN_CASE] = "case";
108    tokenToName[TOKEN_CDO] = "cdo";
109    tokenToName[TOKEN_CFOR] = "cfor";
110    tokenToName[TOKEN_CIF] = "cif";
111    tokenToName[TOKEN_CWHILE] = "cwhile";
112    tokenToName[TOKEN_CONST] = "const";
113    tokenToName[TOKEN_CONTINUE] = "continue";
114    tokenToName[TOKEN_DEFAULT] = "default";
115    tokenToName[TOKEN_DO] = "do";
116    tokenToName[TOKEN_DELETE] = "delete";
117    tokenToName[TOKEN_DOUBLE] = "double";
118    tokenToName[TOKEN_ELSE] = "else";
119    tokenToName[TOKEN_ENUM] = "enum";
120    tokenToName[TOKEN_EXPORT] = "export";
121    tokenToName[TOKEN_EXTERN] = "extern";
122    tokenToName[TOKEN_FALSE] = "false";
123    tokenToName[TOKEN_FLOAT] = "float";
124    tokenToName[TOKEN_FOR] = "for";
125    tokenToName[TOKEN_FOREACH] = "foreach";
126    tokenToName[TOKEN_FOREACH_ACTIVE] = "foreach_active";
127    tokenToName[TOKEN_FOREACH_TILED] = "foreach_tiled";
128    tokenToName[TOKEN_FOREACH_UNIQUE] = "foreach_unique";
129    tokenToName[TOKEN_GOTO] = "goto";
130    tokenToName[TOKEN_IF] = "if";
131    tokenToName[TOKEN_IN] = "in";
132    tokenToName[TOKEN_INLINE] = "inline";
133    tokenToName[TOKEN_NOINLINE] = "noinline";
134    tokenToName[TOKEN_VECTORCALL] = "__vectorcall";
135    tokenToName[TOKEN_INT] = "int";
136    tokenToName[TOKEN_UINT] = "uint";
137    tokenToName[TOKEN_INT8] = "int8";
138    tokenToName[TOKEN_UINT8] = "uint8";
139    tokenToName[TOKEN_INT16] = "int16";
140    tokenToName[TOKEN_UINT16] = "uint16";
141    tokenToName[TOKEN_INT] = "int";
142    tokenToName[TOKEN_INT64] = "int64";
143    tokenToName[TOKEN_UINT64] = "uint64";
144    tokenToName[TOKEN_LAUNCH] = "launch";
145    tokenToName[TOKEN_NEW] = "new";
146    tokenToName[TOKEN_NULL] = "NULL";
147    tokenToName[TOKEN_PRINT] = "print";
148    tokenToName[TOKEN_RETURN] = "return";
149    tokenToName[TOKEN_SOA] = "soa";
150    tokenToName[TOKEN_SIGNED] = "signed";
151    tokenToName[TOKEN_SIZEOF] = "sizeof";
152    tokenToName[TOKEN_ALLOCA] = "alloca";
153    tokenToName[TOKEN_STATIC] = "static";
154    tokenToName[TOKEN_STRUCT] = "struct";
155    tokenToName[TOKEN_SWITCH] = "switch";
156    tokenToName[TOKEN_SYNC] = "sync";
157    tokenToName[TOKEN_TASK] = "task";
158    tokenToName[TOKEN_TRUE] = "true";
159    tokenToName[TOKEN_TYPEDEF] = "typedef";
160    tokenToName[TOKEN_UNIFORM] = "uniform";
161    tokenToName[TOKEN_UNMASKED] = "unmasked";
162    tokenToName[TOKEN_UNSIGNED] = "unsigned";
163    tokenToName[TOKEN_VARYING] = "varying";
164    tokenToName[TOKEN_VOID] = "void";
165    tokenToName[TOKEN_WHILE] = "while";
166    tokenToName[TOKEN_STRING_C_LITERAL] = "\"C\"";
167    tokenToName[TOKEN_DOTDOTDOT] = "...";
168    tokenToName[TOKEN_FLOAT_CONSTANT] = "TOKEN_FLOAT_CONSTANT";
169    tokenToName[TOKEN_DOUBLE_CONSTANT] = "TOKEN_DOUBLE_CONSTANT";
170    tokenToName[TOKEN_INT8_CONSTANT] = "TOKEN_INT8_CONSTANT";
171    tokenToName[TOKEN_UINT8_CONSTANT] = "TOKEN_UINT8_CONSTANT";
172    tokenToName[TOKEN_INT16_CONSTANT] = "TOKEN_INT16_CONSTANT";
173    tokenToName[TOKEN_UINT16_CONSTANT] = "TOKEN_UINT16_CONSTANT";
174    tokenToName[TOKEN_INT32_CONSTANT] = "TOKEN_INT32_CONSTANT";
175    tokenToName[TOKEN_UINT32_CONSTANT] = "TOKEN_UINT32_CONSTANT";
176    tokenToName[TOKEN_INT64_CONSTANT] = "TOKEN_INT64_CONSTANT";
177    tokenToName[TOKEN_UINT64_CONSTANT] = "TOKEN_UINT64_CONSTANT";
178    tokenToName[TOKEN_INC_OP] = "++";
179    tokenToName[TOKEN_DEC_OP] = "--";
180    tokenToName[TOKEN_LEFT_OP] = "<<";
181    tokenToName[TOKEN_RIGHT_OP] = ">>";
182    tokenToName[TOKEN_LE_OP] = "<=";
183    tokenToName[TOKEN_GE_OP] = ">=";
184    tokenToName[TOKEN_EQ_OP] = "==";
185    tokenToName[TOKEN_NE_OP] = "!=";
186    tokenToName[TOKEN_AND_OP] = "&&";
187    tokenToName[TOKEN_OR_OP] = "||";
188    tokenToName[TOKEN_MUL_ASSIGN] = "*=";
189    tokenToName[TOKEN_DIV_ASSIGN] = "/=";
190    tokenToName[TOKEN_MOD_ASSIGN] = "%=";
191    tokenToName[TOKEN_ADD_ASSIGN] = "+=";
192    tokenToName[TOKEN_SUB_ASSIGN] = "-=";
193    tokenToName[TOKEN_LEFT_ASSIGN] = "<<=";
194    tokenToName[TOKEN_RIGHT_ASSIGN] = ">>=";
195    tokenToName[TOKEN_AND_ASSIGN] = "&=";
196    tokenToName[TOKEN_XOR_ASSIGN] = "^=";
197    tokenToName[TOKEN_OR_ASSIGN] = "|=";
198    tokenToName[TOKEN_PTR_OP] = "->";
199    tokenToName[';'] = ";";
200    tokenToName['{'] = "{";
201    tokenToName['}'] = "}";
202    tokenToName[','] = ",";
203    tokenToName[':'] = ":";
204    tokenToName['='] = "=";
205    tokenToName['('] = "(";
206    tokenToName[')'] = ")";
207    tokenToName['['] = "[";
208    tokenToName[']'] = "]";
209    tokenToName['.'] = ".";
210    tokenToName['&'] = "&";
211    tokenToName['!'] = "!";
212    tokenToName['~'] = "~";
213    tokenToName['-'] = "-";
214    tokenToName['+'] = "+";
215    tokenToName['*'] = "*";
216    tokenToName['/'] = "/";
217    tokenToName['%'] = "%";
218    tokenToName['<'] = "<";
219    tokenToName['>'] = ">";
220    tokenToName['^'] = "^";
221    tokenToName['|'] = "|";
222    tokenToName['?'] = "?";
223    tokenToName[';'] = ";";
224
225    tokenNameRemap["TOKEN_ASSERT"] = "\'assert\'";
226    tokenNameRemap["TOKEN_BOOL"] = "\'bool\'";
227    tokenNameRemap["TOKEN_BREAK"] = "\'break\'";
228    tokenNameRemap["TOKEN_CASE"] = "\'case\'";
229    tokenNameRemap["TOKEN_CDO"] = "\'cdo\'";
230    tokenNameRemap["TOKEN_CFOR"] = "\'cfor\'";
231    tokenNameRemap["TOKEN_CIF"] = "\'cif\'";
232    tokenNameRemap["TOKEN_CWHILE"] = "\'cwhile\'";
233    tokenNameRemap["TOKEN_CONST"] = "\'const\'";
234    tokenNameRemap["TOKEN_CONTINUE"] = "\'continue\'";
235    tokenNameRemap["TOKEN_DEFAULT"] = "\'default\'";
236    tokenNameRemap["TOKEN_DO"] = "\'do\'";
237    tokenNameRemap["TOKEN_DELETE"] = "\'delete\'";
238    tokenNameRemap["TOKEN_DOUBLE"] = "\'double\'";
239    tokenNameRemap["TOKEN_ELSE"] = "\'else\'";
240    tokenNameRemap["TOKEN_ENUM"] = "\'enum\'";
241    tokenNameRemap["TOKEN_EXPORT"] = "\'export\'";
242    tokenNameRemap["TOKEN_EXTERN"] = "\'extern\'";
243    tokenNameRemap["TOKEN_FALSE"] = "\'false\'";
244    tokenNameRemap["TOKEN_FLOAT"] = "\'float\'";
245    tokenNameRemap["TOKEN_FOR"] = "\'for\'";
246    tokenNameRemap["TOKEN_FOREACH"] = "\'foreach\'";
247    tokenNameRemap["TOKEN_FOREACH_ACTIVE"] = "\'foreach_active\'";
248    tokenNameRemap["TOKEN_FOREACH_TILED"] = "\'foreach_tiled\'";
249    tokenNameRemap["TOKEN_FOREACH_UNIQUE"] = "\'foreach_unique\'";
250    tokenNameRemap["TOKEN_GOTO"] = "\'goto\'";
251    tokenNameRemap["TOKEN_IDENTIFIER"] = "identifier";
252    tokenNameRemap["TOKEN_IF"] = "\'if\'";
253    tokenNameRemap["TOKEN_IN"] = "\'in\'";
254    tokenNameRemap["TOKEN_INLINE"] = "\'inline\'";
255    tokenNameRemap["TOKEN_NOINLINE"] = "\'noinline\'";
256    tokenNameRemap["TOKEN_VECTORCALL"] = "\'__vectorcall\'";
257    tokenNameRemap["TOKEN_INT"] = "\'int\'";
258    tokenNameRemap["TOKEN_UINT"] = "\'uint\'";
259    tokenNameRemap["TOKEN_INT8"] = "\'int8\'";
260    tokenNameRemap["TOKEN_UINT8"] = "\'uint8\'";
261    tokenNameRemap["TOKEN_INT16"] = "\'int16\'";
262    tokenNameRemap["TOKEN_UINT16"] = "\'uint16\'";
263    tokenNameRemap["TOKEN_INT"] = "\'int\'";
264    tokenNameRemap["TOKEN_INT64"] = "\'int64\'";
265    tokenNameRemap["TOKEN_UINT64"] = "\'uint64\'";
266    tokenNameRemap["TOKEN_LAUNCH"] = "\'launch\'";
267    tokenNameRemap["TOKEN_NEW"] = "\'new\'";
268    tokenNameRemap["TOKEN_NULL"] = "\'NULL\'";
269    tokenNameRemap["TOKEN_PRINT"] = "\'print\'";
270    tokenNameRemap["TOKEN_RETURN"] = "\'return\'";
271    tokenNameRemap["TOKEN_SOA"] = "\'soa\'";
272    tokenNameRemap["TOKEN_SIGNED"] = "\'signed\'";
273    tokenNameRemap["TOKEN_SIZEOF"] = "\'sizeof\'";
274    tokenNameRemap["TOKEN_ALLOCA"] = "\'TOKEN_ALLOCA\'";
275    tokenNameRemap["TOKEN_STATIC"] = "\'static\'";
276    tokenNameRemap["TOKEN_STRUCT"] = "\'struct\'";
277    tokenNameRemap["TOKEN_SWITCH"] = "\'switch\'";
278    tokenNameRemap["TOKEN_SYNC"] = "\'sync\'";
279    tokenNameRemap["TOKEN_TASK"] = "\'task\'";
280    tokenNameRemap["TOKEN_TRUE"] = "\'true\'";
281    tokenNameRemap["TOKEN_TYPEDEF"] = "\'typedef\'";
282    tokenNameRemap["TOKEN_UNIFORM"] = "\'uniform\'";
283    tokenNameRemap["TOKEN_UNMASKED"] = "\'unmasked\'";
284    tokenNameRemap["TOKEN_UNSIGNED"] = "\'unsigned\'";
285    tokenNameRemap["TOKEN_VARYING"] = "\'varying\'";
286    tokenNameRemap["TOKEN_VOID"] = "\'void\'";
287    tokenNameRemap["TOKEN_WHILE"] = "\'while\'";
288    tokenNameRemap["TOKEN_STRING_C_LITERAL"] = "\"C\"";
289    tokenNameRemap["TOKEN_DOTDOTDOT"] = "\'...\'";
290    tokenNameRemap["TOKEN_FLOAT_CONSTANT"] = "float constant";
291    tokenNameRemap["TOKEN_DOUBLE_CONSTANT"] = "double constant";
292    tokenNameRemap["TOKEN_INT8_CONSTANT"] = "int8 constant";
293    tokenNameRemap["TOKEN_UINT8_CONSTANT"] = "unsigned int8 constant";
294    tokenNameRemap["TOKEN_INT16_CONSTANT"] = "int16 constant";
295    tokenNameRemap["TOKEN_UINT16_CONSTANT"] = "unsigned int16 constant";
296    tokenNameRemap["TOKEN_INT32_CONSTANT"] = "int32 constant";
297    tokenNameRemap["TOKEN_UINT32_CONSTANT"] = "unsigned int32 constant";
298    tokenNameRemap["TOKEN_INT64_CONSTANT"] = "int64 constant";
299    tokenNameRemap["TOKEN_UINT64_CONSTANT"] = "unsigned int64 constant";
300    tokenNameRemap["TOKEN_INC_OP"] = "\'++\'";
301    tokenNameRemap["TOKEN_DEC_OP"] = "\'--\'";
302    tokenNameRemap["TOKEN_LEFT_OP"] = "\'<<\'";
303    tokenNameRemap["TOKEN_RIGHT_OP"] = "\'>>\'";
304    tokenNameRemap["TOKEN_LE_OP"] = "\'<=\'";
305    tokenNameRemap["TOKEN_GE_OP"] = "\'>=\'";
306    tokenNameRemap["TOKEN_EQ_OP"] = "\'==\'";
307    tokenNameRemap["TOKEN_NE_OP"] = "\'!=\'";
308    tokenNameRemap["TOKEN_AND_OP"] = "\'&&\'";
309    tokenNameRemap["TOKEN_OR_OP"] = "\'||\'";
310    tokenNameRemap["TOKEN_MUL_ASSIGN"] = "\'*=\'";
311    tokenNameRemap["TOKEN_DIV_ASSIGN"] = "\'/=\'";
312    tokenNameRemap["TOKEN_MOD_ASSIGN"] = "\'%=\'";
313    tokenNameRemap["TOKEN_ADD_ASSIGN"] = "\'+=\'";
314    tokenNameRemap["TOKEN_SUB_ASSIGN"] = "\'-=\'";
315    tokenNameRemap["TOKEN_LEFT_ASSIGN"] = "\'<<=\'";
316    tokenNameRemap["TOKEN_RIGHT_ASSIGN"] = "\'>>=\'";
317    tokenNameRemap["TOKEN_AND_ASSIGN"] = "\'&=\'";
318    tokenNameRemap["TOKEN_XOR_ASSIGN"] = "\'^=\'";
319    tokenNameRemap["TOKEN_OR_ASSIGN"] = "\'|=\'";
320    tokenNameRemap["TOKEN_PTR_OP"] = "\'->\'";
321    tokenNameRemap["$end"] = "end of file";
322}
323
324
325inline int ispcRand() {
326#ifdef ISPC_HOST_IS_WINDOWS
327    return rand();
328#else
329    return lrand48();
330#endif
331}
332
333#define RT \
334    if (g->enableFuzzTest) { \
335        int r = ispcRand() % 40; \
336        if (r == 0) { \
337            Warning(yylloc, "Fuzz test dropping token"); \
338        } \
339        else if (r == 1) { \
340            Assert (tokenToName.size() > 0); \
341            int nt = sizeof(allTokens) / sizeof(allTokens[0]); \
342            int tn = ispcRand() % nt; \
343            yylval.stringVal = new std::string(yytext); /* just in case */\
344            Warning(yylloc, "Fuzz test replaced token with \"%s\"", tokenToName[allTokens[tn]].c_str()); \
345            return allTokens[tn]; \
346        } \
347        else if (r == 2) { \
348            Symbol *sym = m->symbolTable->RandomSymbol(); \
349            if (sym != NULL) { \
350                yylval.stringVal = new std::string(sym->name); \
351                Warning(yylloc, "Fuzz test replaced with identifier \"%s\".", sym->name.c_str()); \
352                return TOKEN_IDENTIFIER; \
353            } \
354        } \
355        /*  TOKEN_TYPE_NAME */ \
356     } else /* swallow semicolon */
357
358%}
359
360%option nounput
361%option noyywrap
362%option nounistd
363
364WHITESPACE [ \t\r]+
365INT_NUMBER (([0-9]+)|(0x[0-9a-fA-F]+)|(0b[01]+))[uUlL]*[kMG]?[uUlL]*
366INT_NUMBER_DOTDOTDOT (([0-9]+)|(0x[0-9a-fA-F]+)|(0b[01]+))[uUlL]*[kMG]?[uUlL]*\.\.\.
367FLOAT_NUMBER (([0-9]+|(([0-9]+\.[0-9]*[fF]?)|(\.[0-9]+)))([eE][-+]?[0-9]+)?[fF]?)
368HEX_FLOAT_NUMBER (0x[01](\.[0-9a-fA-F]*)?p[-+]?[0-9]+[fF]?)
369FORTRAN_DOUBLE_NUMBER (([0-9]+\.[0-9]*[dD])|([0-9]+\.[0-9]*[dD][-+]?[0-9]+)|([0-9]+[dD][-+]?[0-9]+)|(\.[0-9]*[dD][-+]?[0-9]+))
370
371
372
373IDENT [a-zA-Z_][a-zA-Z_0-9]*
374INTRINSIC_CALL [@][l][l][v][m][.][.a-zA-Z_0-9]*
375ZO_SWIZZLE ([01]+[w-z]+)+|([01]+[rgba]+)+|([01]+[uv]+)+
376
377%%
378"/*"            { lCComment(&yylloc); }
379"//"            { lCppComment(&yylloc); }
380"#pragma" {
381    if (lConsumePragma(&yylval, &yylloc)) {
382        return TOKEN_PRAGMA;
383    }
384}
385
386
387__assert { RT; return TOKEN_ASSERT; }
388bool { RT; return TOKEN_BOOL; }
389break { RT; return TOKEN_BREAK; }
390case { RT; return TOKEN_CASE; }
391cbreak { RT; Warning(yylloc, "\"cbreak\" is deprecated. Use \"break\"."); return TOKEN_BREAK; }
392ccontinue { RT; Warning(yylloc, "\"ccontinue\" is deprecated. Use \"continue\"."); return TOKEN_CONTINUE; }
393cdo { RT; return TOKEN_CDO; }
394cfor { RT; return TOKEN_CFOR; }
395cif { RT; return TOKEN_CIF; }
396cwhile { RT; return TOKEN_CWHILE; }
397const { RT; return TOKEN_CONST; }
398continue { RT; return TOKEN_CONTINUE; }
399creturn { RT; Warning(yylloc, "\"creturn\" is deprecated. Use \"return\"."); return TOKEN_RETURN; }
400__declspec { RT; return TOKEN_DECLSPEC; }
401default { RT; return TOKEN_DEFAULT; }
402do { RT; return TOKEN_DO; }
403delete { RT; return TOKEN_DELETE; }
404delete\[\] { RT; return TOKEN_DELETE; }
405double { RT; return TOKEN_DOUBLE; }
406else { RT; return TOKEN_ELSE; }
407enum { RT; return TOKEN_ENUM; }
408export { RT; return TOKEN_EXPORT; }
409extern { RT; return TOKEN_EXTERN; }
410false { RT; return TOKEN_FALSE; }
411float { RT; return TOKEN_FLOAT; }
412for { RT; return TOKEN_FOR; }
413foreach { RT; return TOKEN_FOREACH; }
414foreach_active { RT; return TOKEN_FOREACH_ACTIVE; }
415foreach_tiled { RT; return TOKEN_FOREACH_TILED; }
416foreach_unique { RT; return TOKEN_FOREACH_UNIQUE; }
417goto { RT; return TOKEN_GOTO; }
418if { RT; return TOKEN_IF; }
419in { RT; return TOKEN_IN; }
420inline { RT; return TOKEN_INLINE; }
421noinline { RT; return TOKEN_NOINLINE; }
422__vectorcall { RT; return TOKEN_VECTORCALL; }
423int { RT; return TOKEN_INT; }
424uint { RT; return TOKEN_UINT; }
425int8 { RT; return TOKEN_INT8; }
426uint8 { RT; return TOKEN_UINT8; }
427int16 { RT; return TOKEN_INT16; }
428uint16 { RT; return TOKEN_UINT16; }
429int32 { RT; return TOKEN_INT; }
430uint32 { RT; return TOKEN_UINT; }
431int64 { RT; return TOKEN_INT64; }
432uint64 { RT; return TOKEN_UINT64; }
433launch { RT; return TOKEN_LAUNCH; }
434new { RT; return TOKEN_NEW; }
435NULL { RT; return TOKEN_NULL; }
436print { RT; return TOKEN_PRINT; }
437return { RT; return TOKEN_RETURN; }
438soa { RT; return TOKEN_SOA; }
439signed { RT; return TOKEN_SIGNED; }
440sizeof { RT; return TOKEN_SIZEOF; }
441alloca { RT; return TOKEN_ALLOCA; }
442static { RT; return TOKEN_STATIC; }
443struct { RT; return TOKEN_STRUCT; }
444switch { RT; return TOKEN_SWITCH; }
445sync { RT; return TOKEN_SYNC; }
446task { RT; return TOKEN_TASK; }
447true { RT; return TOKEN_TRUE; }
448typedef { RT; return TOKEN_TYPEDEF; }
449uniform { RT; return TOKEN_UNIFORM; }
450unmasked { RT; return TOKEN_UNMASKED; }
451unsigned { RT; return TOKEN_UNSIGNED; }
452varying { RT; return TOKEN_VARYING; }
453void { RT; return TOKEN_VOID; }
454while { RT; return TOKEN_WHILE; }
455\"C\" { RT; return TOKEN_STRING_C_LITERAL; }
456\.\.\. { RT; return TOKEN_DOTDOTDOT; }
457
458"operator*"  { return TOKEN_IDENTIFIER; }
459"operator+"  { return TOKEN_IDENTIFIER; }
460"operator-"  { return TOKEN_IDENTIFIER; }
461"operator<<" { return TOKEN_IDENTIFIER; }
462"operator>>" { return TOKEN_IDENTIFIER; }
463"operator/" { return TOKEN_IDENTIFIER; }
464"operator%" { return TOKEN_IDENTIFIER; }
465
466L?\"(\\.|[^\\"])*\" { lStringConst(&yylval, &yylloc); return TOKEN_STRING_LITERAL; }
467
468{IDENT} {
469    RT;
470    /* We have an identifier--is it a type name or an identifier?
471       The symbol table will straighten us out... */
472    yylval.stringVal = new std::string(yytext);
473    if (m->symbolTable->LookupType(yytext) != NULL)
474        return TOKEN_TYPE_NAME;
475    else
476        return TOKEN_IDENTIFIER;
477}
478
479{INTRINSIC_CALL} {
480    RT;
481    /* We have a potential llvm intrinsic call.*/
482    yylval.stringVal = new std::string(yytext);
483    return TOKEN_INTRINSIC_CALL;
484}
485
486{INT_NUMBER} {
487    RT;
488    return lParseInteger(false);
489}
490
491{INT_NUMBER_DOTDOTDOT} {
492    RT;
493    return lParseInteger(true);
494}
495
496{FORTRAN_DOUBLE_NUMBER} {
497    RT;
498    {
499      int i = 0;
500      while (yytext[i] != 'd' && yytext[i] != 'D') i++;
501      yytext[i] = 'E';
502    }
503    yylval.doubleVal = atof(yytext);
504    return TOKEN_DOUBLE_CONSTANT;
505}
506
507
508{FLOAT_NUMBER} {
509    RT;
510    yylval.floatVal = (float)atof(yytext);
511    return TOKEN_FLOAT_CONSTANT;
512}
513
514{HEX_FLOAT_NUMBER} {
515    RT;
516    yylval.floatVal = (float)lParseHexFloat(yytext);
517    return TOKEN_FLOAT_CONSTANT;
518}
519
520
521
522"++" { RT; return TOKEN_INC_OP; }
523"--" { RT; return TOKEN_DEC_OP; }
524"<<" { RT; return TOKEN_LEFT_OP; }
525">>" { RT; return TOKEN_RIGHT_OP; }
526"<=" { RT; return TOKEN_LE_OP; }
527">=" { RT; return TOKEN_GE_OP; }
528"==" { RT; return TOKEN_EQ_OP; }
529"!=" { RT; return TOKEN_NE_OP; }
530"&&" { RT; return TOKEN_AND_OP; }
531"||" { RT; return TOKEN_OR_OP; }
532"*=" { RT; return TOKEN_MUL_ASSIGN; }
533"/=" { RT; return TOKEN_DIV_ASSIGN; }
534"%=" { RT; return TOKEN_MOD_ASSIGN; }
535"+=" { RT; return TOKEN_ADD_ASSIGN; }
536"-=" { RT; return TOKEN_SUB_ASSIGN; }
537"<<=" { RT; return TOKEN_LEFT_ASSIGN; }
538">>=" { RT; return TOKEN_RIGHT_ASSIGN; }
539"&=" { RT; return TOKEN_AND_ASSIGN; }
540"^=" { RT; return TOKEN_XOR_ASSIGN; }
541"|=" { RT; return TOKEN_OR_ASSIGN; }
542"->" { RT; return TOKEN_PTR_OP; }
543";"             { RT; return ';'; }
544("{"|"<%")      { RT; return '{'; }
545("}"|"%>")      { RT; return '}'; }
546","             { RT; return ','; }
547":"             { RT; return ':'; }
548"="             { RT; return '='; }
549"("             { RT; return '('; }
550")"             { RT; return ')'; }
551("["|"<:")      { RT; return '['; }
552("]"|":>")      { RT; return ']'; }
553"."             { RT; return '.'; }
554"&"             { RT; return '&'; }
555"!"             { RT; return '!'; }
556"~"             { RT; return '~'; }
557"-"             { RT; return '-'; }
558"+"             { RT; return '+'; }
559"*"             { RT; return '*'; }
560"/"             { RT; return '/'; }
561"%"             { RT; return '%'; }
562"<"             { RT; return '<'; }
563">"             { RT; return '>'; }
564"^"             { RT; return '^'; }
565"|"             { RT; return '|'; }
566"?"             { RT; return '?'; }
567
568{WHITESPACE} { }
569
570\n {
571    yylloc.last_line++;
572    yylloc.last_column = 1;
573}
574
575#(line)?[ ][0-9]+[ ]\"(\\.|[^\\"])*\"[^\n]* {
576    lHandleCppHash(&yylloc);
577}
578
579. {
580    Error(yylloc, "Illegal character: %c (0x%x)", yytext[0], int(yytext[0]));
581    YY_USER_ACTION
582}
583
584%%
585
586/*short { return TOKEN_SHORT; }*/
587/*long { return TOKEN_LONG; }*/
588/*signed { return TOKEN_SIGNED; }*/
589/*volatile { return TOKEN_VOLATILE; }*/
590/*"long"[ \t\v\f\n]+"long" { return TOKEN_LONGLONG; }*/
591/*union { return TOKEN_UNION; }*/
592/*"..." { return TOKEN_ELLIPSIS; }*/
593
594/** Return the integer version of a binary constant from a string.
595 */
596static uint64_t
597lParseBinary(const char *ptr, SourcePos pos, char **endPtr) {
598    uint64_t val = 0;
599    bool warned = false;
600
601    while (*ptr == '0' || *ptr == '1') {
602        if ((val & (((int64_t)1)<<63)) && warned == false) {
603            // We're about to shift out a set bit
604            Warning(pos, "Can't represent binary constant with a 64-bit integer type");
605            warned = true;
606        }
607
608        val = (val << 1) | (*ptr == '0' ? 0 : 1);
609        ++ptr;
610    }
611    *endPtr = (char *)ptr;
612    return val;
613}
614
615
616static int
617lParseInteger(bool dotdotdot) {
618    int ls = 0, us = 0;
619
620    char *endPtr = NULL;
621    if (yytext[0] == '0' && yytext[1] == 'b')
622        yylval.intVal = lParseBinary(yytext+2, yylloc, &endPtr);
623    else {
624#if defined(ISPC_HOST_IS_WINDOWS) && !defined(__MINGW32__)
625        yylval.intVal = _strtoui64(yytext, &endPtr, 0);
626#else
627        // FIXME: should use strtouq and then issue an error if we can't
628        // fit into 64 bits...
629        yylval.intVal = strtoull(yytext, &endPtr, 0);
630#endif
631    }
632
633    bool kilo = false, mega = false, giga = false;
634    for (; *endPtr; endPtr++) {
635        if (*endPtr == 'k')
636            kilo = true;
637        else if (*endPtr == 'M')
638            mega = true;
639        else if (*endPtr == 'G')
640            giga = true;
641        else if (*endPtr == 'l' || *endPtr == 'L')
642            ls++;
643        else if (*endPtr == 'u' || *endPtr == 'U')
644            us++;
645        else
646            Assert(dotdotdot && *endPtr == '.');
647    }
648    if (kilo)
649        yylval.intVal *= 1024;
650    if (mega)
651        yylval.intVal *= 1024*1024;
652    if (giga)
653        yylval.intVal *= 1024*1024*1024;
654
655    if (dotdotdot) {
656        if (ls >= 2)
657            return us ? TOKEN_UINT64DOTDOTDOT_CONSTANT : TOKEN_INT64DOTDOTDOT_CONSTANT;
658        else if (ls == 1)
659            return us ? TOKEN_UINT32DOTDOTDOT_CONSTANT : TOKEN_INT32DOTDOTDOT_CONSTANT;
660
661        // See if we can fit this into a 32-bit integer...
662        if ((yylval.intVal & 0xffffffff) == yylval.intVal)
663            return us ? TOKEN_UINT32DOTDOTDOT_CONSTANT : TOKEN_INT32DOTDOTDOT_CONSTANT;
664        else
665            return us ? TOKEN_UINT64DOTDOTDOT_CONSTANT : TOKEN_INT64DOTDOTDOT_CONSTANT;
666    }
667    else {
668        if (ls >= 2)
669            return us ? TOKEN_UINT64_CONSTANT : TOKEN_INT64_CONSTANT;
670        else if (ls == 1)
671            return us ? TOKEN_UINT32_CONSTANT : TOKEN_INT32_CONSTANT;
672        else if (us) {
673            // u suffix only
674            if (yylval.intVal <= 0xffffffffL)
675                return TOKEN_UINT32_CONSTANT;
676            else
677                return TOKEN_UINT64_CONSTANT;
678        }
679        else {
680            // No u or l suffix
681            // If we're compiling to an 8-bit mask target and the constant
682            // fits into 8 bits, return an 8-bit int.
683            if (g->target->getDataTypeWidth() == 8) {
684                if (yylval.intVal <= 0x7fULL)
685                    return TOKEN_INT8_CONSTANT;
686                else if (yylval.intVal <= 0xffULL)
687                    return TOKEN_UINT8_CONSTANT;
688            }
689            // And similarly for 16-bit masks and constants
690            if (g->target->getDataTypeWidth() == 16) {
691                if (yylval.intVal <= 0x7fffULL)
692                    return TOKEN_INT16_CONSTANT;
693                else if (yylval.intVal <= 0xffffULL)
694                    return TOKEN_UINT16_CONSTANT;
695            }
696            // Otherwise, see if we can fit this into a 32-bit integer...
697            if (yylval.intVal <= 0x7fffffffULL)
698                return TOKEN_INT32_CONSTANT;
699            else if (yylval.intVal <= 0xffffffffULL)
700                return TOKEN_UINT32_CONSTANT;
701            else if (yylval.intVal <= 0x7fffffffffffffffULL)
702                return TOKEN_INT64_CONSTANT;
703            else
704                return TOKEN_UINT64_CONSTANT;
705        }
706    }
707}
708
709
710/** Handle a C-style comment in the source.
711 */
712static void
713lCComment(SourcePos *pos) {
714    char c, prev = 0;
715
716    while ((c = yyinput()) != 0) {
717        ++pos->last_column;
718
719        if (c == '\n') {
720            pos->last_line++;
721            pos->last_column = 1;
722        }
723        if (c == '/' && prev == '*')
724            return;
725        prev = c;
726    }
727    Error(*pos, "unterminated comment");
728}
729
730static void lNextValidChar(SourcePos *pos, char const*&currChar) {
731    while ((*currChar == ' ') || (*currChar == '\t') || (*currChar == '\r')) {
732        ++pos->last_column;
733        currChar++;
734    }
735}
736
737/** Handle pragma directive to unroll loops.
738*/
739static void lPragmaUnroll(YYSTYPE *yylval, SourcePos *pos, std::string fromUserReq, bool isNounroll) {
740
741    const char *currChar = fromUserReq.data();
742    yylval->pragmaAttributes = new PragmaAttributes();
743    yylval->pragmaAttributes->aType = PragmaAttributes::AttributeType::pragmaloop;
744    int count = -1;
745
746    lNextValidChar(pos, currChar);
747
748    if (isNounroll) {
749        if (*currChar == '\n') {
750            yylval->pragmaAttributes->unrollType = Globals::pragmaUnrollType::nounroll;
751            pos->last_column = 1;
752            pos->last_line++;
753            return;
754        }
755        pos->last_column = 1;
756        pos->last_line++;
757        Warning(*pos, "extra tokens at end of '#pragma nounroll'.");
758        return;
759
760    }
761
762    if (*currChar == '\n') {
763        yylval->pragmaAttributes->unrollType = Globals::pragmaUnrollType::unroll;
764        pos->last_column = 1;
765        pos->last_line++;
766        return;
767    }
768
769    bool popPar = false;
770    if (*currChar == '(') {
771        popPar = true;
772        currChar++;
773        ++pos->last_column;
774    }
775
776    char *endPtr = NULL;
777#if defined(ISPC_HOST_IS_WINDOWS) && !defined(__MINGW32__)
778    count = _strtoui64(currChar, &endPtr, 0);
779#else
780    // FIXME: should use strtouq and then issue an error if we can't
781    // fit into 64 bits...
782    count = strtoull(currChar, &endPtr, 0);
783#endif
784
785    if((count == 0) && (endPtr != currChar)){
786        Error(*pos, "'#pragma unroll()' invalid value '0'; must be positive.");
787    }
788
789    lNextValidChar(pos, const_cast<const char*&>(endPtr));
790
791    if (popPar == true) {
792        if (*endPtr == ')') {
793            ++pos->last_column;
794            endPtr++;
795            lNextValidChar(pos, const_cast<const char*&>(endPtr));
796        }
797        else {
798            Error(*pos, "Incomplete '#pragma unroll()' : expected ')'.");
799        }
800    }
801
802    yylval->pragmaAttributes->unrollType = Globals::pragmaUnrollType::count;
803    yylval->pragmaAttributes->count = count;
804    pos->last_line++;
805    pos->last_column = 1;
806}
807
808/** Handle pragma directive to ignore warning.
809*/
810static void
811lPragmaIgnoreWarning(SourcePos *pos, std::string fromUserReq) {
812    std::string userReq;
813    const char *currChar = fromUserReq.data();
814    bool perfWarningOnly = false;
815    lNextValidChar(pos, currChar);
816
817    if (*currChar == '\n') {
818        pos->last_column = 1;
819        pos->last_line++;
820        std::pair<int, std::string> key = std::pair<int, std::string>(pos->last_line, pos->name);
821        g->turnOffWarnings[key] = perfWarningOnly;
822        return;
823    }
824    else if (*currChar == '(') {
825        currChar++;
826        lNextValidChar(pos, currChar);
827        while (*currChar != 0 && *currChar != '\n' && *currChar != ' ' && *currChar != ')') {
828            userReq += *currChar;
829            currChar++;
830            ++pos->last_column;
831        }
832        if ((*currChar == ' ') || (*currChar == ')')) {
833            lNextValidChar(pos, currChar);
834            if (*currChar == ')') {
835                currChar++;
836                ++pos->last_column;
837                lNextValidChar(pos, currChar);
838                if (*currChar == '\n') {
839                    pos->last_column = 1;
840                    pos->last_line++;
841                    if (userReq.compare("perf") == 0) {
842                        perfWarningOnly = true;
843                        std::pair<int, std::string> key = std::pair<int, std::string>(pos->last_line, pos->name);
844                        g->turnOffWarnings[key] = perfWarningOnly;
845                    }
846                    else if (userReq.compare("all") == 0) {
847                        std::pair<int, std::string> key = std::pair<int, std::string>(pos->last_line, pos->name);
848                        g->turnOffWarnings[key] = perfWarningOnly;
849                    }
850                    else {
851                        Error(*pos, "Incorrect argument for '#pragma ignore warning()'.");
852                    }
853                    return;
854                }
855            }
856        }
857        else if (*currChar == '\n') {
858            Error(*pos, "Incomplete '#pragma ignore warning()' : expected ')'.");
859            pos->last_column = 1;
860            pos->last_line++;
861            return;
862        }
863    }
864    Error(*pos, "Undefined #pragma.");
865}
866
867/** Consume line starting with '#pragma' and decide on next action based on
868 * directive.
869 */
870static bool lConsumePragma(YYSTYPE *yylval, SourcePos *pos) {
871    char c;
872    std::string userReq;
873    do {
874        c = yyinput();
875        ++pos->last_column;
876    } while ((c == ' ') || (c == '\t') || (c == '\r'));
877    if (c == '\n') {
878        // Ignore pragma since - directive provided.
879        pos->last_column = 1;
880        pos->last_line++;
881        return false;
882    }
883
884    while (c != '\n') {
885        userReq += c;
886        c = yyinput();
887    }
888    userReq += c;
889    std::string loopUnroll("unroll"), loopNounroll("nounroll"), ignoreWarning("ignore warning");
890    if (loopUnroll == userReq.substr(0, loopUnroll.size())) {
891        pos->last_column += loopUnroll.size();
892        lPragmaUnroll(yylval, pos, userReq.erase(0, loopUnroll.size()), false);
893        return true;
894    }
895    else if (loopNounroll == userReq.substr(0, loopNounroll.size())) {
896        pos->last_column += loopNounroll.size();
897        lPragmaUnroll(yylval, pos, userReq.erase(0, loopNounroll.size()), true);
898        return true;
899    }
900    else if (ignoreWarning == userReq.substr(0, ignoreWarning.size())) {
901        pos->last_column += ignoreWarning.size();
902        lPragmaIgnoreWarning(pos, userReq.erase(0, ignoreWarning.size()));
903        return false;
904    }
905
906    // Ignore pragma : invalid directive provided.
907    Warning(*pos, "unknown pragma ignored.");
908    return false;
909}
910
911/** Handle a C++-style comment--eat everything up until the end of the line.
912 */
913static void
914lCppComment(SourcePos *pos) {
915    char c;
916    do {
917        c = yyinput();
918    } while (c != 0 && c != '\n');
919    if (c == '\n') {
920        pos->last_line++;
921        pos->last_column = 1;
922    }
923}
924
925/** Handle a line that starts with a # character; this should be something
926    left behind by the preprocessor indicating the source file/line
927    that our current position corresponds to.
928 */
929static void lHandleCppHash(SourcePos *pos) {
930    char *ptr, *src;
931
932    // Advance past the opening stuff on the line.
933    Assert(yytext[0] == '#');
934    if (yytext[1] == ' ')
935        // On Linux/OSX, the preprocessor gives us lines like
936        // # 1234 "foo.c"
937        ptr = yytext + 2;
938    else {
939        // On windows, cl.exe's preprocessor gives us lines of the form:
940        // #line 1234 "foo.c"
941        Assert(!strncmp(yytext+1, "line ", 5));
942        ptr = yytext + 6;
943    }
944
945    // Now we can set the line number based on the integer in the string
946    // that ptr is pointing at.
947    pos->last_line = strtol(ptr, &src, 10) - 1;
948    pos->last_column = 1;
949    // Make sure that the character after the integer is a space and that
950    // then we have open quotes
951    Assert(src != ptr && src[0] == ' ' && src[1] == '"');
952    src += 2;
953
954    // And the filename is everything up until the closing quotes
955    std::string filename;
956    while (*src != '"') {
957        Assert(*src && *src != '\n');
958        filename.push_back(*src);
959        ++src;
960    }
961    pos->name = strdup(filename.c_str());
962    RegisterDependency(filename);
963}
964
965
966/** Given a pointer to a position in a string, return the character that it
967    represents, accounting for the escape characters supported in string
968    constants.  (i.e. given the literal string "\\", return the character
969    '/').  The return value is the new position in the string and the
970    decoded character is returned in *pChar.
971*/
972static char *
973lEscapeChar(char *str, char *pChar, SourcePos *pos)
974{
975    if (*str != '\\') {
976        *pChar = *str;
977    }
978    else {
979        char *tail;
980        ++str;
981        switch (*str) {
982        case '\'': *pChar = '\''; break;
983        case '\"': *pChar = '\"'; break;
984        case '?':  *pChar = '\?'; break;
985        case '\\': *pChar = '\\'; break;
986        case 'a':  *pChar = '\a'; break;
987        case 'b':  *pChar = '\b'; break;
988        case 'f':  *pChar = '\f'; break;
989        case 'n':  *pChar = '\n'; break;
990        case 'r':  *pChar = '\r'; break;
991        case 't':  *pChar = '\t'; break;
992        case 'v':  *pChar = '\v'; break;
993        // octal constants \012
994        case '0': case '1': case '2': case '3': case '4':
995        case '5': case '6': case '7':
996            *pChar = (char)strtol(str, &tail, 8);
997            str = tail - 1;
998            break;
999        // hexidecimal constant \xff
1000        case 'x':
1001            *pChar = (char)strtol(str, &tail, 16);
1002            str = tail - 1;
1003            break;
1004        default:
1005            Error(*pos, "Bad character escape sequence: '%s'.", str);
1006            break;
1007        }
1008    }
1009    ++str;
1010    return str;
1011}
1012
1013
1014/** Parse a string constant in the source file.  For each character in the
1015    string, handle any escaped characters with lEscapeChar() and keep eating
1016    characters until we come to the closing quote.
1017*/
1018static void
1019lStringConst(YYSTYPE *yylval, SourcePos *pos)
1020{
1021    char *p;
1022    std::string str;
1023    p = strchr(yytext, '"') + 1;
1024    if (p == NULL)
1025       return;
1026
1027    while (*p != '\"') {
1028       char cval = '\0';
1029       p = lEscapeChar(p, &cval, pos);
1030       str.push_back(cval);
1031    }
1032    yylval->stringVal = new std::string(str);
1033}
1034
1035
1036/** Compute the value 2^n, where the exponent is given as an integer.
1037    There are more efficient ways to do this, for example by just slamming
1038    the bits into the appropriate bits of the double, but let's just do the
1039    obvious thing.
1040*/
1041static double
1042ipow2(int exponent) {
1043    if (exponent < 0)
1044        return 1. / ipow2(-exponent);
1045
1046    double ret = 1.;
1047    while (exponent > 16) {
1048        ret *= 65536.;
1049        exponent -= 16;
1050    }
1051    while (exponent-- > 0)
1052        ret *= 2.;
1053    return ret;
1054}
1055
1056
1057/** Parse a hexadecimal-formatted floating-point number (C99 hex float
1058    constant-style).
1059*/
1060static double
1061lParseHexFloat(const char *ptr) {
1062    Assert(ptr != NULL);
1063
1064    Assert(ptr[0] == '0' && ptr[1] == 'x');
1065    ptr += 2;
1066
1067    // Start initializing the mantissa
1068    Assert(*ptr == '0' || *ptr == '1');
1069    double mantissa = (*ptr == '1') ? 1. : 0.;
1070    ++ptr;
1071
1072    if (*ptr == '.') {
1073        // Is there a fraction part?  If so, the i'th digit we encounter
1074        // gives the 1/(16^i) component of the mantissa.
1075        ++ptr;
1076
1077        double scale = 1. / 16.;
1078        // Keep going until we come to the 'p', which indicates that we've
1079        // come to the exponent
1080        while (*ptr != 'p') {
1081            // Figure out the raw value from 0-15
1082            int digit;
1083            if (*ptr >= '0' && *ptr <= '9')
1084                digit = *ptr - '0';
1085            else if (*ptr >= 'a' && *ptr <= 'f')
1086                digit = 10 + *ptr - 'a';
1087            else {
1088                Assert(*ptr >= 'A' && *ptr <= 'F');
1089                digit = 10 + *ptr - 'A';
1090            }
1091
1092            // And add its contribution to the mantissa
1093            mantissa += scale * digit;
1094            scale /= 16.;
1095            ++ptr;
1096        }
1097    }
1098    else
1099        // If there's not a '.', then we better be going straight to the
1100        // exponent
1101        Assert(*ptr == 'p');
1102
1103    ++ptr; // skip the 'p'
1104
1105    // interestingly enough, the exponent is provided base 10..
1106    char* endptr = NULL;
1107    int exponent = (int)strtol(ptr, &endptr, 10);
1108    Assert(ptr != endptr);
1109
1110    // Does stdlib exp2() guarantee exact results for integer n where can
1111    // be represented exactly as doubles?  I would hope so but am not sure,
1112    // so let's be sure.
1113    return mantissa * ipow2(exponent);
1114}
1115