Question
· Oct 29

Failure to compile *.inc with simple "If" statement

Environment:
    Targeted *.inc file (with hundreds of defined macros) is in use throughout the application and included into every class declaration.
    Statement "set a = $$$TestIf(3)" is included into a classmethod with no other code in. Expected output 5
Same macro options in *.inc:
    #define TestIf(%arr)    if %arr>0 QUIT 5
    #define TestIf(%arr)    if (%arr>0) {QUIT 5}
Issue:
    failure to compile class with the same error on all tried definition options as:

Product version: IRIS 2022.1
$ZV: IRIS for Windows (x86-64) 2022.1.2 (Build 574_0_22407U) Wed Apr 5 2023 11:19:54 EDT
Discussion (13)5
Log in or sign up to continue

The issue of failure to compile the macro containing a simple "if" statement in a .inc file can arise due to several factors related to how macros are defined and used. Here are key considerations:

  1. Macro Definition Syntax:
    Ensure the macro syntax is correct. Macros are defined using #define, and any spaces within the expression in the macro definition can cause syntax errors. For example:

    #define TestIf(%arr) if (%arr>0) {QUIT 5}
    

    The entire expression, if (%arr>0) {QUIT 5}, should not contain spaces that disrupt the syntax [1][2].

  2. Including Macros Properly:
    The targeted .inc file must be included correctly at the top of the class definition or method where the macro is used. The inclusion syntax is:

    include MacroFileName
    

    Make sure the .inc file name is referenced without the .inc suffix [1][3].

    Additionally, when using macros in methods directly, you may need a specific inclusion inside the body of that method if required at compile time [1].

  3. Preprocessor Directive Spacing:
    It’s critical that spaces do not appear unnecessarily in expressions in macro definitions. For example:

    #define TestIf(%arr) if %arr>0 QUIT 5
    

    Spaces must follow conventions depending on where they appear in definitions and expressions [1].

  4. Macro Symbol and Order:
    When invoking macros ($$$macroname), ensure they are defined earlier in the code or included properly in the .inc file. Undefined macros or incorrectly referenced ones will lead to compilation errors [1].

  5. Use of System Macros:
    If the macro references system macros (e.g., $SYSTEM or $GET), verify that the appropriate system .inc files are included in the class. For example:

    include %occStatus
    

    Missing system includes can cause recognized macros (such as $$$OK) to fail [4].

Addressing these aspects should resolve the compilation issues for a macro with an "if" statement. Let me know if you need further clarification or examples.

Sources:

right, I was misled by the error message, which is the same for what you'd get if you omit that initial whitespace.

Given that is your macro use, the error is because the expanded statement (which you can see from the generated .INT code) is invalid:

set a = if %arr>0 QUIT 5

A macro gets expanded before code gets compiled, and should not be mistaken for a function call.

If you want to use that macro on the RHS of a set command, you'll need to rewrite it into something like $select(%arr>0, 5, 123).

@Benjamin De Boe

Sorry for bothering - I was so exited macro was compiled that didn't check for real life usage. Please, help me find a syntax error in:  #define logDebug(%arr) $SELECT(^GlFSL("Debug")>0:Entry^HS.Local.VA.Util.Log(%arr,,"D"),:QUIT).

FYI, "Direct" macro as: #define logDebug(%arr)     Entry^HS.Local.VA.Util.Log(%arr,,"D") works, but I would like to use "SELECT" (or "IF").  

Thank you 

1) you miss a final condition in $SELECT(). It's the 1:
$SELECT(^GlFSL("Debug")>0:Entry^HS.Local.VA.Util.Log(%arr,,"D"),1:QUIT)
2) QUIT doesn't return a value but <UNDEFINED> error if you don't have 
a SET QUIT=""  somwhere before or use $GET()
this may fit

$SELECT(^GlFSL("Debug")>0:Entry^HS.Local.VA.Util.Log(%arr,,"D"),1:$GET(QUIT))

Command QUIT is just appropriate with $CASE(...)
https://docs.intersystems.com/iris20252/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_fselect#RCOS_fselect_select_and_case

You might be having problems with some of the unusual syntax of ObjectScript.

Like other languages created in the 1960s (E.g., FORTRAN IV and PL/I), ObjectScript does not have any reserved words.  The word 'if' can be a command if it appears where a command is specified; it can be local variable if it appears where an expression value is expected; it can sometimes be a global variable or program name if it appears after '^';  it can be a function name if it appears after '$$';  it can be a macro name if it appears after '$$$';  it be a routine or procedure name if it appears after the 'do' command; if can sometimes be a method or a property name if it appears after '.'; etc.

Your macros
    #define TestIf(%arr)    if %arr>0 QUIT 5          ;; legacy IF command
    #define TestIf(%arr)    if (%arr>0) {QUIT 5}     ;; more modern IF command
looks as if TestIf should be expanded where a command is expected so that it conditionally executes the command 'QUIT 5' which would exit the current function with a return value of 5.

However, your macro placement
    set a = $$$TestIf(3)
looks like $$$TestIf is going to be an expression containing a value for the right side of an assignment (a SET command).  It expands as
    set a = if 3 < 0 QUIT 5
which is a syntactically correct assignment of 'a' assigned the value of the variable 'if'  and that 'set' command is followed by a command named '3' which is bad syntax.

Your macro
#define logDebug(%arr) $SELECT(^GlFSL("Debug")>0:Entry^HS.Local.VA.Util.Log(%arr,,"D"),:QUIT)

looks like a macro  containing the built-in '$select' function and the macro should expand into conditional expression (as opposed to a macro containing the command 'if' which should expand in to a conditional statement.)  The first $select argument ^GlFSL("Debug")>0:Entry^HS.Local.VA.Util.Log(%arr,,"D") evaluates ^GlFSL("Debug")>0 and it that comparison is true, the $select evaluates Entry^HS.Local.VA.Util.Log(%arr,,"D") as the final value of the $select expression.  If the selector of the first argument is false then $select goes on the second argument ,:QUIT.  This has the syntax for the default value of a argument of the built-in $case function rather than be correct syntax for a $select argument.  For a $select you would write 1:QUIT which would return the value of the QUIT variable.  However,  I suspect you might have wanted a QUIT command which would terminated the current routine/procedure.  In that case you should write  the conditional commands
    if 
^GlFSL("Debug")>0 then { set temp=Entry^HS.Local.VA.Util.Log(%arr,,"D")} else {QUIT}
if you do not execute the QUIT command then you can continue executing the next command line using the conditionally set 'temp' variable.

"Real life" *.inc entry is to execute macro routine "Entry^HS.Local.VA.Util.Log(%arr,,"D")" if Global ^GlFSL("Debug") is >0 by calling from a classmethod as $$$TestIf(arr) - no return value is required.
Initial attempt was by defining in *inc file as:
    #define TestIf(%arr)        if (^GlFSL("Debug")>0) {Entry^HS.Local.VA.Util.Log(%arr,,"D")}
        Note: Without "IF", definition as "#define TestIf(%arr)        Entry^HS.Local.VA.Util.Log(%arr,,"D")" works fine
Upon failures, macro was step-by-step simplified trying to figure out compilation issue -so, "set a = $$$TestIf(5)" is just oversimplified line.
Thank you
 

"Real life" *.inc entry is to execute macro routine "Entry^HS.Local.VA.Util.Log(%arr,,"D")" if Global ^GlFSL("Debug") is >0 by calling from a classmethod as $$$TestIf(arr) - no return value is required.

#define TestIf(%arr) if (^GlFSL("Debug")>0) {do Entry^HS.Local.VA.Util.Log(%arr,,"D")}
;or
#define TestIf(%arr) do:^GlFSL("Debug")>0 Entry^HS.Local.VA.Util.Log(%arr,,"D")

Usage:

$$$TestIf(5)