diff options
Diffstat (limited to 'Source/Preprocessor')
| -rw-r--r-- | Source/Preprocessor/cpp.c | 1854 | ||||
| -rw-r--r-- | Source/Preprocessor/expr.c | 436 | ||||
| -rw-r--r-- | Source/Preprocessor/preprocessor.h | 38 |
3 files changed, 2328 insertions, 0 deletions
diff --git a/Source/Preprocessor/cpp.c b/Source/Preprocessor/cpp.c new file mode 100644 index 0000000..b456b5e --- /dev/null +++ b/Source/Preprocessor/cpp.c @@ -0,0 +1,1854 @@ +/* ----------------------------------------------------------------------------- + * See the LICENSE file for information on copyright, usage and redistribution + * of SWIG, and the README file for authors - http://www.swig.org/release.html. + * + * cpp.c + * + * An implementation of a C preprocessor plus some support for additional + * SWIG directives. + * + * - SWIG directives such as %include, %extern, and %import are handled + * - A new macro %define ... %enddef can be used for multiline macros + * - No preprocessing is performed in %{ ... %} blocks + * - Lines beginning with %# are stripped down to #... and passed through. + * ----------------------------------------------------------------------------- */ + +char cvsroot_cpp_c[] = "$Id: cpp.c 11098 2009-01-30 10:32:59Z bhy $"; + +#include "swig.h" +#include "preprocessor.h" +#include <ctype.h> + +static Hash *cpp = 0; /* C preprocessor data */ +static int include_all = 0; /* Follow all includes */ +static int ignore_missing = 0; +static int import_all = 0; /* Follow all includes, but as %import statements */ +static int imported_depth = 0; /* Depth of %imported files */ +static int single_include = 1; /* Only include each file once */ +static Hash *included_files = 0; +static List *dependencies = 0; +static Scanner *id_scan = 0; +static int error_as_warning = 0; /* Understand the cpp #error directive as a special #warning */ + +/* Test a character to see if it starts an identifier */ +#define isidentifier(c) ((isalpha(c)) || (c == '_') || (c == '$')) + +/* Test a character to see if it valid in an identifier (after the first letter) */ +#define isidchar(c) ((isalnum(c)) || (c == '_') || (c == '$')) + +DOH *Preprocessor_replace(DOH *); + +/* Skip whitespace */ +static void skip_whitespace(String *s, String *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(String *s, int ch, String *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(const DOH *s1, DOH *s2) { + Setfile(s2, Getfile((DOH *) s1)); + Setline(s2, Getline((DOH *) s1)); +} + +static String *cpp_include(const_String_or_char_ptr fn, int sysfile) { + String *s = sysfile ? Swig_include_sys(fn) : Swig_include(fn); + if (s && single_include) { + String *file = Getfile(s); + if (Getattr(included_files, file)) { + Delete(s); + return 0; + } + Setattr(included_files, file, file); + } + if (!s) { + /* XXX(bhy) may not need the seek */ + /* Seek(fn, 0, SEEK_SET); */ + if (ignore_missing) { + Swig_warning(WARN_PP_MISSING_FILE, Getfile(fn), Getline(fn), "Unable to find '%s'\n", fn); + } else { + Swig_error(Getfile(fn), Getline(fn), "Unable to find '%s'\n", fn); + } + } else { + String *lf; + Seek(s, 0, SEEK_SET); + if (!dependencies) { + dependencies = NewList(); + } + lf = Copy(Swig_last_file()); + Append(dependencies, lf); + Delete(lf); + } + return s; +} + +List *Preprocessor_depend(void) { + return dependencies; +} + +/* ----------------------------------------------------------------------------- + * void Preprocessor_cpp_init() - Initialize the preprocessor + * ----------------------------------------------------------------------------- */ +static String *kpp_args = 0; +static String *kpp_define = 0; +static String *kpp_defined = 0; +static String *kpp_elif = 0; +static String *kpp_else = 0; +static String *kpp_endif = 0; +static String *kpp_expanded = 0; +static String *kpp_if = 0; +static String *kpp_ifdef = 0; +static String *kpp_ifndef = 0; +static String *kpp_name = 0; +static String *kpp_swigmacro = 0; +static String *kpp_symbols = 0; +static String *kpp_undef = 0; +static String *kpp_value = 0; +static String *kpp_varargs = 0; +static String *kpp_error = 0; +static String *kpp_warning = 0; +static String *kpp_line = 0; +static String *kpp_include = 0; +static String *kpp_pragma = 0; +static String *kpp_level = 0; + +static String *kpp_dline = 0; +static String *kpp_ddefine = 0; +static String *kpp_dinclude = 0; +static String *kpp_dimport = 0; +static String *kpp_dextern = 0; + +static String *kpp_LINE = 0; +static String *kpp_FILE = 0; + +void Preprocessor_init(void) { + Hash *s; + + kpp_args = NewString("args"); + kpp_define = NewString("define"); + kpp_defined = NewString("defined"); + kpp_else = NewString("else"); + kpp_elif = NewString("elif"); + kpp_endif = NewString("endif"); + kpp_expanded = NewString("*expanded*"); + kpp_if = NewString("if"); + kpp_ifdef = NewString("ifdef"); + kpp_ifndef = NewString("ifndef"); + kpp_name = NewString("name"); + kpp_swigmacro = NewString("swigmacro"); + kpp_symbols = NewString("symbols"); + kpp_undef = NewString("undef"); + kpp_value = NewString("value"); + kpp_error = NewString("error"); + kpp_warning = NewString("warning"); + kpp_pragma = NewString("pragma"); + kpp_level = NewString("level"); + kpp_line = NewString("line"); + kpp_include = NewString("include"); + kpp_varargs = NewString("varargs"); + + kpp_dinclude = NewString("%include"); + kpp_dimport = NewString("%import"); + kpp_dextern = NewString("%extern"); + kpp_ddefine = NewString("%define"); + kpp_dline = NewString("%line"); + + + kpp_LINE = NewString("__LINE__"); + kpp_FILE = NewString("__FILE__"); + + cpp = NewHash(); + s = NewHash(); + Setattr(cpp, kpp_symbols, s); + Delete(s); + Preprocessor_expr_init(); /* Initialize the expression evaluator */ + included_files = NewHash(); + + id_scan = NewScanner();; + +} + +void Preprocessor_delete(void) { + Delete(kpp_args); + Delete(kpp_define); + Delete(kpp_defined); + Delete(kpp_else); + Delete(kpp_elif); + Delete(kpp_endif); + Delete(kpp_expanded); + Delete(kpp_if); + Delete(kpp_ifdef); + Delete(kpp_ifndef); + Delete(kpp_name); + Delete(kpp_swigmacro); + Delete(kpp_symbols); + Delete(kpp_undef); + Delete(kpp_value); + Delete(kpp_error); + Delete(kpp_warning); + Delete(kpp_pragma); + Delete(kpp_level); + Delete(kpp_line); + Delete(kpp_include); + Delete(kpp_varargs); + + Delete(kpp_dinclude); + Delete(kpp_dimport); + Delete(kpp_dextern); + Delete(kpp_ddefine); + Delete(kpp_dline); + + + Delete(kpp_LINE); + Delete(kpp_FILE); + Delete(cpp); + Delete(included_files); + Preprocessor_expr_delete(); + DelScanner(id_scan); + + Delete(dependencies); + + Delete(Swig_add_directory(0)); +} + +/* ----------------------------------------------------------------------------- + * void Preprocessor_include_all() - Instruct preprocessor to include all files + * ----------------------------------------------------------------------------- */ +void Preprocessor_include_all(int a) { + include_all = a; +} + +void Preprocessor_import_all(int a) { + import_all = a; +} + +void Preprocessor_ignore_missing(int a) { + ignore_missing = a; +} + +void Preprocessor_error_as_warning(int a) { + error_as_warning = a; +} + + +/* ----------------------------------------------------------------------------- + * Preprocessor_define() + * + * Defines a new C preprocessor symbol. swigmacro specifies whether or not the macro has + * SWIG macro semantics. + * ----------------------------------------------------------------------------- */ + + +String *Macro_vararg_name(const_String_or_char_ptr str, const_String_or_char_ptr line) { + String *argname; + String *varargname; + char *s, *dots; + + argname = Copy(str); + s = Char(argname); + dots = strchr(s, '.'); + if (!dots) { + Delete(argname); + return NULL; + } + + if (strcmp(dots, "...") != 0) { + Swig_error(Getfile(line), Getline(line), "Illegal macro argument name '%s'\n", str); + Delete(argname); + return NULL; + } + if (dots == s) { + varargname = NewString("__VA_ARGS__"); + } else { + *dots = '\0'; + varargname = NewString(s); + } + Delete(argname); + return varargname; +} + +Hash *Preprocessor_define(const_String_or_char_ptr _str, int swigmacro) { + String *macroname = 0, *argstr = 0, *macrovalue = 0, *file = 0, *s = 0; + Hash *macro = 0, *symbols = 0, *m1; + List *arglist = 0; + int c, line; + int varargs = 0; + String *str; + + assert(cpp); + assert(_str); + + /* First make sure that string is actually a string */ + if (DohCheck(_str)) { + s = Copy(_str); + copy_location(_str, s); + str = s; + } else { + str = NewString((char *) _str); + } + Seek(str, 0, SEEK_SET); + line = Getline(str); + file = Getfile(str); + + /* Skip over any leading whitespace */ + skip_whitespace(str, 0); + + /* Now look for a macro name */ + macroname = NewStringEmpty(); + while ((c = Getc(str)) != EOF) { + if (c == '(') { + argstr = NewStringEmpty(); + copy_location(str, argstr); + /* It is a macro. Go extract its argument string */ + while ((c = Getc(str)) != EOF) { + if (c == ')') + break; + else + Putc(c, argstr); + } + if (c != ')') { + Swig_error(Getfile(str), Getline(str), "Missing \')\' in macro parameters\n"); + goto macro_error; + } + break; + } else if (isidchar(c) || (c == '%')) { + Putc(c, macroname); + } else if (isspace(c)) { + break; + } else if (c == '\\') { + c = Getc(str); + if (c != '\n') { + Ungetc(c, str); + Ungetc('\\', str); + break; + } + } else { + /*Swig_error(Getfile(str),Getline(str),"Illegal character in macro name\n"); + goto macro_error; */ + Ungetc(c, str); + break; + } + } + if (!swigmacro) + skip_whitespace(str, 0); + macrovalue = NewStringEmpty(); + while ((c = Getc(str)) != EOF) { + Putc(c, macrovalue); + } + + /* If there are any macro arguments, convert into a list */ + if (argstr) { + String *argname, *varargname; + arglist = NewList(); + Seek(argstr, 0, SEEK_SET); + argname = NewStringEmpty(); + while ((c = Getc(argstr)) != EOF) { + if (c == ',') { + varargname = Macro_vararg_name(argname, str); + if (varargname) { + Delete(varargname); + Swig_error(Getfile(str), Getline(str), "Variable-length macro argument must be last parameter\n"); + } else { + Append(arglist, argname); + } + Delete(argname); + argname = NewStringEmpty(); + } else if (isidchar(c) || (c == '.')) { + Putc(c, argname); + } else if (!(isspace(c) || (c == '\\'))) { + Delete(argname); + Swig_error(Getfile(str), Getline(str), "Illegal character in macro argument name\n"); + goto macro_error; + } + } + if (Len(argname)) { + /* Check for varargs */ + varargname = Macro_vararg_name(argname, str); + if (varargname) { + Append(arglist, varargname); + Delete(varargname); + varargs = 1; + } else { + Append(arglist, argname); + } + } + Delete(argname); + } + + if (!swigmacro) { + Replace(macrovalue, "\\\n", " ", DOH_REPLACE_NOQUOTE); + } + + /* Look for special # substitutions. We only consider # that appears + outside of quotes and comments */ + + { + int state = 0; + char *cc = Char(macrovalue); + while (*cc) { + switch (state) { + case 0: + if (*cc == '#') + *cc = '\001'; + else if (*cc == '/') + state = 10; + else if (*cc == '\'') + state = 20; + else if (*cc == '\"') + state = 30; + break; + case 10: + if (*cc == '*') + state = 11; + else if (*cc == '/') + state = 15; + else { + state = 0; + cc--; + } + break; + case 11: + if (*cc == '*') + state = 12; + break; + case 12: + if (*cc == '/') + state = 0; + else if (*cc != '*') + state = 11; + break; + case 15: + if (*cc == '\n') + state = 0; + break; + case 20: + if (*cc == '\'') + state = 0; + if (*cc == '\\') + state = 21; + break; + case 21: + state = 20; + break; + case 30: + if (*cc == '\"') + state = 0; + if (*cc == '\\') + state = 31; + break; + case 31: + state = 30; + break; + default: + break; + } + cc++; + } + } + + /* Get rid of whitespace surrounding # */ + /* Replace(macrovalue,"#","\001",DOH_REPLACE_NOQUOTE); */ + while (strstr(Char(macrovalue), "\001 ")) { + Replace(macrovalue, "\001 ", "\001", DOH_REPLACE_ANY); + } + while (strstr(Char(macrovalue), " \001")) { + Replace(macrovalue, " \001", "\001", DOH_REPLACE_ANY); + } + /* Replace '##' with a special token */ + Replace(macrovalue, "\001\001", "\002", DOH_REPLACE_ANY); + /* Replace '#@' with a special token */ + Replace(macrovalue, "\001@", "\004", DOH_REPLACE_ANY); + /* Replace '##@' with a special token */ + Replace(macrovalue, "\002@", "\005", DOH_REPLACE_ANY); + + /* Go create the macro */ + macro = NewHash(); + Setattr(macro, kpp_name, macroname); + + if (arglist) { + Setattr(macro, kpp_args, arglist); + Delete(arglist); + if (varargs) { + Setattr(macro, kpp_varargs, "1"); + } + } + Setattr(macro, kpp_value, macrovalue); + Setline(macro, line); + Setfile(macro, file); + if (swigmacro) { + Setattr(macro, kpp_swigmacro, "1"); + } + symbols = Getattr(cpp, kpp_symbols); + if ((m1 = Getattr(symbols, macroname))) { + if (!Checkattr(m1, kpp_value, macrovalue)) { + Swig_error(Getfile(str), Getline(str), "Macro '%s' redefined,\n", macroname); + Swig_error(Getfile(m1), Getline(m1), "previous definition of '%s'.\n", macroname); + goto macro_error; + } + } else { + Setattr(symbols, macroname, macro); + Delete(macro); + } + + Delete(macroname); + Delete(macrovalue); + + Delete(str); + Delete(argstr); + return macro; + +macro_error: + Delete(str); + Delete(argstr); + Delete(arglist); + Delete(macroname); + Delete(macrovalue); + return 0; +} + +/* ----------------------------------------------------------------------------- + * Preprocessor_undef() + * + * Undefines a macro. + * ----------------------------------------------------------------------------- */ +void Preprocessor_undef(const_String_or_char_ptr str) { + Hash *symbols; + assert(cpp); + symbols = Getattr(cpp, kpp_symbols); + Delattr(symbols, str); +} + +/* ----------------------------------------------------------------------------- + * find_args() + * + * Isolates macro arguments and returns them in a list. For each argument, + * leading and trailing whitespace is stripped (ala K&R, pg. 230). + * ----------------------------------------------------------------------------- */ +static List *find_args(String *s) { + List *args; + String *str; + int c, level; + long pos; + + /* Create a new list */ + args = NewList(); + copy_location(s, args); + + /* First look for a '(' */ + pos = Tell(s); + skip_whitespace(s, 0); + + /* Now see if the next character is a '(' */ + c = Getc(s); + if (c != '(') { + /* Not a macro, bail out now! */ + Seek(s, pos, SEEK_SET); + Delete(args); + return 0; + } + 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 = NewStringEmpty(); + 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; + } + Chop(str); + if (Len(args) || Len(str)) + Append(args, str); + Delete(str); + + /* if (Len(str) && (c != ')')) + Append(args,str); */ + + if (c == ')') + return args; + c = Getc(s); + } +unterm: + Swig_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 String *get_filename(String *str, int *sysfile) { + String *fn; + int c; + + skip_whitespace(str, 0); + fn = NewStringEmpty(); + copy_location(str, fn); + c = Getc(str); + *sysfile = 0; + if (c == '\"') { + while (((c = Getc(str)) != EOF) && (c != '\"')) + Putc(c, fn); + } else if (c == '<') { + *sysfile = 1; + 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); + } + Swig_filename_correct(fn); + Seek(fn, 0, SEEK_SET); + return fn; +} + +static String *get_options(String *str) { + + int c; + skip_whitespace(str, 0); + c = Getc(str); + if (c == '(') { + String *opt; + int level = 1; + opt = NewString("("); + while (((c = Getc(str)) != EOF)) { + Putc(c, opt); + if (c == ')') { + level--; + if (!level) + return opt; + } + if (c == '(') + level++; + } + Delete(opt); + return 0; + } else { + Ungetc(c, str); + return 0; + } +} + +/* ----------------------------------------------------------------------------- + * expand_macro() + * + * Perform macro expansion and return a new string. Returns NULL if some sort + * of error occurred. + * ----------------------------------------------------------------------------- */ + +static String *expand_macro(String *name, List *args) { + String *ns; + DOH *symbols, *macro, *margs, *mvalue, *temp, *tempa, *e; + int i, l; + int isvarargs = 0; + + symbols = Getattr(cpp, kpp_symbols); + if (!symbols) + return 0; + + /* See if the name is actually defined */ + macro = Getattr(symbols, name); + if (!macro) + return 0; + if (Getattr(macro, kpp_expanded)) { + ns = NewStringEmpty(); + Append(ns, name); + if (args) { + int lenargs = Len(args); + if (lenargs) + Putc('(', ns); + for (i = 0; i < lenargs; i++) { + Append(ns, Getitem(args, i)); + if (i < (lenargs - 1)) + Putc(',', ns); + } + if (i) + Putc(')', ns); + } + return ns; + } + + /* Get macro arguments and value */ + mvalue = Getattr(macro, kpp_value); + assert(mvalue); + margs = Getattr(macro, kpp_args); + + if (args && Getattr(macro, kpp_varargs)) { + isvarargs = 1; + /* Variable length argument macro. We need to collect all of the extra arguments into a single argument */ + if (Len(args) >= (Len(margs) - 1)) { + int i; + int vi, na; + String *vararg = NewStringEmpty(); + vi = Len(margs) - 1; + na = Len(args); + for (i = vi; i < na; i++) { + Append(vararg, Getitem(args, i)); + if ((i + 1) < na) { + Append(vararg, ","); + } + } + /* Remove arguments */ + for (i = vi; i < na; i++) { + Delitem(args, vi); + } + Append(args, vararg); + Delete(vararg); + } + } + /* If there are arguments, see if they match what we were given */ + if (args && (margs) && (Len(margs) != Len(args))) { + if (Len(margs) > (1 + isvarargs)) + Swig_error(Getfile(args), Getline(args), "Macro '%s' expects %d arguments\n", name, Len(margs) - isvarargs); + else if (Len(margs) == (1 + isvarargs)) + Swig_error(Getfile(args), Getline(args), "Macro '%s' expects 1 argument\n", name); + else + Swig_error(Getfile(args), Getline(args), "Macro '%s' expects no arguments\n", name); + return 0; + } + + /* If the macro expects arguments, but none were supplied, we leave it in place */ + if (!args && (margs) && Len(margs) > 0) { + return NewString(name); + } + + /* 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 */ + + temp = NewStringEmpty(); + tempa = NewStringEmpty(); + if (args && margs) { + l = Len(margs); + for (i = 0; i < l; i++) { + DOH *arg, *aname; + String *reparg; + arg = Getitem(args, i); /* Get an argument value */ + reparg = Preprocessor_replace(arg); + 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); + Replace(ns, temp, tempa, DOH_REPLACE_ID_END); + } + if (strstr(Char(ns), "\002")) { + /* Look for concatenation tokens */ + Clear(temp); + Clear(tempa); + Printf(temp, "\002%s", aname); + Append(tempa, "\002\003"); + Replace(ns, temp, tempa, DOH_REPLACE_ID_END); + Clear(temp); + Clear(tempa); + Printf(temp, "%s\002", aname); + Append(tempa, "\003\002"); + Replace(ns, temp, tempa, DOH_REPLACE_ID_BEGIN); + } + + /* Non-standard macro expansion. The value `x` is replaced by a quoted + version of the argument except that if the argument is already quoted + nothing happens */ + + if (strchr(Char(ns), '`')) { + String *rep; + char *c; + Clear(temp); + Printf(temp, "`%s`", aname); + c = Char(arg); + if (*c == '\"') { + rep = arg; + } else { + Clear(tempa); + Printf(tempa, "\"%s\"", arg); + rep = tempa; + } + Replace(ns, temp, rep, DOH_REPLACE_ANY); + } + + /* Non-standard mangle expansions. + The #@Name is replaced by mangle_arg(Name). */ + if (strstr(Char(ns), "\004")) { + String *marg = Swig_string_mangle(arg); + Clear(temp); + Printf(temp, "\004%s", aname); + Replace(ns, temp, marg, DOH_REPLACE_ID_END); + Delete(marg); + } + if (strstr(Char(ns), "\005")) { + String *marg = Swig_string_mangle(arg); + Clear(temp); + Clear(tempa); + Printf(temp, "\005%s", aname); + Printf(tempa, "\"%s\"", marg); + Replace(ns, temp, tempa, DOH_REPLACE_ID_END); + Delete(marg); + } + + if (isvarargs && i == l - 1 && Len(arg) == 0) { + /* Zero length varargs macro argument. We search for commas that might appear before and nuke them */ + char *a, *s, *t, *name; + int namelen; + s = Char(ns); + name = Char(aname); + namelen = Len(aname); + a = strstr(s, name); + while (a) { + char ca = a[namelen + 1]; + if (!isidchar((int) ca)) { + /* Matched the entire vararg name, not just a prefix */ + t = a - 1; + if (*t == '\002') { + t--; + while (t >= s) { + if (isspace((int) *t)) + t--; + else if (*t == ',') { + *t = ' '; + } else + break; + } + } + } + a = strstr(a + namelen, name); + } + } + /* Replace(ns, aname, arg, DOH_REPLACE_ID); */ + Replace(ns, aname, reparg, DOH_REPLACE_ID); /* Replace expanded args */ + Replace(ns, "\003", arg, DOH_REPLACE_ANY); /* Replace unexpanded arg */ + Delete(reparg); + } + } + Replace(ns, "\002", "", DOH_REPLACE_ANY); /* Get rid of concatenation tokens */ + Replace(ns, "\001", "#", DOH_REPLACE_ANY); /* Put # back (non-standard C) */ + Replace(ns, "\004", "#@", DOH_REPLACE_ANY); /* Put # back (non-standard C) */ + + /* Expand this macro even further */ + Setattr(macro, kpp_expanded, "1"); + + e = Preprocessor_replace(ns); + + Delattr(macro, kpp_expanded); + Delete(ns); + + if (Getattr(macro, kpp_swigmacro)) { + String *g; + String *f = NewStringEmpty(); + Seek(e, 0, SEEK_SET); + copy_location(macro, e); + g = Preprocessor_parse(e); + +#if 0 + /* Drop the macro in place, but with a marker around it */ + Printf(f, "/*@%s,%d,%s@*/%s/*@@*/", Getfile(macro), Getline(macro), name, g); +#else + /* Use simplified around markers to properly count lines in cscanner.c */ + if (strchr(Char(g), '\n')) { + Printf(f, "/*@SWIG:%s,%d,%s@*/%s/*@SWIG@*/", Getfile(macro), Getline(macro), name, g); +#if 0 + Printf(f, "/*@SWIG:%s@*/%s/*@SWIG@*/", name, g); +#endif + } else { + Append(f, g); + } +#endif + + Delete(g); + Delete(e); + e = f; + } + Delete(temp); + Delete(tempa); + return e; +} + +/* ----------------------------------------------------------------------------- + * evaluate_args() + * + * Evaluate the arguments of a macro + * ----------------------------------------------------------------------------- */ + +List *evaluate_args(List *x) { + Iterator i; + List *nl = NewList(); + + for (i = First(x); i.item; i = Next(i)) { + Append(nl, Preprocessor_replace(i.item)); + } + return nl; +} + +/* ----------------------------------------------------------------------------- + * DOH *Preprocessor_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. + * ----------------------------------------------------------------------------- */ + +/* #define SWIG_PUT_BUFF */ + +DOH *Preprocessor_replace(DOH *s) { + DOH *ns, *symbols, *m; + int c, i, state = 0; + + String *id = NewStringEmpty(); + + assert(cpp); + symbols = Getattr(cpp, kpp_symbols); + + ns = NewStringEmpty(); + copy_location(s, ns); + Seek(s, 0, SEEK_SET); + + /* Try to locate identifiers in s and replace them with macro replacements */ + while ((c = Getc(s)) != EOF) { + switch (state) { + case 0: + if (isidentifier(c) || (c == '%')) { + Clear(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 (Equal(kpp_defined, id)) { + int lenargs = 0; + DOH *args = 0; + /* See whether or not a paranthesis has been used */ + skip_whitespace(s, 0); + c = Getc(s); + if (c == '(') { + Ungetc(c, s); + args = find_args(s); + } else if (isidchar(c)) { + DOH *arg = NewStringEmpty(); + args = NewList(); + Putc(c, arg); + while (((c = Getc(s)) != EOF)) { + if (!isidchar(c)) { + Ungetc(c, s); + break; + } + Putc(c, arg); + } + if (Len(arg)) + Append(args, arg); + Delete(arg); + } else { + Seek(s, -1, SEEK_CUR); + } + lenargs = Len(args); + if ((!args) || (!lenargs)) { + /* This is not a defined() macro. */ + Append(ns, id); + state = 0; + break; + } + for (i = 0; i < lenargs; i++) { + DOH *o = Getitem(args, i); + if (!Getattr(symbols, o)) { + break; + } + } + if (i < lenargs) + Putc('0', ns); + else + Putc('1', ns); + Delete(args); + state = 0; + break; + } + if (Equal(kpp_LINE, id)) { + Printf(ns, "%d", Getline(s)); + state = 0; + break; + } + if (Equal(kpp_FILE, id)) { + String *fn = Copy(Getfile(s)); + Replaceall(fn, "\\", "\\\\"); + Printf(ns, "\"%s\"", fn); + Delete(fn); + 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, kpp_args)) { + /* Yep. We need to go find the arguments and do a substitution */ + args = find_args(s); + if (!Len(args)) { + Delete(args); + args = 0; + } + } else { + args = 0; + } + e = expand_macro(id, args); + if (e) { + Append(ns, e); + } + Delete(e); + Delete(args); + } else { + Append(ns, 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 (Equal(kpp_defined, id)) { + Swig_error(Getfile(s), Getline(s), "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")) { + Swig_error(Getfile(id),Getline(id),"Macro arguments expected.\n"); + } */ + e = expand_macro(id, 0); + Append(ns, e); + Delete(e); + } else { + Append(ns, id); + } + } + Delete(id); + return ns; +} + + +/* ----------------------------------------------------------------------------- + * int checkpp_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 checkpp_id(DOH *s) { + int c; + int hastok = 0; + Scanner *scan = id_scan; + + Seek(s, 0, SEEK_SET); + + Scanner_clear(scan); + s = Copy(s); + Seek(s, SEEK_SET, 0); + Scanner_push(scan, s); + while ((c = Scanner_token(scan))) { + hastok = 1; + if ((c == SWIG_TOKEN_ID) || (c == SWIG_TOKEN_LBRACE) || (c == SWIG_TOKEN_RBRACE)) + return 1; + } + if (!hastok) + return 1; + 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 = Preprocessor_replace(chunk); + addline(ns, echunk, allow); + Delete(echunk); + } else { + addline(ns, chunk, 0); + } + Clear(chunk); +} + +/* + push/pop_imported(): helper functions for defining and undefining + SWIGIMPORTED (when %importing a file). + */ +static void push_imported() { + if (imported_depth == 0) { + Preprocessor_define("SWIGIMPORTED 1", 0); + } + ++imported_depth; +} + +static void pop_imported() { + --imported_depth; + if (imported_depth == 0) { + Preprocessor_undef("SWIGIMPORTED"); + } +} + + +/* ----------------------------------------------------------------------------- + * Preprocessor_parse() + * + * 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). + * ----------------------------------------------------------------------------- */ + +String *Preprocessor_parse(String *s) { + String *ns; /* New string containing the preprocessed text */ + String *chunk, *decl; + Hash *symbols; + String *id = 0, *value = 0, *comment = 0; + int i, state, e, c; + int start_line = 0; + int allow = 1; + int level = 0; + int dlevel = 0; + int mask = 0; + int start_level = 0; + int cpp_lines = 0; + int cond_lines[256]; + + /* Blow away all carriage returns */ + Replace(s, "\015", "", DOH_REPLACE_ANY); + + ns = NewStringEmpty(); /* Return result */ + + decl = NewStringEmpty(); + id = NewStringEmpty(); + value = NewStringEmpty(); + comment = NewStringEmpty(); + chunk = NewStringEmpty(); + copy_location(s, chunk); + copy_location(s, ns); + symbols = Getattr(cpp, kpp_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) { + Swig_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) { + Swig_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 == '\n') { + Putc('\n', chunk); + state = 0; + } else if (isspace(c)) { + state = 40; + } 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) || (!isalpha(c)))) { + Clear(value); + Clear(comment); + if (c == '\n') { + Ungetc(c, s); + state = 50; + } else { + state = 42; + if (!isspace(c)) { + Ungetc(c, s); + } + } + + 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 if (c == '\"') { + Putc(c, value); + skip_tochar(s, '\"', value); + } else if (c == '\'') { + Putc(c, value); + skip_tochar(s, '\'', value); + } else { + Putc(c, value); + if (c == '\\') + state = 44; + } + break; + + case 44: + if (c == '\n') { + Putc(c, value); + cpp_lines++; + } else { + Ungetc(c, s); + } + 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 if (c == '\n') { + Putc('/', value); + Ungetc(c, s); + cpp_lines++; + state = 50; + } 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 */ + Chop(value); + if (Equal(id, kpp_define)) { + if (allow) { + DOH *m, *v, *v1; + Seek(value, 0, SEEK_SET); + m = Preprocessor_define(value, 0); + if ((m) && !(Getattr(m, kpp_args))) { + v = Copy(Getattr(m, kpp_value)); + if (Len(v)) { + Swig_error_silent(1); + v1 = Preprocessor_replace(v); + Swig_error_silent(0); + /* Printf(stdout,"checking '%s'\n", v1); */ + if (!checkpp_id(v1)) { + if (Len(comment) == 0) + Printf(ns, "%%constant %s = %s;\n", Getattr(m, kpp_name), v1); + else + Printf(ns, "%%constant %s = %s; /*%s*/\n", Getattr(m, kpp_name), v1, comment); + cpp_lines--; + } + Delete(v1); + } + Delete(v); + } + } + } else if (Equal(id, kpp_undef)) { + if (allow) + Preprocessor_undef(value); + } else if (Equal(id, kpp_ifdef)) { + 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 (Equal(id, kpp_ifndef)) { + 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 (Equal(id, kpp_else)) { + if (level <= 0) { + Swig_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 * mask; + } + } + } else if (Equal(id, kpp_endif)) { + level--; + if (level < 0) { + Swig_error(Getfile(id), Getline(id), "Extraneous #endif.\n"); + level = 0; + } else { + if (level < start_level) { + allow = 1; + start_level--; + } + } + } else if (Equal(id, kpp_if)) { + cond_lines[level] = Getline(id); + level++; + if (allow) { + int val; + String *sval = Preprocessor_replace(value); + start_level = level; + Seek(sval, 0, SEEK_SET); + /* Printf(stdout,"Evaluating '%s'\n", sval); */ + val = Preprocessor_expr(sval, &e); + if (e) { + char *msg = Preprocessor_expr_error(); + Seek(value, 0, SEEK_SET); + Swig_warning(WARN_PP_EVALUATION, Getfile(value), Getline(value), "Could not evaluate '%s'\n", value); + if (msg) + Swig_warning(WARN_PP_EVALUATION, Getfile(value), Getline(value), "Error: '%s'\n", msg); + allow = 0; + } else { + if (val == 0) + allow = 0; + } + mask = 1; + } + } else if (Equal(id, kpp_elif)) { + if (level == 0) { + Swig_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) { + int val; + String *sval = Preprocessor_replace(value); + Seek(sval, 0, SEEK_SET); + val = Preprocessor_expr(sval, &e); + if (e) { + char *msg = Preprocessor_expr_error(); + Seek(value, 0, SEEK_SET); + Swig_warning(WARN_PP_EVALUATION, Getfile(value), Getline(value), "Could not evaluate '%s'\n", value); + if (msg) + Swig_warning(WARN_PP_EVALUATION, Getfile(value), Getline(value), "Error: '%s'\n", msg); + allow = 0; + } else { + if (val) + allow = 1 * mask; + else + allow = 0; + } + } + } + } else if (Equal(id, kpp_warning)) { + if (allow) { + Swig_warning(WARN_PP_CPP_WARNING, Getfile(s), Getline(id), "CPP #warning, %s\n", value); + } + } else if (Equal(id, kpp_error)) { + if (allow) { + if (error_as_warning) { + Swig_warning(WARN_PP_CPP_ERROR, Getfile(s), Getline(id), "CPP #error \"%s\".\n", value); + } else { + Swig_error(Getfile(s), Getline(id), "CPP #error \"%s\". Use the -cpperraswarn option to continue swig processing.\n", value); + } + } + } else if (Equal(id, kpp_line)) { + } else if (Equal(id, kpp_include)) { + if (((include_all) || (import_all)) && (allow)) { + String *s1, *s2, *fn; + char *dirname; + int sysfile = 0; + if (include_all && import_all) { + Swig_warning(WARN_PP_INCLUDEALL_IMPORTALL, Getfile(s), Getline(id), "Both includeall and importall are defined: using includeall\n"); + import_all = 0; + } + Seek(value, 0, SEEK_SET); + fn = get_filename(value, &sysfile); + s1 = cpp_include(fn, sysfile); + if (s1) { + if (include_all) + Printf(ns, "%%includefile \"%s\" [\n", Swig_last_file()); + else if (import_all) { + Printf(ns, "%%importfile \"%s\" [\n", Swig_last_file()); + push_imported(); + } + + /* See if the filename has a directory component */ + dirname = Swig_file_dirname(Swig_last_file()); + if (sysfile || !strlen(dirname)) + dirname = 0; + if (dirname) { + dirname[strlen(dirname) - 1] = 0; /* Kill trailing directory delimiter */ + Swig_push_directory(dirname); + } + s2 = Preprocessor_parse(s1); + addline(ns, s2, allow); + Append(ns, "\n]"); + if (dirname) { + Swig_pop_directory(); + } + if (import_all) { + pop_imported(); + } + Delete(s2); + } + Delete(s1); + Delete(fn); + } + } else if (Equal(id, kpp_pragma)) { + if (Strncmp(value, "SWIG ", 5) == 0) { + char *c = Char(value) + 5; + while (*c && (isspace((int) *c))) + c++; + if (*c) { + if (strncmp(c, "nowarn=", 7) == 0) { + String *val = NewString(c + 7); + String *nowarn = Preprocessor_replace(val); + Swig_warnfilter(nowarn, 1); + Delete(nowarn); + Delete(val); + } else if (strncmp(c, "cpperraswarn=", 13) == 0) { + error_as_warning = atoi(c + 13); + } else { + Swig_error(Getfile(s), Getline(id), "Unknown SWIG pragma: %s\n", c); + } + } + } + } else if (Equal(id, kpp_level)) { + Swig_error(Getfile(s), Getline(id), "cpp debug: level = %d, startlevel = %d\n", level, start_level); + } + 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 == '#') { + add_chunk(ns, chunk, allow); + Putc(c, chunk); + state = 107; + } else if (isidentifier(c)) { + Clear(decl); + Putc('%', decl); + Putc(c, decl); + state = 110; + } else { + Putc('%', chunk); + 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 107: + Putc(c, chunk); + if (c == '\n') { + addline(ns, chunk, allow); + Clear(chunk); + state = 0; + } else if (c == '\\') { + state = 108; + } + break; + + case 108: + Putc(c, chunk); + state = 107; + break; + + case 110: + if (!isidchar(c)) { + Ungetc(c, s); + /* Look for common Swig directives */ + if (Equal(decl, kpp_dinclude) || Equal(decl, kpp_dimport) || Equal(decl, kpp_dextern)) { + /* Got some kind of file inclusion directive */ + if (allow) { + DOH *s1, *s2, *fn, *opt; + int sysfile = 0; + + if (Equal(decl, kpp_dextern)) { + Swig_warning(WARN_DEPRECATED_EXTERN, Getfile(s), Getline(s), "%%extern is deprecated. Use %%import instead.\n"); + Clear(decl); + Append(decl, "%%import"); + } + opt = get_options(s); + fn = get_filename(s, &sysfile); + s1 = cpp_include(fn, sysfile); + if (s1) { + char *dirname; + add_chunk(ns, chunk, allow); + copy_location(s, chunk); + Printf(ns, "%sfile%s \"%s\" [\n", decl, opt, Swig_last_file()); + if (Equal(decl, kpp_dimport)) { + push_imported(); + } + dirname = Swig_file_dirname(Swig_last_file()); + if (sysfile || !strlen(dirname)) + dirname = 0; + if (dirname) { + dirname[strlen(dirname) - 1] = 0; /* Kill trailing directory delimiter */ + Swig_push_directory(dirname); + } + s2 = Preprocessor_parse(s1); + if (dirname) { + Swig_pop_directory(); + } + if (Equal(decl, kpp_dimport)) { + pop_imported(); + } + addline(ns, s2, allow); + Append(ns, "\n]"); + Delete(s2); + Delete(s1); + } + Delete(fn); + } + state = 1; + } else if (Equal(decl, kpp_dline)) { + /* Got a line directive */ + state = 1; + } else if (Equal(decl, kpp_ddefine)) { + /* Got a define directive */ + dlevel++; + add_chunk(ns, chunk, allow); + copy_location(s, chunk); + Clear(value); + copy_location(s, value); + state = 150; + } else { + Append(chunk, decl); + state = 1; + } + } else { + Putc(c, decl); + } + break; + + /* Searching for the end of a %define statement */ + case 150: + Putc(c, value); + if (c == '%') { + const char *ed = "enddef"; + const char *df = "define"; + char statement[7]; + int i = 0; + for (i = 0; i < 6;) { + c = Getc(s); + Putc(c, value); + statement[i++] = (char)c; + if (strncmp(statement, ed, i) && strncmp(statement, df, i)) + break; + } + c = Getc(s); + Ungetc(c, s); + if ((i == 6) && (isspace(c))) { + if (strncmp(statement, df, i) == 0) { + ++dlevel; + } else { + if (strncmp(statement, ed, i) == 0) { + --dlevel; + if (!dlevel) { + /* Got the macro */ + for (i = 0; i < 7; i++) { + Delitem(value, DOH_END); + } + if (allow) { + Seek(value, 0, SEEK_SET); + Preprocessor_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) { + Swig_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); + Swig_error(Getfile(s), -1, "Missing %%enddef for macro starting on line %d\n", Getline(value)); + } + if ((state >= 105) && (state < 107)) { + Swig_error(Getfile(s), -1, "Unterminated %%{ ... %%} block starting on line %d\n", start_line); + } + if ((state >= 30) && (state < 40)) { + Swig_error(Getfile(s), -1, "Unterminated comment starting on line %d\n", start_line); + } + add_chunk(ns, chunk, allow); + copy_location(s, chunk); + + /* DelScope(scp); */ + Delete(decl); + Delete(id); + Delete(value); + Delete(comment); + Delete(chunk); + + /* fprintf(stderr,"cpp: %d\n", Len(Getattr(cpp,"symbols"))); */ + return ns; +} diff --git a/Source/Preprocessor/expr.c b/Source/Preprocessor/expr.c new file mode 100644 index 0000000..e6e9459 --- /dev/null +++ b/Source/Preprocessor/expr.c @@ -0,0 +1,436 @@ +/* ----------------------------------------------------------------------------- + * See the LICENSE file for information on copyright, usage and redistribution + * of SWIG, and the README file for authors - http://www.swig.org/release.html. + * + * expr.c + * + * Integer arithmetic expression evaluator used to handle expressions + * encountered during preprocessing. + * ----------------------------------------------------------------------------- */ + +char cvsroot_expr_c[] = "$Id: expr.c 10310 2008-03-17 00:36:35Z olly $"; + +#include "swig.h" +#include "preprocessor.h" + +static Scanner *scan = 0; + +typedef struct { + int op; + long value; + String *svalue; +} exprval; + +#define EXPR_TOP 1 +#define EXPR_VALUE 2 +#define EXPR_OP 3 +#define EXPR_GROUP 4 +#define EXPR_UMINUS 100 + +static exprval stack[256]; /* Parsing stack */ +static int sp = 0; /* Stack pointer */ +static int prec[256]; /* Precedence rules */ +static int expr_init = 0; /* Initialization flag */ +static char *errmsg = 0; /* Parsing error */ + +/* Initialize the precedence table for various operators. Low values have higher precedence */ +static void init_precedence() { + prec[SWIG_TOKEN_NOT] = 10; + prec[EXPR_UMINUS] = 10; + prec[SWIG_TOKEN_STAR] = 20; + prec[SWIG_TOKEN_SLASH] = 20; + prec[SWIG_TOKEN_PERCENT] = 20; + prec[SWIG_TOKEN_PLUS] = 30; + prec[SWIG_TOKEN_MINUS] = 30; + prec[SWIG_TOKEN_LSHIFT] = 40; + prec[SWIG_TOKEN_RSHIFT] = 40; + prec[SWIG_TOKEN_AND] = 50; + prec[SWIG_TOKEN_XOR] = 60; + prec[SWIG_TOKEN_OR] = 70; + prec[SWIG_TOKEN_EQUALTO] = 80; + prec[SWIG_TOKEN_NOTEQUAL] = 80; + prec[SWIG_TOKEN_LESSTHAN] = 80; + prec[SWIG_TOKEN_GREATERTHAN] = 80; + prec[SWIG_TOKEN_LTEQUAL] = 80; + prec[SWIG_TOKEN_GTEQUAL] = 80; + prec[SWIG_TOKEN_LNOT] = 90; + prec[SWIG_TOKEN_LAND] = 100; + prec[SWIG_TOKEN_LOR] = 110; + expr_init = 1; +} + +#define UNARY_OP(token) (((token) == SWIG_TOKEN_NOT) || \ + ((token) == SWIG_TOKEN_LNOT) || \ + ((token) == EXPR_UMINUS)) + +/* Reduce a single operator on the stack */ +/* return 0 on failure, 1 on success */ +static int reduce_op() { + long op_token = stack[sp - 1].value; + assert(sp > 0); + assert(stack[sp - 1].op == EXPR_OP); + /* do some basic checking first: */ + if (stack[sp].op != EXPR_VALUE) { + errmsg = "Right-hand side is not value"; + return 0; + } + if (UNARY_OP(op_token)) { + if (stack[sp].svalue) { + errmsg = "Syntax error: attempt to apply unary operator to string"; + return 0; + } + } else { + /* binary operator: */ + if (sp == 1) { + /* top of stack: don't attempt to use sp-2! */ + errmsg = "Missing left-hand side for binary operator"; + return 0; + } + if (stack[sp].op != EXPR_VALUE) { + errmsg = "Left-hand side of binary operator is not a value"; + return 0; + } + if ((!stack[sp - 2].svalue) != (!stack[sp].svalue)) { + errmsg = "Can't mix strings and integers in expression"; + return 0; + } + } + if (stack[sp].svalue) { + /* A binary string expression */ + switch (stack[sp - 1].value) { + case SWIG_TOKEN_EQUALTO: + stack[sp - 2].value = (Strcmp(stack[sp - 2].svalue, stack[sp].svalue) == 0); + Delete(stack[sp - 2].svalue); + Delete(stack[sp].svalue); + sp -= 2; + break; + case SWIG_TOKEN_NOTEQUAL: + stack[sp - 2].value = (Strcmp(stack[sp - 2].svalue, stack[sp].svalue) != 0); + Delete(stack[sp - 2].svalue); + Delete(stack[sp].svalue); + sp -= 2; + break; + default: + errmsg = "Syntax error: bad binary operator for strings"; + return 0; + break; + } + } else { + switch (op_token) { + case SWIG_TOKEN_STAR: + stack[sp - 2].value = stack[sp - 2].value * stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_EQUALTO: + stack[sp - 2].value = stack[sp - 2].value == stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_NOTEQUAL: + stack[sp - 2].value = stack[sp - 2].value != stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_PLUS: + stack[sp - 2].value = stack[sp - 2].value + stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_MINUS: + stack[sp - 2].value = stack[sp - 2].value - stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_AND: + stack[sp - 2].value = stack[sp - 2].value & stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_LAND: + stack[sp - 2].value = stack[sp - 2].value && stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_OR: + stack[sp - 2].value = stack[sp - 2].value | stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_LOR: + stack[sp - 2].value = stack[sp - 2].value || stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_XOR: + stack[sp - 2].value = stack[sp - 2].value ^ stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_LESSTHAN: + stack[sp - 2].value = stack[sp - 2].value < stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_GREATERTHAN: + stack[sp - 2].value = stack[sp - 2].value > stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_LTEQUAL: + stack[sp - 2].value = stack[sp - 2].value <= stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_GTEQUAL: + stack[sp - 2].value = stack[sp - 2].value >= stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_NOT: + stack[sp - 1].value = ~stack[sp].value; + sp--; + break; + case SWIG_TOKEN_LNOT: + stack[sp - 1].value = !stack[sp].value; + sp--; + break; + case EXPR_UMINUS: + stack[sp - 1].value = -stack[sp].value; + sp--; + break; + case SWIG_TOKEN_SLASH: + stack[sp - 2].value = stack[sp - 2].value / stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_PERCENT: + stack[sp - 2].value = stack[sp - 2].value % stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_LSHIFT: + stack[sp - 2].value = stack[sp - 2].value << stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_RSHIFT: + stack[sp - 2].value = stack[sp - 2].value >> stack[sp].value; + sp -= 2; + break; + default: + errmsg = "Syntax error: bad operator"; + return 0; + break; + } + } + stack[sp].op = EXPR_VALUE; + stack[sp].svalue = 0; /* ensure it's not a string! */ + return 1; +} + +/* ----------------------------------------------------------------------------- + * Preprocessor_expr_init() + * + * Initialize the expression evaluator + * ----------------------------------------------------------------------------- */ + +void Preprocessor_expr_init(void) { + if (!expr_init) + init_precedence(); + if (!scan) + scan = NewScanner(); +} + +void Preprocessor_expr_delete(void) { + DelScanner(scan); +} + + +/* ----------------------------------------------------------------------------- + * Tokenizer + * ----------------------------------------------------------------------------- */ + +static int expr_token(Scanner * s) { + int t; + while (1) { + t = Scanner_token(s); + if (!((t == SWIG_TOKEN_BACKSLASH) || (t == SWIG_TOKEN_ENDLINE) || (t == SWIG_TOKEN_COMMENT))) + break; + } + return t; +} + +/* ----------------------------------------------------------------------------- + * Preprocessor_expr() + * + * Evaluates an arithmetic expression. Returns the result and sets an error code. + * ----------------------------------------------------------------------------- */ + +int Preprocessor_expr(DOH *s, int *error) { + int token = 0; + int op = 0; + + sp = 0; + assert(s); + assert(scan); + + Seek(s, 0, SEEK_SET); + /* Printf(stdout,"evaluating : '%s'\n", s); */ + *error = 0; + Scanner_clear(scan); + Scanner_push(scan, s); + + /* Put initial state onto the stack */ + stack[sp].op = EXPR_TOP; + stack[sp].value = 0; + + while (1) { + /* Look at the top of the stack */ + switch (stack[sp].op) { + case EXPR_TOP: + /* An expression. Can be a number or another expression enclosed in parens */ + token = expr_token(scan); + if (!token) { + errmsg = "Expected an expression"; + *error = 1; + return 0; + } + if ((token == SWIG_TOKEN_INT) || (token == SWIG_TOKEN_UINT) || (token == SWIG_TOKEN_LONG) || (token == SWIG_TOKEN_ULONG)) { + /* A number. Reduce EXPR_TOP to an EXPR_VALUE */ + char *c = Char(Scanner_text(scan)); + stack[sp].value = (long) strtol(c, 0, 0); + stack[sp].svalue = 0; + /* stack[sp].value = (long) atol(Char(Scanner_text(scan))); */ + stack[sp].op = EXPR_VALUE; + } else if (token == SWIG_TOKEN_PLUS) { + } else if ((token == SWIG_TOKEN_MINUS) || (token == SWIG_TOKEN_LNOT) || (token == SWIG_TOKEN_NOT)) { + if (token == SWIG_TOKEN_MINUS) + token = EXPR_UMINUS; + stack[sp].value = token; + stack[sp++].op = EXPR_OP; + stack[sp].op = EXPR_TOP; + stack[sp].svalue = 0; + } else if ((token == SWIG_TOKEN_LPAREN)) { + stack[sp++].op = EXPR_GROUP; + stack[sp].op = EXPR_TOP; + stack[sp].value = 0; + stack[sp].svalue = 0; + } else if (token == SWIG_TOKEN_ENDLINE) { + } else if ((token == SWIG_TOKEN_STRING)) { + stack[sp].svalue = NewString(Scanner_text(scan)); + stack[sp].op = EXPR_VALUE; + } else if ((token == SWIG_TOKEN_ID)) { + stack[sp].value = 0; + stack[sp].svalue = 0; + stack[sp].op = EXPR_VALUE; + } else + goto syntax_error; + break; + case EXPR_VALUE: + /* A value is on the stack. We may reduce or evaluate depending on what the next token is */ + token = expr_token(scan); + if (!token) { + /* End of input. Might have to reduce if an operator is on stack */ + while (sp > 0) { + if (stack[sp - 1].op == EXPR_OP) { + if (!reduce_op()) + goto reduce_error; + } else if (stack[sp - 1].op == EXPR_GROUP) { + errmsg = "Missing \')\'"; + *error = 1; + return 0; + } else + goto syntax_error; + } + return stack[sp].value; + } + /* Token must be an operator */ + switch (token) { + case SWIG_TOKEN_STAR: + case SWIG_TOKEN_EQUALTO: + case SWIG_TOKEN_NOTEQUAL: + case SWIG_TOKEN_PLUS: + case SWIG_TOKEN_MINUS: + case SWIG_TOKEN_AND: + case SWIG_TOKEN_LAND: + case SWIG_TOKEN_OR: + case SWIG_TOKEN_LOR: + case SWIG_TOKEN_XOR: + case SWIG_TOKEN_LESSTHAN: + case SWIG_TOKEN_GREATERTHAN: + case SWIG_TOKEN_LTEQUAL: + case SWIG_TOKEN_GTEQUAL: + case SWIG_TOKEN_SLASH: + case SWIG_TOKEN_PERCENT: + case SWIG_TOKEN_LSHIFT: + case SWIG_TOKEN_RSHIFT: + if ((sp == 0) || (stack[sp - 1].op == EXPR_GROUP)) { + /* No possibility of reduce. Push operator and expression */ + sp++; + stack[sp].op = EXPR_OP; + stack[sp].value = token; + sp++; + stack[sp].op = EXPR_TOP; + stack[sp].value = 0; + } else { + if (stack[sp - 1].op != EXPR_OP) + goto syntax_error_expected_operator; + op = stack[sp - 1].value; /* Previous operator */ + + /* Now, depending on the precedence relationship between the last operator and the current + we will reduce or push */ + + if (prec[op] <= prec[token]) { + /* Reduce the previous operator */ + if (!reduce_op()) + goto reduce_error; + } + sp++; + stack[sp].op = EXPR_OP; + stack[sp].value = token; + sp++; + stack[sp].op = EXPR_TOP; + stack[sp].value = 0; + } + break; + case SWIG_TOKEN_RPAREN: + if (sp == 0) + goto extra_rparen; + + /* Might have to reduce operators first */ + while ((sp > 0) && (stack[sp - 1].op == EXPR_OP)) { + if (!reduce_op()) + goto reduce_error; + } + if ((sp == 0) || (stack[sp - 1].op != EXPR_GROUP)) + goto extra_rparen; + stack[sp - 1].op = EXPR_VALUE; + stack[sp - 1].value = stack[sp].value; + sp--; + break; + default: + goto syntax_error_expected_operator; + break; + } + break; + + default: + fprintf(stderr, "Internal error in expression evaluator.\n"); + abort(); + } + } + +syntax_error: + errmsg = "Syntax error"; + *error = 1; + return 0; + +syntax_error_expected_operator: + errmsg = "Syntax error: expected operator"; + *error = 1; + return 0; + +reduce_error: + /* errmsg has been set by reduce_op */ + *error = 1; + return 0; + +extra_rparen: + errmsg = "Extra \')\'"; + *error = 1; + return 0; +} + +/* ----------------------------------------------------------------------------- + * Preprocessor_expr_error() + * + * Return error message set by the evaluator (if any) + * ----------------------------------------------------------------------------- */ + +char *Preprocessor_expr_error() { + return errmsg; +} diff --git a/Source/Preprocessor/preprocessor.h b/Source/Preprocessor/preprocessor.h new file mode 100644 index 0000000..2b90808 --- /dev/null +++ b/Source/Preprocessor/preprocessor.h @@ -0,0 +1,38 @@ +/* ----------------------------------------------------------------------------- + * See the LICENSE file for information on copyright, usage and redistribution + * of SWIG, and the README file for authors - http://www.swig.org/release.html. + * + * preprocessor.h + * + * SWIG preprocessor module. + * ----------------------------------------------------------------------------- */ + +/* $Id: preprocessor.h 11080 2009-01-24 13:15:51Z bhy $ */ + +#ifndef SWIG_PREPROCESSOR_H_ +#define SWIG_PREPROCESSOR_H_ + +#include "swigwarn.h" + +#ifdef __cplusplus +extern "C" { +#endif + extern int Preprocessor_expr(String *s, int *error); + extern char *Preprocessor_expr_error(void); + extern Hash *Preprocessor_define(const_String_or_char_ptr str, int swigmacro); + extern void Preprocessor_undef(const_String_or_char_ptr name); + extern void Preprocessor_init(void); + extern void Preprocessor_delete(void); + extern String *Preprocessor_parse(String *s); + extern void Preprocessor_include_all(int); + extern void Preprocessor_import_all(int); + extern void Preprocessor_ignore_missing(int); + extern void Preprocessor_error_as_warning(int); + extern List *Preprocessor_depend(void); + extern void Preprocessor_expr_init(void); + extern void Preprocessor_expr_delete(void); + +#ifdef __cplusplus +} +#endif +#endif |
