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<std::string> 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<token>()->get_string()
 											   : values.back().as<token>()->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<token>()->get_string(), values, false, type::PRIVATE);
@@ -904,7 +903,10 @@ namespace FreeOCL
 				const smartptr<node> 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<std::string> 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 + '\'');
