iverilog/ivlpp/lexor.lex

1820 lines
44 KiB
Plaintext

%{
/*
* Copyright (c) 1999-2008 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
# include "config.h"
# include <stdio.h>
#ifdef HAVE_MALLOC_H
# include <malloc.h>
#endif
# include <stdlib.h>
# include <string.h>
# include <ctype.h>
# include <assert.h>
# include "globals.h"
static void output_init();
#define YY_USER_INIT output_init()
static void def_start();
static void def_add_arg();
static void def_finish();
static void def_undefine();
static void do_define();
static int def_is_done();
static int is_defined(const char*name);
static int macro_needs_args(const char*name);
static void macro_start_args();
static void macro_add_to_arg();
static void macro_finish_arg();
static void do_expand(int use_args);
static char* macro_name();
static void include_filename();
static void do_include();
static int load_next_input();
struct include_stack_t
{
char* path;
/* If the current input is from a file, this member is set. */
FILE* file;
/* If we are reparsing a macro expansion, file is 0 and this
* member points to the string in progress
*/
const char* str;
/* If we are reparsing a macro expansion, this member indicates
* the amount of space it occupies in the macro expansion buffer.
* This will be zero for macros without arguments.
*/
int ebs;
int stringify_flag;
unsigned lineno;
YY_BUFFER_STATE yybs;
struct include_stack_t* next;
/* A single line comment can be associated with this include. */
char* comment;
};
static void emit_pathline(struct include_stack_t* isp);
/*
* The file_queue is a singly-linked list of the files that were
* listed on the command line/file list.
*/
static struct include_stack_t* file_queue = 0;
static int do_expand_stringify_flag = 0;
/*
* The istack is the inclusion stack.
*/
static struct include_stack_t* istack = 0;
static struct include_stack_t* standby = 0;
/*
* Keep a stack of active ifdef, so that I can report errors
* when there are missing endifs.
*/
struct ifdef_stack_t
{
char* path;
unsigned lineno;
struct ifdef_stack_t* next;
};
static struct ifdef_stack_t* ifdef_stack = 0;
static void ifdef_enter(void)
{
struct ifdef_stack_t*cur;
cur = (struct ifdef_stack_t*) calloc(1, sizeof(struct ifdef_stack_t));
if (istack->path) cur->path = strdup(istack->path);
cur->lineno = istack->lineno;
cur->next = ifdef_stack;
ifdef_stack = cur;
}
static void ifdef_leave(void)
{
struct ifdef_stack_t* cur;
assert(ifdef_stack);
cur = ifdef_stack;
ifdef_stack = cur->next;
/* If either path is from a non-file context e.g.(macro expansion)
* we assume that the non-file part is from this file. */
if (istack->path != NULL && cur->path != NULL &&
strcmp(istack->path,cur->path) != 0) {
fprintf
(
stderr,
"%s:%u: warning: This `endif matches an ifdef in another file.\n",
istack->path,
istack->lineno+1
);
fprintf
(
stderr,
"%s:%u: This is the odd matched `ifdef.\n",
cur->path,
cur->lineno+1
);
}
free(cur->path);
free(cur);
}
#define YY_INPUT(buf,result,max_size) do { \
if (istack->file) { \
size_t rc = fread(buf, 1, max_size, istack->file); \
result = (rc == 0) ? YY_NULL : rc; \
} else { \
if (*istack->str == 0) \
result = YY_NULL; \
else { \
buf[0] = *istack->str++; \
result = 1; \
} \
} \
} while (0)
static int comment_enter = 0;
static int pragma_enter = 0;
static int string_enter = 0;
static int ma_parenthesis_level = 0;
%}
%option stack
%option nounput
%option noinput
%option noyy_top_state
%option noyywrap
%x PPINCLUDE
%x DEF_NAME
%x DEF_ARG
%x DEF_SEP
%x DEF_TXT
%x MA_START
%x MA_ADD
%x CCOMMENT
%x IFCCOMMENT
%x PCOMENT
%x CSTRING
%x ERROR_LINE
%x IFDEF_FALSE
%s IFDEF_TRUE
%x IFDEF_SUPR
W [ \t\b\f]+
/* The grouping parentheses are necessary for compatibility with
* older versions of flex (at least 2.5.31); they are supposed to
* be implied, according to the flex manual.
*/
keywords (include|define|undef|ifdef|ifndef|else|elseif|endif)
%%
"//"[^\r\n]* { ECHO; }
/* detect multiline, c-style comments, passing them directly to the
* output. This is necessary to allow for ignoring directives that
* are included within the comments.
*/
"/*" { comment_enter = YY_START; BEGIN(CCOMMENT); ECHO; }
<CCOMMENT>[^\r\n] { ECHO; }
<CCOMMENT>\n\r |
<CCOMMENT>\r\n |
<CCOMMENT>\n |
<CCOMMENT>\r { istack->lineno += 1; fputc('\n', yyout); }
<CCOMMENT>"*/" { BEGIN(comment_enter); ECHO; }
/* Detect and pass multiline pragma comments. As with C-style
* comments, pragma comments are passed through, and preprocessor
* directives contained within are ignored. Contains macros are
* expanded, however.
*/
"(*"{W}?")" { ECHO; }
"(*" { pragma_enter = YY_START; BEGIN(PCOMENT); ECHO; }
<PCOMENT>[^\r\n] { ECHO; }
<PCOMENT>\n\r |
<PCOMENT>\r\n |
<PCOMENT>\n |
<PCOMENT>\r { istack->lineno += 1; fputc('\n', yyout); }
<PCOMENT>"*)" { BEGIN(pragma_enter); ECHO; }
<PCOMENT>`{keywords} {
emit_pathline(istack);
error_count += 1;
fprintf
(
stderr,
"error: macro names cannot be directive keywords ('%s'); replaced with nothing.\n",
yytext
);
}
<PCOMENT>`[a-zA-Z][a-zA-Z0-9_$]* {
if (macro_needs_args(yytext+1)) yy_push_state(MA_START); else do_expand(0);
}
/* Strings do not contain preprocessor directives, but can expand
* macros. If that happens, they get expanded in the context of the
* string.
*/
\" { string_enter = YY_START; BEGIN(CSTRING); ECHO; }
<CSTRING>\\\" |
<CSTRING>\\` { ECHO; }
<CSTRING>\r\n |
<CSTRING>\n\r |
<CSTRING>\n |
<CSTRING>\r { fputc('\n', yyout); }
<CSTRING>\" { BEGIN(string_enter); ECHO; }
<CSTRING>. { ECHO; }
/* This set of patterns matches the include directive and the name
* that follows it. when the directive ends, the do_include function
* performs the include operation.
*/
^{W}?`include { yy_push_state(PPINCLUDE); }
<PPINCLUDE>`{keywords} {
emit_pathline(istack);
error_count += 1;
fprintf
(
stderr,
"error: macro names cannot be directive keywords ('%s'); replaced with nothing.\n",
yytext
);
}
<PPINCLUDE>`[a-zA-Z][a-zA-Z0-9_]* {
if (macro_needs_args(yytext+1)) yy_push_state(MA_START); else do_expand(0);
}
<PPINCLUDE>\"[^\"]*\" { include_filename(); }
<PPINCLUDE>[ \t\b\f] { ; }
/* Catch single-line comments that share the line with an include
* directive. And while I'm at it, I might as well preserve the
* comment in the output stream. This will be printed after the
* file has been included.
*/
<PPINCLUDE>"//"[^\r\n]* { standby->comment = strdup(yytext); }
/* These finish the include directive (EOF or EOL) so I revert the
* lexor state and execute the inclusion.
*/
/* There is a bug in flex <= 2.5.34 that prevents the continued action '|'
* from working properly when the final action is associated with <<EOF>>.
* Therefore, the action is repeated. */
<PPINCLUDE>\r\n |
<PPINCLUDE>\n\r |
<PPINCLUDE>\n |
<PPINCLUDE>\r { istack->lineno += 1; yy_pop_state(); do_include(); }
<PPINCLUDE><<EOF>> { istack->lineno += 1; yy_pop_state(); do_include(); }
/* Anything that is not matched by the above is an error of some
* sort. Print an error message and absorb the rest of the line.
*/
<PPINCLUDE>. {
emit_pathline(istack);
fprintf(stderr, "error: malformed `include directive. Did you quote the file name?\n");
error_count += 1;
BEGIN(ERROR_LINE);
}
/* Detect the define directive, and match the name. If followed by a
* '(', collect the formal arguments. Consume any white space, then
* go into DEF_TXT mode and collect the defined value.
*/
`define{W} { yy_push_state(DEF_NAME); }
<DEF_NAME>{keywords}{W}? {
emit_pathline(istack);
error_count += 1;
BEGIN(ERROR_LINE);
fprintf
(
stderr,
"error: malformed `define directive: macro names cannot be directive keywords\n"
);
}
<DEF_NAME>[a-zA-Z_][a-zA-Z0-9_$]*"("{W}? { BEGIN(DEF_ARG); def_start(); }
<DEF_NAME>[a-zA-Z_][a-zA-Z0-9_$]*{W}? { BEGIN(DEF_TXT); def_start(); }
<DEF_ARG>[a-zA-Z_][a-zA-Z0-9_$]*{W}? { BEGIN(DEF_SEP); def_add_arg(); }
<DEF_SEP>","{W}? { BEGIN(DEF_ARG); }
<DEF_SEP>")"{W}? { BEGIN(DEF_TXT); }
<DEF_ARG,DEF_SEP>"//"[^\r\n]* { ECHO; }
<DEF_ARG,DEF_SEP>"/*" { comment_enter = YY_START; BEGIN(CCOMMENT); ECHO; }
<DEF_ARG,DEF_SEP>{W} {}
<DEF_ARG,DEF_SEP>(\n|"\r\n"|"\n\r"|\r){W}? { istack->lineno += 1; fputc('\n', yyout); }
<DEF_NAME,DEF_ARG,DEF_SEP>. {
emit_pathline(istack);
fprintf(stderr, "error: malformed `define directive.\n");
error_count += 1;
BEGIN(ERROR_LINE);
}
<DEF_TXT>.*[^\r\n] { do_define(); }
<DEF_TXT>(\n|"\r\n"|"\n\r"|\r) {
if (def_is_done()) {
def_finish();
yy_pop_state();
}
istack->lineno += 1;
fputc('\n', yyout);
}
/* If the define is terminated by an EOF, then finish the define
* whether there was a continuation or not.
*/
<DEF_TXT><<EOF>> {
def_finish();
istack->lineno += 1;
fputc('\n', yyout);
yy_pop_state();
if (!load_next_input())
yyterminate();
}
`undef{W}[a-zA-Z_][a-zA-Z0-9_$]*{W}?.* { def_undefine(); }
/* Detect conditional compilation directives, and parse them. If I
* find the name defined, switch to the IFDEF_TRUE state and stay
* there until I get an `else or `endif. Otherwise, switch to the
* IFDEF_FALSE state and start tossing data.
*
* Handle suppressed `ifdef with an additional suppress start
* condition that stacks on top of the IFDEF_FALSE so that output is
* not accidentally turned on within nested ifdefs.
*/
`ifdef{W}[a-zA-Z_][a-zA-Z0-9_$]* {
char* name = strchr(yytext, '`'); assert(name);
name += 6;
name += strspn(name, " \t\b\f");
ifdef_enter();
if (is_defined(name))
yy_push_state(IFDEF_TRUE);
else
yy_push_state(IFDEF_FALSE);
}
`ifndef{W}[a-zA-Z_][a-zA-Z0-9_$]* {
char* name = strchr(yytext, '`'); assert(name);
name += 7;
name += strspn(name, " \t\b\f");
ifdef_enter();
if (!is_defined(name))
yy_push_state(IFDEF_TRUE);
else
yy_push_state(IFDEF_FALSE);
}
<IFDEF_FALSE,IFDEF_SUPR>`ifdef{W} |
<IFDEF_FALSE,IFDEF_SUPR>`ifndef{W} { ifdef_enter(); yy_push_state(IFDEF_SUPR); }
<IFDEF_TRUE>`elsif{W}[a-zA-Z_][a-zA-Z0-9_$]* { BEGIN(IFDEF_SUPR); }
<IFDEF_FALSE>`elsif{W}[a-zA-Z_][a-zA-Z0-9_$]* {
char* name = strchr(yytext, '`'); assert(name);
name += 6;
name += strspn(name, " \t\b\f");
if (is_defined(name))
BEGIN(IFDEF_TRUE);
else
BEGIN(IFDEF_FALSE);
}
<IFDEF_SUPR>`elsif{W}[a-zA-Z_][a-zA-Z0-9_$]* { }
<IFDEF_TRUE>`else { BEGIN(IFDEF_SUPR); }
<IFDEF_FALSE>`else { BEGIN(IFDEF_TRUE); }
<IFDEF_SUPR>`else {}
<IFDEF_FALSE,IFDEF_SUPR>"//"[^\r\n]* {}
<IFDEF_FALSE,IFDEF_SUPR>"/*" { comment_enter = YY_START; BEGIN(IFCCOMMENT); }
<IFCCOMMENT>[^\r\n] {}
<IFCCOMMENT>\n\r |
<IFCCOMMENT>\r\n |
<IFCCOMMENT>\n |
<IFCCOMMENT>\r { istack->lineno += 1; }
<IFCCOMMENT>"*/" { BEGIN(comment_enter); }
<IFDEF_FALSE,IFDEF_SUPR>[^\r\n] { }
<IFDEF_FALSE,IFDEF_SUPR>\n\r |
<IFDEF_FALSE,IFDEF_SUPR>\r\n |
<IFDEF_FALSE,IFDEF_SUPR>\n |
<IFDEF_FALSE,IFDEF_SUPR>\r { istack->lineno += 1; fputc('\n', yyout); }
<IFDEF_FALSE,IFDEF_TRUE,IFDEF_SUPR>`endif { ifdef_leave(); yy_pop_state(); }
`ifdef {
error_count += 1;
fprintf
(
stderr,
"%s:%u: `ifdef without a macro name - ignored.\n",
istack->path, istack->lineno+1
);
}
`ifndef {
error_count += 1;
fprintf
(
stderr,
"%s:%u: `ifndef without a macro name - ignored.\n",
istack->path, istack->lineno+1
);
}
`elsif {
error_count += 1;
fprintf
(
stderr,
"%s:%u: `elsif without a macro name - ignored.\n",
istack->path, istack->lineno+1
);
}
`elsif{W}[a-zA-Z_][a-zA-Z0-9_$]* {
error_count += 1;
fprintf
(
stderr,
"%s:%u: `elsif without a matching `ifdef - ignored.\n",
istack->path, istack->lineno+1
);
}
`else {
error_count += 1;
fprintf
(
stderr,
"%s:%u: `else without a matching `ifdef - ignored.\n",
istack->path, istack->lineno+1
);
}
`endif {
error_count += 1;
fprintf
(
stderr,
"%s:%u: `endif without a matching `ifdef - ignored.\n",
istack->path, istack->lineno+1
);
}
`{keywords} {
emit_pathline(istack);
error_count += 1;
fprintf
(
stderr,
"error: macro names cannot be directive keywords ('%s'); replaced with nothing.\n",
yytext
);
}
/* This pattern notices macros and arranges for them to be replaced. */
`[a-zA-Z_][a-zA-Z0-9_$]* {
if (macro_needs_args(yytext+1))
yy_push_state(MA_START);
else
do_expand(0);
}
/* Stringified version of macro expansion. */
``[a-zA-Z_][a-zA-Z0-9_$]* {
assert(do_expand_stringify_flag == 0);
do_expand_stringify_flag = 1;
fputc('"', yyout);
if (macro_needs_args(yytext+2))
yy_push_state(MA_START);
else
do_expand(0);
}
<MA_START>\( { BEGIN(MA_ADD); macro_start_args(); }
<MA_START>{W} {}
<MA_START>(\n|"\r\n"|"\n\r"|\r){W}? {
istack->lineno += 1;
fputc('\n', yyout);
}
<MA_START>. {
emit_pathline(istack);
fprintf(stderr, "error: missing argument list for `%s.\n", macro_name());
error_count += 1;
yy_pop_state();
yyless(0);
}
<MA_ADD>\"[^\"\n\r]*\" { macro_add_to_arg(0); }
<MA_ADD>\"[^\"\n\r]* {
emit_pathline(istack);
fprintf(stderr, "error: unterminated string.\n");
error_count += 1;
BEGIN(ERROR_LINE);
}
<MA_ADD>'[^\n\r]' { macro_add_to_arg(0); }
<MA_ADD>{W} { macro_add_to_arg(1); }
<MA_ADD>"(" { macro_add_to_arg(0); ma_parenthesis_level++; }
<MA_ADD>"," { macro_finish_arg(); }
<MA_ADD>")" {
if (ma_parenthesis_level > 0) {
macro_add_to_arg(0);
ma_parenthesis_level--;
} else {
macro_finish_arg();
yy_pop_state();
do_expand(1);
}
}
<MA_ADD>(\n|"\r\n"|"\n\r"|\r){W}? {
macro_add_to_arg(1);
istack->lineno += 1;
fputc('\n', yyout);
}
<MA_ADD>. { macro_add_to_arg(0); }
<MA_START,MA_ADD>"//"[^\r\n]* { ECHO; }
<MA_START,MA_ADD>"/*" { comment_enter = YY_START; BEGIN(CCOMMENT); ECHO; }
/* Any text that is not a directive just gets passed through to the
* output. Very easy.
*/
[^\r\n] { ECHO; }
\n\r |
\r\n |
\n |
\r { istack->lineno += 1; fputc('\n', yyout); }
/* Absorb the rest of the line when a broken directive is detected. */
<ERROR_LINE>[^\r\n]* { yy_pop_state(); }
<ERROR_LINE>(\n|"\r\n"|"\n\r"|\r) {
yy_pop_state();
istack->lineno += 1;
fputc('\n', yyout);
}
<<EOF>> { if (!load_next_input()) yyterminate(); }
%%
/* Defined macros are kept in this table for convenient lookup. As
* `define directives are matched (and the do_define() function
* called) the tree is built up to match names with values. If a
* define redefines an existing name, the new value it taken.
*/
struct define_t
{
char* name;
char* value;
int keyword; /* keywords don't get rescanned for fresh values. */
int argc;
struct define_t* left;
struct define_t* right;
struct define_t* up;
};
static struct define_t* def_table = 0;
static struct define_t* def_lookup(const char*name)
{
struct define_t* cur = def_table;
if (cur == 0)
return 0;
assert(cur->up == 0);
while (cur)
{
int cmp = strcmp(name, cur->name);
if (cmp == 0)
return cur;
cur = (cmp < 0) ? cur->left : cur->right;
}
return 0;
}
static int is_defined(const char*name)
{
return def_lookup(name) != 0;
}
/*
* The following variables are used to temporarily hold the name and
* formal arguments of a macro definition as it is being parsed. As
* for C program arguments, def_argc counts the arguments (including
* the macro name), the value returned by def_argv(0) points to the
* macro name, the value returned by def_argv(1) points to the first
* argument, and so on.
*
* These variables are also used for storing the actual arguments when
* a macro is instantiated.
*/
#define MAX_DEF_ARG 256 /* allows argument IDs to be stored in a single char */
#define DEF_BUF_CHUNK 256
static char* def_buf = 0;
static int def_buf_size = 0;
static int def_buf_free = 0;
static int def_argc = 0;
static int def_argo[MAX_DEF_ARG]; /* offset of first character in arg */
static int def_argl[MAX_DEF_ARG]; /* length of arg string. */
/*
* Return a pointer to the start of argument 'arg'. Returned pointers
* may go stale after a call to def_buf_grow_to_fit.
*/
static /* inline */ char* def_argv(int arg)
{
return def_buf + def_argo[arg];
}
static void check_for_max_args()
{
if (def_argc == MAX_DEF_ARG)
{
emit_pathline(istack);
fprintf(stderr, "error: too many macro arguments - aborting\n");
exit(1);
}
}
static void def_buf_grow_to_fit(int length)
{
while (length >= def_buf_free)
{
def_buf_size += DEF_BUF_CHUNK;
def_buf_free += DEF_BUF_CHUNK;
def_buf = realloc(def_buf, def_buf_size);
assert(def_buf != 0);
}
}
static void def_start()
{
def_buf_free = def_buf_size;
def_argc = 0;
def_add_arg();
}
static void def_add_arg()
{
int length = yyleng;
check_for_max_args();
/* Remove trailing white space and, if necessary, opening brace. */
while (isspace(yytext[length - 1]))
length--;
if (yytext[length - 1] == '(')
length--;
yytext[length] = 0;
/* Make sure there's room in the buffer for the new argument. */
def_buf_grow_to_fit(length);
/* Store the new argument. */
def_argl[def_argc] = length;
def_argo[def_argc] = def_buf_size - def_buf_free;
strcpy(def_argv(def_argc++), yytext);
def_buf_free -= length + 1;
}
void define_macro(const char* name, const char* value, int keyword, int argc)
{
struct define_t* def;
def = malloc(sizeof(struct define_t));
def->name = strdup(name);
def->value = strdup(value);
def->keyword = keyword;
def->argc = argc;
def->left = 0;
def->right = 0;
def->up = 0;
if (def_table == 0)
def_table = def;
else
{
struct define_t* cur = def_table;
while (1)
{
int cmp = strcmp(def->name, cur->name);
if (cmp == 0)
{
free(cur->value);
cur->value = def->value;
free(def->name);
free(def);
break;
}
else if (cmp < 0)
{
if (cur->left != 0)
cur = cur->left;
else
{
cur->left = def;
def->up = cur;
break;
}
}
else
{
if (cur->right != 0)
cur = cur->right;
else
{
cur->right = def;
def->up = cur;
break;
}
}
}
}
}
static void free_macro(struct define_t* def)
{
if (def == 0) return;
free_macro(def->left);
free_macro(def->right);
free(def->name);
free(def->value);
free(def);
}
void free_macros()
{
free_macro(def_table);
}
/*
* The do_define function accumulates the defined value in these
* variables. When the define is over, the def_finish() function
* executes the define and clears this text. The define_continue_flag
* is set if do_define detects that the definition is to be continued
* on the next line.
*/
static char* define_text = 0;
static size_t define_cnt = 0;
static int define_continue_flag = 0;
/*
* Define a special character code used to mark the insertion point
* for arguments in the macro text. This should be a character that
* will not occur in the Verilog source code.
*/
#define ARG_MARK '\a'
#define _STR1(x) #x
#define _STR2(x) _STR1(x)
/*
* Find an argument, but only if it is not directly preceded by something
* that would make it part of another simple identifier ([a-zA-Z0-9_$]).
*/
static char *find_arg(char*ptr, char*head, char*arg)
{
char *cp = ptr;
size_t len = strlen(arg);
while (1) {
/* Look for a candidate match, just return if none is found. */
cp = strstr(cp, arg);
if (!cp) break;
/* If we are not at the start of the string verify that this
* match is not in the middle of another identifier.
*/
if (cp != head &&
(isalnum(*(cp-1)) || *(cp-1) == '_' || *(cp-1) == '$' ||
isalnum(*(cp+len)) || *(cp+len) == '_' || *(cp+len) == '$')) {
cp++;
continue;
}
break;
}
return cp;
}
/*
* Collect the definition. Normally, this returns 0. If there is a
* continuation, then return 1 and this function may be called again
* to collect another line of the definition.
*/
static void do_define()
{
char* cp;
char* head;
char* tail;
int added_cnt;
int arg;
define_continue_flag = 0;
/* Look for comments in the definition, and remove them. The
* "//" style comments go to the end of the line and terminate
* the definition, but the multi-line comments are simply cut
* out, and the define continues.
*/
cp = strchr(yytext, '/');
while (cp && *cp)
{
if (cp[1] == '/') {
*cp = 0;
break;
}
if (cp[1] == '*')
{
tail = strstr(cp+2, "*/");
if (tail == 0)
{
*cp = 0;
fprintf
(
stderr,
"%s:%u: Unterminated comment in define\n",
istack->path, istack->lineno+1
);
break;
}
memmove(cp, tail+2, strlen(tail+2)+1);
continue;
}
cp = strchr(cp+1, '/');
}
/* Trim trailing white space. */
cp = yytext + strlen(yytext);
while (cp > yytext)
{
if (!isspace(cp[-1]))
break;
cp -= 1;
*cp = 0;
}
/* Detect the continuation sequence. If I find it, remove it
* and the white space that precedes it, then replace all that
* with a single newline.
*/
if ((cp > yytext) && (cp[-1] == '\\'))
{
cp -= 1;
cp[0] = 0;
while ((cp > yytext) && isspace(cp[-1])) {
cp -= 1;
*cp = 0;
}
*cp++ = '\n';
*cp = 0;
define_continue_flag = 1;
}
/* Accumulate this text into the define_text string. */
define_text = realloc(define_text, define_cnt + (cp-yytext) + 1); assert(define_text != 0);
head = &define_text[define_cnt];
strcpy(head, yytext);
define_cnt += cp-yytext;
tail = &define_text[define_cnt];
/* If the text for a macro with arguments contains occurrences
* of ARG_MARK, issue an error message and suppress the macro.
*/
if ((def_argc > 1) && strchr(head, ARG_MARK))
{
emit_pathline(istack);
error_count += 1;
def_argc = 0;
fprintf
(
stderr,
"error: implementation restriction - macro text may not contain a %s character\n",
_STR2(ARG_MARK)
);
}
/* Look for formal argument names in the definition, and replace
* each occurrence with the sequence ARG_MARK,'\ddd' where ddd is
* the formal argument index number.
*/
added_cnt = 0;
for (arg = 1; arg < def_argc; arg++)
{
int argl = def_argl[arg];
cp = find_arg(head, head, def_argv(arg));
while (cp && *cp)
{
added_cnt += 2 - argl;
if (added_cnt > 0)
{
char* base = define_text;
define_cnt += added_cnt;
define_text = realloc(define_text, define_cnt + 1); assert(define_text != 0);
head = &define_text[head - base];
tail = &define_text[tail - base];
cp = &define_text[cp - base];
added_cnt = 0;
}
memmove(cp+2, cp+argl, tail-(cp+argl)+1);
tail += 2 - argl;
*cp++ = ARG_MARK;
*cp++ = arg;
cp = find_arg(cp, head, def_argv(arg));
}
}
define_cnt += added_cnt;
}
/*
* Return true if the definition text is done. This is the opposite of
* the define_continue_flag.
*/
static int def_is_done()
{
return !define_continue_flag;
}
/*
* After some number of calls to do_define, this function is called to
* assigned value to the parsed name. If there is no value, then
* assign the string "" (empty string.)
*/
static void def_finish()
{
define_continue_flag = 0;
if (def_argc <= 0)
return;
if (!define_text)
define_macro(def_argv(0), "", 0, def_argc);
else
{
define_macro(def_argv(0), define_text, 0, def_argc);
free(define_text);
define_text = 0;
define_cnt = 0;
}
def_argc = 0;
}
static void def_undefine()
{
struct define_t* cur;
struct define_t* tail;
/* def_buf is used to store the macro name. Make sure there is
* enough space.
*/
def_buf_grow_to_fit(yyleng);
sscanf(yytext, "`undef %s", def_buf);
cur = def_lookup(def_buf);
if (cur == 0) return;
if (cur->up == 0)
{
if ((cur->left == 0) && (cur->right == 0))
def_table = 0;
else if (cur->left == 0)
{
def_table = cur->right;
if (cur->right)
cur->right->up = 0;
}
else if (cur->right == 0)
{
assert(cur->left);
def_table = cur->left;
def_table->up = 0;
}
else
{
tail = cur->left;
while (tail->right)
tail = tail->right;
tail->right = cur->right;
tail->right->up = tail;
def_table = cur->left;
def_table->up = 0;
}
}
else if (cur->left == 0)
{
if (cur->up->left == cur)
cur->up->left = cur->right;
else
{
assert(cur->up->right == cur);
cur->up->right = cur->right;
}
if (cur->right)
cur->right->up = cur->up;
}
else if (cur->right == 0)
{
assert(cur->left);
if (cur->up->left == cur)
cur->up->left = cur->left;
else
{
assert(cur->up->right == cur);
cur->up->right = cur->left;
}
cur->left->up = cur->up;
}
else
{
tail = cur->left;
assert(cur->left && cur->right);
while (tail->right)
tail = tail->right;
tail->right = cur->right;
tail->right->up = tail;
if (cur->up->left == cur)
cur->up->left = cur->left;
else
{
assert(cur->up->right == cur);
cur->up->right = cur->left;
}
cur->left->up = cur->up;
}
free(cur->name);
free(cur->value);
free(cur);
}
/*
* When a macro is instantiated in the source, macro_needs_args() is
* used to look up the name and return whether it is a macro that
* takes arguments. A pointer to the macro descriptor is stored in
* cur_macro so that do_expand() doesn't need to look it up again.
*/
static struct define_t* cur_macro = 0;
static int macro_needs_args(const char*text)
{
cur_macro = def_lookup(text);
if (cur_macro)
return (cur_macro->argc > 1);
else
{
emit_pathline(istack);
fprintf(stderr, "warning: macro %s undefined (and assumed null) at this point.\n", text);
return 0;
}
}
static char* macro_name()
{
return cur_macro ? cur_macro->name : "";
}
static void macro_start_args()
{
/* The macro name can be found via cur_macro, so create a null
* entry for arg 0. This will be used by macro_finish_arg() to
* calculate the buffer location for arg 1.
*/
def_buf_free = def_buf_size - 1;
def_buf[0] = 0;
def_argo[0] = 0;
def_argl[0] = 0;
def_argc = 1;
};
static void macro_add_to_arg(int is_white_space)
{
char* tail;
int length = yyleng;
check_for_max_args();
/* Replace any run of white space with a single space */
if (is_white_space)
{
yytext[0] = ' ';
yytext[1] = 0;
length = 1;
}
/* Make sure there's room in the buffer for the new argument. */
def_buf_grow_to_fit(length);
/* Store the new text. */
tail = &def_buf[def_buf_size - def_buf_free];
strcpy(tail, yytext);
def_buf_free -= length;
}
static void macro_finish_arg()
{
char* tail = &def_buf[def_buf_size - def_buf_free];
check_for_max_args();
*tail = 0;
def_argo[def_argc] = def_argo[def_argc-1] + def_argl[def_argc-1] + 1;
def_argl[def_argc] = tail - def_argv(def_argc);
def_buf_free -= 1;
def_argc++;
}
/*
* The following variables are used to hold macro expansions that are
* built dynamically using supplied arguments. Buffer space is allocated
* as the macro is expanded, and is only released once the expansion has
* been reparsed. This means that the buffer acts as a stack for nested
* macro expansions.
*
* The expansion buffer is only used for macros with arguments - the
* text for simple macros can be taken directly from the macro table.
*/
#define EXP_BUF_CHUNK 256
static char*exp_buf = 0;
static int exp_buf_size = 0;
static int exp_buf_free = 0;
static void exp_buf_grow_to_fit(int length)
{
while (length >= exp_buf_free)
{
exp_buf_size += EXP_BUF_CHUNK;
exp_buf_free += EXP_BUF_CHUNK;
exp_buf = realloc(exp_buf, exp_buf_size); assert(exp_buf != 0);
}
}
static void expand_using_args()
{
char* head;
char* tail;
char* dest;
int arg;
int length;
if (def_argc != cur_macro->argc)
{
emit_pathline(istack);
fprintf(stderr, "error: wrong number of arguments for `%s\n", cur_macro->name);
return;
}
head = cur_macro->value;
tail = head;
while (*tail)
{
if (*tail != ARG_MARK)
tail++;
else
{
arg = tail[1]; assert(arg < def_argc);
length = (tail - head) + def_argl[arg];
exp_buf_grow_to_fit(length);
dest = &exp_buf[exp_buf_size - exp_buf_free];
memcpy(dest, head, tail - head);
dest += tail - head;
memcpy(dest, def_argv(arg), def_argl[arg]);
exp_buf_free -= length;
head = tail + 2;
tail = head;
}
}
length = tail - head;
exp_buf_grow_to_fit(length);
dest = &exp_buf[exp_buf_size - exp_buf_free];
memcpy(dest, head, length + 1);
exp_buf_free -= length + 1;
}
/*
* When a macro use is discovered in the source, this function is
* used to emit the substitution in its place.
*/
static void do_expand(int use_args)
{
if (cur_macro)
{
struct include_stack_t*isp;
int head = 0;
int tail = 0;
if (cur_macro->keyword)
{
fprintf(yyout, "%s", cur_macro->value);
return;
}
if (use_args)
{
head = exp_buf_size - exp_buf_free;
expand_using_args();
tail = exp_buf_size - exp_buf_free;
if (tail == head)
return;
}
isp = (struct include_stack_t*) calloc(1, sizeof(struct include_stack_t));
isp->stringify_flag = do_expand_stringify_flag;
do_expand_stringify_flag = 0;
if (use_args)
{
isp->str = &exp_buf[head];
isp->ebs = tail - head;
}
else
{
isp->str = cur_macro->value;
isp->ebs = 0;
}
isp->next = istack;
istack->yybs = YY_CURRENT_BUFFER;
istack = isp;
yy_switch_to_buffer(yy_create_buffer(istack->file, YY_BUF_SIZE));
} else {
if (do_expand_stringify_flag) {
do_expand_stringify_flag = 0;
fputc('"', yyout);
}
}
}
/*
* Include file handling works by keeping an include stack of the
* files that are opened and being processed. The first item on the
* stack is the current file being scanned. If I get to an include
* statement,
*
* open the new file,
* save the current buffer context,
* create a new buffer context,
* and push the new file information.
*
* When the file runs out, it is closed and the buffer is deleted
* If after popping the current file information there is another
* file on the stack, that file's buffer context is restored and
* parsing resumes.
*/
static void output_init()
{
if (line_direct_flag)
fprintf(yyout, "`line 1 \"%s\" 0\n", istack->path);
}
static void include_filename()
{
if(standby)
{
emit_pathline(istack);
fprintf
(
stderr,
"error: malformed `include directive. Extra junk on line?\n"
);
exit(1);
}
standby = malloc(sizeof(struct include_stack_t));
standby->path = strdup(yytext+1);
standby->path[strlen(standby->path)-1] = 0;
standby->lineno = 0;
standby->comment = NULL;
}
static void do_include()
{
/* standby is defined by include_filename() */
if (standby->path[0] == '/')
{
if ((standby->file = fopen(standby->path, "r")))
goto code_that_switches_buffers;
}
else
{
unsigned idx, start = 0;
char path[4096];
char *cp;
/* Add the current path to the start of the include_dir list. */
strcpy(path, istack->path);
cp = strrchr(path, '/');
if (cp == 0)
start = 1; /* A base file so already in [1] */
else
{
*cp = '\0';
/* We do not need a strdup here since the path is read before
* it is overridden. If the search order is changed add a
* strdup here and a free below.
*/
include_dir[0] = path;
}
for (idx = start ; idx < include_cnt ; idx += 1)
{
sprintf(path, "%s/%s", include_dir[idx], standby->path);
if ((standby->file = fopen(path, "r")))
{
/* Free the original path before we overwrite it. */
free(standby->path);
standby->path = strdup(path);
goto code_that_switches_buffers;
}
}
}
fprintf(stderr, "%s:%u: Include file %s not found\n", istack->path, istack->lineno, standby->path);
exit(1);
code_that_switches_buffers:
/* Clear the current files path from the search list. */
include_dir[0] = 0;
if(depend_file)
fprintf(depend_file, "%s\n", standby->path);
if (line_direct_flag)
fprintf(yyout, "\n`line 1 \"%s\" 1\n", standby->path);
standby->next = istack;
standby->stringify_flag = 0;
istack->yybs = YY_CURRENT_BUFFER;
istack = standby;
standby = 0;
yy_switch_to_buffer(yy_create_buffer(istack->file, YY_BUF_SIZE));
}
/* walk the include stack until we find an entry with a valid pathname,
* and print the file and line from that entry for use in an error message.
* The istack entries created by do_expand() for macro expansions do not
* contain pathnames. This finds instead the real file in which the outermost
* macro was used.
*/
static void emit_pathline(struct include_stack_t* isp)
{
while(isp && (isp->path == NULL))
isp = isp->next;
assert(isp);
fprintf(stderr, "%s:%u: ", isp->path, isp->lineno+1);
}
static void lexor_done()
{
while (ifdef_stack)
{
struct ifdef_stack_t*cur = ifdef_stack;
ifdef_stack = cur->next;
fprintf
(
stderr,
"%s:%u: error: This `ifdef lacks an `endif.\n",
cur->path, cur->lineno+1
);
free(cur->path);
free(cur);
error_count += 1;
}
}
static int load_next_input()
{
int line_mask_flag = 0;
struct include_stack_t* isp = istack;
istack = isp->next;
/* Delete the current input buffers, and free the cell. */
yy_delete_buffer(YY_CURRENT_BUFFER);
/* If there was a comment for this include print it before we
* return to the previous input stream. This technically belongs
* to the previous stream, but it should not create any problems
* since it is only a comment.
*/
if (isp->comment) {
fprintf(yyout, "%s\n", isp->comment);
free(isp->comment);
isp->comment = NULL;
}
if (isp->file)
{
free(isp->path);
fclose(isp->file);
}
else
{
/* If I am printing line directives and I just finished
* macro substitution, I should terminate the line and
* arrange for a new directive to be printed.
*/
if (line_direct_flag && istack && istack->path && isp->lineno)
fprintf(yyout, "\n");
else
line_mask_flag = 1;
exp_buf_free += isp->ebs;
}
if (isp->stringify_flag) {
fputc('"', yyout);
}
free(isp);
/* If I am out of include stack, the main input is
* done. Look for another file to process in the input
* queue. If none are there, give up. Otherwise, open the file
* and continue parsing.
*/
if (istack == 0)
{
if (file_queue == 0)
{
lexor_done();
return 0;
}
istack = file_queue;
file_queue = file_queue->next;
istack->next = 0;
istack->lineno = 0;
istack->file = fopen(istack->path, "r");
if (istack->file == 0)
{
perror(istack->path);
error_count += 1;
return 0;
}
if (line_direct_flag)
fprintf(yyout, "\n`line 1 \"%s\" 0\n", istack->path);
if(depend_file)
fprintf(depend_file, "%s\n", istack->path);
/* This works around an issue in flex yyrestart() where it
* uses yyin to create a new buffer when one does not exist.
* I would have assumed that it would use the file argument.
* The problem is that we have deleted the buffer and freed
* yyin (isp->file) above. */
yyin = 0;
yyrestart(istack->file);
return 1;
}
/* Otherwise, resume the input buffer that is the new stack
* top. If I need to print a line directive, do so.
*/
yy_switch_to_buffer(istack->yybs);
if (line_direct_flag && istack->path && !line_mask_flag)
fprintf(yyout, "\n`line %u \"%s\" 2\n", istack->lineno+1, istack->path);
return 1;
}
/*
* The dump_precompiled_defines() and load_precompiled_defines()
* functions dump/load macro definitions to/from a file. The defines
* are in the form:
*
* <name>:<argc>:<len>:<value>
*
* for easy extraction. The value is already precompiled to handle
* macro substitution. The <len> is the number of bytes in the
* <value>. This is necessary because the value may contain arbitrary
* text, including ':' and \n characters.
*
* Each record is terminated by a \n character.
*/
static void do_dump_precompiled_defines(FILE* out, struct define_t* table)
{
if (!table->keyword)
#ifdef __MINGW32__ /* MinGW does not know about z. */
fprintf(out, "%s:%d:%d:%s\n", table->name, table->argc, strlen(table->value), table->value);
#else
fprintf(out, "%s:%d:%zd:%s\n", table->name, table->argc, strlen(table->value), table->value);
#endif
if (table->left)
do_dump_precompiled_defines(out, table->left);
if (table->right)
do_dump_precompiled_defines(out, table->right);
}
void dump_precompiled_defines(FILE* out)
{
if (def_table)
do_dump_precompiled_defines(out, def_table);
}
void load_precompiled_defines(FILE* src)
{
char buf[4096];
int ch;
while ((ch = fgetc(src)) != EOF)
{
char* cp = buf;
char* name = 0;
char* value = 0;
int argc = 0;
size_t len = 0;
name = cp;
*cp++ = ch;
while ((ch = fgetc(src)) != EOF && ch != ':')
*cp++ = ch;
if (ch != ':')
return;
/* Terminate the name string. */
*cp++ = 0;
/* Read the argc number */
while (isdigit(ch = fgetc(src)))
argc = 10*argc + ch-'0';
if (ch != ':')
return;
while (isdigit(ch = fgetc(src)))
len = 10*len + ch-'0';
if (ch != ':')
return;
value = cp;
while (len > 0)
{
ch = fgetc(src);
if (ch == EOF)
return;
*cp++ = ch;
len -= 1;
}
*cp++ = 0;
ch = fgetc(src);
if (ch != '\n')
return;
define_macro(name, value, 0, argc);
}
}
/*
* This function initializes the whole process. The first file is
* opened, and the lexor is initialized. The include stack is cleared
* and ready to go.
*/
void reset_lexor(FILE* out, char* paths[])
{
unsigned idx;
struct include_stack_t* isp;
struct include_stack_t* tail = 0;
isp = malloc(sizeof(struct include_stack_t));
isp->next = 0;
isp->path = strdup(paths[0]);
isp->file = fopen(paths[0], "r");
isp->str = 0;
isp->ebs = 0;
isp->lineno = 0;
isp->stringify_flag = 0;
isp->comment = NULL;
if (isp->file == 0)
{
perror(paths[0]);
exit(1);
}
if(depend_file)
fprintf(depend_file, "%s\n", paths[0]);
yyout = out;
yyrestart(isp->file);
assert(istack == 0);
istack = isp;
/* Now build up a queue of all the remaining file names, so
* that load_next_input() can pull them when needed.
*/
for (idx = 1 ; paths[idx] ; idx += 1)
{
isp = malloc(sizeof(struct include_stack_t));
isp->path = strdup(paths[idx]);
isp->file = 0;
isp->str = 0;
isp->ebs = 0;
isp->next = 0;
isp->lineno = 0;
isp->stringify_flag = 0;
isp->comment = NULL;
if (tail)
tail->next = isp;
else
file_queue = isp;
tail = isp;
}
}
/*
* Modern version of flex (>=2.5.9) can clean up the scanner data.
*/
void destroy_lexor()
{
# ifdef FLEX_SCANNER
# if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5
# if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9
yylex_destroy();
# endif
# endif
# endif
}