xref: /linux/scripts/genksyms/lex.l (revision d4b996f9ef1fe83d9ce9ad5c1ca0bd8231638ce5)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * Lexical analysis for genksyms.
4  * Copyright 1996, 1997 Linux International.
5  *
6  * New implementation contributed by Richard Henderson <rth@tamu.edu>
7  * Based on original work by Bjorn Ekwall <bj0rn@blox.se>
8  *
9  * Taken from Linux modutils 2.4.22.
10  */
11 
12 %{
13 
14 #include <limits.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <ctype.h>
18 
19 #include "genksyms.h"
20 #include "parse.tab.h"
21 
22 /* We've got a two-level lexer here.  We let flex do basic tokenization
23    and then we categorize those basic tokens in the second stage.  */
24 #define YY_DECL		static int yylex1(void)
25 
26 %}
27 
28 IDENT			[A-Za-z_\$][A-Za-z0-9_\$]*
29 
30 O_INT			0[0-7]*
31 D_INT			[1-9][0-9]*
32 X_INT			0[Xx][0-9A-Fa-f]+
33 I_SUF			[Uu]|[Ll]|[Uu][Ll]|[Ll][Uu]
34 INT			({O_INT}|{D_INT}|{X_INT}){I_SUF}?
35 
36 FRAC			([0-9]*\.[0-9]+)|([0-9]+\.)
37 EXP			[Ee][+-]?[0-9]+
38 F_SUF			[FfLl]
39 REAL			({FRAC}{EXP}?{F_SUF}?)|([0-9]+{EXP}{F_SUF}?)
40 
41 STRING			L?\"([^\\\"]*\\.)*[^\\\"]*\"
42 CHAR			L?\'([^\\\']*\\.)*[^\\\']*\'
43 
44 MC_TOKEN		([~%^&*+=|<>/-]=)|(&&)|("||")|(->)|(<<)|(>>)
45 
46 /* We don't do multiple input files.  */
47 %option noyywrap
48 
49 %option noinput
50 
51 %%
52 
53 
54  /* Keep track of our location in the original source files.  */
55 ^#[ \t]+{INT}[ \t]+\"[^\"\n]+\".*\n	return FILENAME;
56 ^#.*\n					cur_line++;
57 \n					cur_line++;
58 
59  /* Ignore all other whitespace.  */
60 [ \t\f\v\r]+				;
61 
62 
63 {STRING}				return STRING;
64 {CHAR}					return CHAR;
65 {IDENT}					return IDENT;
66 
67  /* The Pedant requires that the other C multi-character tokens be
68     recognized as tokens.  We don't actually use them since we don't
69     parse expressions, but we do want whitespace to be arranged
70     around them properly.  */
71 {MC_TOKEN}				return OTHER;
72 {INT}					return INT;
73 {REAL}					return REAL;
74 
75 "..."					return DOTS;
76 
77  /* All other tokens are single characters.  */
78 .					return yytext[0];
79 
80 
81 %%
82 
83 /* Bring in the keyword recognizer.  */
84 
85 #include "keywords.c"
86 
87 
88 /* Macros to append to our phrase collection list.  */
89 
90 /*
91  * We mark any token, that that equals to a known enumerator, as
92  * SYM_ENUM_CONST. The parser will change this for struct and union tags later,
93  * the only problem is struct and union members:
94  *    enum e { a, b }; struct s { int a, b; }
95  * but in this case, the only effect will be, that the ABI checksums become
96  * more volatile, which is acceptable. Also, such collisions are quite rare,
97  * so far it was only observed in include/linux/telephony.h.
98  */
99 #define _APP(T,L)	do {						   \
100 			  cur_node = next_node;				   \
101 			  next_node = xmalloc(sizeof(*next_node));	   \
102 			  next_node->next = cur_node;			   \
103 			  cur_node->string = memcpy(xmalloc(L+1), T, L+1); \
104 			  cur_node->tag =				   \
105 			    find_symbol(cur_node->string, SYM_ENUM_CONST, 1)?\
106 			    SYM_ENUM_CONST : SYM_NORMAL ;		   \
107 			  cur_node->in_source_file = in_source_file;       \
108 			} while (0)
109 
110 #define APP		_APP(yytext, yyleng)
111 
112 
113 /* The second stage lexer.  Here we incorporate knowledge of the state
114    of the parser to tailor the tokens that are returned.  */
115 
116 int
117 yylex(void)
118 {
119   static enum {
120     ST_NOTSTARTED, ST_NORMAL, ST_ATTRIBUTE, ST_ASM, ST_TYPEOF, ST_TYPEOF_1,
121     ST_BRACKET, ST_BRACE, ST_EXPRESSION, ST_STATIC_ASSERT,
122   } lexstate = ST_NOTSTARTED;
123 
124   static int suppress_type_lookup, dont_want_brace_phrase;
125   static struct string_list *next_node;
126   static char *source_file;
127 
128   int token, count = 0;
129   struct string_list *cur_node;
130 
131   if (lexstate == ST_NOTSTARTED)
132     {
133       next_node = xmalloc(sizeof(*next_node));
134       next_node->next = NULL;
135       lexstate = ST_NORMAL;
136     }
137 
138 repeat:
139   token = yylex1();
140 
141   if (token == 0)
142     return 0;
143   else if (token == FILENAME)
144     {
145       char *file, *e;
146 
147       /* Save the filename and line number for later error messages.  */
148 
149       if (cur_filename)
150 	free(cur_filename);
151 
152       file = strchr(yytext, '\"')+1;
153       e = strchr(file, '\"');
154       *e = '\0';
155       cur_filename = memcpy(xmalloc(e-file+1), file, e-file+1);
156       cur_line = atoi(yytext+2);
157 
158       if (!source_file) {
159         source_file = xstrdup(cur_filename);
160         in_source_file = 1;
161       } else {
162         in_source_file = (strcmp(cur_filename, source_file) == 0);
163       }
164 
165       goto repeat;
166     }
167 
168   switch (lexstate)
169     {
170     case ST_NORMAL:
171       switch (token)
172 	{
173 	case IDENT:
174 	  APP;
175 	  {
176 	    int r = is_reserved_word(yytext, yyleng);
177 	    if (r >= 0)
178 	      {
179 		switch (token = r)
180 		  {
181 		  case ATTRIBUTE_KEYW:
182 		    lexstate = ST_ATTRIBUTE;
183 		    count = 0;
184 		    goto repeat;
185 		  case ASM_KEYW:
186 		    lexstate = ST_ASM;
187 		    count = 0;
188 		    goto repeat;
189 		  case TYPEOF_KEYW:
190 		    lexstate = ST_TYPEOF;
191 		    count = 0;
192 		    goto repeat;
193 
194 		  case STRUCT_KEYW:
195 		  case UNION_KEYW:
196 		  case ENUM_KEYW:
197 		    dont_want_brace_phrase = 3;
198 		    suppress_type_lookup = 2;
199 		    goto fini;
200 
201 		  case EXPORT_SYMBOL_KEYW:
202 		      goto fini;
203 
204 		  case STATIC_ASSERT_KEYW:
205 		    lexstate = ST_STATIC_ASSERT;
206 		    count = 0;
207 		    goto repeat;
208 		  }
209 	      }
210 	    if (!suppress_type_lookup)
211 	      {
212 		if (find_symbol(yytext, SYM_TYPEDEF, 1))
213 		  token = TYPE;
214 	      }
215 	  }
216 	  break;
217 
218 	case '[':
219 	  APP;
220 	  lexstate = ST_BRACKET;
221 	  count = 1;
222 	  goto repeat;
223 
224 	case '{':
225 	  APP;
226 	  if (dont_want_brace_phrase)
227 	    break;
228 	  lexstate = ST_BRACE;
229 	  count = 1;
230 	  goto repeat;
231 
232 	case '=': case ':':
233 	  APP;
234 	  lexstate = ST_EXPRESSION;
235 	  break;
236 
237 	default:
238 	  APP;
239 	  break;
240 	}
241       break;
242 
243     case ST_ATTRIBUTE:
244       APP;
245       switch (token)
246 	{
247 	case '(':
248 	  ++count;
249 	  goto repeat;
250 	case ')':
251 	  if (--count == 0)
252 	    {
253 	      lexstate = ST_NORMAL;
254 	      token = ATTRIBUTE_PHRASE;
255 	      break;
256 	    }
257 	  goto repeat;
258 	default:
259 	  goto repeat;
260 	}
261       break;
262 
263     case ST_ASM:
264       APP;
265       switch (token)
266 	{
267 	case '(':
268 	  ++count;
269 	  goto repeat;
270 	case ')':
271 	  if (--count == 0)
272 	    {
273 	      lexstate = ST_NORMAL;
274 	      token = ASM_PHRASE;
275 	      break;
276 	    }
277 	  goto repeat;
278 	default:
279 	  goto repeat;
280 	}
281       break;
282 
283     case ST_TYPEOF_1:
284       if (token == IDENT)
285 	{
286 	  if (is_reserved_word(yytext, yyleng) >= 0
287 	      || find_symbol(yytext, SYM_TYPEDEF, 1))
288 	    {
289 	      yyless(0);
290 	      unput('(');
291 	      lexstate = ST_NORMAL;
292 	      token = TYPEOF_KEYW;
293 	      break;
294 	    }
295 	  _APP("(", 1);
296 	}
297 	lexstate = ST_TYPEOF;
298 	/* FALLTHRU */
299 
300     case ST_TYPEOF:
301       switch (token)
302 	{
303 	case '(':
304 	  if ( ++count == 1 )
305 	    lexstate = ST_TYPEOF_1;
306 	  else
307 	    APP;
308 	  goto repeat;
309 	case ')':
310 	  APP;
311 	  if (--count == 0)
312 	    {
313 	      lexstate = ST_NORMAL;
314 	      token = TYPEOF_PHRASE;
315 	      break;
316 	    }
317 	  goto repeat;
318 	default:
319 	  APP;
320 	  goto repeat;
321 	}
322       break;
323 
324     case ST_BRACKET:
325       APP;
326       switch (token)
327 	{
328 	case '[':
329 	  ++count;
330 	  goto repeat;
331 	case ']':
332 	  if (--count == 0)
333 	    {
334 	      lexstate = ST_NORMAL;
335 	      token = BRACKET_PHRASE;
336 	      break;
337 	    }
338 	  goto repeat;
339 	default:
340 	  goto repeat;
341 	}
342       break;
343 
344     case ST_BRACE:
345       APP;
346       switch (token)
347 	{
348 	case '{':
349 	  ++count;
350 	  goto repeat;
351 	case '}':
352 	  if (--count == 0)
353 	    {
354 	      lexstate = ST_NORMAL;
355 	      token = BRACE_PHRASE;
356 	      break;
357 	    }
358 	  goto repeat;
359 	default:
360 	  goto repeat;
361 	}
362       break;
363 
364     case ST_EXPRESSION:
365       switch (token)
366 	{
367 	case '(': case '[': case '{':
368 	  ++count;
369 	  APP;
370 	  goto repeat;
371 	case '}':
372 	  /* is this the last line of an enum declaration? */
373 	  if (count == 0)
374 	    {
375 	      /* Put back the token we just read so's we can find it again
376 		 after registering the expression.  */
377 	      unput(token);
378 
379 	      lexstate = ST_NORMAL;
380 	      token = EXPRESSION_PHRASE;
381 	      break;
382 	    }
383 	  /* FALLTHRU */
384 	case ')': case ']':
385 	  --count;
386 	  APP;
387 	  goto repeat;
388 	case ',': case ';':
389 	  if (count == 0)
390 	    {
391 	      /* Put back the token we just read so's we can find it again
392 		 after registering the expression.  */
393 	      unput(token);
394 
395 	      lexstate = ST_NORMAL;
396 	      token = EXPRESSION_PHRASE;
397 	      break;
398 	    }
399 	  APP;
400 	  goto repeat;
401 	default:
402 	  APP;
403 	  goto repeat;
404 	}
405       break;
406 
407     case ST_STATIC_ASSERT:
408       APP;
409       switch (token)
410 	{
411 	case '(':
412 	  ++count;
413 	  goto repeat;
414 	case ')':
415 	  if (--count == 0)
416 	    {
417 	      lexstate = ST_NORMAL;
418 	      token = STATIC_ASSERT_PHRASE;
419 	      break;
420 	    }
421 	  goto repeat;
422 	default:
423 	  goto repeat;
424 	}
425       break;
426 
427     default:
428       exit(1);
429     }
430 fini:
431 
432   if (suppress_type_lookup > 0)
433     --suppress_type_lookup;
434   if (dont_want_brace_phrase > 0)
435     --dont_want_brace_phrase;
436 
437   yylval = &next_node->next;
438 
439   return token;
440 }
441