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