1 %{ 2 /* $NetBSD: aicasm_scan.l,v 1.5 2005/12/11 12:22:18 christos Exp $ */ 3 /* 4 * Lexical Analyzer for the Aic7xxx SCSI Host adapter sequencer assembler. 5 * 6 * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs. 7 * Copyright (c) 2001, 2002 Adaptec Inc. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions, and the following disclaimer, 15 * without modification. 16 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 17 * substantially similar to the "NO WARRANTY" disclaimer below 18 * ("Disclaimer") and any redistribution must be conditioned upon 19 * including a substantially similar Disclaimer requirement for further 20 * binary redistribution. 21 * 3. Neither the names of the above-listed copyright holders nor the names 22 * of any contributors may be used to endorse or promote products derived 23 * from this software without specific prior written permission. 24 * 25 * Alternatively, this software may be distributed under the terms of the 26 * GNU General Public License ("GPL") version 2 as published by the Free 27 * Software Foundation. 28 * 29 * NO WARRANTY 30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 31 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 32 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 33 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 34 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 38 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 39 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 40 * POSSIBILITY OF SUCH DAMAGES. 41 * 42 * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_scan.l,v 1.21 2002/09/27 03:23:02 gibbs Exp $ 43 */ 44 45 #include <sys/types.h> 46 47 #include <inttypes.h> 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 65 static char string_buf[MAX_STR_CONST]; 66 static char *string_buf_ptr; 67 static int parren_count; 68 static int quote_count; 69 static char buf[255]; 70 %} 71 72 PATH ([/]*[-A-Za-z0-9_.])+ 73 WORD [A-Za-z_][-A-Za-z_0-9]* 74 SPACE [ \t]+ 75 MCARG [^(), \t]+ 76 MBODY ((\\[^\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; } 96 if[ \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 133 VERSION { return T_VERSION; } 134 PREFIX { return T_PREFIX; } 135 PATCH_ARG_LIST { return T_PATCH_ARG_LIST; } 136 \" { 137 string_buf_ptr = string_buf; 138 BEGIN STRING; 139 } 140 <STRING>[^"]+ { 141 char *yptr; 142 143 yptr = yytext; 144 while (*yptr) 145 *string_buf_ptr++ = *yptr++; 146 } 147 <STRING>\" { 148 /* All done */ 149 BEGIN INITIAL; 150 *string_buf_ptr = '\0'; 151 yylval.str = string_buf; 152 return T_STRING; 153 } 154 {SPACE} ; 155 156 /* Register/SCB/SRAM definition keywords */ 157 export { return T_EXPORT; } 158 register { return T_REGISTER; } 159 const { yylval.value = FALSE; return T_CONST; } 160 download { return T_DOWNLOAD; } 161 address { return T_ADDRESS; } 162 access_mode { return T_ACCESS_MODE; } 163 modes { return T_MODES; } 164 RW|RO|WO { 165 if (strcmp(yytext, "RW") == 0) 166 yylval.value = RW; 167 else if (strcmp(yytext, "RO") == 0) 168 yylval.value = RO; 169 else 170 yylval.value = WO; 171 return T_MODE; 172 } 173 BEGIN_CRITICAL { return T_BEGIN_CS; } 174 END_CRITICAL { return T_END_CS; } 175 SET_SRC_MODE { return T_SET_SRC_MODE; } 176 SET_DST_MODE { return T_SET_DST_MODE; } 177 field { return T_FIELD; } 178 enum { return T_ENUM; } 179 mask { return T_MASK; } 180 alias { return T_ALIAS; } 181 size { return T_SIZE; } 182 scb { return T_SCB; } 183 scratch_ram { return T_SRAM; } 184 accumulator { return T_ACCUM; } 185 mode_pointer { return T_MODE_PTR; } 186 allones { return T_ALLONES; } 187 allzeros { return T_ALLZEROS; } 188 none { return T_NONE; } 189 sindex { return T_SINDEX; } 190 A { return T_A; } 191 192 /* Opcodes */ 193 shl { return T_SHL; } 194 shr { return T_SHR; } 195 ror { return T_ROR; } 196 rol { return T_ROL; } 197 mvi { return T_MVI; } 198 mov { return T_MOV; } 199 clr { return T_CLR; } 200 jmp { return T_JMP; } 201 jc { return T_JC; } 202 jnc { return T_JNC; } 203 je { return T_JE; } 204 jne { return T_JNE; } 205 jz { return T_JZ; } 206 jnz { return T_JNZ; } 207 call { return T_CALL; } 208 add { return T_ADD; } 209 adc { return T_ADC; } 210 bmov { return T_BMOV; } 211 inc { return T_INC; } 212 dec { return T_DEC; } 213 stc { return T_STC; } 214 clc { return T_CLC; } 215 cmp { return T_CMP; } 216 not { return T_NOT; } 217 xor { return T_XOR; } 218 test { return T_TEST;} 219 and { return T_AND; } 220 or { return T_OR; } 221 ret { return T_RET; } 222 nop { return T_NOP; } 223 else { return T_ELSE; } 224 225 /* Allowed Symbols */ 226 \<\< { return T_EXPR_LSHIFT; } 227 \>\> { return T_EXPR_RSHIFT; } 228 [-+,:()~|&."{};<>[\]/*!=] { return yytext[0]; } 229 230 /* Number processing */ 231 0[0-7]* { 232 yylval.value = strtol(yytext, NULL, 8); 233 return T_NUMBER; 234 } 235 236 0[xX][0-9a-fA-F]+ { 237 yylval.value = strtoul(yytext + 2, NULL, 16); 238 return T_NUMBER; 239 } 240 241 [1-9][0-9]* { 242 yylval.value = strtol(yytext, NULL, 10); 243 return T_NUMBER; 244 } 245 /* Include Files */ 246 #include{SPACE} { 247 BEGIN INCLUDE; 248 quote_count = 0; 249 return T_INCLUDE; 250 } 251 <INCLUDE>[<] { return yytext[0]; } 252 <INCLUDE>[>] { BEGIN INITIAL; return yytext[0]; } 253 <INCLUDE>[\"] { 254 if (quote_count != 0) 255 BEGIN INITIAL; 256 quote_count++; 257 return yytext[0]; 258 } 259 <INCLUDE>{PATH} { 260 char *yptr; 261 262 yptr = yytext; 263 string_buf_ptr = string_buf; 264 while (*yptr) 265 *string_buf_ptr++ = *yptr++; 266 yylval.str = string_buf; 267 *string_buf_ptr = '\0'; 268 return T_PATH; 269 } 270 <INCLUDE>. { stop("Invalid include line", EX_DATAERR); } 271 #define{SPACE} { 272 BEGIN MACRODEF; 273 return T_DEFINE; 274 } 275 <MACRODEF>{WORD}{SPACE} { 276 char *yptr; 277 278 /* Strip space and return as a normal symbol */ 279 yptr = yytext; 280 while (*yptr != ' ' && *yptr != '\t') 281 yptr++; 282 *yptr = '\0'; 283 yylval.sym = symtable_get(yytext); 284 string_buf_ptr = string_buf; 285 BEGIN MACROBODY; 286 return T_SYMBOL; 287 } 288 <MACRODEF>{WORD}\( { 289 /* 290 * We store the symbol with its opening 291 * parren so we can differentiate macros 292 * that take args from macros with the 293 * same name that do not take args as 294 * is allowed in C. 295 */ 296 BEGIN MACROARGLIST; 297 yylval.sym = symtable_get(yytext); 298 unput('('); 299 return T_SYMBOL; 300 } 301 <MACROARGLIST>{WORD} { 302 yylval.str = yytext; 303 return T_ARG; 304 } 305 <MACROARGLIST>{SPACE} ; 306 <MACROARGLIST>[(,] { 307 return yytext[0]; 308 } 309 <MACROARGLIST>[)] { 310 string_buf_ptr = string_buf; 311 BEGIN MACROBODY; 312 return ')'; 313 } 314 <MACROARGLIST>. { 315 snprintf(buf, sizeof(buf), "Invalid character " 316 "'%c' in macro argument list", 317 yytext[0]); 318 stop(buf, EX_DATAERR); 319 } 320 <MACROCALLARGS>{SPACE} ; 321 <MACROCALLARGS>\( { 322 parren_count++; 323 if (parren_count == 1) 324 return ('('); 325 *string_buf_ptr++ = '('; 326 } 327 <MACROCALLARGS>\) { 328 parren_count--; 329 if (parren_count == 0) { 330 BEGIN INITIAL; 331 return (')'); 332 } 333 *string_buf_ptr++ = ')'; 334 } 335 <MACROCALLARGS>{MCARG} { 336 char *yptr; 337 338 yptr = yytext; 339 while (*yptr) 340 *string_buf_ptr++ = *yptr++; 341 } 342 <MACROCALLARGS>\, { 343 if (string_buf_ptr != string_buf) { 344 /* 345 * Return an argument and 346 * rescan this comma so we 347 * can return it as well. 348 */ 349 *string_buf_ptr = '\0'; 350 yylval.str = string_buf; 351 string_buf_ptr = string_buf; 352 unput(','); 353 return T_ARG; 354 } 355 return ','; 356 } 357 <MACROBODY>\\\n { 358 /* Eat escaped newlines. */ 359 ++yylineno; 360 } 361 <MACROBODY>\n { 362 /* Macros end on the first unescaped newline. */ 363 BEGIN INITIAL; 364 *string_buf_ptr = '\0'; 365 yylval.str = string_buf; 366 ++yylineno; 367 return T_MACROBODY; 368 } 369 <MACROBODY>{MBODY} { 370 char *yptr; 371 372 yptr = yytext; 373 while (*yptr) 374 *string_buf_ptr++ = *yptr++; 375 } 376 {WORD}\( { 377 char *yptr; 378 char *ycopy; 379 380 /* May be a symbol or a macro invocation. */ 381 yylval.sym = symtable_get(yytext); 382 if (yylval.sym->type == MACRO) { 383 YY_BUFFER_STATE old_state; 384 YY_BUFFER_STATE temp_state; 385 386 ycopy = strdup(yytext); 387 yptr = ycopy + yyleng; 388 while (yptr > ycopy) 389 unput(*--yptr); 390 old_state = YY_CURRENT_BUFFER; 391 temp_state = 392 yy_create_buffer(stdin, 393 YY_BUF_SIZE); 394 yy_switch_to_buffer(temp_state); 395 mm_switch_to_buffer(old_state); 396 mmparse(); 397 mm_switch_to_buffer(temp_state); 398 yy_switch_to_buffer(old_state); 399 mm_delete_buffer(temp_state); 400 expand_macro(yylval.sym); 401 } else { 402 if (yylval.sym->type == UNINITIALIZED) { 403 /* Try without the '(' */ 404 symbol_delete(yylval.sym); 405 yytext[yyleng-1] = '\0'; 406 yylval.sym = 407 symtable_get(yytext); 408 } 409 unput('('); 410 return T_SYMBOL; 411 } 412 } 413 {WORD} { 414 yylval.sym = symtable_get(yytext); 415 if (yylval.sym->type == MACRO) { 416 expand_macro(yylval.sym); 417 } else { 418 return T_SYMBOL; 419 } 420 } 421 . { 422 snprintf(buf, sizeof(buf), "Invalid character " 423 "'%c'", yytext[0]); 424 stop(buf, EX_DATAERR); 425 } 426 %% 427 428 typedef struct include { 429 YY_BUFFER_STATE buffer; 430 int lineno; 431 char *filename; 432 SLIST_ENTRY(include) links; 433 }include_t; 434 435 SLIST_HEAD(, include) include_stack; 436 437 void 438 include_file(char *file_name, include_type type) 439 { 440 FILE *newfile; 441 include_t *include; 442 443 newfile = NULL; 444 /* Try the current directory first */ 445 if (includes_search_curdir != 0 || type == SOURCE_FILE) 446 newfile = fopen(file_name, "r"); 447 448 if (newfile == NULL && type != SOURCE_FILE) { 449 path_entry_t include_dir; 450 for (include_dir = search_path.slh_first; 451 include_dir != NULL; 452 include_dir = include_dir->links.sle_next) { 453 char fullname[PATH_MAX]; 454 455 if ((include_dir->quoted_includes_only == TRUE) 456 && (type != QUOTED_INCLUDE)) 457 continue; 458 459 snprintf(fullname, sizeof(fullname), 460 "%s/%s", include_dir->directory, file_name); 461 462 if ((newfile = fopen(fullname, "r")) != NULL) 463 break; 464 } 465 } 466 467 if (newfile == NULL) { 468 perror(file_name); 469 stop("Unable to open input file", EX_SOFTWARE); 470 /* NOTREACHED */ 471 } 472 473 if (type != SOURCE_FILE) { 474 include = (include_t *)malloc(sizeof(include_t)); 475 if (include == NULL) { 476 stop("Unable to allocate include stack entry", 477 EX_SOFTWARE); 478 /* NOTREACHED */ 479 } 480 include->buffer = YY_CURRENT_BUFFER; 481 include->lineno = yylineno; 482 include->filename = yyfilename; 483 SLIST_INSERT_HEAD(&include_stack, include, links); 484 } 485 yy_switch_to_buffer(yy_create_buffer(newfile, YY_BUF_SIZE)); 486 yylineno = 1; 487 yyfilename = strdup(file_name); 488 } 489 490 static void next_substitution(struct symbol *mac_symbol, const char *body_pos, 491 const char **next_match, 492 struct macro_arg **match_marg, regmatch_t *match); 493 494 void 495 expand_macro(struct symbol *macro_symbol) 496 { 497 struct macro_arg *marg; 498 struct macro_arg *match_marg; 499 const char *body_head; 500 const char *body_pos; 501 const char *next_match; 502 503 /* 504 * Due to the nature of unput, we must work 505 * backwards through the macro body performing 506 * any expansions. 507 */ 508 body_head = macro_symbol->info.macroinfo->body; 509 body_pos = body_head + strlen(body_head); 510 while (body_pos > body_head) { 511 regmatch_t match; 512 513 next_match = body_head; 514 match_marg = NULL; 515 next_substitution(macro_symbol, body_pos, &next_match, 516 &match_marg, &match); 517 518 /* Put back everything up until the replacement. */ 519 while (body_pos > next_match) 520 unput(*--body_pos); 521 522 /* Perform the replacement. */ 523 if (match_marg != NULL) { 524 const char *strp; 525 526 next_match = match_marg->replacement_text; 527 strp = next_match + strlen(next_match); 528 while (strp > next_match) 529 unput(*--strp); 530 531 /* Skip past the unexpanded macro arg. */ 532 body_pos -= match.rm_eo - match.rm_so; 533 } 534 } 535 536 /* Cleanup replacement text. */ 537 STAILQ_FOREACH(marg, ¯o_symbol->info.macroinfo->args, links) { 538 free(marg->replacement_text); 539 } 540 } 541 542 /* 543 * Find the next substitution in the macro working backwards from 544 * body_pos until the beginning of the macro buffer. next_match 545 * should be initialized to the beginning of the macro buffer prior 546 * to calling this routine. 547 */ 548 static void 549 next_substitution(struct symbol *mac_symbol, const char *body_pos, 550 const char **next_match, struct macro_arg **match_marg, 551 regmatch_t *match) 552 { 553 regmatch_t matches[2]; 554 struct macro_arg *marg; 555 const char *search_pos; 556 int retval; 557 558 do { 559 search_pos = *next_match; 560 561 STAILQ_FOREACH(marg, &mac_symbol->info.macroinfo->args, links) { 562 563 retval = regexec(&marg->arg_regex, search_pos, 2, 564 matches, 0); 565 if (retval == 0 566 && (matches[1].rm_eo + search_pos) <= body_pos 567 && (matches[1].rm_eo + search_pos) > *next_match) { 568 *match = matches[1]; 569 *next_match = match->rm_eo + search_pos; 570 *match_marg = marg; 571 } 572 } 573 } while (search_pos != *next_match); 574 } 575 576 int 577 yywrap() 578 { 579 include_t *include; 580 581 yy_delete_buffer(YY_CURRENT_BUFFER); 582 (void)fclose(yyin); 583 if (yyfilename != NULL) 584 free(yyfilename); 585 yyfilename = NULL; 586 include = include_stack.slh_first; 587 if (include != NULL) { 588 yy_switch_to_buffer(include->buffer); 589 yylineno = include->lineno; 590 yyfilename = include->filename; 591 SLIST_REMOVE_HEAD(&include_stack, links); 592 free(include); 593 return (0); 594 } 595 return (1); 596 } 597