GrammarWML

From The Battle for Wesnoth Wiki
Revision as of 06:57, 19 March 2026 by Celtic Minstrel (talk | contribs) (Context-free Grammars: Add new section covering Context-free grammar)

[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.

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} ;
digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ;
char = ?any unicode character? ;

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 = {char - '+' | '"' | ?newline?} ;
string = '"' . {char - '"' | '""'} , '"' ;
raw_string = '<<' , {char - '>' | '>' , char - '>'} , '>>' ;
textdomain = '#textdomain' domain_char , {domain_char} , ?newline? ;
wml_id_char = lowercase | uppercase | digit | '_' ;
lowercase = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm'
          | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' ;
uppercase = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M'
          | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' ;
digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ;
domain_char = wml_id_char | '-' ;
char = ?any unicode character? ;

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} , ']' ; (* as is digit *)
wml_var_default = '?' , default_char, {default_char} , '|' ;
default_char = char - '|' ;
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 = lowercase | uppercase | digit | '_' ; (* Unsure on this, need to check it *)
alternative = {terminal} ;
terminal = '{' , symbol , '}' | '{!}' | '{(}' | '{)}' | (?any unicode character? - '}') ;

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 = char - '#' ;
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 = lowercase | uppercase | '_' ; (* 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 = char - ("'" | "[") ;
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] , ')' ;