Rewriting macros as tags

Discussion of Lua and LuaWML support, development, and ideas.

Moderator: Forum Moderators

User avatar
zookeeper
WML Wizard
Posts: 9742
Joined: September 11th, 2004, 10:40 pm
Location: Finland

Re: Rewriting macros as tags

Post by zookeeper »

Yeah, that looks pretty nice syntax.

I recently wrote a somewhat similar [foreach_side] tag for use in the Diplomacy Era:

Code: Select all

local function foreach_side_handler(cfg)
    local iterator = cfg.iterator or helper.wml_error("[[foreach_side]] missing required iterator= attribute.")
    local side_variable = cfg.side_variable or helper.wml_error("[[foreach_side]] missing required side_variable= attribute.")
    local command = helper.get_child(cfg, "command")

    -- TODO: use 1.9 wesnoth.sides instead
    for i=1,10 do
        if #wesnoth.get_units({ side = i }) > 0 then
            wesnoth.set_variable(iterator, i)

            wesnoth.fire("store_side", { side=i, variable=side_variable })
            wesnoth.fire("command", command)
        end
    end
end

    [event]
        name=start

        [foreach_side]
            iterator=i
            side_variable=stored_side_i

            [command]
                [message]
                    speaker=narrator
                    message="There's a side $i with $stored_side_i.gold in the game."
                [/message]
            [/command]
        [/foreach_side]
    [/event]
Arguably you don't really need the [command] tags.
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: Rewriting macros as tags

Post by Anonymissimus »

zookeeper wrote:Arguably you don't really need the [command] tags.
Why not ?
Can one do wesnoth.fire for whatever tag this way (tags that are defined nowhere / assigned a function to)? The message tag could be arbitrary action wml.
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
silene
Posts: 1109
Joined: August 28th, 2004, 10:02 pm

Re: Rewriting macros as tags

Post by silene »

Anonymissimus wrote:
zookeeper wrote:Arguably you don't really need the [command] tags.
Why not ?
Can one do wesnoth.fire for whatever tag this way (tags that are defined nowhere / assigned a function to)? The message tag could be arbitrary action wml.
I'm not sure to understand what you mean, but the [command] tag is indeed superfluous. One can just as well execute all the subtags of [foreach]:

Code: Select all

for i = 1, #cfg do
  local cmd = cfg[i]
  wesnoth.fire(cmd[1], cmd[2])
end
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: Rewriting macros as tags

Post by Anonymissimus »

An implementation for [foreach], appears definitely nicer to me than the FOREACH macro, and supports continue and break. There is a lua error in wml_tags.lua for unknown action tags (like [invalid]).
No defaults for index and value, it might overwrite existing wml variables.
Spoiler:
Spoiler:
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
Exasperation
Posts: 462
Joined: June 8th, 2006, 3:25 am

Re: Rewriting macros as tags

Post by Exasperation »

I think you may be losing some capabilities here. Consider the following:

Code: Select all

{FOREACH foo f}
    {IF_VAR foo[$f].bar equals "baz" (
        [then]
            {CLEAR_VARIABLE foo[$f]}
            {VARIABLE_OP f sub 1}
        [/then]
    )}
{NEXT f}
I think this would break when used with your current code, and this pattern is actually useful in many cases.
User avatar
pauxlo
Posts: 1047
Joined: September 19th, 2006, 8:54 pm

Re: Rewriting macros as tags

Post by pauxlo »

Exasperation wrote:I think you may be losing some capabilities here. Consider the following:

Code: Select all

{FOREACH foo f}
    {IF_VAR foo[$f].bar equals "baz" (
        [then]
            {CLEAR_VARIABLE foo[$f]}
            {VARIABLE_OP f sub 1}
        [/then]
    )}
{NEXT f}
I think this would break when used with your current code, and this pattern is actually useful in many cases.
This pattern is not a real for-each-loop, but misuses the internal workings of the FOREACH-macro.
It may be useful, but there should be some special tag for "delete current element of foreach loop".
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: Rewriting macros as tags

Post by Anonymissimus »

Exasperation wrote:I think this would break when used with your current code
Surprisingly, it does not. In 1.9 there are implementations for while and switch too, in wml_tags.lua. In 1.8 I would expect that even the wml_actions[whatever_tag](cfg) doesn't work.

EDIT
Ah, the problem is modifying the array while looping over it. Doing that is critical in many languages however, there should actually be a sort of secured iterator for doing that.
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
Exasperation
Posts: 462
Joined: June 8th, 2006, 3:25 am

Re: Rewriting macros as tags

Post by Exasperation »

Yeah, that's my concern. I was just typing up a more thorough explanation when you edited your post. Modifying WML arrays in that fashion is often necessary for some of the more complicated stuff I work with, but I would be very surprised if WML arrays have anything even remotely approaching a secured iterator.
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: Rewriting macros as tags

Post by Anonymissimus »

Sorry for the wasted post, I hope it wasn't finished. ;)
Well, but even this modifying is supposed to work, since the wml array isn't touched by the lua script after retriving in the beginning. You can't modify the iterating index since it's resetted in the next step in the loop, but you don't need to.
There however seems to be a bug currently, but it's not my code I think.
http://gna.org/bugs/?16475
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
Exasperation
Posts: 462
Joined: June 8th, 2006, 3:25 am

Re: Rewriting macros as tags

Post by Exasperation »

The issue is that when you append/insert to or remove elements from a WML array the engine automatically re-indexes everything, but your code assumes that the original indices are still valid. So when you do {CLEAR_VARIABLE foo[$f]}, foo[$f] now references the array element that was immediately following the one you deleted, and incrementing f at the end of the loop skips over an array element that hasn't been considered yet. That's why the {VARIABLE_OP f sub 1} in my example is necessary; it adjusts the iterator so that when it gets incremented it now points to the element immediately following the deleted element, instead of the one two after it. Also, having deleted an element from the array causes array.length to be reduced by 1, but your code effectively uses a cached version of array.length for its termination condition, which will cause it to go past the end of the (now shortened) array.

Similarly, appending an element to the array will cause the loop to terminate before considering the new element. Inserting an element at or before the current index will cause the current element to be re-evaluated and an (old) element at the end to be ignored, while inserting an element after the current index but before the original end of the array will cause the new element to be evaluated but an old element to be ignored, and inserting an element after the original end of the array will be treated more or less the same as appending an element (new element ignored).

In some ways, it may help to think of WML arrays as being more like linked lists, with array[0] being the head and array[n] being what you get by traversing n steps down the list from the head. If you look at it like that, then the behavior of removing, inserting, or appending elements between FOREACH and NEXT macros is pretty straightforward (I know it's probably nothing like the implementation, but the behavior is similar).

Also, you get 'value' from the cached copy of the array, so if you have some situation where you modify array[<n>].foo (where <n> > index) during the loop then when you do get to index == <n> you will have $value.foo ~= $array[$index].foo; I don't know how intentional this is, since 'value' doesn't exist in the macro version.
Post Reply