浏览代码

Merge pull request #3419 from ghalliday/issue7856

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

Reviewed-By: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 12 年之前
父节点
当前提交
c47298593f

+ 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>