[engine] Some thoughts about WML

Brainstorm ideas of possible additions to the game. Read this before posting!

Moderator: Forum Moderators

Forum rules
Before posting a new idea, you must read the following:
Post Reply
SlowThinker
Posts: 876
Joined: November 28th, 2008, 6:18 pm

[engine] Some thoughts about WML

Post by SlowThinker »

I don't think this thread follows all the rules specificated in [MUST-READ] About the Ideas Forum, it is rather an unrestrained theoreticizing at this moment.
But in past I discussed a similar kind of (different) thoughts about WML in the "WML workshop" forum and the thread was closed with a recommendation I should have posted here.
So if this is not the appropriate place then don't delete this thread without warning please and move it to the appropriate forum.

This thread comes from these talks

(Next if I say "Wesnoth engine works this way:" then I mean "Wesnoth engine behaves similarly like this algorithm:". In fact I don't know how the Wesnoth engine works, I never read its code.
The purpose of the following text is to introduce some ideas, not to explain details about the WML implementation)

(next any WML code is only theoretical, unless it is marked by '# this is a present WML syntax')


*****************************************************************
Post 1 : types of tags

--------------------------------------
tags-with-parameters
--------------------------------------

Let us look at this tag

Code: Select all

# this is a present WML syntax
[store_reachable_locations] 
	[filter]
		...
	[filter]
	[filter_location]
		...
	[filter_location]
	range=
	viewing_side=
	variable=
[/store_reachable_locations]

How is the code processed by the Wesnoth engine?
First the individual elements of [store_reachable_locations] (filter, filter_location, range, viewing_side, variable) are sub-processed, that is
  • attributes: special symbols ($, +, " ...) are processed; the result is a scalar value
  • tags: the result is a data structure (for example the result of [filter] is a list of units)
then the results of the subprocesses act as parameters of [store_reachable_locations]

So [store_reachable_locations] is a process and its elements are its parameters.
The subprocessing of the elements is isolated, the elements don't affect one another: if the tag [filter] is moved to any other part of the code then it will behave identically (i.e. if the game state will be identical then the result of the tag (= a list of units) will be identical too).
But the elements themselves are not isolated: after the subprocessing the Wesnoth engine must keep all the results so that they can act together as parameters.

Summary of tags-with-parameters:
  • the elements represent data, and the result of their subprocessing are data
  • the subprocessing of elements is isolated
  • the elements are not isolated: the results of the subprocessing (those data) are processed together
  • the order of the elements is not important (except arrays of course) (the elements are distinguished by their names: filter_location, range...)

--------------------------------------
tags-with-actions
--------------------------------------

Now let us look at this tag

Code: Select all

# this is a present WML syntax
[command]
	[set_variable]
		...
	[set_variable]
	[store_reachable_locations]
		...
	[store_reachable_locations]
	[another action WML]
		...
	[another action WML]
	...
[/command]
The elements of [command] are set_variable, store_reachable_locations etc.
Here the result of the subprocessing of the elements is performing of actions: the actions change the game state and/or provide info to players (messages, sounds ..). The elements are fully isolated - once the subtags are subprocessed they may be thrown away.

Summary of tags-with-actions:
  • the elements represent actions, and the result of their subprocessing are actions
  • the subprocessing of the elements is isolated
  • the elements are isolated
  • the order of the elements is important - they are subprocessed sequentially (therefore the tags don't need to be distinguished by their names and the names of tags determine the action type)


--------------------------------------
Some notes, tags-with-code
--------------------------------------
(less important)
Let us look at this code:

Code: Select all

# this is a present WML syntax
[event]
	name=something
	id=something
	..action..
	..action..
	...
[/event]
It looks elements of [event] are both parameters (name=, id=) and actions, but it is not so true. The notation above is rather a shortcut of the following code:

Code: Select all

# this is a present WML syntax
[event]
	name=something
	id=something
	[command]
		..action..
		..action..
		...
	[/command]
[/event]
In the code above [event] is clearly a tag-with-parameters. How is it processed?
Name=, id= are subprocessed normal way, but [command] does not act as a child tag-with-actions at this moment. Nothing happens with its elements during subprocessing and its content is only copied to a specific place (to the list of event handlers). Let us call this tag type a tag-with-code.
After the event will be fired it will act as a tag-with-actions though.

*************************************************************
Post 2: WML syntax should try to reflect the implementation

Now I will react to this post:
[url=http://forums.wesnoth.org/viewtopic.php?p=520140#p520140]here[/url] Anonymissimus wrote:... wml key values are variable substituted if the engine decides to do it and at the time it does so. It is arbitrary and doesn't follow a special logic. That causes the above discrepancies, it depends on the way wesnoth is coded
--------------------------------------------------------
At this moment I am still confused by the debate about name= in that thread, but let us suppose this code...

Code: Select all

# this is a present WML syntax
[event]
	id=
	name=
	delayed_variable_substitution=no
	...actions
[/event]
...works this way:

Code: Select all

[define_event]
	id=
	[event]
		name=
		delayed_variable_substitution=no
		...actions
	[/event]
[/define_event]
[event] is exactly the part of the code that is just variable substituted and then moved to the list of event handlers.
the last code reflects that
  • id= is fully processed when the event is created, and so the substitution in id= is fixed (i.e. unafected by delayed_...=) and "non-delayed"
  • the substitution in name= depends on delayed_...=

----------------------------------------------
SlowThinker wrote:the subprocessing of parameters is isolated
In fact this is not always true now.
delayed_variable_substitution= affects (some) sibling parameters, and so the subprocessing of parameters of [event] is not isolated. This causes a problem: WML has no way how to express such relationships between parameters, and so it is very unclear which parameters are affected by delayed_variable_substitution and which ones are not (see the link to Anonymissimus' post in the beginning of "Post 2").
However, for example this WML syntax would make things clear:

Code: Select all

[define_event]
	id=
	[event]
		name=
		...
		[non_delayed_variable_substitution]
			...
		[/non_delayed_variable_substitution]
		...
	[/event]
[/define_event]
([non_delayed_variable_substitution] is a kind of a tag-with-code)

In contrast to the present WML syntax, the moment of the variable substitution is not arbitrary and follows clear rules:
If the attribute is processed as a part of processing of a tag-with-parameters (i.e. its parent is a tag-with-parameters) then
  • the substitution is performed when the attribute is processed
  • the attribute is processed with other sibling elements (parameters) of the parent tag, i.e. just before the parent tag is processed
(If the attribute is processed as a part of processing of a tag-with-code then things are even simpler.)



**************************************************************
Post 3: action code everywhere

I am mising this feature of WML: a possibility to insert an action code everywhere and so to run auxiliary computations if needed. (a side note: if an action tag might be placed anywhere then also insert_tag might be placed anywhere - so it would be another positive effect)

An example: Let us at look at this code:

Code: Select all

# this is a present WML syntax
[message]
	[option]
		message=
		[command]
			...
		[command]
	[/option]
[/message]
Let us say we need to create a text within an [option] just before the option is shown to a player. So we want to be able to mix parameters with actions...

Code: Select all

[message]
	[option]
		... action tag that prepares the text into a variable
		... action tag that prepares the text into a variable
		...
		message= # the variable is used here
		[command]
			...
		[command]
	[/option]
[/message]
... and to assure the actions are subprocessed before message=. But this is easy once the subprocessing of parameters is isolated and so the order of subprocessing is arbitrary and so the applied order can always correspond to the order written within the [option] tag.
So the (sub)processing of the elements of [option] is simple and follows the principles described in Post 1. The elements are processed one by one; action tags may be thrown away right after they are processed, results of processing of parameters (parameter name + data) must be kept until [option] is processed.

details (not so important)
In the code above the action tags and message= belong to each other. Therefore next syntax may be more appropriate:

Code: Select all

[message]
	[option]
		[block]
			... action tag that prepares the text
			... action tag that prepares the text
			...
			message=
		[/block]
		[command]
		[command]
	[/option]
[/message]
[block] has no real effect, it is processed this way: its children are just placed in place of [block]...[/block].
The syntax above is longer than the previous one, but it has some advantages: not only it shows the logic of the code but also [block] may be inserted via insert_tag.
super-top-level event

Code: Select all

# this is a present WML syntax
[multiplayer]
	[side]
	[/side]
	[side]
	[/side]
	...
	[side]
	[/side]
[/multiplayer]
I would like to simulate this behaviour:

Code: Select all

[multiplayer]
	... action tags: count number of sides from map_data= and put the result in nr_of_sides
	{VARIABLE aux_side 1} 
	[while]
		{CONDITION aux_side less_than_equal_to nr_of_sides}
		[do]
			[side]
				color=$aux_side
				...
			[/side]

			{VARIABLE_OP aux_side add 1}
		[/do]
	[/while] 
[/multiplayer]
In fact this is possible with the new syntax I described in this thread ... so let's think about another solution:
Of course the code above would not be possible because [side] must be a children parameter of [multiplayer], but insert_tag will do the job:

Code: Select all

[multiplayer]
	... action tags: count number of sides from map_data= and put the result in nr_of_sides
	{VARIABLE aux_side 1} 
	[while]
		{CONDITION aux_side less_than_equal_to nr_of_sides}
		[do]
			[set_variables]
				name=array_of_sides.side[$aux_side]
				...
			[set_variables]
			{VARIABLE_OP aux_side add 1}
		[/do]
	[/while] 
	[insert_tag]
		name=block
		variable=array_of_sides
	[/insert_tag]
[/multiplayer]
... but ... the notation of the code above is rather complicated ...

Although [multiplayer] is a tag-with-parameters, things would be much easier if the parameters [side], [event] were considered as actions: [define_side], [define_event]
Then one could simply write:

Code: Select all

[multiplayer]
	{VARIABLE aux_side 1} 
	[while]
		{CONDITION aux_side less_than_equal_to nr_of_sides}
		[do]
			[define_side]
				...
			[/define_side]
			{VARIABLE_OP aux_side add 1}
		[/do]
	[/while] 
[/multiplayer]
But can [multiplayer] become a tag-with-actions? It can, because in terms of Post 1 its elements are isolated - the parameters don't act together but can act one by one.

Once [multiplayer] becomes a tag-with-actions (like [events] are), we get another positive effect:
Whole [multiplayer] tag may be considered as a super-top-level event with name=load. Then all the exceptions for nested / top-level events are gone, because now even "top-level" events have their parent.

********************************************************
post 4: Summary, addendum

I will stop now, my text is long enough :)

Summary:
It looks the WML syntax that has been described in previous posts would solve some of the mess and missing logic and would make WML more powerfull:
  • action tags (including insert_tag) are possible everywhere
  • the WML syntax reflects the implementation
  • the implementation is not so arbitrary and follows some basic rules
  • it is clear when a variable substitution happens
  • some exceptions are removed (like the difference between top-level and nested events)
Addendum: stamps
In the previous text I considered delayed_variable_substitution=no only because it is a common WML tool now.
I have serious reservations against it:
[url=http://forums.wesnoth.org/viewtopic.php?p=473260#p473260]in this post[/url] SlowThinker wrote:With "non-delayed" nested events the meaning of $var depends on the context.
So personally I would remove delayed_variable_substitution=no and replace it with 'stamps': a detailed proposition


---------------------------------------------------------
Edit, Mar 12, 2012 5:35 pm: I added the Addendum, and slightly polished the text.
Last edited by Gambit on March 12th, 2012, 2:14 pm, edited 1 time in total.
Reason: You've been here long enough to know our policies on double posting...
I work on Conquest Minus • I use DFoolWide, Retro Terrain Package and the add-on 'High Contrast Water'
I moved to Nosebane's corner (Doc Paterson's signature); I am spending my time there, so PM me if I don't answer your post in forums
Post Reply