Difference between revisions of "GrammarWML"

From The Battle for Wesnoth Wiki
(Add an "under construction" notice)
(Progressive Values: Add new section covering AnimationWML's progressive values)
 
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
----
 
 
'''Note:''' This page is currently undergoing reconstruction. Information on the page may become misleading or occasionally wrong for brief periods of time.
 
 
----
 
 
 
{{WML Tags}}
 
{{WML Tags}}
This page contains a formal grammar of the Wesnoth domain-specific languages, including WML and its preprocessor. It does not attempt to capture any of the ways that the Wesnoth engine may interpret a string, such as WML variable substitution. It also doesn't fully capture the potential consequences of macros, for example the use of unbalanced WML tags. The syntax used is regular-expression-like (which is not quite the same as regex-like!), with the following conventions:
+
This page contains a formal grammar of the Wesnoth domain-specific languages, including WML and its preprocessor. It does not attempt to capture any of the ways that the Wesnoth engine may interpret a string, such as WML variable substitution. It also doesn't fully capture the potential consequences of macros, for example the use of unbalanced WML tags. The syntax used is [https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form Extended Backus-Naur form]:
  
* Literal values are enclosed in either 'single quotes' or "double quotes".
+
* Every rule is of the form <tt>nonterminal = rule ;</tt> with a terminating semicolon.
* Square brackets enclose character classes, with initial ^ inverting them
+
* Comments are enclosed in <tt>(* these *)</tt>.
* Whitespace within an expression (unless quoted) is used only for readability or to separate non-terminals
+
* Literal values are enclosed in either double or single quotes. There is no special syntax inside quotes.
* The meta-characters * + ? | have the same meaning as is typical in regular expressions
+
* <tt>(...)</tt> can be used for grouping.
* The sequence «tab» represents a tab character, and «nl» represents an end-of-line character or character sequence
+
* <tt>N * A</tt> where N is a number means "exactly N instances of A".
* Multiple definitions of a non-terminal are equivalent to alternation (ie, x:=4 and x:=7 combine to produce x:=4|7)
+
* <tt>[A]</tt> means "zero or one instances of A".
 +
* <tt>{A}</tt> means "zero or more instances of A".
 +
* <tt>A , B</tt> means "A followed by B".
 +
* <tt>A | B</tt> means "either A or B".
 +
* <tt>A - B</tt> means "A but not B".]
 +
* Some special values are enclosed in <tt>?...?</tt>. This document uses the following special values:
 +
** <tt>?newline?</tt>, <tt>?tab?</tt>, and <tt>?space?</tt> represent the named non-printable characters.
 +
** <tt>?unicode?</tt> represents any valid Unicode character.
 +
** <tt>?digit?</tt> represents any decimal digit.
 +
** <tt>?hexdigit?</tt> represents any hexadecimal digit.
 +
** <tt>?letter?</tt> represents any ASCII Latin letter in either uppercase or lowercase.
  
 
== WML Preprocessor ==
 
== WML Preprocessor ==
Line 19: Line 23:
 
The WML preprocessor knows little of the grammar of the WML language itself; it is primarily just a text-substitution engine. Currently this is just a draft and may not be entirely accurate.
 
The WML preprocessor knows little of the grammar of the WML language itself; it is primarily just a text-substitution engine. Currently this is just a draft and may not be entirely accurate.
  
preproc_doc := (preproc_directive | preproc_line)*
+
<syntaxhighlight lang=ebnf>
preproc_directive := simple_directive | macro_definition | if_block
+
preproc_doc = {preproc_directive | preproc_line} ;
preproc_line := (preproc_text | '<<' macro_free_text '>>' | macro_inclusion)* comment? «nl»
+
preproc_directive = simple_directive | macro_definition | if_block ;
preproc_text := (preproc_char | '<' preproc_char)* '<'?
+
preproc_line = {preproc_text} , ['<'] , [comment] , ?newline? ;
preproc_char := [^<{#«nl»]
+
preproc_text = preproc_char | '<' , preproc_char | '<<' , macro_free_text , '>>' | macro_inclusion ;
macro_free_text := (macro_free_char | '>' macro_free_char)*
+
preproc_char = char - ('<' | '{' | '#' | ?newline?) ;
macro_free_char := [^>]
+
macro_free_text = {macro_free_char | '>' , macro_free_char} ;
macro_inclusion := '{' ([^}]+ | macro_function) '}'
+
macro_free_char = char - '>' ; (* Note: this can include newlines! *)
macro_function := macro_name_char+ (macro_argument)*
+
macro_inclusion = '{' , macro_name , {req_ws , macro_argument} , '}' ;
macro_name_char := [^} «tab»]
+
macro_name = macro_name_char , {macro_name_char}
macro_argument := (macro_name_char | macro_inclusion)+
+
macro_name_char = char - ('}' | ws) ;
macro_argument := '(' preproc_doc? ')' | '_'? '"' ([^}"]
+
macro_argument = macro_component , {macro_component}
macro_argument := ('""' | macro_inclusion)* '"'
+
macro_component = macro_name
macro_argument := '<<' macro_free_text '>>'
+
                | macro_inclusion
comment := '#' [^«nl»]+ «nl»
+
                | '(' , [preproc_doc] , ')'
ws := ' ' | «tab»
+
                | ['_'] , opt_ws , '"' , {quoted_char | macro_inclusion} , '"'
simple_directive := '#undef' ws+ macro_name_char+ ws* «nl»
+
                | '<<' , macro_free_text , '>>'
simple_directive := ('#warning' | '#error') ws+ [^«nl»]* «nl»
+
                ;
macro_definition := '#define' ws+ macro_name_char+ (ws+ macro_name_char+)* «nl» (opt_arg_definition)* (simple_directive | if_block | preproc_line)+ '#enddef' «nl»
+
quoted_char = char - ( '}' | '"' ) | '""' ;
opt_arg_definition := '#arg' ws+ macro_name_char+ «nl» (simple_directive | if_block | preproc_line)+ '#endarg' «nl»
+
comment = opt_ws , '#' , {comment_char} , ?newline? ;
if_block := (ifdef_header | ifver_header | ifhave_header) «nl» preproc_doc ('#else' «nl» preproc_doc)? '#endif' «nl»
+
comment_char = char - ?newline? ;
ifdef_header := ('#ifdef' | '#ifndef') ws+ macro_name_char+
+
ws = ?space? | ?tab?
ifver_header := ('#ifver' | '#ifnver') ws+ macro_name_char+ ws* comparison_op ws* version_string
+
opt_ws = {ws}
ifhave_header := ('#ifhave' | '#ifnhave') ws+ [^«nl»]+
+
req_ws = ws , {ws}
comparison_op := '<' | '<=' | '==' | '!=' | '>=' | '>'
+
simple_directive = '#undef' , req_ws , macro_name , opt_ws , ?newline?
version_string := integer ('.' integer)*
+
                | ('#warning' | '#error') req_ws , {comment_char} , ?newline? ;
integer := [0-9]+
+
macro_definition = '#define' , req_ws , macro_name , {req_ws macro_name} , ?newline? , {opt_arg_definition} , {macro_content} , '#enddef' , ?newline? ;
 +
macro_content = simple_directive | if_block | preproc_line ;
 +
opt_arg_definition = '#arg' , req_ws , macro_name , ?newline? , {macro_content} , '#endarg' , ?newline? ;
 +
if_block = (ifdef_header | ifver_header | ifhave_header) , ?newline? , preproc_doc ['#else' , ?newline? , preproc_doc] '#endif' , ?newline? ;
 +
ifdef_header = ('#ifdef' | '#ifndef') , req_ws , macro_name ;
 +
ifver_header = ('#ifver' | '#ifnver') , req_ws , macro_name , opt_ws , comparison_op , opt_ws , version_string ;
 +
ifhave_header = ('#ifhave' | '#ifnhave') , req_ws , comment_char ;
 +
comparison_op = '<' | '<=' | '==' | '!=' | '>=' | '>' ;
 +
version_string = integer , ['.' , integer] ;
 +
integer = ?digit? , {?digit?} ;
 +
</syntaxhighlight>
  
 
== WML ==
 
== WML ==
Line 51: Line 65:
 
This grammar describes WML '''after''' the preprocessor is finished with it, and as such does not account for macros, preprocessor directives, or comments. It also assumes tokenization has already occurred and thus does not specify whitespace, except for newlines. Note that it omits the requirement for opening and closing tags to match.
 
This grammar describes WML '''after''' the preprocessor is finished with it, and as such does not account for macros, preprocessor directives, or comments. It also assumes tokenization has already occurred and thus does not specify whitespace, except for newlines. Note that it omits the requirement for opening and closing tags to match.
  
wml_doc := (wml_tag | wml_attribute)*
+
<syntaxhighlight lang=ebnf>
wml_tag := '[' '+'? wml_name ']' wml_doc '[/' wml_name ']'
+
wml_doc = {wml_tag | wml_attribute} ;
wml_name := [a-zA-Z0-9_]+
+
wml_tag = '[' , ['+'] , wml_name , ']' , wml_doc , '[/' , wml_name , ']' ;
wml_attribute := textdomain? wml_key_sequence '=' wml_value «nl»
+
wml_name = wml_id_char , {wml_id_char} ;
wml_key_sequence := wml_name (',' wml_name)*
+
wml_attribute = [textdomain] , wml_key_sequence , '=' , wml_value , ?newline? ;
wml_value := wml_value_component ('+' («nl» textdomain?)? wml_value_component)*
+
wml_key_sequence = wml_name , {',' , wml_name} ;
wml_value_component := text | '_'? string | '_'? raw_string
+
wml_value = wml_value_component , {'+' , [?newline? , [textdomain]] , wml_value_component} ;
+
wml_value_component = text | ['_'] , (string | raw_string) ;
text := [^+«nl»"]*
+
text = {?unicode? - '+' | '"' | ?newline?} ;
string := '"' ([^"] | '""')* '"'
+
string = '"' . {?unicode? - '"' | '""'} , '"' ;
raw_string := '<<' ([^>] | >[^>])* '>>'
+
raw_string = '<<' , {?unicode? - '>' | '>' , ?unicode? - '>'} , '>>' ;
textdomain := '#textdomain' [a-zA-Z0-9_-]+ «nl»
+
textdomain = '#textdomain' domain_char , {domain_char} , ?newline? ;
 +
wml_id_char = ?letter? | ?digit? | '_' ;
 +
domain_char = wml_id_char | '-' ;
 +
</syntaxhighlight>
  
 
== WML Substitutions ==
 
== WML Substitutions ==
Line 68: Line 85:
 
This grammar describes the syntax of WML substitutions, the syntax used to specify that variables should be substituted into the value of a WML attribute. The grammar here describes a single placeholder, without regard to the fact that they can be nested. Thus, parsing a string using this grammar would only succeed if done from right to left ''while'' performing the substitutions.
 
This grammar describes the syntax of WML substitutions, the syntax used to specify that variables should be substituted into the value of a WML attribute. The grammar here describes a single placeholder, without regard to the fact that they can be nested. Thus, parsing a string using this grammar would only succeed if done from right to left ''while'' performing the substitutions.
  
wml_substitution := wml_var | wml_formula | '$|'
+
<syntaxhighlight lang=ebnf>
wml_var := '$' wml_var_path (wml_var_default | '|')?
+
wml_substitution = wml_var | wml_formula | '$|' ;
wml_var_path := (wml_var_name wml_var_index? '.')* wml_var_name
+
wml_var = '$' , wml_var_path , [wml_var_default | '|'] ;
wml_var_name := [a-zA-Z0-9_]+
+
wml_var_path = {wml_var_name , [wml_var_index] , '.'} , wml_var_name ;
wml_var_index := '[' [0-9]+ ']'
+
wml_var_name = wml_id_char, {wml_id_char} ; (* wml_id_char is defined in the WML grammar, above *)
wml_var_default := '?' [^|]+ '|'
+
wml_var_index = '[' , ?digit? , {?digit?} , ']' ;
wml_formula := '$' '(' wfl_document ')'
+
wml_var_default = '?' , default_char, {default_char} , '|' ;
 +
default_char = ?unicode? - '|' ;
 +
wml_formula = '$' , '(' , wfl_document , ')' ;
 +
</syntaxhighlight>
 +
 
 +
== Context-free Grammars ==
 +
 
 +
This grammar describes the syntax of a [[Context-free grammar]] used for random text generation.
 +
 
 +
<syntaxhighlight lang=ebnf>
 +
grammar = production , {production} ;
 +
production = symbol , '=' , alternative , {'|' , alternative} , ?newline? ;
 +
symbol = symbol_char , {symbol_char} ;
 +
symbol_char = ?letter? | ?digit? | '_' ; (* Unsure on this, need to check it *)
 +
alternative = {terminal} ;
 +
terminal = '{' , symbol , '}' | '{!}' | '{(}' | '{)}' | (?unicode? - '}') ;
 +
</syntaxhighlight>
  
 
== Wesnoth Formula Language ==
 
== Wesnoth Formula Language ==
Line 80: Line 113:
 
This grammar describes the syntax of the [[Wesnoth Formula Language]]. Though it specifies the format of comments and file markers, they are not integrated into the main grammar since it treats the equivalently to whitespace. The grammar may not be completely accurate to the actual in-game parser.
 
This grammar describes the syntax of the [[Wesnoth Formula Language]]. Though it specifies the format of comments and file markers, they are not integrated into the main grammar since it treats the equivalently to whitespace. The grammar may not be completely accurate to the actual in-game parser.
  
wfl_comment := '#' [^#]* '#'
+
<syntaxhighlight lang=ebnf>
wfl_file_run := 'wfl' wfl_string «any token»* 'wflend'
+
wfl_comment = '#' , {wfl_comment_char} , '#' ;
wfl_document := wfl_function_definition* wfl_formula
+
wfl_comment_char = ?unicode? - '#' ;
wfl_function_definition := 'def' wfl_name '(' wfl_function_args ')' wfl_formula ';'
+
wfl_file_run = 'wfl' , wfl_string , wfl_file_content , 'wflend' ;
wfl_function_args := (wfl_function_arg (',' wfl_function_arg)*)?
+
wfl_file_content = {?any token? - 'wflend'} ;
wfl_function_arg := wfl_name '*'?
+
wfl_document = {wfl_function_definition} , wfl_formula ;
wfl_formula := 'not'? where_expression
+
wfl_function_definition = 'def' , wfl_name , '(' , [wfl_function_args] , ')' , wfl_formula , ';' ;
wfl_formula := bracketed_expression
+
wfl_function_args = wfl_function_arg , {',' , wfl_function_arg} ;
bracketed_expression := '(' wfl_formula ')'
+
wfl_function_arg = wfl_name , ['*'] ;
where_expression := (boolean_or_expression | bracketed_expression) ('where' wfl_variables)*
+
wfl_formula = ['not'] , where_expression | bracketed_expression ;
wfl_variables := wfl_variable (',' wfl_variable)*
+
bracketed_expression = '(' , wfl_formula , ')' ;
wfl_variable := wfl_name '=' wfl_formula
+
where_expression = (boolean_or_expression | bracketed_expression) , {'where' , wfl_variables} ;
boolean_or_expression := (boolean_and_expression | bracketed_expression) ('or' wfl_formula)*
+
wfl_variables = wfl_variable , {',' , wfl_variable} ;
boolean_and_expression := (comparison_expression | bracketed_expression) ('and' wfl_formula)*
+
wfl_variable = wfl_name , '=' , wfl_formula ;
comparison_expression := (containment_expression | bracketed_expression) (comparison_op wfl_formula)*
+
boolean_or_expression = (boolean_and_expression | bracketed_expression) , {'or' , wfl_formula} ;
comparison_op := '=' | '!=' | '<' | '>' | '<=' | '>='
+
boolean_and_expression = (comparison_expression | bracketed_expression) , {'and' , wfl_formula} ;
containment_expression := (range_expression | bracketed_expression) ('in' wfl_formula)*
+
comparison_expression = (containment_expression | bracketed_expression) , {comparison_op , wfl_formula} ;
range_expression := (additive_expression | bracketed_expression) ('~' wfl_formula)*
+
comparison_op = '=' | '!=' | '<' | '>' | '<=' | '>=' ;
additive_expression := negation_opt? (multiplicative_expression | bracketed_expression) (additive_op wfl_formula)*
+
containment_expression = (range_expression | bracketed_expression) , {'in' , wfl_formula} ;
negation_op := '-' | '+'
+
range_expression = (additive_expression | bracketed_expression) , {'~' , wfl_formula} ;
additive_op := '-' | '+' | '..'
+
additive_expression = [negation_op] , (multiplicative_expression | bracketed_expression) , {additive_op , wfl_formula} ;
multiplicative_expression := (exponent_expression | bracketed_expression) (multiplicative_op wfl_formula)*
+
negation_op = '-' | '+' ;
muliplicative_op := '*' | '/' | '%'
+
additive_op = '-' | '+' | '..' ;
exponent_expression := (wfl_formula '^')* (dice_expression | bracketed_expression)
+
multiplicative_expression := (exponent_expression | bracketed_expression) , {multiplicative_op , wfl_formula} ;
dice_expression := (dot_expression | bracketed_expression) ('d' wfl_formula)*
+
muliplicative_op = '*' | '/' | '%' ;
dot_expression := (wfl_value | bracketed_expression) ('.' wfl_formula)*
+
exponent_expression = {wfl_formula , '^'} , (dice_expression | bracketed_expression) ;
wfl_value := 'functions' | wfl_name | wfl_number | wfl_string | wfl_container | wfl_function_call
+
dice_expression = (dot_expression | bracketed_expression) , {'d' , wfl_formula} ;
wfl_name := [a-zA-Z_]+
+
dot_expression = (wfl_value | bracketed_expression) , {'.' , wfl_formula} ;
wfl_number := [0-9]+ ('.' [0-9]+)?
+
wfl_value = 'functions' | wfl_name | wfl_number | wfl_string | wfl_container | wfl_function_call ;
wfl_string := "'" ([^'[]+ | wfl_string_subst | wfl_string_escape)* "'"
+
wfl_name = wfl_id_char , {wfl_id_char} ;
wfl_string_subst := '[' wfl_formula ']'
+
wfl_id_char = ?letter? | '_' ; (* Note: digits are NOT allowed! *)
wfl_string_escape := "[']" | '[(]' | '[)]'
+
wfl_number = ?digit? , {?digit?} , ['.' , ?digit? , {?digit?}] ;
wfl_container := '[' ('->' | wfl_expression_list | wfl_key_value_list)? ']'
+
wfl_string = "'" , {wfl_string_char | wfl_string_subst | wfl_string_escape} , "'" ;
wfl_expression_list := wfl_formula (',' wfl_formula)*
+
wfl_string_char = ?unicode? - ("'" | "[") ;
wfl_key_value_list := wfl_formula '->' wfl_formula (',' wfl_formula '->' wfl_formula)*
+
wfl_string_subst = '[' , wfl_formula , ']' ;
wfl_function_call := wfl_name '(' wfl_expression_list? ')'
+
wfl_string_escape = "[']" | '[(]' | '[)]' ;
 +
wfl_container = '[' , ['->' | wfl_expression_list | wfl_key_value_list] , ']' ;
 +
wfl_expression_list = wfl_formula , {',' , wfl_formula} ;
 +
wfl_key_value_list = wml_key_value , {',' , wml_key_value} ;
 +
wml_key_value = wfl_formula , '->' , wfl_formula ;
 +
wfl_function_call = wfl_name , '(' , [wfl_expression_list] , ')' ;
 +
</syntaxhighlight>
 +
 
 +
== Help Markup ==
 +
 
 +
This grammar defines the [[help markup]] used in Wesnoth help topics and GUI2 rich labels. It's ''almost'' HTML and ''almost'' XMl but not quite either.
 +
 
 +
<syntaxhighlight lang=ebnf>
 +
document = {text | tag} ;
 +
text = {literal | entity | escape} ;
 +
literal = ?unicode? - ('<' , '&' , '\') ;
 +
escape = '\' , ?unicode? ;
 +
entity = '&#' , ?digit? , {?digit?} , ';'
 +
      | '&#x' , ?hexdigit? , {?hexdigit?} , ';'
 +
      | '&' , name , ';'
 +
      ;
 +
tag = '<' , name , {attribute} , '/' '>' (* Self-closing tag, XML-style *)
 +
    | '<' , name , {attribute} , '>' , document , '</' , name , '>' (* HTML-style tag, nestable *)
 +
    | '<' , name , '>' , {attribute} , [text] , '</' , name , '>' (* Old-style tag, not nestable *)
 +
    ;
 +
attribute = name , ['=' , attribute_value] ;
 +
attribute_value = {?unicode? - ("'" | '"' | ?space?)} (* Unquoted value *)
 +
                | "'" , text - "'" , "'"
 +
                | '"' , text - '"' , '"'
 +
                ;
 +
name = name_char , {name_char} ;
 +
name_char = ?letter? | ?digit? | '_'
 +
</syntaxhighlight>
 +
 
 +
== Progressive Values ==
 +
 
 +
This grammar describes progressive strings and numbers used by animation frames.
 +
 
 +
<syntaxhighlight lang=ebnf>
 +
range_integer = digits , ['~' , digits] , [timing] ;
 +
progressive_integer = range_integer , {',' , range_integer} ;
 +
 
 +
range_real = digits , ['.' , digits] , ['~' , digits , [',' , digits]] , [timing] ;
 +
progressive_real = range_real , {',' , range_real} ;
 +
 
 +
range_string = {literal | expansion_list} , [timing | ':' , expansion_list] ;
 +
progressive_string = range_string , {',' , range_string} ;
 +
literal = ?unicode? - ('[' | ',' | ':') ; (* Colon actually might be permitted, not sure *)
 +
expansion_list = '[' , expansion , {',' , expansion} , ']' ;
 +
expansion = range_integer | digits , '*' , digits ;
 +
 
 +
digits = digit , {digit} ;
 +
timing = ':' , digits ;
 +
</syntaxhighlight>
  
 
[[Category:WML Reference]]
 
[[Category:WML Reference]]

Latest revision as of 07:45, 19 March 2026

[edit]WML Tags

A:

abilities, about, achievement, achievement_group, add_ai_behavior, advanced_preference, advancefrom, advancement, advances, affect_adjacent, ai, allied_with, allow_end_turn, allow_extra_recruit, allow_recruit, allow_undo, and, animate_unit, animation, aspect, attack (replay, weapon), attack_anim, attacks (special, stats), avoid;

B:

base_unit, background_layer, berserk, binary_path, break, brush;

C:

campaign, cancel_action, candidate_action, capture_village, case, chance_to_hit, change_theme, chat, checkbox, choice, choose, clear_global_variable, clear_menu_item, clear_variable, color_adjust, color_palette, color_range, command (action, replay), continue, core, credits_group, criteria;

D:

damage, damage_type, death, deaths, default, defend, defends, defense, delay, deprecated_message, destination, difficulty, disable, disallow_end_turn, disallow_extra_recruit, disallow_recruit, do, do_command, drains, draw_weapon_anim;

E:

editor_group, editor_music, editor_times, effect, else (action, animation), elseif, endlevel, end_turn (action, replay), enemy_of, engine, entry (credits, options), era, event, experimental_filter_ability, experimental_filter_ability_active, experimental_filter_specials, extra_anim;

F:

facet, facing, fake_unit, false, feedback, female, filter (concept, event), filter_adjacent, filter_adjacent_location, filter_attack, filter_attacker, filter_base_value, filter_condition, filter_defender, filter_enemy, filter_location, filter_opponent, filter_own, filter_owner, filter_radius, filter_recall, filter_second, filter_second_attack, filter_self, filter_side, filter_student, filter_vision, filter_weapon, filter_wml, find_path, fire_event, firststrike, floating_text, fonts, for, foreach, found_item, frame;

G:

game_config, get_global_variable, goal, gold, gold_carryover;

H:

harm_unit, has_ally, has_attack, has_unit, has_achievement, have_location, have_side, have_unit, heal_on_hit, heal_unit, healed_anim, healing_anim, heals, hide_help, hide_unit, hides;

I:

idle_anim, if (action, animation, intro), illuminates, image (intro, terrain), init_side, insert_tag, inspect, item, item_group;

J:

jamming_costs, join;

K:

kill, killed;

L:

label, language, leader, leader_goal, leadership, leading_anim, levelin_anim, levelout_anim, lift_fog, limit, literal, load_resource, locale, lock_view, lua;

M:

male, map_data, message, micro_ai, missile_frame, modification, modifications, modify_ai, modify_side, modify_turns, modify_unit, modify_unit_type, move, move_unit, move_unit_fake, move_units_fake, movement_anim, movement costs, movetype, multiplayer, multiplayer_side, music;

N:

not, note;

O:

object, objective, objectives, on_undo, open_help, option, options, or;

P:

part, petrifies, petrify, place_shroud, plague, poison, post_movement_anim, pre_movement_anim, primary_attack, primary_unit, print, progress_achievement, put_to_recall_list;

R:

race, random_placement, recall (action, replay), recalls, recruit, recruit_anim, recruiting_anim, recruits, redraw, regenerate, remove_event, remove_item, remove_object, remove_shroud, remove_sound_source, remove_time_area, remove_trait, remove_unit_overlay, repeat, replace_map, replace_schedule, replay, reset_fog, resistance (ability, unit), resistance_defaults, resolution, resource, return, role, rule;

S:

scenario, screen_fade, scroll, scroll_to, scroll_to_unit, secondary_attack, secondary_unit, section, select_unit, sequence, set_achievement, set_extra_recruit, set_global_variable, set_menu_item, set_recruit, set_specials, set_variable, set_variables, sheath_weapon_anim, show_if (message, objective, set_menu_item), show_objectives, side, skirmisher, slider, slow, sound, sound_source, source (replay, teleport), special_note, specials, split, stage, standing_anim, statistics, status, store_gold, store_items, store_locations, store_map_dimensions, store_reachable_locations, store_relative_direction, store_side, store_starting_location, store_time_of_day, store_turns, store_unit, store_unit_defense, store_unit_defense_on, store_unit_type, store_unit_type_ids, store_villages, story, swarm, sub_achievement, switch, sync_variable;

T:

target, team, teleport (ability, action), teleport_anim, terrain, terrain_defaults, terrain_graphics, terrain_mask, terrain_type, test, test_condition, test_do_attack_by_id, text_input, textdomain, theme, then, tile, time, time_area, topic, toplevel, trait, transform_unit, traveler, true, tunnel;

U:

unhide_unit, unit (action, scenario), unit_overlay, unit_type, unit_worth, units, unlock_view, unpetrify, unstore_unit, unsynced;

V:

value, variable, variables, variant, variation, victory_anim, village, vision_costs, volume;

W:

while, wml_message, wml_schema;

Z:

zoom;

This page contains a formal grammar of the Wesnoth domain-specific languages, including WML and its preprocessor. It does not attempt to capture any of the ways that the Wesnoth engine may interpret a string, such as WML variable substitution. It also doesn't fully capture the potential consequences of macros, for example the use of unbalanced WML tags. The syntax used is Extended Backus-Naur form:

  • Every rule is of the form nonterminal = rule ; with a terminating semicolon.
  • Comments are enclosed in (* these *).
  • Literal values are enclosed in either double or single quotes. There is no special syntax inside quotes.
  • (...) can be used for grouping.
  • N * A where N is a number means "exactly N instances of A".
  • [A] means "zero or one instances of A".
  • {A} means "zero or more instances of A".
  • A , B means "A followed by B".
  • A | B means "either A or B".
  • A - B means "A but not B".]
  • Some special values are enclosed in ?...?. This document uses the following special values:
    • ?newline?, ?tab?, and ?space? represent the named non-printable characters.
    • ?unicode? represents any valid Unicode character.
    • ?digit? represents any decimal digit.
    • ?hexdigit? represents any hexadecimal digit.
    • ?letter? represents any ASCII Latin letter in either uppercase or lowercase.

WML Preprocessor

The WML preprocessor knows little of the grammar of the WML language itself; it is primarily just a text-substitution engine. Currently this is just a draft and may not be entirely accurate.

preproc_doc = {preproc_directive | preproc_line} ;
preproc_directive = simple_directive | macro_definition | if_block ;
preproc_line = {preproc_text} , ['<'] , [comment] , ?newline? ;
preproc_text = preproc_char | '<' , preproc_char | '<<' , macro_free_text , '>>' | macro_inclusion ;
preproc_char = char - ('<' | '{' | '#' | ?newline?) ;
macro_free_text = {macro_free_char | '>' , macro_free_char} ;
macro_free_char = char - '>' ; (* Note: this can include newlines! *)
macro_inclusion = '{' , macro_name , {req_ws , macro_argument} , '}' ;
macro_name = macro_name_char , {macro_name_char}
macro_name_char = char - ('}' | ws) ;
macro_argument = macro_component , {macro_component}
macro_component = macro_name
                | macro_inclusion
                | '(' , [preproc_doc] , ')'
                | ['_'] , opt_ws , '"' , {quoted_char | macro_inclusion} , '"'
                | '<<' , macro_free_text , '>>'
                ;
quoted_char = char - ( '}' | '"' ) | '""' ;
comment = opt_ws , '#' , {comment_char} , ?newline? ;
comment_char = char - ?newline? ;
ws = ?space? | ?tab?
opt_ws = {ws}
req_ws = ws , {ws}
simple_directive = '#undef' , req_ws , macro_name , opt_ws , ?newline?
                 | ('#warning' | '#error') req_ws , {comment_char} , ?newline? ;
macro_definition = '#define' , req_ws , macro_name , {req_ws macro_name} , ?newline? , {opt_arg_definition} , {macro_content} , '#enddef' , ?newline? ;
macro_content = simple_directive | if_block | preproc_line ;
opt_arg_definition = '#arg' , req_ws , macro_name , ?newline? , {macro_content} , '#endarg' , ?newline? ;
if_block = (ifdef_header | ifver_header | ifhave_header) , ?newline? , preproc_doc ['#else' , ?newline? , preproc_doc] '#endif' , ?newline? ;
ifdef_header = ('#ifdef' | '#ifndef') , req_ws , macro_name ;
ifver_header = ('#ifver' | '#ifnver') , req_ws , macro_name , opt_ws , comparison_op , opt_ws , version_string ;
ifhave_header = ('#ifhave' | '#ifnhave') , req_ws , comment_char ;
comparison_op = '<' | '<=' | '==' | '!=' | '>=' | '>' ;
version_string = integer , ['.' , integer] ;
integer = ?digit? , {?digit?} ;

WML

This grammar describes WML after the preprocessor is finished with it, and as such does not account for macros, preprocessor directives, or comments. It also assumes tokenization has already occurred and thus does not specify whitespace, except for newlines. Note that it omits the requirement for opening and closing tags to match.

wml_doc = {wml_tag | wml_attribute} ;
wml_tag = '[' , ['+'] , wml_name , ']' , wml_doc , '[/' , wml_name , ']' ;
wml_name = wml_id_char , {wml_id_char} ;
wml_attribute = [textdomain] , wml_key_sequence , '=' , wml_value , ?newline? ;
wml_key_sequence = wml_name , {',' , wml_name} ;
wml_value = wml_value_component , {'+' , [?newline? , [textdomain]] , wml_value_component} ;
wml_value_component = text | ['_'] , (string | raw_string) ;
text = {?unicode? - '+' | '"' | ?newline?} ;
string = '"' . {?unicode? - '"' | '""'} , '"' ;
raw_string = '<<' , {?unicode? - '>' | '>' , ?unicode? - '>'} , '>>' ;
textdomain = '#textdomain' domain_char , {domain_char} , ?newline? ;
wml_id_char = ?letter? | ?digit? | '_' ;
domain_char = wml_id_char | '-' ;

WML Substitutions

This grammar describes the syntax of WML substitutions, the syntax used to specify that variables should be substituted into the value of a WML attribute. The grammar here describes a single placeholder, without regard to the fact that they can be nested. Thus, parsing a string using this grammar would only succeed if done from right to left while performing the substitutions.

wml_substitution = wml_var | wml_formula | '$|' ;
wml_var = '$' , wml_var_path , [wml_var_default | '|'] ;
wml_var_path = {wml_var_name , [wml_var_index] , '.'} , wml_var_name ;
wml_var_name = wml_id_char, {wml_id_char} ; (* wml_id_char is defined in the WML grammar, above *)
wml_var_index = '[' , ?digit? , {?digit?} , ']' ;
wml_var_default = '?' , default_char, {default_char} , '|' ;
default_char = ?unicode? - '|' ;
wml_formula = '$' , '(' , wfl_document , ')' ;

Context-free Grammars

This grammar describes the syntax of a Context-free grammar used for random text generation.

grammar = production , {production} ;
production = symbol , '=' , alternative , {'|' , alternative} , ?newline? ;
symbol = symbol_char , {symbol_char} ;
symbol_char = ?letter? | ?digit? | '_' ; (* Unsure on this, need to check it *)
alternative = {terminal} ;
terminal = '{' , symbol , '}' | '{!}' | '{(}' | '{)}' | (?unicode? - '}') ;

Wesnoth Formula Language

This grammar describes the syntax of the Wesnoth Formula Language. Though it specifies the format of comments and file markers, they are not integrated into the main grammar since it treats the equivalently to whitespace. The grammar may not be completely accurate to the actual in-game parser.

wfl_comment = '#' , {wfl_comment_char} , '#' ;
wfl_comment_char = ?unicode? - '#' ;
wfl_file_run = 'wfl' , wfl_string , wfl_file_content , 'wflend' ;
wfl_file_content = {?any token? - 'wflend'} ;
wfl_document = {wfl_function_definition} , wfl_formula ;
wfl_function_definition = 'def' , wfl_name , '(' , [wfl_function_args] , ')' , wfl_formula , ';' ;
wfl_function_args = wfl_function_arg , {',' , wfl_function_arg} ;
wfl_function_arg = wfl_name , ['*'] ;
wfl_formula = ['not'] , where_expression | bracketed_expression ;
bracketed_expression = '(' , wfl_formula , ')' ;
where_expression = (boolean_or_expression | bracketed_expression) , {'where' , wfl_variables} ;
wfl_variables = wfl_variable , {',' , wfl_variable} ;
wfl_variable = wfl_name , '=' , wfl_formula ;
boolean_or_expression = (boolean_and_expression | bracketed_expression) , {'or' , wfl_formula} ;
boolean_and_expression = (comparison_expression | bracketed_expression) , {'and' , wfl_formula} ;
comparison_expression = (containment_expression | bracketed_expression) , {comparison_op , wfl_formula} ;
comparison_op = '=' | '!=' | '<' | '>' | '<=' | '>=' ;
containment_expression = (range_expression | bracketed_expression) , {'in' , wfl_formula} ;
range_expression = (additive_expression | bracketed_expression) , {'~' , wfl_formula} ;
additive_expression = [negation_op] , (multiplicative_expression | bracketed_expression) , {additive_op , wfl_formula} ;
negation_op = '-' | '+' ;
additive_op = '-' | '+' | '..' ;
multiplicative_expression := (exponent_expression | bracketed_expression) , {multiplicative_op , wfl_formula} ;
muliplicative_op = '*' | '/' | '%' ;
exponent_expression = {wfl_formula , '^'} , (dice_expression | bracketed_expression) ;
dice_expression = (dot_expression | bracketed_expression) , {'d' , wfl_formula} ;
dot_expression = (wfl_value | bracketed_expression) , {'.' , wfl_formula} ;
wfl_value = 'functions' | wfl_name | wfl_number | wfl_string | wfl_container | wfl_function_call ;
wfl_name = wfl_id_char , {wfl_id_char} ;
wfl_id_char = ?letter? | '_' ; (* Note: digits are NOT allowed! *)
wfl_number = ?digit? , {?digit?} , ['.' , ?digit? , {?digit?}] ;
wfl_string = "'" , {wfl_string_char | wfl_string_subst | wfl_string_escape} , "'" ;
wfl_string_char = ?unicode? - ("'" | "[") ;
wfl_string_subst = '[' , wfl_formula , ']' ;
wfl_string_escape = "[']" | '[(]' | '[)]' ;
wfl_container = '[' , ['->' | wfl_expression_list | wfl_key_value_list] , ']' ;
wfl_expression_list = wfl_formula , {',' , wfl_formula} ;
wfl_key_value_list = wml_key_value , {',' , wml_key_value} ;
wml_key_value = wfl_formula , '->' , wfl_formula ;
wfl_function_call = wfl_name , '(' , [wfl_expression_list] , ')' ;

Help Markup

This grammar defines the help markup used in Wesnoth help topics and GUI2 rich labels. It's almost HTML and almost XMl but not quite either.

document = {text | tag} ;
text = {literal | entity | escape} ;
literal = ?unicode? - ('<' , '&' , '\') ;
escape = '\' , ?unicode? ;
entity = '&#' , ?digit? , {?digit?} , ';'
       | '&#x' , ?hexdigit? , {?hexdigit?} , ';'
       | '&' , name , ';'
       ;
tag = '<' , name , {attribute} , '/' '>' (* Self-closing tag, XML-style *)
    | '<' , name , {attribute} , '>' , document , '</' , name , '>' (* HTML-style tag, nestable *)
    | '<' , name , '>' , {attribute} , [text] , '</' , name , '>' (* Old-style tag, not nestable *)
    ;
attribute = name , ['=' , attribute_value] ;
attribute_value = {?unicode? - ("'" | '"' | ?space?)} (* Unquoted value *)
                | "'" , text - "'" , "'"
                | '"' , text - '"' , '"'
                ;
name = name_char , {name_char} ;
name_char = ?letter? | ?digit? | '_'

Progressive Values

This grammar describes progressive strings and numbers used by animation frames.

range_integer = digits , ['~' , digits] , [timing] ;
progressive_integer = range_integer , {',' , range_integer} ;

range_real = digits , ['.' , digits] , ['~' , digits , [',' , digits]] , [timing] ;
progressive_real = range_real , {',' , range_real} ;

range_string = {literal | expansion_list} , [timing | ':' , expansion_list] ;
progressive_string = range_string , {',' , range_string} ;
literal = ?unicode? - ('[' | ',' | ':') ; (* Colon actually might be permitted, not sure *)
expansion_list = '[' , expansion , {',' , expansion} , ']' ;
expansion = range_integer | digits , '*' , digits ;

digits = digit , {digit} ;
timing = ':' , digits ;
This page was last edited on 19 March 2026, at 07:45.