%{ /* $OpenBSD: aicasm_scan.l,v 1.9 2004/09/18 19:51:53 mickey Exp $ */ /* * Lexical Analyzer for the Aic7xxx SCSI Host adapter sequencer assembler. * * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs. * Copyright (c) 2001, 2002 Adaptec Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * $Id: aicasm_scan.l,v 1.9 2004/09/18 19:51:53 mickey Exp $ * * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_scan.l,v 1.22 2003/12/16 23:54:07 gibbs Exp $ */ #include #include #include #include #include #include #include #ifdef __linux__ #include "../queue.h" #else #include #endif #include "aicasm.h" #include "aicasm_symbol.h" #include "aicasm_gram.h" /* This is used for macro body capture too, so err on the large size. */ #define MAX_STR_CONST 4096 static char string_buf[MAX_STR_CONST]; static char *string_buf_ptr; static int parren_count; static int quote_count; static char buf[255]; %} PATH ([/]*[-A-Za-z0-9_.])+ WORD [A-Za-z_][-A-Za-z_0-9]* SPACE [ \t]+ MCARG [^(), \t]+ MBODY ((\\[^\n])*[^\n\\]*)+ %x COMMENT %x CEXPR %x INCLUDE %x STRING %x MACRODEF %x MACROARGLIST %x MACROCALLARGS %x MACROBODY %% \n { ++yylineno; } \r ; "/*" { BEGIN COMMENT; /* Enter comment eating state */ } "/*" { fprintf(stderr, "Warning! Comment within comment."); } \n { ++yylineno; } [^*/\n]* ; "*"+[^*/\n]* ; "/"+[^*/\n]* ; "*"+"/" { BEGIN INITIAL; } if[ \t]*\( { string_buf_ptr = string_buf; parren_count = 1; BEGIN CEXPR; return T_IF; } \( { *string_buf_ptr++ = '('; parren_count++; } \) { parren_count--; if (parren_count == 0) { /* All done */ BEGIN INITIAL; *string_buf_ptr = '\0'; yylval.sym = symtable_get(string_buf); return T_CEXPR; } else { *string_buf_ptr++ = ')'; } } \n { ++yylineno; } \r ; [^()\n]+ { char *yptr; yptr = yytext; while (*yptr != '\0') { /* Remove duplicate spaces */ if (*yptr == '\t') *yptr = ' '; if (*yptr == ' ' && string_buf_ptr != string_buf && string_buf_ptr[-1] == ' ') yptr++; else *string_buf_ptr++ = *yptr++; } } VERSION { return T_VERSION; } PREFIX { return T_PREFIX; } PATCH_ARG_LIST { return T_PATCH_ARG_LIST; } \" { string_buf_ptr = string_buf; BEGIN STRING; } [^"]+ { char *yptr; yptr = yytext; while (*yptr) *string_buf_ptr++ = *yptr++; } \" { /* All done */ BEGIN INITIAL; *string_buf_ptr = '\0'; yylval.str = string_buf; return T_STRING; } {SPACE} ; /* Register/SCB/SRAM definition keywords */ export { return T_EXPORT; } register { return T_REGISTER; } const { yylval.value = FALSE; return T_CONST; } download { return T_DOWNLOAD; } address { return T_ADDRESS; } access_mode { return T_ACCESS_MODE; } modes { return T_MODES; } RW|RO|WO { if (strcmp(yytext, "RW") == 0) yylval.value = RW; else if (strcmp(yytext, "RO") == 0) yylval.value = RO; else yylval.value = WO; return T_MODE; } BEGIN_CRITICAL { return T_BEGIN_CS; } END_CRITICAL { return T_END_CS; } SET_SRC_MODE { return T_SET_SRC_MODE; } SET_DST_MODE { return T_SET_DST_MODE; } field { return T_FIELD; } enum { return T_ENUM; } mask { return T_MASK; } alias { return T_ALIAS; } size { return T_SIZE; } scb { return T_SCB; } scratch_ram { return T_SRAM; } accumulator { return T_ACCUM; } mode_pointer { return T_MODE_PTR; } allones { return T_ALLONES; } allzeros { return T_ALLZEROS; } none { return T_NONE; } sindex { return T_SINDEX; } A { return T_A; } /* Opcodes */ shl { return T_SHL; } shr { return T_SHR; } ror { return T_ROR; } rol { return T_ROL; } mvi { return T_MVI; } mov { return T_MOV; } clr { return T_CLR; } jmp { return T_JMP; } jc { return T_JC; } jnc { return T_JNC; } je { return T_JE; } jne { return T_JNE; } jz { return T_JZ; } jnz { return T_JNZ; } call { return T_CALL; } add { return T_ADD; } adc { return T_ADC; } bmov { return T_BMOV; } inc { return T_INC; } dec { return T_DEC; } stc { return T_STC; } clc { return T_CLC; } cmp { return T_CMP; } not { return T_NOT; } xor { return T_XOR; } test { return T_TEST;} and { return T_AND; } or { return T_OR; } ret { return T_RET; } nop { return T_NOP; } else { return T_ELSE; } /* Allowed Symbols */ \<\< { return T_EXPR_LSHIFT; } \>\> { return T_EXPR_RSHIFT; } [-+,:()~|&."{};<>[\]/*!=] { return yytext[0]; } /* Number processing */ 0[0-7]* { yylval.value = strtol(yytext, NULL, 8); return T_NUMBER; } 0[xX][0-9a-fA-F]+ { yylval.value = strtoul(yytext + 2, NULL, 16); return T_NUMBER; } [1-9][0-9]* { yylval.value = strtol(yytext, NULL, 10); return T_NUMBER; } /* Include Files */ #include{SPACE} { BEGIN INCLUDE; quote_count = 0; return T_INCLUDE; } [<] { return yytext[0]; } [>] { BEGIN INITIAL; return yytext[0]; } [\"] { if (quote_count != 0) BEGIN INITIAL; quote_count++; return yytext[0]; } {PATH} { char *yptr; yptr = yytext; string_buf_ptr = string_buf; while (*yptr) *string_buf_ptr++ = *yptr++; yylval.str = string_buf; *string_buf_ptr = '\0'; return T_PATH; } . { stop("Invalid include line", EX_DATAERR); } #define{SPACE} { BEGIN MACRODEF; return T_DEFINE; } {WORD}{SPACE} { char *yptr; /* Strip space and return as a normal symbol */ yptr = yytext; while (*yptr != ' ' && *yptr != '\t') yptr++; *yptr = '\0'; yylval.sym = symtable_get(yytext); string_buf_ptr = string_buf; BEGIN MACROBODY; return T_SYMBOL; } {WORD}\( { /* * We store the symbol with its opening * parren so we can differentiate macros * that take args from macros with the * same name that do not take args as * is allowed in C. */ BEGIN MACROARGLIST; yylval.sym = symtable_get(yytext); unput('('); return T_SYMBOL; } {WORD} { yylval.str = yytext; return T_ARG; } {SPACE} ; [(,] { return yytext[0]; } [)] { string_buf_ptr = string_buf; BEGIN MACROBODY; return ')'; } . { snprintf(buf, sizeof(buf), "Invalid character " "'%c' in macro argument list", yytext[0]); stop(buf, EX_DATAERR); } {SPACE} ; \( { parren_count++; if (parren_count == 1) return ('('); *string_buf_ptr++ = '('; } \) { parren_count--; if (parren_count == 0) { BEGIN INITIAL; return (')'); } *string_buf_ptr++ = ')'; } {MCARG} { char *yptr; yptr = yytext; while (*yptr) *string_buf_ptr++ = *yptr++; } \, { if (string_buf_ptr != string_buf) { /* * Return an argument and * rescan this comma so we * can return it as well. */ *string_buf_ptr = '\0'; yylval.str = string_buf; string_buf_ptr = string_buf; unput(','); return T_ARG; } return ','; } \\\n { /* Eat escaped newlines. */ ++yylineno; } \r ; \n { /* Macros end on the first unescaped newline. */ BEGIN INITIAL; *string_buf_ptr = '\0'; yylval.str = string_buf; ++yylineno; return T_MACROBODY; } {MBODY} { char *yptr; char c; yptr = yytext; while (c = *yptr++) { /* * Strip carriage returns. */ if (c == '\r') continue; *string_buf_ptr++ = c; } } {WORD}\( { char *yptr; char *ycopy; /* May be a symbol or a macro invocation. */ yylval.sym = symtable_get(yytext); if (yylval.sym->type == MACRO) { YY_BUFFER_STATE old_state; YY_BUFFER_STATE temp_state; ycopy = strdup(yytext); yptr = ycopy + yyleng; while (yptr > ycopy) unput(*--yptr); old_state = YY_CURRENT_BUFFER; temp_state = yy_create_buffer(stdin, YY_BUF_SIZE); yy_switch_to_buffer(temp_state); mm_switch_to_buffer(old_state); mmparse(); mm_switch_to_buffer(temp_state); yy_switch_to_buffer(old_state); mm_delete_buffer(temp_state); expand_macro(yylval.sym); } else { if (yylval.sym->type == UNINITIALIZED) { /* Try without the '(' */ symbol_delete(yylval.sym); yytext[yyleng-1] = '\0'; yylval.sym = symtable_get(yytext); } unput('('); return T_SYMBOL; } } {WORD} { yylval.sym = symtable_get(yytext); if (yylval.sym->type == MACRO) { expand_macro(yylval.sym); } else { return T_SYMBOL; } } . { snprintf(buf, sizeof(buf), "Invalid character " "'%c'", yytext[0]); stop(buf, EX_DATAERR); } %% typedef struct include { YY_BUFFER_STATE buffer; int lineno; char *filename; SLIST_ENTRY(include) links; }include_t; SLIST_HEAD(, include) include_stack; void include_file(char *file_name, include_type type) { FILE *newfile; include_t *include; newfile = NULL; /* Try the current directory first */ if (includes_search_curdir != 0 || type == SOURCE_FILE) newfile = fopen(file_name, "r"); if (newfile == NULL && type != SOURCE_FILE) { path_entry_t include_dir; for (include_dir = search_path.slh_first; include_dir != NULL; include_dir = include_dir->links.sle_next) { char fullname[PATH_MAX]; if ((include_dir->quoted_includes_only == TRUE) && (type != QUOTED_INCLUDE)) continue; snprintf(fullname, sizeof(fullname), "%s/%s", include_dir->directory, file_name); if ((newfile = fopen(fullname, "r")) != NULL) break; } } if (newfile == NULL) { perror(file_name); stop("Unable to open input file", EX_SOFTWARE); /* NOTREACHED */ } if (type != SOURCE_FILE) { include = (include_t *)malloc(sizeof(include_t)); if (include == NULL) { stop("Unable to allocate include stack entry", EX_SOFTWARE); /* NOTREACHED */ } include->buffer = YY_CURRENT_BUFFER; include->lineno = yylineno; include->filename = yyfilename; SLIST_INSERT_HEAD(&include_stack, include, links); } yy_switch_to_buffer(yy_create_buffer(newfile, YY_BUF_SIZE)); yylineno = 1; yyfilename = strdup(file_name); } static void next_substitution(struct symbol *mac_symbol, const char *body_pos, const char **next_match, struct macro_arg **match_marg, regmatch_t *match); void expand_macro(struct symbol *macro_symbol) { struct macro_arg *marg; struct macro_arg *match_marg; const char *body_head; const char *body_pos; const char *next_match; /* * Due to the nature of unput, we must work * backwards through the macro body performing * any expansions. */ body_head = macro_symbol->info.macroinfo->body; body_pos = body_head + strlen(body_head); while (body_pos > body_head) { regmatch_t match; next_match = body_head; match_marg = NULL; next_substitution(macro_symbol, body_pos, &next_match, &match_marg, &match); /* Put back everything up until the replacement. */ while (body_pos > next_match) unput(*--body_pos); /* Perform the replacement. */ if (match_marg != NULL) { const char *strp; next_match = match_marg->replacement_text; strp = next_match + strlen(next_match); while (strp > next_match) unput(*--strp); /* Skip past the unexpanded macro arg. */ body_pos -= match.rm_eo - match.rm_so; } } /* Cleanup replacement text. */ TAILQ_FOREACH(marg, ¯o_symbol->info.macroinfo->args, links) { free(marg->replacement_text); } } /* * Find the next substitution in the macro working backwards from * body_pos until the beginning of the macro buffer. next_match * should be initialized to the beginning of the macro buffer prior * to calling this routine. */ static void next_substitution(struct symbol *mac_symbol, const char *body_pos, const char **next_match, struct macro_arg **match_marg, regmatch_t *match) { regmatch_t matches[2]; struct macro_arg *marg; const char *search_pos; int retval; do { search_pos = *next_match; TAILQ_FOREACH(marg, &mac_symbol->info.macroinfo->args, links) { retval = regexec(&marg->arg_regex, search_pos, 2, matches, 0); if (retval == 0 && (matches[1].rm_eo + search_pos) <= body_pos && (matches[1].rm_eo + search_pos) > *next_match) { *match = matches[1]; *next_match = match->rm_eo + search_pos; *match_marg = marg; } } } while (search_pos != *next_match); } int yywrap() { include_t *include; yy_delete_buffer(YY_CURRENT_BUFFER); (void)fclose(yyin); if (yyfilename != NULL) free(yyfilename); yyfilename = NULL; include = include_stack.slh_first; if (include != NULL) { yy_switch_to_buffer(include->buffer); yylineno = include->lineno; yyfilename = include->filename; SLIST_REMOVE_HEAD(&include_stack, links); free(include); return (0); } return (1); }