diff --git a/src/codebuilder.cpp b/src/codebuilder.cpp index a2452c7..9ea22e2 100644 --- a/src/codebuilder.cpp +++ b/src/codebuilder.cpp @@ -311,7 +311,40 @@ namespace FreeOCL preproc.define("__ENDIAN_LITTLE__", "1"); preproc.define("__IMAGE_SUPPORT__", "1"); for(size_t i = 0 ; i < options.size() ; ++i) - preproc.define(options[i].first, options[i].second); + { + const std::string &name = options[i].first; + const std::string &value = options[i].second; + // Check if this is a function-like macro: NAME(params) + const size_t paren = name.find('('); + if (paren != std::string::npos && name.size() > paren + 1 && name[name.size() - 1] == ')') + { + // Extract macro name and parameters + const std::string macro_name = name.substr(0, paren); + const std::string params_str = name.substr(paren + 1, name.size() - paren - 2); + std::vector params; + if (!params_str.empty()) + { + size_t start = 0; + for (size_t j = 0; j <= params_str.size(); ++j) + { + if (j == params_str.size() || params_str[j] == ',') + { + std::string param = params_str.substr(start, j - start); + // Trim whitespace + while (!param.empty() && isspace(param[0])) param.erase(0, 1); + while (!param.empty() && isspace(param[param.size() - 1])) param.erase(param.size() - 1); + params.push_back(param); + start = j + 1; + } + } + } + preproc.define(macro_name, params, value); + } + else + { + preproc.define(name, value); + } + } // Add math defines preproc.define("MAXFLOAT", "0x1.fffffep127f"); diff --git a/src/parser/parser.cpp b/src/parser/parser.cpp index d601ed3..0880c59 100644 --- a/src/parser/parser.cpp +++ b/src/parser/parser.cpp @@ -876,7 +876,6 @@ namespace FreeOCL const std::string &enum_name = p_chunk ? p_chunk->front().as()->get_string() : values.back().as()->get_string(); - warning(FreeOCL::to_string(i) + ":" + enum_name); symbols->insert(enum_name, new var(enum_name, native_type::t_int)); } d_val__ = new enum_type(N[1].as()->get_string(), values, false, type::PRIVATE); @@ -904,7 +903,10 @@ namespace FreeOCL const smartptr N1 = d_val__; if (!__enumerator()) { - roll_back_to(l); + // C99 allows trailing comma in enum: enum { A, B, } + // If next token is '}', this is a valid trailing comma - don't roll back + if (peek_token() != '}') + roll_back_to(l); break; } N->push_back(N1); diff --git a/src/preprocessor/macro_expansion.cpp b/src/preprocessor/macro_expansion.cpp index eb4f8ed..7e32c13 100644 --- a/src/preprocessor/macro_expansion.cpp +++ b/src/preprocessor/macro_expansion.cpp @@ -173,7 +173,16 @@ namespace FreeOCL { if (args.size() != m.params.size()) error("wrong number of macro parameters (" + to_string(args.size()) + " but " + to_string(m.params.size()) + " expected)"); - ret += macro_expansion(parameters_substitution(m.value, m.params, args), already_expanded); + // Expand macros in arguments before substitution + // This is needed for proper stringification with nested macros like: + // #define XM2S(x) #x + // #define M2S(x) XM2S(x) + // M2S(SOME_MACRO) should expand SOME_MACRO before stringifying + std::vector expanded_args; + expanded_args.reserve(args.size()); + for (size_t ai = 0; ai < args.size(); ++ai) + expanded_args.push_back(macro_expansion(args[ai], already_expanded)); + ret += macro_expansion(parameters_substitution(m.value, m.params, expanded_args), already_expanded); } } } @@ -239,7 +248,7 @@ namespace FreeOCL concat = true; stringify = false; } - + } else if (isalpha(s[i]) || s[i] == '_') { @@ -279,6 +288,12 @@ namespace FreeOCL ret += word; } } + else if (isspace(s[i])) + { + // Don't reset stringify on whitespace - allow "# x" same as "#x" + if (!stringify) + ret += s[i]; + } else { ret += s[i]; diff --git a/src/preprocessor/preprocessor.cpp b/src/preprocessor/preprocessor.cpp index 20b6bbc..ccd25c8 100644 --- a/src/preprocessor/preprocessor.cpp +++ b/src/preprocessor/preprocessor.cpp @@ -84,8 +84,42 @@ namespace FreeOCL else if (word == "include") { skip_whitespaces(); - const std::string &path = get_path(); - get_line(); + std::string path; + // Check if path starts with < or " (direct include) + // or if it's a macro that needs expansion first (computed include) + int first_char = peek(); + if (first_char == '<' || first_char == '"') + { + path = get_path(); + } + else + { + // Computed include: expand macros first + std::string include_arg = get_line(); + std::string expanded = macro_expansion(include_arg); + // Remove leading/trailing whitespace + size_t start = expanded.find_first_not_of(" \t"); + size_t end = expanded.find_last_not_of(" \t"); + if (start != std::string::npos) + expanded = expanded.substr(start, end - start + 1); + // Now extract the path from the expanded string + if (!expanded.empty() && (expanded[0] == '"' || expanded[0] == '<')) + { + char end_char = (expanded[0] == '"') ? '"' : '>'; + size_t close = expanded.find(end_char, 1); + if (close != std::string::npos) + path = expanded.substr(1, close - 1); + else + error("malformed include path after macro expansion: " + expanded); + } + else + { + error("expected '<' or '\"' after macro expansion, got: " + expanded); + } + } + // get_line() already called in computed include case + if (first_char == '<' || first_char == '"') + get_line(); const size_t cur_line = line; @@ -97,8 +131,25 @@ namespace FreeOCL else { std::fstream include_file; + std::string actual_path = path; + // Try absolute path first (or path relative to current dir) + include_file.open(path.c_str(), std::ios_base::in); + // Try relative to current file's directory + if (!include_file.is_open() && !current_file.empty()) + { + size_t last_slash = current_file.find_last_of('/'); + if (last_slash != std::string::npos) + { + actual_path = current_file.substr(0, last_slash + 1) + path; + include_file.open(actual_path.c_str(), std::ios_base::in); + } + } + // Then try include paths for(size_t i = 0 ; i < include_paths.size() && !include_file.is_open() ; ++i) - include_file.open((include_paths[i] + '/' + path).c_str(), std::ios_base::in); + { + actual_path = include_paths[i] + '/' + path; + include_file.open(actual_path.c_str(), std::ios_base::in); + } if (!include_file.is_open()) error("could not open file '" + path + '\'');