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