xref: /freebsd/sys/dev/aic7xxx/aicasm/aicasm_scan.l (revision 5e53a4f90f82c4345f277dd87cc9292f26e04a29)
1 %{
2 /*-
3  * Lexical Analyzer for the Aic7xxx SCSI Host adapter sequencer assembler.
4  *
5  * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
6  * Copyright (c) 2001, 2002 Adaptec Inc.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions, and the following disclaimer,
14  *    without modification.
15  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
16  *    substantially similar to the "NO WARRANTY" disclaimer below
17  *    ("Disclaimer") and any redistribution must be conditioned upon
18  *    including a substantially similar Disclaimer requirement for further
19  *    binary redistribution.
20  * 3. Neither the names of the above-listed copyright holders nor the names
21  *    of any contributors may be used to endorse or promote products derived
22  *    from this software without specific prior written permission.
23  *
24  * Alternatively, this software may be distributed under the terms of the
25  * GNU General Public License ("GPL") version 2 as published by the Free
26  * Software Foundation.
27  *
28  * NO WARRANTY
29  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
32  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
38  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39  * POSSIBILITY OF SUCH DAMAGES.
40  *
41  * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_scan.l#19 $
42  *
43  * $FreeBSD$
44  */
45 
46 #include <sys/types.h>
47 
48 #include <inttypes.h>
49 #include <limits.h>
50 #include <regex.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <sysexits.h>
54 #include <sys/queue.h>
55 
56 #include "aicasm.h"
57 #include "aicasm_symbol.h"
58 #include "aicasm_gram.h"
59 
60 /* This is used for macro body capture too, so err on the large size. */
61 #define MAX_STR_CONST 4096
62 static char string_buf[MAX_STR_CONST];
63 static char *string_buf_ptr;
64 static int  parren_count;
65 static int  quote_count;
66 static char msgbuf[255];
67 
68 extern int yylex(void);
69 extern int mmlex(void);
70 extern int mmparse(void);
71 extern void mm_switch_to_buffer(YY_BUFFER_STATE);
72 extern void mm_delete_buffer(YY_BUFFER_STATE);
73 %}
74 
75 %option noinput
76 
77 PATH		([/]*[-A-Za-z0-9_.])+
78 WORD		[A-Za-z_][-A-Za-z_0-9]*
79 SPACE		[ \t]+
80 MCARG		[^(), \t]+
81 MBODY		((\\[^\n])*[^\n\\]*)+
82 
83 %x COMMENT
84 %x CEXPR
85 %x INCLUDE
86 %x STRING
87 %x MACRODEF
88 %x MACROARGLIST
89 %x MACROCALLARGS
90 %x MACROBODY
91 
92 %%
93 \n			{ ++yylineno; }
94 \r			;
95 "/*"			{ BEGIN COMMENT;  /* Enter comment eating state */ }
96 <COMMENT>"/*"		{ fprintf(stderr, "Warning! Comment within comment."); }
97 <COMMENT>\n		{ ++yylineno; }
98 <COMMENT>[^*/\n]*	;
99 <COMMENT>"*"+[^*/\n]*	;
100 <COMMENT>"/"+[^*/\n]*	;
101 <COMMENT>"*"+"/"	{ BEGIN INITIAL; }
102 if[ \t]*\(		{
103 				string_buf_ptr = string_buf;
104 				parren_count = 1;
105 				BEGIN CEXPR;
106 				return T_IF;
107 			}
108 <CEXPR>\(		{	*string_buf_ptr++ = '('; parren_count++; }
109 <CEXPR>\)		{
110 				parren_count--;
111 				if (parren_count == 0) {
112 					/* All done */
113 					BEGIN INITIAL;
114 					*string_buf_ptr = '\0';
115 					yylval.sym = symtable_get(string_buf);
116 					return T_CEXPR;
117 				} else {
118 					*string_buf_ptr++ = ')';
119 				}
120 			}
121 <CEXPR>\n		{ ++yylineno; }
122 <CEXPR>\r		;
123 <CEXPR>[^()\n]+	{
124 				char *yptr;
125 
126 				yptr = yytext;
127 				while (*yptr != '\0') {
128 					/* Remove duplicate spaces */
129 					if (*yptr == '\t')
130 						*yptr = ' ';
131 					if (*yptr == ' '
132 					 && string_buf_ptr != string_buf
133 					 && string_buf_ptr[-1] == ' ')
134 						yptr++;
135 					else
136 						*string_buf_ptr++ = *yptr++;
137 				}
138 			}
139 
140 VERSION			{ return T_VERSION; }
141 PREFIX			{ return T_PREFIX; }
142 PATCH_ARG_LIST		{ return T_PATCH_ARG_LIST; }
143 \"			{
144 				string_buf_ptr = string_buf;
145 				BEGIN STRING;
146 			}
147 <STRING>[^"]+		{
148 				char *yptr;
149 
150 				yptr = yytext;
151 				while (*yptr)
152 					*string_buf_ptr++ = *yptr++;
153 			}
154 <STRING>\"		{
155 				/* All done */
156 				BEGIN INITIAL;
157 				*string_buf_ptr = '\0';
158 				yylval.str = string_buf;
159 				return T_STRING;
160 			}
161 {SPACE}			 ;
162 
163 	/* Register/SCB/SRAM definition keywords */
164 export			{ return T_EXPORT; }
165 register		{ return T_REGISTER; }
166 const			{ yylval.value = FALSE; return T_CONST; }
167 download		{ return T_DOWNLOAD; }
168 address			{ return T_ADDRESS; }
169 access_mode		{ return T_ACCESS_MODE; }
170 modes			{ return T_MODES; }
171 RW|RO|WO		{
172 				 if (strcmp(yytext, "RW") == 0)
173 					yylval.value = RW;
174 				 else if (strcmp(yytext, "RO") == 0)
175 					yylval.value = RO;
176 				 else
177 					yylval.value = WO;
178 				 return T_MODE;
179 			}
180 BEGIN_CRITICAL		{ return T_BEGIN_CS; }
181 END_CRITICAL		{ return T_END_CS; }
182 SET_SRC_MODE		{ return T_SET_SRC_MODE; }
183 SET_DST_MODE		{ return T_SET_DST_MODE; }
184 field			{ return T_FIELD; }
185 enum			{ return T_ENUM; }
186 mask			{ return T_MASK; }
187 alias			{ return T_ALIAS; }
188 size			{ return T_SIZE; }
189 scb			{ return T_SCB; }
190 scratch_ram		{ return T_SRAM; }
191 accumulator		{ return T_ACCUM; }
192 mode_pointer		{ return T_MODE_PTR; }
193 allones			{ return T_ALLONES; }
194 allzeros		{ return T_ALLZEROS; }
195 none			{ return T_NONE; }
196 sindex			{ return T_SINDEX; }
197 A			{ return T_A; }
198 
199 	/* Opcodes */
200 shl			{ return T_SHL; }
201 shr			{ return T_SHR; }
202 ror			{ return T_ROR; }
203 rol			{ return T_ROL; }
204 mvi			{ return T_MVI; }
205 mov			{ return T_MOV; }
206 clr			{ return T_CLR; }
207 jmp			{ return T_JMP; }
208 jc			{ return T_JC;	}
209 jnc			{ return T_JNC;	}
210 je			{ return T_JE;	}
211 jne			{ return T_JNE;	}
212 jz			{ return T_JZ;	}
213 jnz			{ return T_JNZ;	}
214 call			{ return T_CALL; }
215 add			{ return T_ADD; }
216 adc			{ return T_ADC; }
217 bmov			{ return T_BMOV; }
218 inc			{ return T_INC; }
219 dec			{ return T_DEC; }
220 stc			{ return T_STC;	}
221 clc			{ return T_CLC; }
222 cmp			{ return T_CMP;	}
223 not			{ return T_NOT;	}
224 xor			{ return T_XOR;	}
225 test			{ return T_TEST;}
226 and			{ return T_AND;	}
227 or			{ return T_OR;	}
228 ret			{ return T_RET; }
229 nop			{ return T_NOP; }
230 else			{ return T_ELSE; }
231 
232 	/* Allowed Symbols */
233 \<\<			{ return T_EXPR_LSHIFT; }
234 \>\>			{ return T_EXPR_RSHIFT; }
235 [-+,:()~|&."{};<>[\]/*!=] { return yytext[0]; }
236 
237 	/* Number processing */
238 0[0-7]*			{
239 				yylval.value = strtol(yytext, NULL, 8);
240 				return T_NUMBER;
241 			}
242 
243 0[xX][0-9a-fA-F]+	{
244 				yylval.value = strtoul(yytext + 2, NULL, 16);
245 				return T_NUMBER;
246 			}
247 
248 [1-9][0-9]*		{
249 				yylval.value = strtol(yytext, NULL, 10);
250 				return T_NUMBER;
251 			}
252 	/* Include Files */
253 #include{SPACE}		{
254 				BEGIN INCLUDE;
255 				quote_count = 0;
256 				return T_INCLUDE;
257 			}
258 <INCLUDE>[<]		{ return yytext[0]; }
259 <INCLUDE>[>]		{ BEGIN INITIAL; return yytext[0]; }
260 <INCLUDE>[\"]		{
261 				if (quote_count != 0)
262 					BEGIN INITIAL;
263 				quote_count++;
264 				return yytext[0];
265 			}
266 <INCLUDE>{PATH}		{
267 				char *yptr;
268 
269 				yptr = yytext;
270 				string_buf_ptr = string_buf;
271 				while (*yptr)
272 					*string_buf_ptr++ = *yptr++;
273 				yylval.str = string_buf;
274 				*string_buf_ptr = '\0';
275 				return T_PATH;
276 			}
277 <INCLUDE>.		{ stop("Invalid include line", EX_DATAERR); }
278 #define{SPACE}		{
279 				BEGIN MACRODEF;
280 				return T_DEFINE;
281 			}
282 <MACRODEF>{WORD}{SPACE}	{
283 				char *yptr;
284 
285 				/* Strip space and return as a normal symbol */
286 				yptr = yytext;
287 				while (*yptr != ' ' && *yptr != '\t')
288 					yptr++;
289 				*yptr = '\0';
290 				yylval.sym = symtable_get(yytext);
291 				string_buf_ptr = string_buf;
292 				BEGIN MACROBODY;
293 				return T_SYMBOL;
294 			}
295 <MACRODEF>{WORD}\(	{
296 				/*
297 				 * We store the symbol with its opening
298 				 * parren so we can differentiate macros
299 				 * that take args from macros with the
300 				 * same name that do not take args as
301 				 * is allowed in C.
302 				 */
303 				BEGIN MACROARGLIST;
304 				yylval.sym = symtable_get(yytext);
305 				unput('(');
306 				return T_SYMBOL;
307 			}
308 <MACROARGLIST>{WORD}	{
309 				yylval.str = yytext;
310 				return T_ARG;
311 			}
312 <MACROARGLIST>{SPACE}   ;
313 <MACROARGLIST>[(,]	{
314 				return yytext[0];
315 			}
316 <MACROARGLIST>[)]	{
317 				string_buf_ptr = string_buf;
318 				BEGIN MACROBODY;
319 				return ')';
320 			}
321 <MACROARGLIST>.		{
322 				snprintf(msgbuf, sizeof(msgbuf), "Invalid character "
323 					 "'%c' in macro argument list",
324 					 yytext[0]);
325 				stop(msgbuf, EX_DATAERR);
326 			}
327 <MACROCALLARGS>{SPACE}  ;
328 <MACROCALLARGS>\(	{
329 				parren_count++;
330 				if (parren_count == 1)
331 					return ('(');
332 				*string_buf_ptr++ = '(';
333 			}
334 <MACROCALLARGS>\)	{
335 				parren_count--;
336 				if (parren_count == 0) {
337 					BEGIN INITIAL;
338 					return (')');
339 				}
340 				*string_buf_ptr++ = ')';
341 			}
342 <MACROCALLARGS>{MCARG}	{
343 				char *yptr;
344 
345 				yptr = yytext;
346 				while (*yptr)
347 					*string_buf_ptr++ = *yptr++;
348 			}
349 <MACROCALLARGS>\,	{
350 				if (string_buf_ptr != string_buf) {
351 					/*
352 					 * Return an argument and
353 					 * rescan this comma so we
354 					 * can return it as well.
355 					 */
356 					*string_buf_ptr = '\0';
357 					yylval.str = string_buf;
358 					string_buf_ptr = string_buf;
359 					unput(',');
360 					return T_ARG;
361 				}
362 				return ',';
363 			}
364 <MACROBODY>\\\n		{
365 				/* Eat escaped newlines. */
366 				++yylineno;
367 			}
368 <MACROBODY>\r		;
369 <MACROBODY>\n		{
370 				/* Macros end on the first unescaped newline. */
371 				BEGIN INITIAL;
372 				*string_buf_ptr = '\0';
373 				yylval.str = string_buf;
374 				++yylineno;
375 				return T_MACROBODY;
376 			}
377 <MACROBODY>{MBODY}	{
378 				char *yptr;
379 				char c;
380 
381 				yptr = yytext;
382 				while ((c = *yptr++)) {
383 					/*
384 					 * Strip carriage returns.
385 					 */
386 					if (c == '\r')
387 						continue;
388 					*string_buf_ptr++ = c;
389 				}
390 			}
391 {WORD}\(		{
392 				char *yptr;
393 				char *ycopy;
394 
395 				/* May be a symbol or a macro invocation. */
396 				yylval.sym = symtable_get(yytext);
397 				if (yylval.sym->type == MACRO) {
398 					YY_BUFFER_STATE old_state;
399 					YY_BUFFER_STATE temp_state;
400 
401 					ycopy = strdup(yytext);
402 					yptr = ycopy + yyleng;
403 					while (yptr > ycopy)
404 						unput(*--yptr);
405 					old_state = YY_CURRENT_BUFFER;
406 					temp_state =
407 					    yy_create_buffer(stdin,
408 							     YY_BUF_SIZE);
409 					yy_switch_to_buffer(temp_state);
410 					mm_switch_to_buffer(old_state);
411 					mmparse();
412 					mm_switch_to_buffer(temp_state);
413 					yy_switch_to_buffer(old_state);
414 					mm_delete_buffer(temp_state);
415 					expand_macro(yylval.sym);
416 				} else {
417 					if (yylval.sym->type == UNINITIALIZED) {
418 						/* Try without the '(' */
419 						symbol_delete(yylval.sym);
420 						yytext[yyleng-1] = '\0';
421 						yylval.sym =
422 						    symtable_get(yytext);
423 					}
424 					unput('(');
425 					return T_SYMBOL;
426 				}
427 			}
428 {WORD}			{
429 				yylval.sym = symtable_get(yytext);
430 				if (yylval.sym->type == MACRO) {
431 					expand_macro(yylval.sym);
432 				} else {
433 					return T_SYMBOL;
434 				}
435 			}
436 .			{
437 				snprintf(msgbuf, sizeof(msgbuf), "Invalid character "
438 					 "'%c'", yytext[0]);
439 				stop(msgbuf, EX_DATAERR);
440 			}
441 %%
442 
443 typedef struct include {
444         YY_BUFFER_STATE  buffer;
445         int              lineno;
446         char            *filename;
447 	SLIST_ENTRY(include) links;
448 }include_t;
449 
450 SLIST_HEAD(, include) include_stack;
451 
452 void
453 include_file(char *file_name, include_type type)
454 {
455 	FILE *newfile;
456 	include_t *include;
457 
458 	newfile = NULL;
459 	/* Try the current directory first */
460 	if (includes_search_curdir != 0 || type == SOURCE_FILE)
461 		newfile = fopen(file_name, "r");
462 
463 	if (newfile == NULL && type != SOURCE_FILE) {
464                 path_entry_t include_dir;
465                 for (include_dir = search_path.slh_first;
466                      include_dir != NULL;
467                      include_dir = include_dir->links.sle_next) {
468 			char fullname[PATH_MAX];
469 
470 			if ((include_dir->quoted_includes_only == TRUE)
471 			 && (type != QUOTED_INCLUDE))
472 				continue;
473 
474 			snprintf(fullname, sizeof(fullname),
475 				 "%s/%s", include_dir->directory, file_name);
476 
477 			if ((newfile = fopen(fullname, "r")) != NULL)
478 				break;
479                 }
480         }
481 
482 	if (newfile == NULL) {
483 		perror(file_name);
484 		stop("Unable to open input file", EX_SOFTWARE);
485 		/* NOTREACHED */
486 	}
487 
488 	if (type != SOURCE_FILE) {
489 		include = (include_t *)malloc(sizeof(include_t));
490 		if (include == NULL) {
491 			stop("Unable to allocate include stack entry",
492 			     EX_SOFTWARE);
493 			/* NOTREACHED */
494 		}
495 		include->buffer = YY_CURRENT_BUFFER;
496 		include->lineno = yylineno;
497 		include->filename = yyfilename;
498 		SLIST_INSERT_HEAD(&include_stack, include, links);
499 	}
500 	yy_switch_to_buffer(yy_create_buffer(newfile, YY_BUF_SIZE));
501 	yylineno = 1;
502 	yyfilename = strdup(file_name);
503 }
504 
505 static void next_substitution(struct symbol *mac_symbol, const char *body_pos,
506 			      const char **next_match,
507 			      struct macro_arg **match_marg, regmatch_t *match);
508 
509 void
510 expand_macro(struct symbol *macro_symbol)
511 {
512 	struct macro_arg *marg;
513 	struct macro_arg *match_marg;
514 	const char *body_head;
515 	const char *body_pos;
516 	const char *next_match;
517 	regmatch_t match = { .rm_so = 0, .rm_eo = 0 };
518 
519 	/*
520 	 * Due to the nature of unput, we must work
521 	 * backwards through the macro body performing
522 	 * any expansions.
523 	 */
524 	body_head = macro_symbol->info.macroinfo->body;
525 	body_pos = body_head + strlen(body_head);
526 	while (body_pos > body_head) {
527 		next_match = body_head;
528 		match_marg = NULL;
529 		next_substitution(macro_symbol, body_pos, &next_match,
530 				  &match_marg, &match);
531 
532 		/* Put back everything up until the replacement. */
533 		while (body_pos > next_match)
534 			unput(*--body_pos);
535 
536 		/* Perform the replacement. */
537 		if (match_marg != NULL) {
538 			const char *strp;
539 
540 			next_match = match_marg->replacement_text;
541 			strp = next_match + strlen(next_match);
542 			while (strp > next_match)
543 				unput(*--strp);
544 
545 			/* Skip past the unexpanded macro arg. */
546 			body_pos -= match.rm_eo - match.rm_so;
547 		}
548 	}
549 
550 	/* Cleanup replacement text. */
551 	STAILQ_FOREACH(marg, &macro_symbol->info.macroinfo->args, links) {
552 		free(marg->replacement_text);
553 	}
554 }
555 
556 /*
557  * Find the next substitution in the macro working backwards from
558  * body_pos until the beginning of the macro buffer.  next_match
559  * should be initialized to the beginning of the macro buffer prior
560  * to calling this routine.
561  */
562 static void
563 next_substitution(struct symbol *mac_symbol, const char *body_pos,
564 		  const char **next_match, struct macro_arg **match_marg,
565 		  regmatch_t *match)
566 {
567 	regmatch_t	  matches[2];
568 	struct macro_arg *marg;
569 	const char	 *search_pos;
570 	int		  retval;
571 
572 	do {
573 		search_pos = *next_match;
574 
575 		STAILQ_FOREACH(marg, &mac_symbol->info.macroinfo->args, links) {
576 
577 			retval = regexec(&marg->arg_regex, search_pos, 2,
578 					 matches, 0);
579 			if (retval == 0
580 			 && (matches[1].rm_eo + search_pos) <= body_pos
581 			 && (matches[1].rm_eo + search_pos) > *next_match) {
582 				*match = matches[1];
583 				*next_match = match->rm_eo + search_pos;
584 				*match_marg = marg;
585 			}
586 		}
587 	} while (search_pos != *next_match);
588 }
589 
590 int
591 yywrap(void)
592 {
593 	include_t *include;
594 
595 	yy_delete_buffer(YY_CURRENT_BUFFER);
596 	(void)fclose(yyin);
597 	if (yyfilename != NULL)
598 		free(yyfilename);
599 	yyfilename = NULL;
600 	include = include_stack.slh_first;
601 	if (include != NULL) {
602 		yy_switch_to_buffer(include->buffer);
603 		yylineno = include->lineno;
604 		yyfilename = include->filename;
605 		SLIST_REMOVE_HEAD(&include_stack, links);
606 		free(include);
607 		return (0);
608 	}
609 	return (1);
610 }
611