swig/SWIG/Source/Preprocessor/cpp.c
2000-01-13 17:20:58 +00:00

1204 lines
30 KiB
C

/****************************************************************************
* Simplified Wrapper and Interface Generator (SWIG)
*
* Author : David Beazley
*
* Department of Computer Science
* University of Chicago
* 1100 E 58th Street
* Chicago, IL 60637
* beazley@cs.uchicago.edu
*
* Please read the file LICENSE for the copyright and terms by which SWIG
* can be used and distributed.
****************************************************************************/
static char cvsroot[] = "$Header$";
#include "swigcpp.h"
#include <ctype.h>
/* -----------------------------------------------------------------------------
* $Header$
*
* File : cpp.c
*
* The SWIG Preprocessor. This works almost exactly like the C preprocessor except
* for a number of extensions:
*
* - SWIG directives such as %include, %extern, and %import are recognized
* - A new macro %define ... %enddef is provided to make it easier to
* write multi-line macros.
* - No preprocessing is performed inside %{ ... %} blocks.
* - Lines beginning with %#... are stripped to #... and passed through
* unmodified.
* ----------------------------------------------------------------------------- */
static DOH *cpp = 0; /* C preprocessor data */
static int include_all = 0; /* Follow all includes */
static int single_include = 1; /* Only include each file once */
static int silent_errors = 0;
static DOH *included_files = 0;
/* Handle an error */
void cpp_error(DOH *file, int line, char *fmt, ...) {
va_list ap;
if (silent_errors) return;
va_start(ap,fmt);
if (line > 0) {
Printf(stderr,"%s:%d ", file, line);
} else {
Printf(stderr,"%s:EOF ",file);
}
vPrintf(stderr,fmt,ap);
va_end(ap);
}
/* Test a character to see if it starts an identifier */
static int
isidentifier(char c) {
if ((isalpha(c)) || (c == '_') || (c == '$')) return 1;
else return 0;
}
/* Test a character to see if it valid in an identifier (after the first letter) */
static int
isidchar(char c) {
if ((isalnum(c)) || (c == '_') || (c == '$')) return 1;
else return 0;
}
/* Skip whitespace */
static void
skip_whitespace(DOH *s, DOH *out) {
int c;
while ((c = Getc(s)) != EOF) {
if (!isspace(c)) {
Ungetc(c,s);
break;
} else if (out) Putc(c,out);
}
}
/* Skip to a specified character taking line breaks into account */
static int
skip_tochar(DOH *s, int ch, DOH *out) {
int c;
while ((c = Getc(s)) != EOF) {
if (out) Putc(c,out);
if (c == ch) break;
if (c == '\\') {
c = Getc(s);
if ((c != EOF) && (out)) Putc(c,out);
}
}
if (c == EOF) return -1;
return 0;
}
static void
copy_location(DOH *s1, DOH *s2) {
Setfile(s2,Getfile(s1));
Setline(s2,Getline(s1));
}
static DOH *cpp_include(DOH *fn) {
DOH *s;
if (single_include) {
if (Getattr(included_files,fn)) return 0;
Setattr(included_files,fn,fn);
}
s = SWIG_include(fn);
if (!s) {
Seek(fn,0,SEEK_SET);
cpp_error(Getfile(fn),Getline(fn),"Unable to find '%s'\n", fn);
} else {
Seek(s,0,SEEK_SET);
}
return s;
}
/* -----------------------------------------------------------------------------
* void SWIG_cpp_init() - Initialize the preprocessor
* ----------------------------------------------------------------------------- */
void SWIG_cpp_init() {
DOH *s;
cpp = NewHash();
s = NewHash();
Setattr(cpp,"symbols",s);
SWIG_expr_init(); /* Initialize the expression evaluator */
included_files = NewHash();
}
/* -----------------------------------------------------------------------------
* void SWIG_cpp_include_all() - Instruct preprocessor to include all files
* ----------------------------------------------------------------------------- */
void SWIG_cpp_include_all(int a) {
include_all = a;
}
/* -----------------------------------------------------------------------------
* DOH *SWIG_cpp_define(DOH *str, int swigmacro)
*
* Defines a new C preprocessor symbol. swigmacro specifies whether or not the macro has
* SWIG macro semantics.
* ----------------------------------------------------------------------------- */
DOH *SWIG_cpp_define(DOH *str, int swigmacro)
{
DOH *macroname = 0, *argstr = 0, *macrovalue = 0, *arglist = 0, *macro = 0, *symbols = 0, *file = 0, *s, *m1;
int c, line;
assert(cpp);
assert(str);
/* First make sure that string is actually a string */
if (String_check(str)) {
s = Copy(str);
copy_location(str,s);
str = s;
} else {
str = NewString((char *) str);
}
line = Getline(str);
file = Getfile(str);
/* Printf(stdout,"%s:%d '%s'\n", file,line,str); */
/* Skip over any leading whitespace */
skip_whitespace(str,0);
/* Now look for a macro name */
macroname = NewString("");
while ((c = Getc(str)) != EOF) {
if (c == '(') {
argstr = NewString("");
copy_location(str,argstr);
/* It is a macro. Go extract it's argument string */
while ((c = Getc(str)) != EOF) {
if (c == ')') break;
else Putc(c,argstr);
}
if (c != ')') {
cpp_error(Getfile(str),Getline(str), "Missing \')\' in macro parameters\n");
goto macro_error;
}
break;
} else if (isidchar(c)) {
Putc(c,macroname);
} else if (isspace(c)) {
break;
} else {
cpp_error(Getfile(str),Getline(str),"Illegal character in macro name\n");
goto macro_error;
}
}
if (!swigmacro)
skip_whitespace(str,0);
macrovalue = NewString("");
while ((c = Getc(str)) != EOF) {
Putc(c,macrovalue);
}
/* If there are any macro arguments, convert into a list */
if (argstr) {
DOH *argname;
arglist = NewList();
Seek(argstr,0,SEEK_SET);
argname = NewString("");
while ((c = Getc(argstr)) != EOF) {
if (c == ',') {
Append(arglist,argname);
argname = NewString("");
} else if (isidchar(c)) {
Putc(c,argname);
} else if (!isspace(c)) {
cpp_error(Getfile(str),Getline(str),"Illegal character in macro name\n");
goto macro_error;
}
}
if (Len(argname)) {
Append(arglist,argname);
}
}
if (!swigmacro) {
String_replace(macrovalue,"\\\n"," ", DOH_REPLACE_ANY);
}
/* Get rid of whitespace surrounding # */
String_replace(macrovalue,"#","\001",DOH_REPLACE_NOQUOTE);
while(strstr(Char(macrovalue),"\001 ")) {
String_replace(macrovalue,"\001 ","\001", DOH_REPLACE_NOQUOTE);
}
while(strstr(Char(macrovalue)," \001")) {
String_replace(macrovalue," \001","\001", DOH_REPLACE_NOQUOTE);
}
/* Replace '##' with a special token */
String_replace(macrovalue,"\001\001","\002", DOH_REPLACE_NOQUOTE);
/* Go create the macro */
macro = NewHash();
Setattr(macro,"name", macroname);
if (arglist)
Setattr(macro,"args",arglist);
Setattr(macro,"value",macrovalue);
Setline(macro,line);
Setfile(macro,file);
if (swigmacro) {
Setattr(macro,"swigmacro","1");
}
symbols = Getattr(cpp,"symbols");
if ((m1 = Getattr(symbols,macroname))) {
if (Cmp(Getattr(m1,"value"),macrovalue))
cpp_error(Getfile(str),Getline(str),"Macro '%s' redefined. Previous definition in \'%s\', Line %d\n", macroname, Getfile(m1), Getline(m1));
}
Setattr(symbols,macroname,macro);
return macro;
macro_error:
return 0;
}
/* -----------------------------------------------------------------------------
* void SWIG_cpp_undef(DOH *str)
*
* Undefines a macro.
* ----------------------------------------------------------------------------- */
void SWIG_cpp_undef(DOH *str)
{
DOH *symbols;
assert(cpp);
symbols = Getattr(cpp,"symbols");
Delattr(symbols,str);
}
/* -----------------------------------------------------------------------------
* DOH *find_args(DOH *s)
*
* Isolates macro arguments and returns them in a list. For each argument,
* leading and trailing whitespace is stripped (ala K&R, pg. 230).
* ----------------------------------------------------------------------------- */
static DOH *
find_args(DOH *s)
{
DOH *args, *str;
int c, level;
/* Create a new list */
args = NewList();
copy_location(s,args);
/* First look for a '(' */
skip_whitespace(s,0);
/* Now see if the next character is a '(' */
c = Getc(s);
if (c != '(') {
/* Not a macro, bail out now! */
cpp_error(Getfile(s),Getline(s),"Missing macro arguments\n");
return args;
}
c = Getc(s);
/* Okay. This appears to be a macro so we will start isolating arguments */
while (c != EOF) {
if (isspace(c)) {
skip_whitespace(s,0); /* Skip leading whitespace */
c = Getc(s);
}
str = NewString("");
copy_location(s,str);
level = 0;
while (c != EOF) {
if (c == '\"') {
Putc(c,str);
skip_tochar(s,'\"',str);
c = Getc(s);
continue;
} else if (c == '\'') {
Putc(c,str);
skip_tochar(s,'\'',str);
c = Getc(s);
continue;
}
if ((c == ',') && (level == 0)) break;
if ((c == ')') && (level == 0)) break;
Putc(c,str);
if (c == '(') level++;
if (c == ')') level--;
c = Getc(s);
}
if (level > 0) {
goto unterm;
}
String_chop(str);
if (Len(args) || Len(str))
Append(args,str);
/* if (Len(str) && (c != ')'))
Append(args,str); */
if (c == ')') return args;
c = Getc(s);
}
unterm:
cpp_error(Getfile(args),Getline(args),"Unterminated macro call.\n");
return args;
}
/* -----------------------------------------------------------------------------
* DOH *get_filename(DOH *str)
*
* Read a filename from str. A filename can be enclose in quotes, angle brackets,
* or bare.
* ----------------------------------------------------------------------------- */
static DOH *
get_filename(DOH *str) {
DOH *fn;
int c;
skip_whitespace(str,0);
fn = NewString("");
copy_location(str,fn);
c = Getc(str);
if (c == '\"') {
while (((c = Getc(str)) != EOF) && (c != '\"')) Putc(c,fn);
} else if (c == '<') {
while (((c = Getc(str)) != EOF) && (c != '>')) Putc(c,fn);
} else {
Putc(c,fn);
while (((c = Getc(str)) != EOF) && (!isspace(c))) Putc(c,fn);
if (isspace(c)) Ungetc(c,str);
}
Seek(fn,0,SEEK_SET);
return fn;
}
/* -----------------------------------------------------------------------------
* DOH *expand_macro(DOH *name, DOH *args)
*
* Perform macro expansion and return a new string. Returns NULL if some sort
* of error occurred.
* ----------------------------------------------------------------------------- */
DOH *expanded_value = 0;
DOH *
expand_macro(DOH *name, DOH *args)
{
DOH *symbols, *ns, *macro, *margs, *mvalue, *temp, *tempa, *e;
DOH *swig_cpp_replace(DOH *);
DOH *SWIG_cpp_parse(DOH *);
int i, l;
symbols = Getattr(cpp,"symbols");
if (!symbols) return 0;
/* See if the name is actually defined */
macro = Getattr(symbols,name);
if (!macro) return 0;
if (Getattr(macro,"*expanded*")) {
ns = NewString("");
Printf(ns,"%s",name);
if (args) {
Putc('(',ns);
for (i = 0; i < Len(args); i++) {
Printf(ns,"%s",Getitem(args,i));
if (i < (Len(args) -1)) Putc(',',ns);
}
Putc(')',ns);
}
return ns;
}
/* Get macro arguments and value */
mvalue = Getattr(macro,"value");
assert(mvalue);
margs = Getattr(macro,"args");
/* If there are arguments, see if they match what we were given */
if ((margs) && (Len(margs) != Len(args))) {
if (Len(margs) > 1)
cpp_error(Getfile(args),Getline(args),"Macro '%s' expects %d arguments\n", name, Len(margs));
else if (Len(margs) == 1)
cpp_error(Getfile(args),Getline(args),"Macro '%s' expects 1 argument\n", name);
else
cpp_error(Getfile(args),Getline(args),"Macro '%s' expects no arguments\n", name);
return 0;
}
/* Copy the macro value */
ns = Copy(mvalue);
copy_location(mvalue,ns);
/* Tag the macro as being expanded. This is to avoid recursion in
macro expansion */
if (!expanded_value) {
expanded_value = NewString("");
DohIntern(expanded_value);
}
Setattr(macro,"*expanded*",expanded_value);
temp = NewString("");
tempa = NewString("");
if (margs) {
l = Len(margs);
for (i = 0; i < l; i++) {
DOH *arg, *aname;
arg = Getitem(args,i); /* Get an argument value */
aname = Getitem(margs,i); /* Get macro argument name */
if (strstr(Char(ns),"\001")) {
/* Try to replace a quoted version of the argument */
Clear(temp);
Clear(tempa);
Printf(temp,"\001%s", aname);
Printf(tempa,"\"%s\"",arg);
String_replace(ns, temp, tempa, DOH_REPLACE_ANY);
}
String_replace(ns, aname, arg, DOH_REPLACE_ID);
}
}
String_replace(ns,"\002","",DOH_REPLACE_ANY); /* Get rid of concatenation tokens */
String_replace(ns,"\001","#",DOH_REPLACE_ANY); /* Put # back (non-standard C) */
/* Expand this macro even further */
e = swig_cpp_replace(ns);
Delattr(macro,"*expanded*");
if (Getattr(macro,"swigmacro")) {
DOH *g;
DOH *f = NewString("");
Printf(f,"%%macro %s, \"%s\", %d {\n", name, Getfile(macro), Getline(macro));
Seek(e,0,SEEK_SET);
copy_location(macro,e);
g = SWIG_cpp_parse(e);
Printf(f,"%s\n", g);
Printf(f,"}\n");
e = f;
}
return e;
}
/* -----------------------------------------------------------------------------
* DOH *swig_cpp_replace(DOH *s)
*
* Performs a macro substitution on a string s. Returns a new string with
* substitutions applied. This function works by walking down s and looking
* for identifiers. When found, a check is made to see if they are macros
* which are then expanded.
* ----------------------------------------------------------------------------- */
DOH *
swig_cpp_replace(DOH *s)
{
DOH *ns, *id, *symbols, *m;
int c, i, state = 0;
assert(cpp);
symbols = Getattr(cpp,"symbols");
ns = NewString("");
copy_location(s,ns);
Seek(s,0,SEEK_SET);
id = NewString("");
/* Try to locate identifiers in s and replace them with macro replacements */
while ((c = Getc(s)) != EOF) {
switch (state) {
case 0:
if (isidentifier(c)) {
Clear(id);
copy_location(s,id);
Putc(c,id);
state = 1;
} else if (c == '\"') {
Putc(c,ns);
skip_tochar(s,'\"',ns);
} else if (c == '\'') {
Putc(c,ns);
skip_tochar(s,'\'',ns);
} else if (c == '/') {
Putc(c,ns);
state = 10;
} else {
Putc(c,ns);
}
break;
case 1: /* An identifier */
if (isidchar(c)) {
Putc(c,id);
state = 1;
} else {
/* We found the end of a valid identifier */
Ungetc(c,s);
/* See if this is the special "defined" macro */
if (Cmp(id,"defined") == 0) {
DOH *args;
/* See whether or not a paranthesis has been used */
skip_whitespace(s,0);
c = Getc(s);
if (c == '(') {
Seek(s,-1,SEEK_CUR);
args = find_args(s);
} else {
DOH *arg = 0;
args = NewList();
arg = NewString("");
while ((c = Getc(s)) != EOF) {
if (!isidchar(c)) {
Seek(s,-1,SEEK_CUR);
break;
}
Putc(c,arg);
}
Append(args,arg);
}
if (!args) {
cpp_error(Getfile(id),Getline(id),"No arguments given to defined()\n");
state = 0;
break;
}
for (i = 0; i < Len(args); i++) {
DOH *o = Getitem(args,i);
if (!Getattr(symbols,o)) {
break;
}
}
if (i < Len(args)) Putc('0',ns);
else Putc('1',ns);
state = 0;
break;
}
if (Cmp(id,"__LINE__") == 0) {
Printf(ns,"%d",Getline(s));
state = 0;
break;
}
if (Cmp(id,"__FILE__") == 0) {
Printf(ns,"\"%s\"",Getfile(s));
state = 0;
break;
}
/* See if the macro is defined in the preprocessor symbol table */
if ((m = Getattr(symbols,id))) {
DOH *args = 0;
DOH *e;
/* See if the macro expects arguments */
if (Getattr(m,"args")) {
/* Yep. We need to go find the arguments and do a substitution */
args = find_args(s);
} else {
args = 0;
}
e = expand_macro(id,args);
if (e) {
Printf(ns,"%s",e);
}
} else {
Printf(ns,"%s",id);
}
state = 0;
}
break;
case 10:
if (c == '/') state = 11;
else if (c == '*') state = 12;
else {
Ungetc(c,s);
state = 0;
break;
}
Putc(c,ns);
break;
case 11:
Putc(c,ns);
if (c == '\n') state = 0;
break;
case 12:
Putc(c,ns);
if (c == '*') state = 13;
break;
case 13:
Putc(c,ns);
if (c == '/') state = 0;
else if (c != '*') state = 12;
break;
default:
state = 0;
break;
}
}
/* Identifier at the end */
if (state == 1) {
/* See if this is the special "defined" macro */
if (Cmp(id,"defined") == 0) {
cpp_error(Getfile(id),Getline(id),"No arguments given to defined()\n");
} else if ((m = Getattr(symbols,id))) {
DOH *e;
/* Yes. There is a macro here */
/* See if the macro expects arguments */
if (Getattr(m,"args")) {
cpp_error(Getfile(id),Getline(id),"Macro arguments expected.\n");
}
e = expand_macro(id,0);
Printf(ns,"%s",e);
} else {
Printf(ns,"%s",id);
}
}
return ns;
}
/* -----------------------------------------------------------------------------
* int check_id(DOH *s)
*
* Checks the string s to see if it contains any unresolved identifiers. This
* function contains the heuristic that determines whether or not a macro
* definition passes through the preprocessor as a constant declaration.
* ----------------------------------------------------------------------------- */
static int
check_id(DOH *s)
{
int c, state = 0;
Seek(s,0,SEEK_SET);
while ((c = Getc(s)) != EOF) {
switch(state) {
case 0:
if (isdigit(c)) state = 1;
else if (isidentifier(c)) return 1;
else if (c == '\"') skip_tochar(s,'\"',0);
else if (c == '\'') skip_tochar(s,'\'',0);
else if (c == '/') state = 3;
break;
case 1:
if ((c == 'L') || (c == 'U') || (c == 'F') || (c == 'l') || (c == 'u')) state = 2;
else if (isidentifier(c)) return 1;
else if (!isdigit(c)) state = 0;
break;
case 2:
if ((c == 'L') || (c == 'U') || (c == 'F') || (c == 'l') || (c == 'u')) state = 2;
else {
Ungetc(c,s);
state = 0;
}
break;
case 3:
if (c == '*') state = 10;
else if (c == '/') state = 20;
else {
Ungetc(c,s);
state = 0;
}
break;
case 10:
if (c == '*') state = 11;
break;
case 11:
if (c == '/') state = 0;
else if (c != '*') state = 10;
break;
case 20:
if (c == '\n') state = 0;
break;
}
}
return 0;
}
/* addline(). Utility function for adding lines to a chunk */
static void
addline(DOH *s1, DOH *s2, int allow)
{
if (allow) {
Append(s1,s2);
} else {
char *c = Char(s2);
while (*c) {
if (*c == '\n') Putc('\n',s1);
c++;
}
}
}
static void add_chunk(DOH *ns, DOH *chunk, int allow) {
DOH *echunk;
Seek(chunk,0,SEEK_SET);
if (allow) {
echunk = swig_cpp_replace(chunk);
addline(ns,echunk,allow);
Delete(echunk);
} else {
addline(ns,chunk,0);
}
Clear(chunk);
}
/* -----------------------------------------------------------------------------
* DOH *SWIG_cpp_parse(DOH *s)
*
* Parses the string s. Returns a new string containing the preprocessed version.
*
* Parsing rules :
* 1. Lines starting with # are C preprocessor directives
* 2. Macro expansion inside strings is not allowed
* 3. All code inside false conditionals is changed to blank lines
* 4. Code in %{, %} is not parsed because it may need to be
* included inline (with all preprocessor directives included).
* ----------------------------------------------------------------------------- */
DOH *
SWIG_cpp_parse(DOH *s)
{
DOH *ns; /* New string containing the preprocessed text */
DOH *chunk, *symbols, *sval, *decl;
DOH *id = 0, *value = 0, *comment = 0;
int i, state, val, e, c;
int start_line = 0;
int allow = 1;
int level = 0;
int mask = 0;
int start_level = 0;
int cpp_lines = 0;
int cond_lines[256];
int scp;
ns = NewString(""); /* Return result */
scp = NewScope();
decl = NewString("");
id = NewString("");
value = NewString("");
comment = NewString("");
chunk = NewString("");
copy_location(s,chunk);
copy_location(s,ns);
symbols = Getattr(cpp,"symbols");
state = 0;
while ((c = Getc(s)) != EOF) {
switch(state) {
case 0: /* Initial state - in first column */
/* Look for C preprocessor directives. Otherwise, go directly to state 1 */
if (c == '#') {
add_chunk(ns,chunk,allow);
copy_location(s,chunk);
cpp_lines = 1;
state = 40;
} else if (isspace(c)) {
Putc(c,chunk);
skip_whitespace(s,chunk);
} else {
state = 1;
Ungetc(c,s);
}
break;
case 1: /* Non-preprocessor directive */
/* Look for SWIG directives */
if (c == '%') {
state = 100;
break;
}
Putc(c,chunk);
if (c == '\n') state = 0;
else if (c == '\"') {
start_line = Getline(s);
if (skip_tochar(s,'\"',chunk) < 0) {
cpp_error(Getfile(s),-1,"Unterminated string constant starting at line %d\n",start_line);
}
} else if (c == '\'') {
start_line = Getline(s);
if (skip_tochar(s,'\'',chunk) < 0) {
cpp_error(Getfile(s),-1,"Unterminated character constant starting at line %d\n",start_line);
}
}
else if (c == '/') state = 30; /* Comment */
break;
case 30: /* Possibly a comment string of some sort */
start_line = Getline(s);
Putc(c,chunk);
if (c == '/') state = 31;
else if (c == '*') state = 32;
else state = 1;
break;
case 31:
Putc(c,chunk);
if (c == '\n') state = 0;
break;
case 32:
Putc(c,chunk);
if (c == '*') state = 33;
break;
case 33:
Putc(c,chunk);
if (c == '/') state = 1;
else if (c != '*') state = 32;
break;
case 40: /* Start of a C preprocessor directive */
if (c == ' ') state = 40; /* Strip off any leading white-space */
else if (c == '\n') {
Putc('\n',chunk);
state = 0;
} else {
/* Got the start of a preprocessor directive */
Ungetc(c,s);
Clear(id);
copy_location(s,id);
state = 41;
}
break;
case 41: /* Build up the name of the preprocessor directive */
if (isspace(c)) {
Clear(value);
Clear(comment);
if (c == '\n') {
Ungetc(c,s);
state = 50;
}
else state = 42;
copy_location(s,value);
break;
}
Putc(c,id);
break;
case 42: /* Strip any leading space before preprocessor value */
if (isspace(c)) {
if (c == '\n') {
Ungetc(c,s);
state = 50;
}
break;
}
state = 43;
/* no break intended here */
case 43:
/* Get preprocessor value */
if (c == '\n') {
Ungetc(c,s);
state = 50;
} else if (c == '/') {
state = 45;
} else {
Putc(c,value);
if (c == '\\') state = 44;
}
break;
case 44:
if (c == '\n') cpp_lines++;
Putc(c,value);
state = 43;
break;
/* States 45-48 are used to remove, but retain comments from macro values. The comments
will be placed in the output in an alternative form */
case 45:
if (c == '/') state = 46;
else if (c == '*') state = 47;
else {
Putc('/',value);
Putc(c,value);
state = 43;
}
break;
case 46:
if (c == '\n') {
Ungetc(c,s);
cpp_lines++;
state = 50;
} else Putc(c,comment);
break;
case 47:
if (c == '*') state = 48;
else Putc(c,comment);
break;
case 48:
if (c == '/') state = 43;
else if (c == '*') Putc(c,comment);
else {
Putc('*',comment);
Putc(c,comment);
state = 47;
}
break;
case 50:
/* Check for various preprocessor directives */
String_chop(value);
if (Cmp(id,"define") == 0) {
if (allow) {
DOH *m, *v, *v1;
Seek(value,0,SEEK_SET);
m = SWIG_cpp_define(value,0);
if ((m) && !(Getattr(m,"args"))) {
v = Copy(Getattr(m,"value"));
if (Len(v)) {
silent_errors = 1;
v1 = swig_cpp_replace(v);
silent_errors = 0;
if (!check_id(v1)) {
if (Len(comment) == 0)
Printf(ns,"%%constant %s %s;\n", Getattr(m,"name"), v1);
else
Printf(ns,"%%constant %s %s; /*%s*/\n", Getattr(m,"name"),v1,comment);
cpp_lines--;
}
}
}
}
} else if (Cmp(id,"undef") == 0) {
if (allow) SWIG_cpp_undef(value);
} else if (Cmp(id,"ifdef") == 0) {
cond_lines[level] = Getline(id);
level++;
if (allow) {
start_level = level;
/* See if the identifier is in the hash table */
if (!Getattr(symbols,value)) allow = 0;
mask = 1;
}
} else if (Cmp(id,"ifndef") == 0) {
cond_lines[level] = Getline(id);
level++;
if (allow) {
start_level = level;
/* See if the identifier is in the hash table */
if (Getattr(symbols,value)) allow = 0;
mask = 1;
}
} else if (Cmp(id,"else") == 0) {
if (level == 0) {
cpp_error(Getfile(s),Getline(id),"Misplaced #else.\n");
} else {
cond_lines[level-1] = Getline(id);
if (allow) {
allow = 0;
mask = 0;
} else if (level == start_level) allow = 1;
}
} else if (Cmp(id,"endif") == 0) {
level--;
if (level < 0) {
cpp_error(Getfile(id),Getline(id),"Extraneous #endif ignored.\n");
level = 0;
} else {
if (level < start_level) allow = 1;
}
} else if (Cmp(id,"if") == 0) {
cond_lines[level] = Getline(id);
level++;
if (allow) {
start_level = level;
sval = swig_cpp_replace(value);
Seek(sval,0,SEEK_SET);
val = SWIG_expr(sval,&e);
if (e) {
Seek(value,0,SEEK_SET);
cpp_error(Getfile(value),Getline(value),"Could not evaluate '%s'\n", value);
allow = 0;
} else {
if (val == 0)
allow = 0;
}
mask = 1;
}
} else if (Cmp(id,"elif") == 0) {
if (level == 0) {
cpp_error(Getfile(s),Getline(id),"Misplaced #elif.\n");
} else {
cond_lines[level-1] = Getline(id);
if (allow) {
allow = 0;
mask = 0;
} else if (level == start_level) {
sval = swig_cpp_replace(value);
Seek(sval,0,SEEK_SET);
val = SWIG_expr(sval,&e);
if (e) {
Seek(value,0,SEEK_SET);
/* cpp_error(Getfile(value),Getline(value),"Could not evaluate '%s'\n", value); */
allow = 0;
} else {
if (val)
allow = 1*mask;
else
allow = 0;
}
}
}
} else if (Cmp(id,"line") == 0) {
} else if (Cmp(id,"include") == 0) {
if ((include_all) && (allow)) {
DOH *s1, *s2, *fn;
Seek(value,0,SEEK_SET);
fn = get_filename(value);
s1 = cpp_include(fn);
if (s1) {
Printf(ns,"%%includefile \"%s\" {\n", SWIG_last_file());
s2 = SWIG_cpp_parse(s1);
addline(ns,s2,allow);
Printf(ns,"\n}\n");
Delete(s2);
}
Delete(s1);
}
} else if (Cmp(id,"pragma") == 0) {
} else {
}
for (i = 0; i < cpp_lines; i++)
Putc('\n',ns);
state = 0;
break;
/* Swig directives */
case 100:
/* %{,%} block */
if (c == '{') {
start_line = Getline(s);
add_chunk(ns,chunk,allow);
copy_location(s,chunk);
Putc('%',chunk);
Putc(c,chunk);
state = 105;
}
/* %#cpp - an embedded C preprocessor directive (we strip off the %) */
else if (c == '#') {
Putc(c,chunk);
state = 0;
} else if (isidentifier(c)) {
Clear(decl);
Putc('%',decl);
Putc(c,decl);
state = 110;
} else {
Putc(c,chunk);
state = 1;
}
break;
case 105:
Putc(c,chunk);
if (c == '%')
state = 106;
break;
case 106:
Putc(c,chunk);
if (c == '}') {
state = 1;
addline(ns,chunk,allow);
Clear(chunk);
copy_location(s,chunk);
} else {
state = 105;
}
break;
case 110:
if (!isidchar(c)) {
Ungetc(c,s);
/* Look for common Swig directives */
if ((Cmp(decl,"%include") == 0) || (Cmp(decl,"%import") == 0) || (Cmp(decl,"%extern") == 0)) {
/* Got some kind of file inclusion directive */
if (allow) {
DOH *s1, *s2, *fn;
fn = get_filename(s);
s1 = cpp_include(fn);
if (s1) {
add_chunk(ns,chunk,allow);
copy_location(s,chunk);
Printf(ns,"%sfile \"%s\" {\n", decl, SWIG_last_file());
if ((Cmp(decl,"%import") == 0) || (Cmp(decl,"%extern") == 0)) {
SWIG_cpp_define("WRAPEXTERN 1", 0);
}
s2 = SWIG_cpp_parse(s1);
if ((Cmp(decl,"%import") == 0) || (Cmp(decl,"%extern") == 0)) {
SWIG_cpp_undef("WRAPEXTERN");
}
addline(ns,s2,allow);
Printf(ns,"\n}\n");
Delete(s2);
Delete(s1);
}
Delete(fn);
}
state = 1;
} else if (Cmp(decl,"%line") == 0) {
/* Got a line directive */
state = 1;
} else if (Cmp(decl,"%define") == 0) {
/* Got a define directive */
add_chunk(ns,chunk,allow);
copy_location(s,chunk);
Clear(value);
copy_location(s,value);
state = 150;
} else {
Printf(chunk,"%s", decl);
state = 1;
}
} else {
Putc(c,decl);
}
break;
/* Searching for the end of a %define statement */
case 150:
Putc(c,value);
if (c == '%') {
int i = 0;
char *d = "enddef\n";
for (i = 0; i < 7; i++) {
c = Getc(s);
Putc(c,value);
if (c != d[i]) break;
}
if (i == 7) {
/* Got the macro */
for (i = 0; i < 8; i++) {
Delitem(value,DOH_END);
}
if (allow) {
Seek(value,0,SEEK_SET);
SWIG_cpp_define(value,1);
}
Putc('\n',ns);
addline(ns,value,0);
state = 0;
}
}
break;
default :
Printf(stderr,"cpp: Invalid parser state %d\n", state);
abort();
break;
}
}
while (level > 0) {
cpp_error(Getfile(s),-1,"Missing #endif for conditional starting on line %d\n", cond_lines[level-1]);
level--;
}
if (state == 150) {
Seek(value,0,SEEK_SET);
cpp_error(Getfile(s),-1,"Missing %%enddef for macro starting on line %d\n",Getline(value));
}
if ((state >= 105) && (state < 107)) {
cpp_error(Getfile(s),-1,"Unterminated %%{ ... %%} block starting on line %d\n", start_line);
}
if ((state >= 30) && (state < 40)) {
cpp_error(Getfile(s),-1,"Unterminated comment starting on line %d\n", start_line);
}
add_chunk(ns,chunk,allow);
copy_location(s,chunk);
DelScope(scp);
return ns;
}