GSoC Week 1: Bugs Ahoy!

This blog post is related to my GSoC 2022 project.

In the past week, I worked mainly on trying to fix the “MSVC x86_64” CI for RadialGM.

I also worked on the DnD code generator this week. After discussing with @R0bert, I learned that the reason the __if__ variable was generated is for scoping using with() statements to work correctly.

[5:21 PM] R0bert: the reason it has to be done is because the if itself (aka "action_if") can be scoped with applies to
[5:22 PM] R0bert: which means you have to run "if" inside "with"
[5:22 PM] R0bert: because it don't you'd otherwise need to prepend "self." to all the var accesses inside the if statement

Thus, I rolled back my change which got rid of the __if__ variable, and redid the with() scope generation code to use who_name() directly, which stores the name of the target which the action applies to.

Therefore, when dealing with “if”/conditional actions (called question actions in GML), instead of just emitting

if (condition) body

we require code in the form of

{
    __if__ = condition;
    if (condition)
        body
}

The problem with the current code generator was that it assumed all actions as a flat sequence, without considering the nesting that happens with a question action and its body (and an else-action if present). This caused an issue where there was no way to know when to emit the closing curly brace for a given question action, as there was no way to know where the body of the question action ended in a flat sequence.

I created two functions to fix this, GetNextAction and Action2Code, whose names should be self-explanatory.

Along with that, I created a function PushIfRequired. This is the system responsible for making sure that within nested actions, unnecessary with() statements are not generated in the form of

with (obj_1) {
    do_something();
    with (obj_1)
        do_something_else();
}

where nested actions which both have “applies to” enabled and target the same object do not emit the same with() twice.

This is handled by using a std::stack<std::string>, which stores the current object being targeted by the action we are currently in. If the stack is empty, it is equivalent to not having a target for the current action, which means targeting self. If the object the current action is targetting is already on top of the stack, there is no need to emit a with() because the action we are within would already have emitted the with() to target that object if required.

At the end of each action, if an object name was pushed onto the stack, it is popped off. This does mean that targets are only tracked within nested actions and not across a sequence of actions, as that would require a pre-processing step to clump together actions which target the same object, which would cost some processing time and would require more complex logic.

I also created a test for the new code generation system, which runs with my PR but not with the current iteration of LGM. This test tests nested question actions, nested “applies to” and action-blocks delimited by ACT_BEGIN and ACT_END.

In the coming week, I will be working mainly on the EDL parser along with its BNF file, and I will slowly start looking over the buffer functions and how they work as well. This will require a dicussion of endianness (for serialization and deserialization), and what binary floating point / complex format is to be used.