huderlem / poryscript

High-level scripting language for gen 3 pokemon decompilation projects
https://www.huderlem.com/poryscript-playground/
MIT License
197 stars 22 forks source link

Support constant expressions in conditionals #36

Closed SBird1337 closed 2 years ago

SBird1337 commented 3 years ago

conditionals, especially while loops only support flag, var or defeated operators as part of their conditional expressions. This does not allow for constant expressions to be used within their respective conditionals.

While this is almost never necessary, it does not allow for endless loops.

script Foo {
     while(1)
     {
         //code
        if(cond)
            break
     }
    //more code
}

In regular assembly syntax you can do something like:

Foo::
    loop:
        //code
       checkflag FLAG_EXP
       gotoifset breakLoop
    b loop
    breakLoop:
       //more code
huderlem commented 3 years ago

While your example code above can easily be rewritten to use a regular condition, it sounds like a plain loop would be what you're looking for. In fact, I don't think it's possible to do more than that, since only flag-, var-, and defeated-related scripting commands set the conditional branching flags used by the scripting engine.

SBird1337 commented 3 years ago

I have a more recent actual example. In this one I just used a temporary flag which was always true to achieve what I needed:

        while(flag(FLAG_SET)) {
            getobjectgfxbyposition(VAR_MIRROR_POS_X, VAR_MIRROR_POS_Y)
            if(var(VAR_RESULT) == OBJ_EVENT_GFX_OBJECT_MIRROR_NORTH_EAST || 
            var(VAR_RESULT) == OBJ_EVENT_GFX_OBJECT_MIRROR_NORTH_WEST || 
            var(VAR_RESULT) == OBJ_EVENT_GFX_OBJECT_MIRROR_SOUTH_EAST || 
            var(VAR_RESULT) == OBJ_EVENT_GFX_OBJECT_MIRROR_SOUTH_WEST || 
            var(VAR_RESULT) == OBJ_EVENT_GFX_OBJECT_MIRROR_NORTH_EAST_TOP || 
            var(VAR_RESULT) == OBJ_EVENT_GFX_OBJECT_MIRROR_NORTH_WEST_TOP || 
            var(VAR_RESULT) == OBJ_EVENT_GFX_OBJECT_MIRROR_SOUTH_EAST_TOP || 
            var(VAR_RESULT) == OBJ_EVENT_GFX_OBJECT_MIRROR_SOUTH_WEST_TOP){
                agbprint(ascii"ran into a mirror: %d", VAR_RESULT)
                call(MirrorPuzzleHandleMirrorBump)
            } else {
                getmetabehavior(VAR_MIRROR_POS_X, VAR_MIRROR_POS_Y, 1)
                if(var(VAR_RESULT) == 0x4C || var(VAR_RESULT) == 0x59) {
                    if(var(VAR_MIRROR_DIRECTION) == TURNOBJECT_DOWN) {
                        movelistpop
                    }
                    //we are done because we just bumped into another lightning thingy
                    break
                } 
                elif(var(VAR_RESULT) == 0x8 || var(VAR_RESULT) == 0xA || var(VAR_RESULT) == 0x10
                    || var(VAR_RESULT) == 0x1 || var(VAR_RESULT) == 0x11 || var(VAR_RESULT) == 0x12
                    || var(VAR_RESULT) == 0x55 || var(VAR_RESULT) == 0x56 || var(VAR_RESULT) == 0x1B9
                    || var(VAR_RESULT) == 0x57 || var(VAR_RESULT) == 0x1BA) {
                        call(MirrorPuzzleHandleWall)
                }
            }
            call(MirrorPuzzleSingleMove)
        }

The problem is that this loop termination is not really trivial, so I did what I would do in other programming: Use a while(true) equivalent. You could always find another way to do this (and still avoid unnecessary goto, maybe, idk) but this seemed like the cleanest solution to me.

It only really applies to loops, at least I cannot think of an example where you would need constant expressions in if or switch statements (maybe preprocessor stuff, can't think of a valid example though)

When opening the issue I was thinking how gcc handles infinite loops like that. It will just generate a b loop without generating actual conditionals. But that's probably because of optimizations and idk if that's applicable for poryscript.

A loop syntax would also work here, like a loop without a conditional that will always be endless until it encounters a break.

Edit: As for real constant expressions. To work poryscript would probably have to always on compile time resolve the expressions and generate the respective branch (or not) - idk if this is really applicable or necessary.

huderlem commented 2 years ago

Resolved with d09289d63cd10f551677687bce619f4e86b8d03b

Infinite loops are now possible by omitting the boolean expression from a while statement.

while {
   ...
}