diff --git a/compiler/src/dmd/cparse.d b/compiler/src/dmd/cparse.d index 536a212536da..a8b50b6f544c 100644 --- a/compiler/src/dmd/cparse.d +++ b/compiler/src/dmd/cparse.d @@ -5854,6 +5854,8 @@ final class CParser(AST) : Parser!AST void addVar(AST.Dsymbol s) { //printf("addVar() %s\n", s.toChars()); +assert(s); +assert(symbols); if (auto v = s.isVarDeclaration()) v.isCmacro(true); // mark it as coming from a C #define /* If it's already defined, replace the earlier @@ -5884,6 +5886,7 @@ final class CParser(AST) : Parser!AST AST.Type t; + Lswitch: switch (token.value) { case TOK.endOfLine: // #define identifier @@ -5956,13 +5959,14 @@ final class CParser(AST) : Parser!AST break; case TOK.leftParenthesis: + { /* Look for: * #define ID ( expression ) * and rewrite it to a template function: * auto ID()() { return expression; } */ if (params) - break; // no parameters + goto caseFunctionLike; // version with parameters nextToken(); eLatch.sawErrors = false; auto exp = cparseExpression(); @@ -5987,6 +5991,92 @@ final class CParser(AST) : Parser!AST addVar(tempdecl); nextDefineLine(); continue; + } + + caseFunctionLike: + { + /* Parse `( a, b ) expression` + * Create template function: + * auto id(__MP1, __MP2)(__MP1 a, __MP1 b) { return expression; } + */ + //printf("functionlike %s\n", id.toChars()); + + // Capture the parameter list + VarArg varargs = VarArg.none; + auto parameters = new AST.Parameters(); + nextToken(); // skip past `(` + Lwhile: + while (1) + { + if (token.value == TOK.rightParenthesis) + break; + if (token.value == TOK.dotDotDot) + { + static if (0) // variadic macros not supported yet + { + varargs = AST.VarArg.variadic; // C-style variadics + nextToken(); + if (token.value == TOK.rightParenthesis) + break Lwhile; + } + break Lswitch; + } + + if (token.value != TOK.identifier) + break Lswitch; + auto param = new AST.Parameter(token.loc, 0, null, token.ident, null, null); + parameters.push(param); + nextToken(); + if (token.value == TOK.comma) + { + nextToken(); + continue; + } + break; + } + if (token.value != TOK.rightParenthesis) + break Lswitch; + nextToken(); + auto parameterList = AST.ParameterList(parameters, varargs, 0); + /* Create a type for each parameter. Add it to the template parameter list, + * and the parameter list. + */ + auto tpl = new AST.TemplateParameters(); + foreach (param; (*parameters)[]) + { + auto idtype = Identifier.generateId("__MP"); + auto loc = param.loc; + auto tp = new AST.TemplateTypeParameter(loc, idtype, null, null); + tpl.push(tp); + + auto at = new AST.TypeIdentifier(loc, idtype); + param.type = at; + } + + eLatch.sawErrors = false; + auto exp = cparseExpression(); + if (eLatch.sawErrors) // parsing errors + break; // abandon this #define + + if (token.value != TOK.endOfLine) + break; + + // Generate function + auto ret = new AST.ReturnStatement(exp.loc, exp); + StorageClass stc = STC.auto_; + auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc); + auto fd = new AST.FuncDeclaration(exp.loc, exp.loc, id, stc, tf, 0); + fd.fbody = ret; + + // Wrap it in an eponymous template + AST.Dsymbols* decldefs = new AST.Dsymbols(); + decldefs.push(fd); + auto tempdecl = new AST.TemplateDeclaration(exp.loc, id, tpl, null, decldefs, false); + addVar(tempdecl); + + nextDefineLine(); + continue; + } default: break; diff --git a/compiler/test/compilable/imports/defines.c b/compiler/test/compilable/imports/defines.c index 8a5601a2d089..a2a3de7e951f 100644 --- a/compiler/test/compilable/imports/defines.c +++ b/compiler/test/compilable/imports/defines.c @@ -30,3 +30,6 @@ _Static_assert(SSS[0] == 'h', "10"); #define ABC 12 #define GHI (size) abbadabba #define DEF (ABC + 5) + +#define ADD(a, b) a + b +#define SUB() 3 - 2 diff --git a/compiler/test/compilable/testdefines.d b/compiler/test/compilable/testdefines.d index 9dd8cf2af8d5..5252308ffad7 100644 --- a/compiler/test/compilable/testdefines.d +++ b/compiler/test/compilable/testdefines.d @@ -15,3 +15,6 @@ static assert(SSS == "hello"); static assert(ABC == 12); static assert(DEF == 17); + +static assert(ADD(3, 4) == 7); +static assert(SUB() == 1);