Known Limitations

Due to the way ipt works it can’t accept all code which Igor Pro allows. In some cases this was also done to avoid excessive effort for what we considered niche issues.

And some limitations are listed here according to our philosophy.

UTF8 source code

ipt can only read source files that are encoded in UTF-8 or ASCII. All non UTF-8 code must therefore converted to UTF-8 first.

You can convert the encoding using Igor Pro by following these steps:

  1. Open the code file in Igor Pro

  2. On the bottom left corner of the text editor is a button that tells you the current encoding. Click on it.

    screenshot in Igor Pro highlighting this button
  3. A new window opens and asks you to select an encoding. Select UTF-8.

    screenshot in Igor Pro highlighting this option
  4. Click on Convert to Encoding

    screenshot in Igor Pro highlighting this button
  5. Save your file.

Statements outside of functions

All statements have to be inside a function, a macro or inside the call statement of a menu item.

print 1 // not supported in ipt, ignored in Igor Pro
Function foo()
    print 2 // supported in ipt and Igor Pro
End

Statements before the first case of a switch statement

A switch statement is used to execute code depending on a variable or string. Similar like a long chain of if statements but nicer to read. In Igor Pro you can write any statement before the first case of a switch statement. Such code is not allowed according to the documentation but it is ignored by the Igor Pro parser. In ipt such code is not allowed.

switch(var)
    print "before" // not supported in ipt, ignored in Igor Pro
    case 1:
        print "first case"
        break
endswitch

Unbalanced parenthesis

In Igor Pro it is possible to omit closing parenthesis or add additional ones at certain places. In ipt all parenthesis must be balanced! Unbalanced parenthesis are treated as an error.

// note the missing parenthesis here: -------------------------v
MatrixOP/FREE rms  = sqrt(sumSqr(data - avg[0]) / numRows(data)

Context breaking #if statements

#if denotes if the contained code should be compiled or not. This is done in Igor Pro before the contained code is compiled. In ipt all code is compiled into an AST first.

Therefore some code has to be written differently:

Not allowed in ipt
switch(var)

    case 1:
#if condition
    case 2:
#endif
        print "<=2"
endswitch
Allowed in ipt
switch(var)

#if condition
    case 2:
#endif
    case 1:
        print "<=2"
endswitch

Other statements have to be refactored:

Not allowed in ipt
// Difficult to understand even for the user
if(var)
    print "some work"
#if condition
endif
#endif

print "other work"
#if !condition
endif
#endif
Allowed in ipt
// A variant on how this can be refactored
if(var)
    print "some work"
    print "other work"
else
#if condition
    print "other work"
#endif
endif

End Function and End Macro

Those are not supported in ipt and not allowed according to the Igor documentation. The Igor Pro parser itself ignores everything after the End.

If you want to mark if this is a macro or function, just use EndMacro for macros (as documented) or write a normal comment.

Function test()
End Function

Superfluous #else branches

In some scenarios it is possible to have multiple #else expressions in Igor Pro. This is not supported in ipt.

#if condition

Function TestA() // will be visible if condition != 0
End

#else

Function TestB() // will be visible if condition == 0
End

#else

Function TestC() // will never be visible
End

#endif

Omitting commas in function arguments

Each argument of a function call must be delimited with a single comma. In Igor Pro its possible to omit some of them.

stringmatch(S_MarqueeWin"Graph")
// missing comma here:  ^

Superfluous commas in declarations

Igor Pro allows commas between the type and the first name, allthough the documentation doesn’t support that idea. In ipt this isn’t supported.

Not allowed in ipt
STRUCT MyStructureName, myStr
// superfluous comma: ^

Division in some operation arguments

The symbol / is used in Igor for division and as a marker for operation flags. Some operations can also have argument flags that are only applied to that specific argument.

Only certain operations do allow flags for their arguments and ipt does only allow flags for these operations. If a flag is allowed for the current argument, ipt consideres every / as a start of the argument flag and no division is allowed.

// considered as the start of the argument flag
//                           v
Differentiate/EP=0 sourcewave/D=testwave
//                           ^^^^^^^^^^^
//                           (argument flag)

You can circumvent this limitation by surrounding the expression with parenthesis:

// Lets ignore that this code is invalid Igor code but this is how you would
// circumvent the limitations for arguments that do allow operation flags
// and you want to have a division:
//                            v
Differentiate/EP=0 (sourcewave/D)

If this argument is a key value pair, all / are considered as argument flags in the key part and all / are considered as division in the value part.

// This argument consists of a key value pair and has two / with different
// usages: start of flag           division
//                     v           v
Make $nameForOutputWave/WAVE=w = p / 2.0
//   ^^^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^
//   key of argument             value of argument

If argument flags are not allowed (default in most cases) all / are parsed as divisions.

// Always a division:
//              v
print nameOfVar / 2

Operators in flag values

Flags can have values attached like in /Name=value. In ipt you cannot use operations like +, -, * or / in value. If you want to use them, you have to surround it with parentheses as in /Name=(value1 + value2). This is the case for most operations in Igor Pro.

If you use reference strings you can always use the + operator to add more parts (like /Name=$part1 + part2) . Although good style suggests to write that in a separate statement.

Function calls in flag values

ipt doesn’t allow function calls in plain flag values, so you have to add parentheses:

// This call
DoAlert/T=func(0) ""
// would be interepreted by ipt as
DoAlert/T=(func) (0),      ""
//     ^^^^^^^^^  ^^^      ^^
//     flag       1st arg  2nd arg
// This call would be interpreted correctly by ipt
DoAlert/T=(func(0))  ""
//     ^^^^^^^^^^^^  ^^
//     flag          1st arg

AS and VS as variable names

AS and VS are used as reserved keywords in operations and ipt doesn’t allow their usage as variable names. This limitation doesn’t exists in Igor Pro.

Encrypted code

Encrypted code is not supported in ipt and results in an error.

Double assignments

Double assignments are not allowed and will be treated as error. In Igor Pro its allowed in some operations currently:

MatrixOp/O wv1=wv2=sum(w)

Return values in macros

According to the Igor Pro documentation return can not be used inside macros. In ipt this is considered an error, in Igor Pro that is ignored.

Macro Test()
    return 1 // no return value allowed
EndMacro

Some global statements require whitespaces

The global statements #include, #pragma, #ifdef, #ifndef, #if, #elif and #define must have at least one whitespace character following:

#include "tools"

Merge of operation flag value with first operation argument

In some cases is the value of the operation flag merged with the first operation argument.

Before formatting with ipt
WAVE/SDFR=root: bigWave
After formatting with ipt
WAVE/SDFR=root:bigWave

This is due to our parsing strategy that ignores the existence of spaces in most places. To temporary fix this problem, you have to add parenthesis to your operation flag value.

No problem in ipt
WAVE/SDFR=(root:) bigWave

Recursive re-evaluation of linting rules

Some linting rules do not produce the optimal fix for the given code. This can happen if another rule can be applied to the first rule’s fix, or if the behavior of a rule changes if the fix were present in the first place. To illustrate this, let’s assume we have the following code:

original code
if(condition1)
    if(condition2)
        return result1
    else
        return result2
    endif
else
    print "something"
endif

After calling ipt lint for the first time, we get the following result:

after first ipt lint --fix
if(condition1)
    if(condition2)
        return result1
    endif

    return result2
else
    print "something"
endif

And calling ipt lint again, we get:

after second ipt lint --fix
if(condition1)
    if(condition2)
        return result1
    endif

    return result2
endif

print "something"

This recursive dependency is not limited to the applied fixes. This can also apply to error messages, as it can be seen here:

source code
switch(value)
    case CASE_1:
        if(condition)
            return result1
        else
            return result2
        endif
endswitch

In the first iteration of calling ipt lint you will get an error message from the rule CodeStyleFallthroughCaseRequireComment that a break is missing for the switch case CASE_1. This is because the rule only checks top-level statements and does not create a full control flow graph.

output of ipt lint --fix
switch(value)
    case CASE_1:
        if(condition)
            return result1
        endif

        return result2
endswitch

After calling ipt lint again on the previous output of ipt lint --fix, there will be no longer an error message from the rule CodeStyleFallthroughCaseRequireComment.

This limitation does only apply to ipt lint --fix. If you do not specify --fix, there is no implication. The best workaround for this limitation is, to just execute ipt lint --fix multiple times until they are no changes to the fixed files and use the error messages from the last iteration.