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