xref: /freebsd/sys/dev/aic7xxx/aicasm/aicasm_scan.l (revision 10b59a9b4add0320d52c15ce057dd697261e7dfc)
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 PATH		([/]*[-A-Za-z0-9_.])+
76 WORD		[A-Za-z_][-A-Za-z_0-9]*
77 SPACE		[ \t]+
78 MCARG		[^(), \t]+
79 MBODY		((\\[^\n])*[^\n\\]*)+
80 
81 %x COMMENT
82 %x CEXPR
83 %x INCLUDE
84 %x STRING
85 %x MACRODEF
86 %x MACROARGLIST
87 %x MACROCALLARGS
88 %x MACROBODY
89 
90 %%
91 \n			{ ++yylineno; }
92 \r			;
93 "/*"			{ BEGIN COMMENT;  /* Enter comment eating state */ }
94 <COMMENT>"/*"		{ fprintf(stderr, "Warning! Comment within comment."); }
95 <COMMENT>\n		{ ++yylineno; }
96 <COMMENT>[^*/\n]*	;
97 <COMMENT>"*"+[^*/\n]*	;
98 <COMMENT>"/"+[^*/\n]*	;
99 <COMMENT>"*"+"/"	{ BEGIN INITIAL; }
100 if[ \t]*\(		{
101 				string_buf_ptr = string_buf;
102 				parren_count = 1;
103 				BEGIN CEXPR;
104 				return T_IF;
105 			}
106 <CEXPR>\(		{	*string_buf_ptr++ = '('; parren_count++; }
107 <CEXPR>\)		{
108 				parren_count--;
109 				if (parren_count == 0) {
110 					/* All done */
111 					BEGIN INITIAL;
112 					*string_buf_ptr = '\0';
113 					yylval.sym = symtable_get(string_buf);
114 					return T_CEXPR;
115 				} else {
116 					*string_buf_ptr++ = ')';
117 				}
118 			}
119 <CEXPR>\n		{ ++yylineno; }
120 <CEXPR>\r		;
121 <CEXPR>[^()\n]+	{
122 				char *yptr;
123 
124 				yptr = yytext;
125 				while (*yptr != '\0') {
126 					/* Remove duplicate spaces */
127 					if (*yptr == '\t')
128 						*yptr = ' ';
129 					if (*yptr == ' '
130 					 && string_buf_ptr != string_buf
131 					 && string_buf_ptr[-1] == ' ')
132 						yptr++;
133 					else
134 						*string_buf_ptr++ = *yptr++;
135 				}
136 			}
137 
138 VERSION			{ return T_VERSION; }
139 PREFIX			{ return T_PREFIX; }
140 PATCH_ARG_LIST		{ return T_PATCH_ARG_LIST; }
141 \"			{
142 				string_buf_ptr = string_buf;
143 				BEGIN STRING;
144 			}
145 <STRING>[^"]+		{
146 				char *yptr;
147 
148 				yptr = yytext;
149 				while (*yptr)
150 					*string_buf_ptr++ = *yptr++;
151 			}
152 <STRING>\"		{
153 				/* All done */
154 				BEGIN INITIAL;
155 				*string_buf_ptr = '\0';
156 				yylval.str = string_buf;
157 				return T_STRING;
158 			}
159 {SPACE}			 ;
160 
161 	/* Register/SCB/SRAM definition keywords */
162 export			{ return T_EXPORT; }
163 register		{ return T_REGISTER; }
164 const			{ yylval.value = FALSE; return T_CONST; }
165 download		{ return T_DOWNLOAD; }
166 address			{ return T_ADDRESS; }
167 access_mode		{ return T_ACCESS_MODE; }
168 modes			{ return T_MODES; }
169 RW|RO|WO		{
170 				 if (strcmp(yytext, "RW") == 0)
171 					yylval.value = RW;
172 				 else if (strcmp(yytext, "RO") == 0)
173 					yylval.value = RO;
174 				 else
175 					yylval.value = WO;
176 				 return T_MODE;
177 			}
178 BEGIN_CRITICAL		{ return T_BEGIN_CS; }
179 END_CRITICAL		{ return T_END_CS; }
180 SET_SRC_MODE		{ return T_SET_SRC_MODE; }
181 SET_DST_MODE		{ return T_SET_DST_MODE; }
182 field			{ return T_FIELD; }
183 enum			{ return T_ENUM; }
184 mask			{ return T_MASK; }
185 alias			{ return T_ALIAS; }
186 size			{ return T_SIZE; }
187 scb			{ return T_SCB; }
188 scratch_ram		{ return T_SRAM; }
189 accumulator		{ return T_ACCUM; }
190 mode_pointer		{ return T_MODE_PTR; }
191 allones			{ return T_ALLONES; }
192 allzeros		{ return T_ALLZEROS; }
193 none			{ return T_NONE; }
194 sindex			{ return T_SINDEX; }
195 A			{ return T_A; }
196 
197 	/* Opcodes */
198 shl			{ return T_SHL; }
199 shr			{ return T_SHR; }
200 ror			{ return T_ROR; }
201 rol			{ return T_ROL; }
202 mvi			{ return T_MVI; }
203 mov			{ return T_MOV; }
204 clr			{ return T_CLR; }
205 jmp			{ return T_JMP; }
206 jc			{ return T_JC;	}
207 jnc			{ return T_JNC;	}
208 je			{ return T_JE;	}
209 jne			{ return T_JNE;	}
210 jz			{ return T_JZ;	}
211 jnz			{ return T_JNZ;	}
212 call			{ return T_CALL; }
213 add			{ return T_ADD; }
214 adc			{ return T_ADC; }
215 bmov			{ return T_BMOV; }
216 inc			{ return T_INC; }
217 dec			{ return T_DEC; }
218 stc			{ return T_STC;	}
219 clc			{ return T_CLC; }
220 cmp			{ return T_CMP;	}
221 not			{ return T_NOT;	}
222 xor			{ return T_XOR;	}
223 test			{ return T_TEST;}
224 and			{ return T_AND;	}
225 or			{ return T_OR;	}
226 ret			{ return T_RET; }
227 nop			{ return T_NOP; }
228 else			{ return T_ELSE; }
229 
230 	/* Allowed Symbols */
231 \<\<			{ return T_EXPR_LSHIFT; }
232 \>\>			{ return T_EXPR_RSHIFT; }
233 [-+,:()~|&."{};<>[\]/*!=] { return yytext[0]; }
234 
235 	/* Number processing */
236 0[0-7]*			{
237 				yylval.value = strtol(yytext, NULL, 8);
238 				return T_NUMBER;
239 			}
240 
241 0[xX][0-9a-fA-F]+	{
242 				yylval.value = strtoul(yytext + 2, NULL, 16);
243 				return T_NUMBER;
244 			}
245 
246 [1-9][0-9]*		{
247 				yylval.value = strtol(yytext, NULL, 10);
248 				return T_NUMBER;
249 			}
250 	/* Include Files */
251 #include{SPACE}		{
252 				BEGIN INCLUDE;
253 				quote_count = 0;
254 				return T_INCLUDE;
255 			}
256 <INCLUDE>[<]		{ return yytext[0]; }
257 <INCLUDE>[>]		{ BEGIN INITIAL; return yytext[0]; }
258 <INCLUDE>[\"]		{
259 				if (quote_count != 0)
260 					BEGIN INITIAL;
261 				quote_count++;
262 				return yytext[0];
263 			}
264 <INCLUDE>{PATH}		{
265 				char *yptr;
266 
267 				yptr = yytext;
268 				string_buf_ptr = string_buf;
269 				while (*yptr)
270 					*string_buf_ptr++ = *yptr++;
271 				yylval.str = string_buf;
272 				*string_buf_ptr = '\0';
273 				return T_PATH;
274 			}
275 <INCLUDE>.		{ stop("Invalid include line", EX_DATAERR); }
276 #define{SPACE}		{
277 				BEGIN MACRODEF;
278 				return T_DEFINE;
279 			}
280 <MACRODEF>{WORD}{SPACE}	{
281 				char *yptr;
282 
283 				/* Strip space and return as a normal symbol */
284 				yptr = yytext;
285 				while (*yptr != ' ' && *yptr != '\t')
286 					yptr++;
287 				*yptr = '\0';
288 				yylval.sym = symtable_get(yytext);
289 				string_buf_ptr = string_buf;
290 				BEGIN MACROBODY;
291 				return T_SYMBOL;
292 			}
293 <MACRODEF>{WORD}\(	{
294 				/*
295 				 * We store the symbol with its opening
296 				 * parren so we can differentiate macros
297 				 * that take args from macros with the
298 				 * same name that do not take args as
299 				 * is allowed in C.
300 				 */
301 				BEGIN MACROARGLIST;
302 				yylval.sym = symtable_get(yytext);
303 				unput('(');
304 				return T_SYMBOL;
305 			}
306 <MACROARGLIST>{WORD}	{
307 				yylval.str = yytext;
308 				return T_ARG;
309 			}
310 <MACROARGLIST>{SPACE}   ;
311 <MACROARGLIST>[(,]	{
312 				return yytext[0];
313 			}
314 <MACROARGLIST>[)]	{
315 				string_buf_ptr = string_buf;
316 				BEGIN MACROBODY;
317 				return ')';
318 			}
319 <MACROARGLIST>.		{
320 				snprintf(msgbuf, sizeof(msgbuf), "Invalid character "
321 					 "'%c' in macro argument list",
322 					 yytext[0]);
323 				stop(msgbuf, EX_DATAERR);
324 			}
325 <MACROCALLARGS>{SPACE}  ;
326 <MACROCALLARGS>\(	{
327 				parren_count++;
328 				if (parren_count == 1)
329 					return ('(');
330 				*string_buf_ptr++ = '(';
331 			}
332 <MACROCALLARGS>\)	{
333 				parren_count--;
334 				if (parren_count == 0) {
335 					BEGIN INITIAL;
336 					return (')');
337 				}
338 				*string_buf_ptr++ = ')';
339 			}
340 <MACROCALLARGS>{MCARG}	{
341 				char *yptr;
342 
343 				yptr = yytext;
344 				while (*yptr)
345 					*string_buf_ptr++ = *yptr++;
346 			}
347 <MACROCALLARGS>\,	{
348 				if (string_buf_ptr != string_buf) {
349 					/*
350 					 * Return an argument and
351 					 * rescan this comma so we
352 					 * can return it as well.
353 					 */
354 					*string_buf_ptr = '\0';
355 					yylval.str = string_buf;
356 					string_buf_ptr = string_buf;
357 					unput(',');
358 					return T_ARG;
359 				}
360 				return ',';
361 			}
362 <MACROBODY>\\\n		{
363 				/* Eat escaped newlines. */
364 				++yylineno;
365 			}
366 <MACROBODY>\r		;
367 <MACROBODY>\n		{
368 				/* Macros end on the first unescaped newline. */
369 				BEGIN INITIAL;
370 				*string_buf_ptr = '\0';
371 				yylval.str = string_buf;
372 				++yylineno;
373 				return T_MACROBODY;
374 			}
375 <MACROBODY>{MBODY}	{
376 				char *yptr;
377 				char c;
378 
379 				yptr = yytext;
380 				while ((c = *yptr++)) {
381 					/*
382 					 * Strip carriage returns.
383 					 */
384 					if (c == '\r')
385 						continue;
386 					*string_buf_ptr++ = c;
387 				}
388 			}
389 {WORD}\(		{
390 				char *yptr;
391 				char *ycopy;
392 
393 				/* May be a symbol or a macro invocation. */
394 				yylval.sym = symtable_get(yytext);
395 				if (yylval.sym->type == MACRO) {
396 					YY_BUFFER_STATE old_state;
397 					YY_BUFFER_STATE temp_state;
398 
399 					ycopy = strdup(yytext);
400 					yptr = ycopy + yyleng;
401 					while (yptr > ycopy)
402 						unput(*--yptr);
403 					old_state = YY_CURRENT_BUFFER;
404 					temp_state =
405 					    yy_create_buffer(stdin,
406 							     YY_BUF_SIZE);
407 					yy_switch_to_buffer(temp_state);
408 					mm_switch_to_buffer(old_state);
409 					mmparse();
410 					mm_switch_to_buffer(temp_state);
411 					yy_switch_to_buffer(old_state);
412 					mm_delete_buffer(temp_state);
413 					expand_macro(yylval.sym);
414 				} else {
415 					if (yylval.sym->type == UNINITIALIZED) {
416 						/* Try without the '(' */
417 						symbol_delete(yylval.sym);
418 						yytext[yyleng-1] = '\0';
419 						yylval.sym =
420 						    symtable_get(yytext);
421 					}
422 					unput('(');
423 					return T_SYMBOL;
424 				}
425 			}
426 {WORD}			{
427 				yylval.sym = symtable_get(yytext);
428 				if (yylval.sym->type == MACRO) {
429 					expand_macro(yylval.sym);
430 				} else {
431 					return T_SYMBOL;
432 				}
433 			}
434 .			{
435 				snprintf(msgbuf, sizeof(msgbuf), "Invalid character "
436 					 "'%c'", yytext[0]);
437 				stop(msgbuf, EX_DATAERR);
438 			}
439 %%
440 
441 typedef struct include {
442         YY_BUFFER_STATE  buffer;
443         int              lineno;
444         char            *filename;
445 	SLIST_ENTRY(include) links;
446 }include_t;
447 
448 SLIST_HEAD(, include) include_stack;
449 
450 void
451 include_file(char *file_name, include_type type)
452 {
453 	FILE *newfile;
454 	include_t *include;
455 
456 	newfile = NULL;
457 	/* Try the current directory first */
458 	if (includes_search_curdir != 0 || type == SOURCE_FILE)
459 		newfile = fopen(file_name, "r");
460 
461 	if (newfile == NULL && type != SOURCE_FILE) {
462                 path_entry_t include_dir;
463                 for (include_dir = search_path.slh_first;
464                      include_dir != NULL;
465                      include_dir = include_dir->links.sle_next) {
466 			char fullname[PATH_MAX];
467 
468 			if ((include_dir->quoted_includes_only == TRUE)
469 			 && (type != QUOTED_INCLUDE))
470 				continue;
471 
472 			snprintf(fullname, sizeof(fullname),
473 				 "%s/%s", include_dir->directory, file_name);
474 
475 			if ((newfile = fopen(fullname, "r")) != NULL)
476 				break;
477                 }
478         }
479 
480 	if (newfile == NULL) {
481 		perror(file_name);
482 		stop("Unable to open input file", EX_SOFTWARE);
483 		/* NOTREACHED */
484 	}
485 
486 	if (type != SOURCE_FILE) {
487 		include = (include_t *)malloc(sizeof(include_t));
488 		if (include == NULL) {
489 			stop("Unable to allocate include stack entry",
490 			     EX_SOFTWARE);
491 			/* NOTREACHED */
492 		}
493 		include->buffer = YY_CURRENT_BUFFER;
494 		include->lineno = yylineno;
495 		include->filename = yyfilename;
496 		SLIST_INSERT_HEAD(&include_stack, include, links);
497 	}
498 	yy_switch_to_buffer(yy_create_buffer(newfile, YY_BUF_SIZE));
499 	yylineno = 1;
500 	yyfilename = strdup(file_name);
501 }
502 
503 static void next_substitution(struct symbol *mac_symbol, const char *body_pos,
504 			      const char **next_match,
505 			      struct macro_arg **match_marg, regmatch_t *match);
506 
507 void
508 expand_macro(struct symbol *macro_symbol)
509 {
510 	struct macro_arg *marg;
511 	struct macro_arg *match_marg;
512 	const char *body_head;
513 	const char *body_pos;
514 	const char *next_match;
515 	regmatch_t match = { .rm_so = 0, .rm_eo = 0 };
516 
517 	/*
518 	 * Due to the nature of unput, we must work
519 	 * backwards through the macro body performing
520 	 * any expansions.
521 	 */
522 	body_head = macro_symbol->info.macroinfo->body;
523 	body_pos = body_head + strlen(body_head);
524 	while (body_pos > body_head) {
525 		next_match = body_head;
526 		match_marg = NULL;
527 		next_substitution(macro_symbol, body_pos, &next_match,
528 				  &match_marg, &match);
529 
530 		/* Put back everything up until the replacement. */
531 		while (body_pos > next_match)
532 			unput(*--body_pos);
533 
534 		/* Perform the replacement. */
535 		if (match_marg != NULL) {
536 			const char *strp;
537 
538 			next_match = match_marg->replacement_text;
539 			strp = next_match + strlen(next_match);
540 			while (strp > next_match)
541 				unput(*--strp);
542 
543 			/* Skip past the unexpanded macro arg. */
544 			body_pos -= match.rm_eo - match.rm_so;
545 		}
546 	}
547 
548 	/* Cleanup replacement text. */
549 	STAILQ_FOREACH(marg, &macro_symbol->info.macroinfo->args, links) {
550 		free(marg->replacement_text);
551 	}
552 }
553 
554 /*
555  * Find the next substitution in the macro working backwards from
556  * body_pos until the beginning of the macro buffer.  next_match
557  * should be initialized to the beginning of the macro buffer prior
558  * to calling this routine.
559  */
560 static void
561 next_substitution(struct symbol *mac_symbol, const char *body_pos,
562 		  const char **next_match, struct macro_arg **match_marg,
563 		  regmatch_t *match)
564 {
565 	regmatch_t	  matches[2];
566 	struct macro_arg *marg;
567 	const char	 *search_pos;
568 	int		  retval;
569 
570 	do {
571 		search_pos = *next_match;
572 
573 		STAILQ_FOREACH(marg, &mac_symbol->info.macroinfo->args, links) {
574 
575 			retval = regexec(&marg->arg_regex, search_pos, 2,
576 					 matches, 0);
577 			if (retval == 0
578 			 && (matches[1].rm_eo + search_pos) <= body_pos
579 			 && (matches[1].rm_eo + search_pos) > *next_match) {
580 				*match = matches[1];
581 				*next_match = match->rm_eo + search_pos;
582 				*match_marg = marg;
583 			}
584 		}
585 	} while (search_pos != *next_match);
586 }
587 
588 int
589 yywrap(void)
590 {
591 	include_t *include;
592 
593 	yy_delete_buffer(YY_CURRENT_BUFFER);
594 	(void)fclose(yyin);
595 	if (yyfilename != NULL)
596 		free(yyfilename);
597 	yyfilename = NULL;
598 	include = include_stack.slh_first;
599 	if (include != NULL) {
600 		yy_switch_to_buffer(include->buffer);
601 		yylineno = include->lineno;
602 		yyfilename = include->filename;
603 		SLIST_REMOVE_HEAD(&include_stack, links);
604 		free(include);
605 		return (0);
606 	}
607 	return (1);
608 }
609