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