Add support for text macros with arguments.
This patch modifies the preprocessor to handle text macros with arguments. It also fixes a bug that prevented a `line directive being issued after a multi-line text macro had been instantiated.
This commit is contained in:
parent
103138cf4b
commit
680196953b
|
|
@ -25,7 +25,8 @@
|
|||
# include <stdio.h>
|
||||
|
||||
extern void reset_lexor(FILE*out, char*paths[]);
|
||||
extern void define_macro(const char*name, const char*value, int keyword);
|
||||
extern void define_macro(const char*name, const char*value, int keyword,
|
||||
int argc);
|
||||
|
||||
/* These variables contain the include directories to be searched when
|
||||
an include directive in encountered. */
|
||||
|
|
|
|||
503
ivlpp/lexor.lex
503
ivlpp/lexor.lex
|
|
@ -39,14 +39,21 @@
|
|||
static void output_init();
|
||||
#define YY_USER_INIT output_init()
|
||||
|
||||
static void def_match();
|
||||
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();
|
||||
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 yywrap();
|
||||
|
|
@ -59,9 +66,14 @@ struct include_stack_t {
|
|||
FILE*file;
|
||||
|
||||
/* If we are reparsing a macro expansion, file is 0 and this
|
||||
member points to the string is progress */
|
||||
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;
|
||||
|
||||
unsigned lineno;
|
||||
YY_BUFFER_STATE yybs;
|
||||
|
||||
|
|
@ -142,12 +154,21 @@ static void ifdef_leave(void)
|
|||
} 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
|
||||
|
||||
%x PPINCLUDE
|
||||
%x PPDEFINE
|
||||
%x DEF_NAME
|
||||
%x DEF_ARG
|
||||
%x DEF_SEP
|
||||
%x DEF_TXT
|
||||
%x MA_START
|
||||
%x MA_ADD
|
||||
%x CCOMMENT
|
||||
%x IFCCOMMENT
|
||||
%x PCOMENT
|
||||
|
|
@ -182,27 +203,31 @@ W [ \t\b\f]+
|
|||
expanded, however. */
|
||||
|
||||
"(*"{W}?")" { ECHO; }
|
||||
"(*" { comment_enter = YY_START; BEGIN(PCOMENT); ECHO; }
|
||||
"(*" { pragma_enter = YY_START; BEGIN(PCOMENT); ECHO; }
|
||||
<PCOMENT>[^\r\n] { ECHO; }
|
||||
<PCOMENT>\n\r { istack->lineno += 1; fputc('\n', yyout); }
|
||||
<PCOMENT>\r\n { istack->lineno += 1; fputc('\n', yyout); }
|
||||
<PCOMENT>\n { istack->lineno += 1; fputc('\n', yyout); }
|
||||
<PCOMENT>\r { istack->lineno += 1; fputc('\n', yyout); }
|
||||
<PCOMENT>"*)" { BEGIN(comment_enter); ECHO; }
|
||||
<PCOMENT>`[a-zA-Z][a-zA-Z0-9_$]* { def_match(); }
|
||||
<PCOMENT>"*)" { BEGIN(pragma_enter); ECHO; }
|
||||
<PCOMENT>`[a-zA-Z][a-zA-Z0-9_$]* {
|
||||
if (macro_needs_args()) 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. */
|
||||
\" { comment_enter = YY_START; BEGIN(CSTRING); ECHO; }
|
||||
\" { string_enter = YY_START; BEGIN(CSTRING); ECHO; }
|
||||
<CSTRING>\\\" { ECHO; }
|
||||
<CSTRING>\r\n { fputc('\n', yyout); }
|
||||
<CSTRING>\n\r { fputc('\n', yyout); }
|
||||
<CSTRING>\n { fputc('\n', yyout); }
|
||||
<CSTRING>\r { fputc('\n', yyout); }
|
||||
<CSTRING>\" { BEGIN(comment_enter); ECHO; }
|
||||
<CSTRING>\" { BEGIN(string_enter); ECHO; }
|
||||
<CSTRING>. { ECHO; }
|
||||
<CSTRING>`[a-zA-Z][a-zA-Z0-9_$]* { def_match(); }
|
||||
<CSTRING>`[a-zA-Z][a-zA-Z0-9_$]* {
|
||||
if (macro_needs_args()) yy_push_state(MA_START); else do_expand(0);
|
||||
}
|
||||
|
||||
/* This set of patterns matches the include directive and the name
|
||||
that follows it. when the directive ends, the do_include function
|
||||
|
|
@ -210,7 +235,9 @@ W [ \t\b\f]+
|
|||
|
||||
^{W}?`include { yy_push_state(PPINCLUDE); }
|
||||
|
||||
<PPINCLUDE>`[a-zA-Z][a-zA-Z0-9_]* { def_match(); }
|
||||
<PPINCLUDE>`[a-zA-Z][a-zA-Z0-9_]* {
|
||||
if (macro_needs_args()) yy_push_state(MA_START); else do_expand(0);
|
||||
}
|
||||
<PPINCLUDE>\"[^\"]*\" { include_filename(); }
|
||||
|
||||
<PPINCLUDE>[ \t\b\f] { ; }
|
||||
|
|
@ -219,7 +246,7 @@ W [ \t\b\f]+
|
|||
directive. And while I'm at it, I might as well preserve the
|
||||
comment in the output stream. */
|
||||
|
||||
<PPINCLUDE>"//".* { ECHO; }
|
||||
<PPINCLUDE>"//"[^\r\n]* { ECHO; }
|
||||
|
||||
/* These finish the include directive (EOF or EOL) so I revert the
|
||||
lexor state and execute the inclusion. */
|
||||
|
|
@ -239,27 +266,52 @@ W [ \t\b\f]+
|
|||
error_count += 1;
|
||||
BEGIN(ERROR_LINE); }
|
||||
|
||||
/* Detect the define directive, and match the name. Match any
|
||||
white space that might be found, as well. After I get the
|
||||
directive and the name, go into PPDEFINE mode and prepare to
|
||||
collect the defined value. */
|
||||
/* 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}[a-zA-Z_][a-zA-Z0-9_$]*{W}? { yy_push_state(PPDEFINE); def_start(); }
|
||||
`define{W} { yy_push_state(DEF_NAME); }
|
||||
|
||||
<PPDEFINE>.*[^\r\n] { do_define(); }
|
||||
<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(); }
|
||||
|
||||
<PPDEFINE>(\n|"\r\n"|"\n\r"|\r) {
|
||||
<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();
|
||||
istack->lineno += 1;
|
||||
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. */
|
||||
<PPDEFINE><<EOF>> {
|
||||
<DEF_TXT><<EOF>> {
|
||||
def_finish();
|
||||
istack->lineno += 1;
|
||||
fputc('\n', yyout);
|
||||
|
|
@ -393,7 +445,67 @@ W [ \t\b\f]+
|
|||
}
|
||||
|
||||
/* This pattern notices macros and arranges for them to be replaced. */
|
||||
`[a-zA-Z_][a-zA-Z0-9_$]* { def_match(); }
|
||||
`[a-zA-Z_][a-zA-Z0-9_$]* {
|
||||
if (macro_needs_args()) 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. */
|
||||
|
|
@ -407,6 +519,12 @@ W [ \t\b\f]+
|
|||
/* 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);
|
||||
}
|
||||
|
||||
%%
|
||||
/* Defined macros are kept in this table for convenient lookup. As
|
||||
`define directives are matched (and the do_define() function
|
||||
|
|
@ -417,6 +535,7 @@ struct define_t {
|
|||
char*value;
|
||||
/* keywords don't get rescanned for fresh values. */
|
||||
int keyword;
|
||||
int argc;
|
||||
|
||||
struct define_t*left, *right, *up;
|
||||
};
|
||||
|
|
@ -446,50 +565,92 @@ static int is_defined(const char*name)
|
|||
return def_lookup(name) != 0;
|
||||
}
|
||||
|
||||
/* When a macro use is discovered in the source, this function is
|
||||
used to look up the name and emit the substitution in its
|
||||
place. If the name is not found, then the `name string is written
|
||||
out instead. */
|
||||
/*
|
||||
* 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
|
||||
|
||||
static void def_match()
|
||||
#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)
|
||||
{
|
||||
struct define_t*cur = def_lookup(yytext+1);
|
||||
if (cur) {
|
||||
struct include_stack_t*isp;
|
||||
return def_buf + def_argo[arg];
|
||||
}
|
||||
|
||||
if (cur->keyword) {
|
||||
fprintf(yyout, "%s", cur->value);
|
||||
return;
|
||||
}
|
||||
|
||||
isp = (struct include_stack_t*)
|
||||
calloc(1, sizeof(struct include_stack_t));
|
||||
isp->str = cur->value;
|
||||
isp->next = istack;
|
||||
istack->yybs = YY_CURRENT_BUFFER;
|
||||
istack = isp;
|
||||
yy_switch_to_buffer(yy_create_buffer(istack->file, YY_BUF_SIZE));
|
||||
|
||||
} else {
|
||||
static void check_for_max_args()
|
||||
{
|
||||
if (def_argc == MAX_DEF_ARG) {
|
||||
emit_pathline(istack);
|
||||
fprintf(stderr, "warning: macro %s undefined "
|
||||
"(and assumed null) at this point.\n", yytext);
|
||||
fprintf(stderr, "error: too many macro arguments - aborting\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static char def_name[256];
|
||||
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()
|
||||
{
|
||||
sscanf(yytext, "`define %s", def_name);
|
||||
def_buf_free = def_buf_size;
|
||||
def_argc = 0;
|
||||
def_add_arg();
|
||||
}
|
||||
|
||||
void define_macro(const char*name, const char*value, int keyword)
|
||||
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 = 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;
|
||||
|
|
@ -549,7 +710,11 @@ static int define_continue_flag = 0;
|
|||
*/
|
||||
static void do_define()
|
||||
{
|
||||
char *cp;
|
||||
char*cp;
|
||||
char*head;
|
||||
char*tail;
|
||||
int added_cnt;
|
||||
int arg;
|
||||
|
||||
define_continue_flag = 0;
|
||||
|
||||
|
|
@ -565,7 +730,7 @@ static void do_define()
|
|||
}
|
||||
|
||||
if (cp[1] == '*') {
|
||||
char*tail = strstr(cp+2, "*/");
|
||||
tail = strstr(cp+2, "*/");
|
||||
if (tail == 0) {
|
||||
fprintf(stderr, "%s:%u: Unterminated comment "
|
||||
"in define\n", istack->path, istack->lineno);
|
||||
|
|
@ -609,8 +774,40 @@ static void do_define()
|
|||
|
||||
/* Accumulate this text into the define_text string. */
|
||||
define_text = realloc(define_text, define_cnt + (cp-yytext) + 1);
|
||||
strcpy(define_text+define_cnt, yytext);
|
||||
assert(define_text != 0);
|
||||
head = &define_text[define_cnt];
|
||||
strcpy(head, yytext);
|
||||
define_cnt += cp-yytext;
|
||||
tail = &define_text[define_cnt];
|
||||
|
||||
/* Look for formal argument names in the definition, and replace
|
||||
each occurrence with the sequence '\a','\ddd' where ddd is the
|
||||
formal argument index number. '\a' is chosen as a character
|
||||
that shouldn't normally occur in the define text. */
|
||||
added_cnt = 0;
|
||||
for (arg = 1; arg < def_argc; arg++) {
|
||||
int argl = def_argl[arg];
|
||||
cp = strstr(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++ = '%';
|
||||
*cp++ = '0' + arg;
|
||||
cp = strstr(cp, def_argv(arg));
|
||||
}
|
||||
}
|
||||
define_cnt += added_cnt;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -631,17 +828,17 @@ static void def_finish()
|
|||
{
|
||||
define_continue_flag = 0;
|
||||
|
||||
if (def_name[0]) {
|
||||
if (def_argc > 0) {
|
||||
if (define_text) {
|
||||
define_macro(def_name, define_text, 0);
|
||||
define_macro(def_argv(0), define_text, 0, def_argc);
|
||||
free(define_text);
|
||||
define_text = 0;
|
||||
define_cnt = 0;
|
||||
|
||||
} else {
|
||||
define_macro(def_name, "", 0);
|
||||
define_macro(def_argv(0), "", 0, def_argc);
|
||||
}
|
||||
def_name[0] = 0;
|
||||
def_argc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -649,9 +846,13 @@ static void def_undefine()
|
|||
{
|
||||
struct define_t*cur, *tail;
|
||||
|
||||
sscanf(yytext, "`undef %s", def_name);
|
||||
/* def_buf is used to store the macro name. Make sure there is
|
||||
enough space. */
|
||||
def_buf_grow_to_fit(yyleng);
|
||||
|
||||
cur = def_lookup(def_name);
|
||||
sscanf(yytext, "`undef %s", def_buf);
|
||||
|
||||
cur = def_lookup(def_buf);
|
||||
if (cur == 0) return;
|
||||
|
||||
if (cur->up == 0) {
|
||||
|
|
@ -729,6 +930,186 @@ static void def_undefine()
|
|||
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()
|
||||
{
|
||||
cur_macro = def_lookup(yytext+1);
|
||||
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", yytext);
|
||||
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)
|
||||
{
|
||||
int length = yyleng;
|
||||
char*tail;
|
||||
|
||||
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 = tail[1] - '0';
|
||||
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;
|
||||
} else {
|
||||
tail++;
|
||||
}
|
||||
}
|
||||
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;
|
||||
int tail;
|
||||
|
||||
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));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Include file handling works by keeping an include stack of the
|
||||
* files that are opened and being processed. The first item on the
|
||||
|
|
@ -821,7 +1202,7 @@ static void do_include()
|
|||
|
||||
/* 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 def_match() for macro expansions do not
|
||||
* 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.
|
||||
*/
|
||||
|
|
@ -875,10 +1256,12 @@ static int yywrap()
|
|||
arrange for a new directive to be printed. */
|
||||
if (line_direct_flag
|
||||
&& istack && istack->path
|
||||
&& strchr(isp->str, '\n'))
|
||||
&& isp->lineno)
|
||||
fprintf(yyout, "\n");
|
||||
else
|
||||
line_mask_flag = 1;
|
||||
|
||||
exp_buf_free += isp->ebs;
|
||||
}
|
||||
free(isp);
|
||||
|
||||
|
|
@ -942,6 +1325,7 @@ void reset_lexor(FILE*out, char*paths[])
|
|||
isp->path = strdup(paths[0]);
|
||||
isp->file = fopen(paths[0], "r");
|
||||
isp->str = 0;
|
||||
isp->ebs = 0;
|
||||
isp->lineno = 0;
|
||||
if (isp->file == 0) {
|
||||
perror(paths[0]);
|
||||
|
|
@ -967,6 +1351,7 @@ void reset_lexor(FILE*out, char*paths[])
|
|||
isp->path = strdup(paths[idx]);
|
||||
isp->file = 0;
|
||||
isp->str = 0;
|
||||
isp->ebs = 0;
|
||||
isp->next = 0;
|
||||
isp->lineno = 0;
|
||||
if (tail)
|
||||
|
|
|
|||
41
ivlpp/main.c
41
ivlpp/main.c
|
|
@ -130,7 +130,7 @@ static int flist_read_flags(const char*path)
|
|||
else
|
||||
val = "1";
|
||||
|
||||
define_macro(arg, val, 0);
|
||||
define_macro(arg, val, 0, 0);
|
||||
|
||||
} else if (strcmp(cp,"I") == 0) {
|
||||
include_dir = realloc(include_dir,
|
||||
|
|
@ -142,7 +142,7 @@ static int flist_read_flags(const char*path)
|
|||
char*buf = malloc(strlen(arg) + 2);
|
||||
buf[0] = '`';
|
||||
strcpy(buf+1, optarg);
|
||||
define_macro(optarg, buf, 1);
|
||||
define_macro(optarg, buf, 1, 0);
|
||||
free(buf);
|
||||
|
||||
} else if (strcmp(cp,"M") == 0) {
|
||||
|
|
@ -202,23 +202,23 @@ int main(int argc, char*argv[])
|
|||
FILE*out;
|
||||
|
||||
/* Define preprocessor keywords that I plan to just pass. */
|
||||
define_macro("celldefine", "`celldefine", 1);
|
||||
define_macro("default_nettype", "`default_nettype", 1);
|
||||
define_macro("delay_mode_distributed", "`delay_mode_distributed", 1);
|
||||
define_macro("delay_mode_unit", "`delay_mode_unit", 1);
|
||||
define_macro("delay_mode_path", "`delay_mode_path", 1);
|
||||
define_macro("disable_portfaults", "`disable_portfaults", 1);
|
||||
define_macro("enable_portfaults", "`enable_portfaults", 1);
|
||||
define_macro("endcelldefine", "`endcelldefine", 1);
|
||||
define_macro("endprotect", "`endprotect", 1);
|
||||
define_macro("nosuppress_faults", "`nosuppress_faults", 1);
|
||||
define_macro("nounconnected_drive", "`nounconnected_drive", 1);
|
||||
define_macro("protect", "`protect", 1);
|
||||
define_macro("resetall", "`resetall", 1);
|
||||
define_macro("suppress_faults", "`suppress_faults", 1);
|
||||
define_macro("timescale", "`timescale", 1);
|
||||
define_macro("unconnected_drive", "`unconnected_drive", 1);
|
||||
define_macro("uselib", "`uselib", 1);
|
||||
define_macro("celldefine", "`celldefine", 1, 0);
|
||||
define_macro("default_nettype", "`default_nettype", 1, 0);
|
||||
define_macro("delay_mode_distributed", "`delay_mode_distributed", 1, 0);
|
||||
define_macro("delay_mode_unit", "`delay_mode_unit", 1, 0);
|
||||
define_macro("delay_mode_path", "`delay_mode_path", 1, 0);
|
||||
define_macro("disable_portfaults", "`disable_portfaults", 1, 0);
|
||||
define_macro("enable_portfaults", "`enable_portfaults", 1, 0);
|
||||
define_macro("endcelldefine", "`endcelldefine", 1, 0);
|
||||
define_macro("endprotect", "`endprotect", 1, 0);
|
||||
define_macro("nosuppress_faults", "`nosuppress_faults", 1, 0);
|
||||
define_macro("nounconnected_drive", "`nounconnected_drive", 1, 0);
|
||||
define_macro("protect", "`protect", 1, 0);
|
||||
define_macro("resetall", "`resetall", 1, 0);
|
||||
define_macro("suppress_faults", "`suppress_faults", 1, 0);
|
||||
define_macro("timescale", "`timescale", 1, 0);
|
||||
define_macro("unconnected_drive", "`unconnected_drive", 1, 0);
|
||||
define_macro("uselib", "`uselib", 1, 0);
|
||||
|
||||
include_cnt = 2;
|
||||
include_dir = malloc(include_cnt*sizeof(char*));
|
||||
|
|
@ -243,7 +243,7 @@ int main(int argc, char*argv[])
|
|||
char*buf = malloc(strlen(optarg) + 2);
|
||||
buf[0] = '`';
|
||||
strcpy(buf+1, optarg);
|
||||
define_macro(optarg, buf, 1);
|
||||
define_macro(optarg, buf, 1, 0);
|
||||
free(buf);
|
||||
break;
|
||||
}
|
||||
|
|
@ -333,4 +333,3 @@ int main(int argc, char*argv[])
|
|||
|
||||
return error_count;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue