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