浏览代码

HPCC-7856 Include macros with parameters-with-defaults in dependencies

This addresses several issues
* You can now sumbit a macro that has default values for all its parameters as
  a query.
* The same macros are now supported in the legacy dependency generation
* Common up some of the macro processing code between the two.
* Sort the module and symbol lists so dependencies are generated in a
  deterministic order.
* Fix potential caching problems if no error handler provided to the dependency
  gathering code.  (It didn't count the number of errors, so the code couldn't
  tell whether an error had occurred processing a definition.

Signed-off-by: Gavin Halliday <gavin.halliday@lexisnexis.com>
Gavin Halliday 12 年之前
父节点
当前提交
f4e1c69c5e

+ 10 - 1
ecl/eclcc/eclcc.cpp

@@ -41,6 +41,8 @@
 #include "build-config.h"
 #include "rmtfile.hpp"
 
+//#define TEST_LEGACY_DEPENDENCY_CODE
+
 #define INIFILE "eclcc.ini"
 #define SYSTEMCONFDIR CONFIG_DIR
 #define DEFAULTINIFILE "eclcc.ini"
@@ -767,6 +769,13 @@ void EclCC::processSingleQuery(EclCompileInstance & instance,
                                IFileContents * queryContents,
                                const char * queryAttributePath)
 {
+#ifdef TEST_LEGACY_DEPENDENCY_CODE
+    setLegacyEclSemantics(instance.legacyMode);
+    Owned<IPropertyTree> dependencies = gatherAttributeDependencies(instance.dataServer, "");
+    if (dependencies)
+        saveXML("depends.xml", dependencies);
+#endif
+
     Owned<IErrorReceiver> wuErrs = new WorkUnitErrorReceiver(instance.wu, "eclcc");
     Owned<IErrorReceiver> errs = createCompoundErrorReceiver(instance.errs, wuErrs);
 
@@ -835,7 +844,7 @@ void EclCC::processSingleQuery(EclCompileInstance & instance,
                 if (instance.legacyMode)
                     importRootModulesToScope(scope, ctx);
 
-                instance.query.setown(parseQuery(scope, queryContents, ctx, NULL, true));
+                instance.query.setown(parseQuery(scope, queryContents, ctx, NULL, NULL, true));
 
                 if (instance.archive)
                 {

+ 9 - 5
ecl/hql/hqlerror.hpp

@@ -88,11 +88,15 @@ class HQL_API NullErrorReceiver : public CInterface, implements IErrorReceiver
 public:
     IMPLEMENT_IINTERFACE;
 
-    void reportError(int errNo, const char *msg,  const char * filename, int _lineno, int _column, int _pos) {}
-    void reportWarning(int warnNo, const char *msg,  const char * filename, int _lineno, int _column, int _pos) {}
-    void report(IECLError*) { }
-    virtual size32_t errCount() { return 0; };
-    virtual size32_t warnCount() { return 0; };
+    void reportError(int errNo, const char *msg,  const char * filename, int _lineno, int _column, int _pos) { numErrors++; }
+    void reportWarning(int warnNo, const char *msg,  const char * filename, int _lineno, int _column, int _pos) { numWarnings++; }
+    void report(IECLError* err) { if (err->isError()) numErrors++; else numWarnings++; }
+    virtual size32_t errCount() { return numErrors; };
+    virtual size32_t warnCount() { return numWarnings; };
+
+private:
+    unsigned numErrors;
+    unsigned numWarnings;
 };
 
 

+ 10 - 19
ecl/hql/hqlexpr.cpp

@@ -15642,28 +15642,14 @@ static void safeLookupSymbol(HqlLookupContext & ctx, IHqlScope * modScope, _ATOM
     try
     {
         OwnedHqlExpr resolved = modScope->lookupSymbol(name, LSFpublic, ctx);
-
-        //Expand macros with no parameters
         if (!resolved || !resolved->isMacro())
             return;
 
-        IHqlExpression * macroBodyExpr;
-        if (resolved->getOperator() == no_funcdef)
-        {
-            if (resolved->queryChild(1)->numChildren() != 0)
-                return;
-            macroBodyExpr = resolved->queryChild(0);
-        }
-        else
-            macroBodyExpr = resolved;
-
-        _ATOM moduleName = modScope->queryName();
-        HqlLookupContext localContext(ctx);
-        localContext.createDependencyEntry(modScope, name);
+        //Macros need special processing to expand their definitions.
+        HqlLookupContext childContext(ctx);
+        childContext.createDependencyEntry(modScope, name);
 
-        IFileContents * macroContents = static_cast<IFileContents *>(macroBodyExpr->queryUnknownExtra());
-        Owned<IHqlScope> scope = createPrivateScope();
-        OwnedHqlExpr query = parseQuery(scope, macroContents, localContext, NULL, true);
+        OwnedHqlExpr expanded = expandMacroDefinition(resolved, childContext, false);
     }
     catch (IException * e)
     {
@@ -15673,8 +15659,11 @@ static void safeLookupSymbol(HqlLookupContext & ctx, IHqlScope * modScope, _ATOM
 
 static void gatherAttributeDependencies(HqlLookupContext & ctx, IHqlScope * modScope)
 {
+    modScope->ensureSymbolsDefined(ctx);
     HqlExprArray symbols;
     modScope->getSymbols(symbols);
+    symbols.sort(compareSymbolsByName);
+
     ForEachItemIn(i, symbols)
         safeLookupSymbol(ctx, modScope, symbols.item(i).queryName());
 }
@@ -15718,7 +15707,8 @@ extern HQL_API IPropertyTree * gatherAttributeDependencies(IEclRepository * data
     HqlParseContext parseCtx(dataServer, NULL);
     parseCtx.nestedDependTree.setown(createPTree("Dependencies"));
 
-    HqlLookupContext ctx(parseCtx, NULL);
+    NullErrorReceiver errorHandler;
+    HqlLookupContext ctx(parseCtx, &errorHandler);
     if (items && *items)
     {
         loop
@@ -15738,6 +15728,7 @@ extern HQL_API IPropertyTree * gatherAttributeDependencies(IEclRepository * data
     {
         HqlScopeArray scopes;   
         getRootScopes(scopes, dataServer, ctx);
+        scopes.sort(compareScopesByName);
         ForEachItemIn(i, scopes)
         {
             IHqlScope & cur = scopes.item(i);

+ 1 - 1
ecl/hql/hqlexpr.hpp

@@ -1382,7 +1382,7 @@ extern HQL_API IHqlExpression * getCastExpr(IHqlExpression * expr, ITypeInfo * t
 
 extern HQL_API void parseModule(IHqlScope *scope, IFileContents * contents, HqlLookupContext & ctx, IXmlScope *xmlScope, bool loadImplicit);
 extern HQL_API IHqlExpression *parseQuery(IHqlScope *scope, IFileContents * contents, 
-                                          HqlLookupContext & ctx, IXmlScope *xmlScope, bool loadImplicit);
+                                          HqlLookupContext & ctx, IXmlScope *xmlScope, IProperties * macroParams, bool loadImplicit);
 extern HQL_API IHqlExpression *parseQuery(const char *in, IErrorReceiver * errs);
 
 extern HQL_API IPropertyTree * gatherAttributeDependencies(IEclRepository * dataServer, const char * items = NULL);

+ 2 - 1
ecl/hql/hqlgram.hpp

@@ -1053,6 +1053,7 @@ class HqlLex
             return sourcePath;
         }
 
+        inline void setMacroParams(IProperties * _macroParams) { macroParms.set(_macroParams); }
         inline void setTokenPosition(YYSTYPE & returnToken)
         {
             returnToken.setPosition(yyLineNo, yyColumn, yyPosition, sourcePath);
@@ -1155,7 +1156,7 @@ private:
         /* to handle recursive macro */
         HqlLex *parentLex;
 
-        IProperties *macroParms;
+        Owned<IProperties> macroParms;
         IIterator *forLoop;
         IHqlExpression *macroExpr;
         StringBuffer forBody;

+ 7 - 4
ecl/hql/hqlgram2.cpp

@@ -11042,7 +11042,7 @@ IHqlExpression * PseudoPatternScope::lookupSymbol(_ATOM name, unsigned lookupFla
 
 //---------------------------------------------------------------------------------------------------------------------
 
-extern HQL_API IHqlExpression * parseQuery(IHqlScope *scope, IFileContents * contents, HqlLookupContext & ctx, IXmlScope *xmlScope, bool loadImplicit)
+extern HQL_API IHqlExpression * parseQuery(IHqlScope *scope, IFileContents * contents, HqlLookupContext & ctx, IXmlScope *xmlScope, IProperties * macroParams, bool loadImplicit)
 {
     assertex(scope);
     try
@@ -11053,6 +11053,7 @@ extern HQL_API IHqlExpression * parseQuery(IHqlScope *scope, IFileContents * con
         parser.setQuery(true);
         parser.getLexer()->set_yyLineNo(1);
         parser.getLexer()->set_yyColumn(1);
+        parser.getLexer()->setMacroParams(macroParams);
         OwnedHqlExpr ret = parser.yyParse(false, true);
         ctx.noteEndQuery();
         return parser.clearFieldMap(ret.getClear());
@@ -11120,7 +11121,7 @@ extern HQL_API IHqlExpression * parseQuery(const char * text, IErrorReceiver * e
     Owned<IHqlScope> scope = createScope();
     HqlDummyLookupContext ctx(errs);
     Owned<IFileContents> contents = createFileContentsFromText(text, NULL);
-    return parseQuery(scope, contents, ctx, NULL, true);
+    return parseQuery(scope, contents, ctx, NULL, NULL, true);
 }
 
 
@@ -11261,12 +11262,14 @@ IHqlExpression *HqlGram::doParse()
     if (eclyyparse(this) != 0)
         return NULL;
     unsigned nowErrors = errorHandler ? errorHandler->errCount() : 0;
-    if (prevErrors != nowErrors)
-        return NULL;
 
     lookupCtx.noteFinishedParse(defineScopes.tos().privateScope);
     if (!parseConstantText)
         lookupCtx.noteFinishedParse(parseScope);
+
+    if (prevErrors != nowErrors)
+        return NULL;
+
     if (parsingTemplateAttribute)
     {
         if (parseResults.ordinality() == 0)

+ 5 - 22
ecl/hql/hqlparse.cpp

@@ -165,7 +165,6 @@ void HqlLex::init(IFileContents * _text)
     text.set(_text);
     inmacro = NULL;
     parentLex = NULL;
-    macroParms = NULL;
     inComment = false;
     inCpp = false;
     hasHashbreak = false;
@@ -202,7 +201,6 @@ HqlLex::~HqlLex()
     delete[] yyBuffer;
     ::Release(xmlScope);
     ::Release(macroExpr);
-    ::Release(macroParms);
     if (inmacro) delete inmacro;        
     ::Release(forLoop);
 }
@@ -329,21 +327,6 @@ void HqlLex::pushText(const char *s)
 #endif
 }
 
-static bool getDefaultParam(IHqlExpression* _defValue, StringBuffer& ret)
-{
-    OwnedHqlExpr defValue = foldExprIfConstant(_defValue);
-
-    IValue * value = defValue->queryValue();
-    if (!value)
-        return false;
-
-    StringBuffer temp;
-    value->getStringValue(temp);
-    //PrintLog("Get macro param: %s", fixed.str());
-    ret.append(temp);
-    return true;
-}
-
 void HqlLex::setMacroParam(const YYSTYPE & errpos, IHqlExpression* funcdef, StringBuffer& curParam, _ATOM argumentName, unsigned& parmno,IProperties *macroParms)
 {
     IHqlExpression * formals = queryFunctionParameters(funcdef);
@@ -384,7 +367,7 @@ void HqlLex::setMacroParam(const YYSTYPE & errpos, IHqlExpression* funcdef, Stri
             }
             else
             {
-                if (!getDefaultParam(def, curParam))
+                if (!getFoldedConstantText(curParam, def))
                 {
                     StringBuffer msg("Default value for parameter ");
                     msg.append(parmno).append(" should be a constant");
@@ -516,7 +499,7 @@ void HqlLex::pushMacro(IHqlExpression *expr)
                 IHqlExpression* def = queryDefaultValue(defaults, idx); 
                 if (def)
                 {
-                    if (!getDefaultParam(def, curParam))
+                    if (!getFoldedConstantText(curParam, def))
                     {
                         StringBuffer msg("Omitted parameter ");
                         msg.append(idx+1);
@@ -570,7 +553,7 @@ void HqlLex::pushMacro(IHqlExpression *expr)
         inmacro->yyLineNo = macroBodyExpr->getStartLine();
         inmacro->yyColumn = macroBodyExpr->getStartColumn();
         inmacro->setParentLex(this);
-        inmacro->macroParms = macroParms.getClear();
+        inmacro->macroParms.setown(macroParms.getClear());
     }
 }
 
@@ -1011,7 +994,7 @@ void HqlLex::doExport(YYSTYPE & returnToken, bool toXml)
         {
             HqlLookupContext ctx(yyParser->lookupCtx);
             Owned<IFileContents> exportContents = createFileContentsFromText(curParam.str(), sourcePath);
-            expr.setown(parseQuery(scope, exportContents, ctx, xmlScope, true));
+            expr.setown(parseQuery(scope, exportContents, ctx, xmlScope, NULL, true));
 
             if (expr && (expr->getOperator() == no_sizeof))
             {
@@ -1437,7 +1420,7 @@ void HqlLex::doIsValid(YYSTYPE & returnToken)
         HqlLookupContext ctx(yyParser->lookupCtx);
         ctx.errs.clear();   //Deliberately ignore any errors
         Owned<IFileContents> contents = createFileContentsFromText(curParam.str(), sourcePath);
-        expr = parseQuery(scope, contents, ctx, xmlScope, true);
+        expr = parseQuery(scope, contents, ctx, xmlScope, NULL, true);
         
         if(expr)
         {   

+ 74 - 29
ecl/hql/hqlutil.cpp

@@ -3831,6 +3831,22 @@ int compareSymbolsByName(IInterface * * pleft, IInterface * * pright)
     return stricmp(left->queryName()->str(), right->queryName()->str());
 }
 
+int compareScopesByName(IInterface * * pleft, IInterface * * pright)
+{
+    IHqlScope * left = static_cast<IHqlScope *>(*pleft);
+    IHqlScope * right = static_cast<IHqlScope *>(*pright);
+
+    const char * leftName = left->queryName()->str();
+    const char * rightName = right->queryName()->str();
+    if (leftName && rightName)
+        return stricmp(leftName, rightName);
+    if (leftName)
+        return +1;
+    if (rightName)
+        return -1;
+    return 0;
+}
+
 class ModuleExpander
 {
 public:
@@ -5550,6 +5566,18 @@ extern HQL_API bool areConstant(const HqlExprArray & args)
 }
 
 
+bool getFoldedConstantText(StringBuffer& ret, IHqlExpression * expr)
+{
+    OwnedHqlExpr folded = foldExprIfConstant(expr);
+
+    IValue * value = folded->queryValue();
+    if (!value)
+        return false;
+
+    value->getStringValue(ret);
+    return true;
+}
+
 //===========================================================================
 
 void appendArray(HqlExprCopyArray & tgt, const HqlExprCopyArray & src)
@@ -7652,43 +7680,60 @@ IHqlExpression * createMappingTransform(IHqlExpression * selfSelector, IHqlExpre
 
 //---------------------------------------------------------------------------------------------------------------------
 
-static IHqlExpression * transformAttributeToQuery(IHqlExpression * expr, HqlLookupContext & ctx)
+IHqlExpression * expandMacroDefinition(IHqlExpression * expr, HqlLookupContext & ctx, bool reportError)
 {
-    if (expr->isMacro())
+    assertex(expr->isMacro());
+
+    Owned<IProperties> macroParms = createProperties();
+    IHqlExpression * macroBodyExpr;
+    if (expr->getOperator() == no_funcdef)
     {
-        IHqlExpression * macroBodyExpr;
-        if (expr->getOperator() == no_funcdef)
+        IHqlExpression * formals = expr->queryChild(1);
+        IHqlExpression * defaults = expr->queryChild(2);
+        ForEachChild(i, formals)
         {
-            if (expr->queryChild(1)->numChildren() != 0)
+            IHqlExpression* formal = formals->queryChild(i);
+            IHqlExpression* def = queryDefaultValue(defaults, i);
+
+            StringBuffer curParam;
+            if (!def || !getFoldedConstantText(curParam, def))
             {
-                ctx.errs->reportError(HQLERR_CannotSubmitMacroX, "Cannot submit a MACRO with parameters()", NULL, 1, 0, 0);
+                if (reportError)
+                    ctx.errs->reportError(HQLERR_CannotSubmitMacroX, "Cannot submit a MACRO with parameters that do no have default values", NULL, 1, 0, 0);
                 return NULL;
             }
-            macroBodyExpr = expr->queryChild(0);
+            macroParms->setProp(formal->queryName()->str(), curParam.str());
         }
-        else
-            macroBodyExpr = expr;
-
-        IFileContents * macroContents = static_cast<IFileContents *>(macroBodyExpr->queryUnknownExtra());
-        size32_t len = macroContents->length();
-
-        //Strangely some macros still have the ENDMACRO on the end, and others don't.  This should be removed really.
-        StringBuffer macroText;
-        macroText.append(len, macroContents->getText());
-        if ((len >= 8) && strieq(macroText.str()+(len-8),"ENDMACRO"))
-            macroText.setLength(len-8);
-        //Now append a semi colon since that is how macros are normally called.
-        macroText.append(";");
-
-        //This might be cleaner if it was implemented by parsing the text myModule.myAttribute().
-        //It would make implementing default parameters easy.  However it could introduce other problems
-        //with implicitly importing myModule.
-        Owned<IFileContents> mappedContents = createFileContentsFromText(macroText.length(), macroText.str(), macroContents->querySourcePath());
-        Owned<IHqlScope> scope = createPrivateScope();
-        if (queryLegacyEclSemantics())
-            importRootModulesToScope(scope, ctx);
-        return parseQuery(scope, mappedContents, ctx, NULL, true);
+        macroBodyExpr = expr->queryChild(0);
     }
+    else
+        macroBodyExpr = expr;
+
+    IFileContents * macroContents = static_cast<IFileContents *>(macroBodyExpr->queryUnknownExtra());
+    size32_t len = macroContents->length();
+
+    //Strangely some macros still have the ENDMACRO on the end, and others don't.  This should be removed really.
+    StringBuffer macroText;
+    macroText.append(len, macroContents->getText());
+    if ((len >= 8) && strieq(macroText.str()+(len-8),"ENDMACRO"))
+        macroText.setLength(len-8);
+    //Now append a semi colon since that is how macros are normally called.
+    macroText.append(";");
+
+    //This might be cleaner if it was implemented by parsing the text myModule.myAttribute().
+    //It would make implementing default parameters easy.  However it could introduce other problems
+    //with implicitly importing myModule.
+    Owned<IFileContents> mappedContents = createFileContentsFromText(macroText.length(), macroText.str(), macroContents->querySourcePath());
+    Owned<IHqlScope> scope = createPrivateScope();
+    if (queryLegacyEclSemantics())
+        importRootModulesToScope(scope, ctx);
+    return parseQuery(scope, mappedContents, ctx, NULL, macroParms, true);
+}
+
+static IHqlExpression * transformAttributeToQuery(IHqlExpression * expr, HqlLookupContext & ctx)
+{
+    if (expr->isMacro())
+        return expandMacroDefinition(expr, ctx, true);
 
     if (expr->isFunction())
     {

+ 3 - 0
ecl/hql/hqlutil.hpp

@@ -54,6 +54,7 @@ extern HQL_API IHqlExpression * queryLastField(IHqlExpression * record);
 extern HQL_API IHqlExpression * queryLastNonAttribute(IHqlExpression * expr);
 extern HQL_API IHqlExpression * queryNextRecordField(IHqlExpression * recorhqlutid, unsigned & idx);
 extern HQL_API int compareSymbolsByName(IInterface * * pleft, IInterface * * pright);
+extern HQL_API int compareScopesByName(IInterface * * pleft, IInterface * * pright);
 extern HQL_API int compareAtoms(IInterface * * pleft, IInterface * * pright);
 extern HQL_API IHqlExpression * getSizetConstant(unsigned size);
 extern HQL_API IHqlExpression * createIntConstant(__int64 val);
@@ -178,6 +179,7 @@ extern HQL_API bool isConstantDataset(IHqlExpression * expr);
 extern HQL_API bool isSimpleTransformToMergeWith(IHqlExpression * expr);
 extern HQL_API IHqlExpression * queryUncastExpr(IHqlExpression * expr);
 extern HQL_API bool areConstant(const HqlExprArray & args);
+extern HQL_API bool getFoldedConstantText(StringBuffer& ret, IHqlExpression * expr);
 
 extern HQL_API IHqlExpression * createTransformForField(IHqlExpression * field, IHqlExpression * value);
 extern HQL_API IHqlExpression * convertScalarToRow(IHqlExpression * value, ITypeInfo * fieldType);
@@ -655,6 +657,7 @@ extern HQL_API IPropertyTree * queryArchiveAttribute(IPropertyTree * module, con
 extern HQL_API IPropertyTree * createArchiveAttribute(IPropertyTree * module, const char * name);
 
 extern HQL_API IECLError * annotateExceptionWithLocation(IException * e, IHqlExpression * location);
+extern HQL_API IHqlExpression * expandMacroDefinition(IHqlExpression * expr, HqlLookupContext & ctx, bool reportError);
 extern HQL_API IHqlExpression * convertAttributeToQuery(IHqlExpression * expr, HqlLookupContext & ctx);
 extern HQL_API StringBuffer & appendLocation(StringBuffer & s, IHqlExpression * location, const char * suffix = NULL);
 extern HQL_API bool userPreventsSort(IHqlExpression * noSortAttr, node_operator side);

+ 1 - 1
ecl/hqlcpp/hqlcpp.cpp

@@ -1360,7 +1360,7 @@ HqlCppTranslator::HqlCppTranslator(IErrorReceiver * _errors, const char * _soNam
             cppSystemScope = createScope();
             Owned<ISourcePath> sysPath = createSourcePath("<system-definitions>");
             Owned<IFileContents> systemContents = createFileContentsFromText(systemText.str(), sysPath);
-            OwnedHqlExpr query = parseQuery(cppSystemScope, systemContents, ctx, NULL, false);
+            OwnedHqlExpr query = parseQuery(cppSystemScope, systemContents, ctx, NULL, NULL, false);
             if (errs.errCount())
             {
                 StringBuffer errtext;

+ 34 - 0
ecl/regress/macroargs.eclxml

@@ -0,0 +1,34 @@
+<Archive>
+    <!--
+
+    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+    <Module name="myModule">
+        <Attribute name="mac">
+EXPORT mac(a = true, b = false) := MACRO
+    import myModule;
+    #if(b and a)
+        myModule.good;
+    #else
+        myModule.bad;
+    #end
+ENDMACRO;
+        </Attribute>
+        <Attribute name="good">'good';</Attribute>
+        <Attribute name="bad">'bad';</Attribute>
+        <Attribute name="z">'z';</Attribute>
+    </Module>
+    <Query attributePath="myModule.mac"/>
+</Archive>

+ 39 - 0
ecl/regress/macroargs2.eclxml

@@ -0,0 +1,39 @@
+<Archive>
+    <!--
+
+    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+    <Module name="myModule">
+        <Attribute name="mac">
+EXPORT mac(a = true, b = false) := MACRO
+    import myModule;
+    #if(b and a)
+        myModule.good;
+    #else
+        myModule.bad;
+    #end
+ENDMACRO;
+        </Attribute>
+        <Attribute name="good">'good';</Attribute>
+        <Attribute name="bad">'bad';</Attribute>
+        <Attribute name="mac2">
+EXPORT mac2() := MACRO
+   IMPORT myModule;
+   myModule.mac(true, true)
+ENDMACRO;
+        </Attribute>
+    </Module>
+    <Query attributePath="myModule.mac2"/>
+</Archive>

+ 30 - 0
ecl/regress/macroargse.eclxml

@@ -0,0 +1,30 @@
+<Archive>
+    <!--
+
+    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+    <Module name="myModule">
+        <Attribute name="mac">
+EXPORT mac(a = true, b) := MACRO
+    #if(b and a)
+        'good';
+    #else
+        'bad';
+    #end
+ENDMACRO;
+        </Attribute>
+    </Module>
+    <Query attributePath="myModule.mac"/>
+</Archive>