1 /* In Emacs, please make this -*-c-*- mode. Thanks. */
2
3 /*******************************************************************************
4 *
5 * McStas, neutron ray-tracing package
6 * Copyright (C) 1997-2007, All rights reserved
7 * Risoe National Laboratory, Roskilde, Denmark
8 * Institut Laue Langevin, Grenoble, France
9 *
10 * Kernel: instrument.l
11 *
12 * %Identification
13 * Written by: K.N.
14 * Date: Jul 1, 1997
15 * Origin: Risoe
16 * Release: McStas 1.6
17 * Version: $Revision$
18 *
19 * Flex scanner for instrument definition files.
20 *
21 *******************************************************************************/
22
23
24 /* Definition section. */
25
26 /* Do not use the `yywrap feature' - only scan a single file (see Flex manual). */
27 %option noyywrap
28
29
30 %{
31 #include <string.h>
32 #include <math.h>
33 #include <stdio.h>
34
35 #include "mccode.h"
36 #include "instrument.tab.h"
37
38 /* Fix things for bison option %pure_parser. */
39 #define YY_DECL int yylex(YYSTYPE *yylvalp)
40 #define yylval (*yylvalp)
41
42
43 /* Structure to hold the state of a file being parsed. */
44 struct file_state
45 {
46 YY_BUFFER_STATE buffer;
47 char *filename;
48 char *switch_line;
49 int line;
50 int oldstate; /* Saved lexer start condition. */
51 int visible_eof; /* If true, tell parser about end-of-file. */
52 };
53
54 #define MAX_INCLUDE 256
55 static struct file_state file_stack[MAX_INCLUDE + 1];
56 static int file_stack_ptr = 0;
57 static char *switch_line = NULL;
58
59 static void push_include(char *name);
60
61 %}
62
63
64 /* Lexer states. */
65
66 /* The state ccomment is used for scanning c-style comments. The state ccode
67 is used when scanning embedded C code blocks. */
68 %x ccomment
69 %x ccode
70
71 /* The state initial_token is only used to output an initial token to
72 discriminate between parsing general instrument definitions and autoloaded
73 component definitions. */
74 %x initial_token
75
76 /* Get file name in %include. */
77 %x inclname
78
79 /* Get full %include line within C code blocks. */
80 %x cfullincl
81
82 /* Get file name in %include within C code blocks. */
83 %x cinclname
84
85
86 /* Abbreviations. */
87 DIGIT [0-9]
88 ALPHA [A-Za-z]
89 ALPHANUM {ALPHA}|{DIGIT}|"_"
90
91 NUMBER -?({DIGIT}*".")?{DIGIT}+([Ee][+-]?{DIGIT}+)?
92 ID {DIGIT}*{ALPHA}{ALPHANUM}*
93 EOL (\r\n|\n|\r)
94 INCLUDE "%include"
95
96 %%
97
98 /* Initially, output a single token to the parser to tell it whether to parse
99 general instrument definitions or autoloaded component definitions. */
100 <initial_token>.|\n |
101 <initial_token><<EOF>> {
102 yyless(0);
103 BEGIN(INITIAL);
104 return parse_restricted ? TOK_RESTRICTED : TOK_GENERAL;
105 }
106
107 ABSOLUTE return TOK_ABSOLUTE;
108 AT return TOK_AT;
109 COMPONENT return TOK_COMPONENT;
110 DECLARE return TOK_DECLARE;
111 DEFINE return TOK_DEFINE;
112 DEFINITION return TOK_DEFINITION;
113 END return TOK_END;
114 MCDISPLAY return TOK_MCDISPLAY;
115 FINALLY return TOK_FINALLY;
116 INITIALIZE return TOK_INITIALIZE;
117 INSTRUMENT return TOK_INSTRUMENT;
118 OUTPUT return TOK_OUTPUT;
119 PARAMETERS return TOK_PARAMETERS;
120 RELATIVE return TOK_RELATIVE;
121 ROTATED return TOK_ROTATED;
122 PREVIOUS return TOK_PREVIOUS;
123 SETTING return TOK_SETTING;
124 TRACE return TOK_TRACE;
125 SHARE return TOK_SHARE;
126 EXTEND return TOK_EXTEND;
127 GROUP return TOK_GROUP; /* extended McStas grammar */
128 SAVE return TOK_SAVE;
129 JUMP return TOK_JUMP; /* extended McStas grammar */
130 WHEN return TOK_WHEN; /* extended McStas grammar */
131 NEXT return TOK_NEXT; /* extended McStas grammar */
132 ITERATE return TOK_ITERATE; /* extended McStas grammar */
133 MYSELF return TOK_MYSELF; /* extended McStas grammar */
134 COPY return TOK_COPY; /* extended McStas grammar */
135 SPLIT return TOK_SPLIT; /* extended McStas grammar */
136 REMOVABLE return TOK_REMOVABLE; /* extended McStas grammar with include */
137
138 "("|")"|"["|"]"|"{"|"}"|"," return yytext[0]; /* Punctuation. */
139 "="|"*" return yytext[0]; /* Operator. */
140
141 {NUMBER} yylval.number = str_dup(yytext); return TOK_NUMBER;
142 /* Note: Since ID overlaps with NUMBER (eg. "2E3"), ID must come
143 after NUMBER */
144 {ID} yylval.string = str_dup(yytext); return TOK_ID;
145
146 /* Scanning all other C tokens used in expressions for component
147 * actual parameters.
148 * IMPORTANT!: Whenever a token is removed from here to make an independent
149 * separate token, the new token must be added to the parser rules for
150 * genatexp/topatexp.
151 */
152 "->"|"."|"!"|"~"|"++"|"--"|"+"|"-"|"&"|"sizeof"|"*"|"/"|"%"|"+"|"-"|"<<"|">>"|"<"|"<="|">"|">="|"=="|"!="|"^"|"|"|"&&"|"||"|"?"|":"|"+="|"-="|"*="|"/="|"%="|"&="|"^="|"|="|"<<="|">>=" {
153 yylval.string = str_dup(yytext);
154 return TOK_CTOK;
155 \
156 }
157
158 /* Scanning embedded C code. */
159
160 "%""{"[\t ]*{EOL} {
161 yylval.linenum = instr_current_line;
162 instr_current_line++;
163 BEGIN(ccode);
164 return TOK_CODE_START;
165 }
166 "%""{"[^\n]*{EOL} {
167 instr_current_line++;
168 print_error("%%{ token not on a line by itself "
169 "on line %d of file '%s': %s.\n",
170 instr_current_line, instr_current_filename, yytext);
171 return TOK_INVALID;
172 }
173
174 <ccode>{
175 /* normal %} symbol to end C code block */
176 [\t ]*"%""}"[\t ]*{EOL} instr_current_line++; BEGIN(INITIAL); return TOK_CODE_END;
177 /* %} symbol surrounded by some unrelevant stuff */
178 [^\n]*"%""}"[^\n]*{EOL} {
179 instr_current_line++;
180 print_warn(NULL, "%%} terminator not on a line by itself "
181 "on line %d of file '%s': %s.\n",
182 instr_current_line, instr_current_filename, yytext);
183 }
184 /* %include full line -> jump to cfullincl state */
185 [\t ]*{INCLUDE}[^\n]*{EOL} {
186 yyless(0); /* re-use the current line, but within cfullincl state */
187 BEGIN(cfullincl);
188 }
189 /* full line as C code */
190 [^\n]*{EOL} {
191 instr_current_line++;
192 yylval.string = str_dup(yytext);
193 return TOK_CODE_LINE;
194 }
195 } /* end ccode */
196
197 /* Quoted strings. Simple version: no escape sequences. */
198 \"[^\"\n\\]*\" {
199 yylval.string =
200 str_dup_n(&yytext[1], strlen(yytext) - 2);
201 return TOK_STRING;
202 }
203 /* Quoted strings with escape sequence (e.g Win32 path): preserve all chars */
204 \"[^\"\n]*\\[^\"\n]*\" {
205 yylval.string = str_dup_n(&yytext[1], strlen(yytext) - 2);
206 return TOK_STRING;
207 }
208 \"[^\n\"]*{EOL} {
209 print_error("Error: Unterminated string "
210 "on line %d of file '%s': %s.\n",
211 instr_current_line, instr_current_filename, yytext);
212 }
213
214 /* %-style comments - ignore everything to end of line. */
215 "%"{EOL} instr_current_line++; /* Ignore comment. */
216 "% "[^\n]*{EOL} instr_current_line++; /* Ignore comment. */
217
218 /* Include files for McStas comp/instr (INITIAL state). */
219 /* then next token is the file name */
220 {INCLUDE}[ \t]+\" BEGIN(inclname);
221 <inclname>{
222 /* name ends with a quote char -> include as INITIAL state */
223 [^\"\n]+\" {
224 yytext[yyleng - 1] = '\0';
225 BEGIN(INITIAL);
226 if (verbose) fprintf(stderr, "Embedding %s\n", yytext);
227 push_include(yytext);
228 }
229 /* name contains char, including quote and \n -> not valid */
230 [\"\n].* {
231 print_error("Error in %%include statement "
232 "on line %d of file '%s': %s.\n",
233 instr_current_line, instr_current_filename, yytext);
234 BEGIN(INITIAL);
235 }
236 } /* end inclname */
237 /* Include files within C code blocks (ccode state)*/
238 /* next token is full line, regenerated by yyless(0) */
239 <cfullincl>{INCLUDE}[ \t]+\" BEGIN(cinclname);
240 <cinclname>{
241 /* name ends with a quote char, with extension -> include as ccode state */
242 [^\"\n]+\.+.\" {
243 yytext[yyleng - 1] = '\0';
244 BEGIN(ccode);
245 if (verbose) fprintf(stderr, "Embedding file %s\n", yytext);
246 push_include(yytext);
247 }
248 /* name ends with a quote char, but no ext -> include as ccode state
249 * this occurs when importing a library .h/.c The .c is only included
250 * when instr->runtime option is true
251 */
252 [^\"\n]+\" {
253 char *tmp0, *tmp1;
254 yytext[yyleng - 1] = '\0';
255 tmp0 = str_dup(yytext);
256 if (!symtab_lookup(lib_instances, tmp0))
257 {
258 tmp1 = str_cat(tmp0, ".h", NULL);
259 if (instrument_definition->include_runtime)
260 {
261 switch_line = str_cat(tmp0, ".c", NULL);
262 }
263 else
264 fprintf(stderr,"Dependency: %s.o\n", tmp0);
265
266 BEGIN(ccode);
267 if (verbose) fprintf(stderr, "Embedding library %s\n", tmp1);
268
269 push_include(tmp1);
270 symtab_add(lib_instances, tmp0, NULL);
271 str_free(tmp1);
272 }
273 else
274 {
275 BEGIN(ccode);
276 instr_current_line++; /* library was previously embedded */
277 }
278 str_free(tmp0);
279 }
280 /* name contains char, including quote and \n -> not valid */
281 [\"\n].* {
282 print_error("Error in %%include statement "
283 "on line %d of file '%s': %s.\n",
284 instr_current_line, instr_current_filename, yytext);
285 BEGIN(ccode);
286 }
287 } /* end cinclname */
288
289 /* C++ "//"-style comments - ignore everything to end of line. */
290 "//"[^\n]*{EOL} instr_current_line++; /* Ignore comment. */
291
292 /* C-style comments. */
293 "/*" BEGIN(ccomment);
294 <ccomment>{
295 [^*\n]* /* Ignore comment. */
296 [^*\n]*{EOL} instr_current_line++; /* Ignore comment. */
297 "*"+[^*/\n]* /* Ignore comment. */
298 "*"+[^*/\n]*{EOL} instr_current_line++; /* Ignore comment. */
299 "*"+"/" BEGIN(INITIAL); /* End of comment. */
300 }
301
302 [ \t]+ /* Ignore whitespace. */
303 [ \t]*{EOL} instr_current_line++; /* Ignore whitespace. */
304
305 <INITIAL,ccode><<EOF>> {
306 if(file_stack_ptr <= 0)
307 {
308 /* EOF on main instrument file. */
309 yyterminate();
310 }
311 else
312 {
313 --file_stack_ptr;
314 yy_delete_buffer(YY_CURRENT_BUFFER);
315 yy_switch_to_buffer(file_stack[file_stack_ptr].buffer);
316 instr_current_filename = file_stack[file_stack_ptr].filename;
317 instr_current_line = file_stack[file_stack_ptr].line;
318 switch_line = file_stack[file_stack_ptr].switch_line;
319 if (switch_line)
320 {
321 char *tmp0;
322 tmp0 = str_dup(switch_line);
323 str_free(switch_line);
324 switch_line = NULL;
325 BEGIN(ccode);
326 if (verbose) fprintf(stderr, "Embedding library %s\n", tmp0);
327 push_include(tmp0);
328 str_free(tmp0);
329 }
330 else
331 {
332 BEGIN(file_stack[file_stack_ptr].oldstate);
333 if(file_stack[file_stack_ptr].visible_eof)
334 yyterminate();
335 }
336
337 }
338 }
339
340 . {
341 print_error("Invalid character `%s' "
342 "on line %d of file '%s'.\n",
343 yytext, instr_current_line, instr_current_filename);
344 return TOK_INVALID;
345 }
346
347
348
349 %%
350
351 /* User code section. */
352
353 /* This flag is set when autoloading component definitions to make the lexer
354 output the special initial token to switch the parser to restricted
355 mode. */
356 int parse_restricted = FALSE;
357
358 /* Prepare to run lexical analysis on new file. */
359 void
lex_new_file(FILE * file)360 lex_new_file(FILE *file)
361 {
362 parse_restricted = FALSE;
363 BEGIN(initial_token);
364 yyrestart(file);
365 }
366
367
368 /* This handles the details of switching the lexer to a new file. */
369 static void
push_file(FILE * file,int restricted,int visible_eof)370 push_file(FILE *file, int restricted, int visible_eof)
371 {
372 if(file_stack_ptr >= MAX_INCLUDE)
373 fatal_error("Too deeply nested includes "
374 "on line %d of file '%s'.\n",
375 instr_current_line, instr_current_filename);
376 file_stack[file_stack_ptr].buffer = YY_CURRENT_BUFFER;
377 file_stack[file_stack_ptr].filename = instr_current_filename;
378 file_stack[file_stack_ptr].line = instr_current_line;
379 file_stack[file_stack_ptr].oldstate = YY_START;
380 file_stack[file_stack_ptr].visible_eof = visible_eof;
381 file_stack[file_stack_ptr].switch_line = switch_line;
382 file_stack_ptr++;
383
384 instr_current_line = 1;
385
386 yy_switch_to_buffer(yy_create_buffer(file, YY_BUF_SIZE));
387 parse_restricted = restricted;
388 }
389
390 /* Handle a new %include file. */
391 void
push_include(char * name)392 push_include(char *name)
393 {
394 FILE *file;
395
396 file = open_file_search(name);
397 if(file == NULL)
398 fatal_error("Cannot open include file '%s' "
399 "on line %d of file '%s'.\n",
400 name, instr_current_line, instr_current_filename);
401 push_file(file, FALSE, FALSE);
402 instr_current_filename = name;
403 instr_current_line = 1;
404 }
405
406 /* Handle a new autoincluded file (uses recursive parser call). */
407 void
push_autoload(FILE * file)408 push_autoload(FILE *file)
409 {
410 push_file(file, TRUE, TRUE);
411 BEGIN(initial_token);
412 }
413
414