RPGHacker / asar

(Now) official repository of the SNES assembler Asar, originally created by Alcaro
Other
199 stars 42 forks source link

Elseif breaks when trying to evaluate variadic values #226

Open jjjordan opened 2 years ago

jjjordan commented 2 years ago

Applies to: 1.81 & latest from master branch

Here is a short repro:

asar 1.81
lorom

macro add_text(...)
    !i #= 0
    while !i < sizeof(...)
        if <!i> == 'A'
            db $00
        elseif <!i> == 'B'
            db $01
        elseif <!i> == 'C'
            db $02
        elseif <!i> == 'D'
            db $03
        else
            db $04
        endif

        !i #= !i+1
    endwhile
endmacro

org $078000

%add_text('A', 'B', 'B', 'A')

Which yields the following errors:

repro.asm:10 (called from repro.asm:25): error: (E5108): Broken elseif command. [elseif == 'C']
repro.asm:12 (called from repro.asm:25): error: (E5108): Broken elseif command. [elseif == 'D']
repro.asm:8 (called from repro.asm:25): error: (E5108): Broken elseif command. [elseif == 'B']
repro.asm:10 (called from repro.asm:25): error: (E5108): Broken elseif command. [elseif == 'C']
repro.asm:12 (called from repro.asm:25): error: (E5108): Broken elseif command. [elseif == 'D']
repro.asm:8 (called from repro.asm:25): error: (E5108): Broken elseif command. [elseif == 'B']
repro.asm:10 (called from repro.asm:25): error: (E5108): Broken elseif command. [elseif == 'C']
repro.asm:12 (called from repro.asm:25): error: (E5108): Broken elseif command. [elseif == 'D']
repro.asm:10 (called from repro.asm:25): error: (E5108): Broken elseif command. [elseif == 'C']
repro.asm:12 (called from repro.asm:25): error: (E5108): Broken elseif command. [elseif == 'D']

I found this comment (but no issue, looks like it predates this repository), however with a debugger I find that at this point, the define is gone since macro parameter substitution has already happened.

Observation: Note that not every elseif in the chain yields an error. It only seems to occur after the first false evaluation. i.e., for the input 'A', the first comparison is true, the second is false, and then we get errors for 'C' and 'D'. For 'B', the first comparison is false so we get errors for 'B', 'C', and 'D'. I found this counterintuitive. If we're evaluating elseif's, I would think that every false result should lead you to treat the next elseif as a brand-new if and once we get a true then we stop caring about any further branches.

Workaround/note: Assigning the variadic argument to a define and using it in further conditionals sometimes works (see below). However, in my actual project I get Broken define declaration errors on the workaround line. I don't have a self-contained repro for this case (yet), so imo it's worth an attempt at fixing this one.

        !c #= <!i>
        if !c == 'A'
            db $00
        elseif !c == 'B'
            db $01
        elseif !c == 'C'
            db $02
        elseif !c == 'D'
            db $03
        else
            db $04
        endif
p4plus2 commented 2 years ago

Another temporary work around is to use a series of if statements without elseif. I will be looking into this bug, I have made some fixes to varargs in 1.9, so I'll try to get this one in there too.

p4plus2 commented 2 years ago

This has been fixed in 27b85c8. We will probably backport this into the 1.9 release that will hopefully be out later this month.