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