1/*
2 * Copyright (c) 2003 Matthijs Hollemans
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 */
22
23%{
24//------------------------------------------------------------------------------
25
26#include <stack>
27#include <string.h>
28
29#include "rdef.h"
30#include "compile.h"
31#include "private.h"
32#include "parser.hpp"
33
34#define LEXERROR(msg) abort_compile(RDEF_COMPILE_ERR, msg);
35
36// Initial size (and increment) of lexbuf.
37#define LEX_BUF_SIZE  (64*1024)
38
39// Temporary buffer that the lexer uses to parse string and raw literals.
40// The buffer will grow if necessary, to accommodate large data blocks.
41static uint8* lexbuf;
42
43static uint8* lexptr;   // current write position in lexbuf
44static size_t lexsize;  // current size of the lex buffer
45static size_t lexcnt;   // how full lexbuf currently is
46
47static void resetbuf();     // resets lexptr and lexcnt
48static void addbuf(uint8);  // appends byte to lexbuf
49
50// When we encounter an #include directive, we push the current
51// buffer, filename, and line number on the include stack, so we
52// can resume lexing that file when we're done with the include.
53struct include_t {
54	YY_BUFFER_STATE buffer;
55	char* filename;
56	int lineno;
57};
58
59static std::stack<include_t> include_stack;
60
61static void open_include();
62static void close_include();
63
64//------------------------------------------------------------------------------
65%}
66
67%option noyywrap
68%option yylineno
69
70LETTER      [a-zA-Z]
71BIN         [01]
72OCT         [0-7]
73DEC         [0-9]
74HEX         [0-9a-fA-F]
75IDENT       [a-zA-Z_][a-zA-Z0-9_]*
76WSPACE      [ \r\t\n\f]
77EXP         [eE][+-]?{DEC}+
78
79%x COMMENT
80%x STRDATA
81%x RAWDATA
82%x INCLUDE
83
84%%
85
86enum                     return ENUM;
87resource                 return RESOURCE;
88array                    return ARRAY;
89message                  return MESSAGE;
90archive                  return ARCHIVE;
91type                     return RTYPE;
92import                   return IMPORT;
93
94false                    yylval.b = false; return BOOL;
95true                     yylval.b = true;  return BOOL;
96
970[xX]{HEX}{1,16}         { yylval.i = strtoull(yytext + 2, NULL, 16);
98                           return INTEGER; }
990{OCT}{1,24}             { yylval.i = strtoull(yytext, NULL, 8);
100                           return INTEGER; }
1010[bB]{BIN}{1,64}         { yylval.i = strtoull(yytext + 2, NULL, 2);
102                           return INTEGER; }
103{DEC}+                   { yylval.i = strtoull(yytext, NULL, 10);
104                           return INTEGER; }
105'....'                   { yylval.i = (yytext[1] << 24)
106                                    | (yytext[2] << 16)
107                                    | (yytext[3] << 8)
108                                    |  yytext[4];
109                           return INTEGER; }
110
111{DEC}+{EXP}              yylval.f = strtod(yytext, NULL); return FLOAT;
112{DEC}*\.{DEC}+{EXP}?     yylval.f = strtod(yytext, NULL); return FLOAT;
113{DEC}+\.{DEC}*{EXP}?     yylval.f = strtod(yytext, NULL); return FLOAT;
114
115#{DEC}+                  { yylval.t = strtoul(yytext + 1, NULL, 10);
116                           return TYPECODE; }
117#0[xX]{HEX}{1,8}         { yylval.t = strtoul(yytext + 3, NULL, 16);
118                           return TYPECODE; }
119#'....'                  { yylval.t = (yytext[2] << 24)
120                                    | (yytext[3] << 16)
121                                    | (yytext[4] << 8)
122                                    |  yytext[5];
123                           return TYPECODE; }
124
125{IDENT}                  { yylval.I = (char*) alloc_mem(yyleng + 1);
126                           memcpy(yylval.I, yytext, yyleng + 1);
127                           return IDENT; }
128
129\"                       BEGIN(STRDATA); resetbuf();
130<STRDATA>\"{WSPACE}+\"   /* concatenate two literals */
131<STRDATA>\"              { BEGIN(INITIAL);
132                           addbuf('\0');
133                           yylval.d.type = get_type("string");
134                           yylval.d.size = lexcnt;
135                           yylval.d.ptr  = alloc_mem(lexcnt);
136                           memcpy(yylval.d.ptr, lexbuf, lexcnt);
137                           return STRING; }
138<STRDATA>\n              LEXERROR("string not terminated")
139<STRDATA>\\{OCT}{3}      addbuf(strtol(yytext + 1, NULL, 8));
140<STRDATA>\\0[xX]{HEX}{2} addbuf(strtol(yytext + 3, NULL, 16));
141<STRDATA>\\[xX]{HEX}{2}  addbuf(strtol(yytext + 2, NULL, 16));
142<STRDATA>\\b             addbuf('\b');
143<STRDATA>\\f             addbuf('\f');
144<STRDATA>\\n             addbuf('\n');
145<STRDATA>\\r             addbuf('\r');
146<STRDATA>\\t             addbuf('\t');
147<STRDATA>\\v             addbuf('\v');
148<STRDATA>\\0             addbuf('\0');
149<STRDATA>\\.             addbuf(yytext[1]);
150<STRDATA>.               addbuf(yytext[0]);
151
152$\"                      BEGIN(RAWDATA); resetbuf();
153<RAWDATA>\"{WSPACE}+$\"  /* concatenate two literals */
154<RAWDATA>\"              { BEGIN(INITIAL);
155                           yylval.d.type = get_type("raw");
156                           yylval.d.size = lexcnt;
157                           yylval.d.ptr  = alloc_mem(lexcnt);
158                           memcpy(yylval.d.ptr, lexbuf, lexcnt);
159                           return RAW; }
160<RAWDATA>\n              LEXERROR("raw data not terminated")
161<RAWDATA>{HEX}{2}        addbuf(strtol(yytext, NULL, 16));
162<RAWDATA>{HEX}           LEXERROR("number of characters must be even")
163<RAWDATA>.               LEXERROR("invalid character in raw data")
164
165"/*"                     BEGIN(COMMENT);  /* eat multi-line comment */
166<COMMENT>[^*\n]*         /* eat anything that is not a '*' */
167<COMMENT>"*"+[^*/\n]*    /* eat up '*'s not followed by '/'s */
168<COMMENT>\n
169<COMMENT>"*"+"/"         BEGIN(INITIAL);
170<COMMENT><<EOF>>         LEXERROR("forgot to close /*..*/ comment")
171
172"//"[^\n]*               /* eat single-line comment */
173{WSPACE}+                /* eat whitespace */
174
175\#include[ \t]+\"        BEGIN(INCLUDE);
176<INCLUDE>[ \t]*          /* eat the whitespace */
177<INCLUDE>[^ \t\n\"]+\"   open_include();
178<INCLUDE>\n              LEXERROR("error in include statement")
179<INCLUDE><<EOF>>         LEXERROR("error in include statement")
180<<EOF>>                  { if (include_stack.empty())
181                               yyterminate();
182                           else
183                               close_include(); }
184
185.                        return yytext[0];
186
187%%
188//------------------------------------------------------------------------------
189
190void
191resetbuf()
192{
193	lexptr = lexbuf;
194	lexcnt = 0;
195}
196
197
198void
199addbuf(uint8 b)
200{
201	if (lexcnt == lexsize) {
202		lexsize += LEX_BUF_SIZE;
203		lexbuf = (uint8*) realloc(lexbuf, lexsize);
204		if (lexbuf == NULL)
205			abort_compile(B_NO_MEMORY, "out of memory");
206
207		lexptr = lexbuf + lexcnt;
208	}
209
210	*lexptr++ = b;
211	++lexcnt;
212}
213
214
215void
216open_include()
217{
218	yytext[yyleng - 1] = '\0';  // remove trailing " quote
219
220	char tmpname[B_PATH_NAME_LENGTH];
221	if (open_file_from_include_dir(yytext, tmpname)) {
222		yyin = fopen(tmpname, "r");
223		if (yyin != NULL) {
224			include_t incl;
225			incl.buffer   = YY_CURRENT_BUFFER;
226			incl.lineno   = yylineno;
227			incl.filename = strdup(lexfile);
228			include_stack.push(incl);
229
230			strcpy(lexfile, tmpname);
231			yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
232			yylineno = 1;
233
234			BEGIN(INITIAL);
235			return;
236		}
237	}
238
239	abort_compile(RDEF_COMPILE_ERR, "cannot open include %s", yytext);
240}
241
242
243void
244close_include()
245{
246	fclose(yyin);
247	yy_delete_buffer(YY_CURRENT_BUFFER);
248
249	include_t incl = include_stack.top();
250	include_stack.pop();
251
252	yy_switch_to_buffer(incl.buffer);
253	yylineno = incl.lineno;
254	strcpy(lexfile, incl.filename);
255
256	free(incl.filename);
257}
258
259
260void
261init_lexer()
262{
263	lexsize = LEX_BUF_SIZE;
264	lexbuf = (uint8*) malloc(lexsize);
265	if (lexbuf == NULL)
266		abort_compile(B_NO_MEMORY, "out of memory");
267
268	yyrestart(yyin);  // necessary for multiple input files
269	yylineno = 1;
270}
271
272
273void
274clean_up_lexer()
275{
276	while (!include_stack.empty()) {
277		close_include();
278	}
279
280	if (stdin != yyin)
281		fclose(yyin);
282	yy_delete_buffer(YY_CURRENT_BUFFER);
283
284	free(lexbuf);
285}
286
287