Implement [break], [continue], [return] tags (they take no arguments)
This commit is contained in:
parent
907d527cc3
commit
3c329408ce
6 changed files with 199 additions and 12 deletions
|
@ -275,9 +275,11 @@ function wml_actions.music(cfg)
|
|||
wesnoth.set_music(cfg)
|
||||
end
|
||||
|
||||
wml_actions.command = utils.handle_event_commands
|
||||
function wml_actions.command(cfg)
|
||||
utils.handle_event_commands(cfg, "plain")
|
||||
end
|
||||
|
||||
-- since if and while are Lua keywords, we can't create functions with such names
|
||||
-- we can't create functions with names that are Lua keywords (eg if, while)
|
||||
-- instead, we store the following anonymous functions directly into
|
||||
-- the table, using the [] operator, rather than by using the point syntax
|
||||
|
||||
|
@ -288,7 +290,8 @@ wml_actions["if"] = function(cfg)
|
|||
|
||||
if wesnoth.eval_conditional(cfg) then -- evaluate [if] tag
|
||||
for then_child in helper.child_range(cfg, "then") do
|
||||
utils.handle_event_commands(then_child)
|
||||
local action = utils.handle_event_commands(then_child, "conditional")
|
||||
if action ~= "none" then break end
|
||||
end
|
||||
return -- stop after executing [then] tags
|
||||
end
|
||||
|
@ -296,15 +299,18 @@ wml_actions["if"] = function(cfg)
|
|||
for elseif_child in helper.child_range(cfg, "elseif") do
|
||||
if wesnoth.eval_conditional(elseif_child) then -- we'll evaluate the [elseif] tags one by one
|
||||
for then_tag in helper.child_range(elseif_child, "then") do
|
||||
utils.handle_event_commands(then_tag)
|
||||
local action = utils.handle_event_commands(then_tag, "conditional")
|
||||
if action ~= "none" then goto exit end
|
||||
end
|
||||
return -- stop on first matched condition
|
||||
end
|
||||
end
|
||||
::exit::
|
||||
|
||||
-- no matched condition, try the [else] tags
|
||||
for else_child in helper.child_range(cfg, "else") do
|
||||
utils.handle_event_commands(else_child)
|
||||
local action = utils.handle_event_commands(else_child, "conditional")
|
||||
if action ~= "none" then break end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -313,10 +319,32 @@ wml_actions["while"] = function( cfg )
|
|||
for i = 1, 65536 do
|
||||
if wesnoth.eval_conditional( cfg ) then
|
||||
for do_child in helper.child_range( cfg, "do" ) do
|
||||
utils.handle_event_commands( do_child )
|
||||
local action = utils.handle_event_commands(do_child, "loop")
|
||||
if action == "break" then
|
||||
utils.set_exiting("none")
|
||||
goto exit
|
||||
elseif action == "continue" then
|
||||
utils.set_exiting("none")
|
||||
break
|
||||
elseif action ~= "none" then
|
||||
goto exit
|
||||
end
|
||||
end
|
||||
else return end
|
||||
end
|
||||
::exit::
|
||||
end
|
||||
|
||||
wml_actions["break"] = function(cfg)
|
||||
utils.set_exiting("break")
|
||||
end
|
||||
|
||||
wml_actions["return"] = function(cfg)
|
||||
utils.set_exiting("return")
|
||||
end
|
||||
|
||||
function wml_actions.continue(cfg)
|
||||
utils.set_exiting("continue")
|
||||
end
|
||||
|
||||
function wml_actions.switch(cfg)
|
||||
|
@ -327,17 +355,20 @@ function wml_actions.switch(cfg)
|
|||
for v in helper.child_range(cfg, "case") do
|
||||
for w in utils.split(v.value) do
|
||||
if w == tostring(var_value) then
|
||||
utils.handle_event_commands(v)
|
||||
local action = utils.handle_event_commands(v, "switch")
|
||||
found = true
|
||||
break
|
||||
if action ~= "none" then goto exit end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
::exit::
|
||||
|
||||
-- Otherwise execute [else] statements.
|
||||
if not found then
|
||||
for v in helper.child_range(cfg, "else") do
|
||||
utils.handle_event_commands(v)
|
||||
local action = utils.handle_event_commands(v, "switch")
|
||||
if action ~= "none" then break end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -71,11 +71,34 @@ function utils.optional_side_filter(cfg, key_name, filter_name)
|
|||
return false
|
||||
end
|
||||
|
||||
function utils.handle_event_commands(cfg)
|
||||
local current_exit = "none"
|
||||
local scope_stack = {
|
||||
push = table.insert,
|
||||
pop = table.remove,
|
||||
}
|
||||
|
||||
--[[ Possible exit types:
|
||||
- none - ordinary execution
|
||||
- break - exiting a loop scope
|
||||
- return - immediate termination (exit all scopes)
|
||||
- continue - jumping to the end of a loop scope
|
||||
]]
|
||||
function utils.set_exiting(exit_type)
|
||||
current_exit = exit_type
|
||||
end
|
||||
|
||||
--[[ Possible scope types:
|
||||
- plain - ordinary scope, no special features; eg [command] or [event]
|
||||
- conditional - scope that's executing because of a condition, eg [then] or [else]
|
||||
- switch - scope that's part of a switch statement, eg [case] or [else]
|
||||
- loop - scope that's part of a loop, eg [do]
|
||||
Currently, only "loop" has any special effects. ]]
|
||||
function utils.handle_event_commands(cfg, scope_type)
|
||||
-- The WML might be modifying the currently executed WML by mixing
|
||||
-- [insert_tag] with [set_variables] and [clear_variable], so we
|
||||
-- have to be careful not to get confused by tags vanishing during
|
||||
-- the execution, hence the manual handling of [insert_tag].
|
||||
scope_stack:push(scope_type)
|
||||
local cmds = helper.shallow_literal(cfg)
|
||||
for i = 1,#cmds do
|
||||
local v = cmds[i]
|
||||
|
@ -104,6 +127,7 @@ function utils.handle_event_commands(cfg)
|
|||
local j = 0
|
||||
repeat
|
||||
cmd(arg)
|
||||
if current_exit ~= "none" then break end
|
||||
j = j + 1
|
||||
if j >= wesnoth.get_variable(insert_from .. ".length") then break end
|
||||
arg = wesnoth.tovconfig(wesnoth.get_variable(string.format("%s[%d]", insert_from, j)))
|
||||
|
@ -112,9 +136,18 @@ function utils.handle_event_commands(cfg)
|
|||
cmd(arg)
|
||||
end
|
||||
end
|
||||
if current_exit ~= "none" then break end
|
||||
end
|
||||
scope_stack:pop()
|
||||
if #scope_stack == 0 then
|
||||
if current_exit == "continue" and scope_type ~= "loop" then
|
||||
helper.wml_error("[continue] found outside a loop scope!")
|
||||
end
|
||||
current_exit = "none"
|
||||
end
|
||||
-- Apply music alterations once all the commands have been processed.
|
||||
wesnoth.set_music()
|
||||
return current_exit
|
||||
end
|
||||
|
||||
-- Splits the string argument on commas, excepting those commas that occur
|
||||
|
|
|
@ -214,7 +214,8 @@ function wesnoth.wml_actions.message(cfg)
|
|||
end
|
||||
|
||||
for i, cmd in ipairs(option_events[option_chosen]) do
|
||||
utils.handle_event_commands(cmd)
|
||||
local action = utils.handle_event_commands(cmd, "plain")
|
||||
if action ~= "none" then break end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -55,7 +55,8 @@ function wml_actions.object(cfg)
|
|||
end
|
||||
|
||||
for cmd in helper.child_range(cfg, command_type) do
|
||||
utils.handle_event_commands(cmd)
|
||||
local action = utils.handle_event_commands(cmd, "conditional")
|
||||
if action ~= "none" then break end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
114
data/test/scenarios/interrupts.cfg
Normal file
114
data/test/scenarios/interrupts.cfg
Normal file
|
@ -0,0 +1,114 @@
|
|||
|
||||
{GENERIC_UNIT_TEST check_interrupts_break (
|
||||
[event]
|
||||
name=start
|
||||
{VARIABLE x 0}
|
||||
[while]
|
||||
[true][/true]
|
||||
[do]
|
||||
[if]
|
||||
{VARIABLE_CONDITIONAL x greater_than 5}
|
||||
[then]
|
||||
[break][/break]
|
||||
[/then]
|
||||
[/if]
|
||||
{VARIABLE_OP x add 1}
|
||||
[/do]
|
||||
[/while]
|
||||
{RETURN ({VARIABLE_CONDITIONAL x equals 6})}
|
||||
[/event]
|
||||
)}
|
||||
|
||||
{GENERIC_UNIT_TEST check_interrupts_return (
|
||||
[event]
|
||||
name=start
|
||||
{VARIABLE x 0}
|
||||
[while]
|
||||
[true][/true]
|
||||
[do]
|
||||
[if]
|
||||
{VARIABLE_CONDITIONAL x greater_than 5}
|
||||
[then]
|
||||
[return][/return]
|
||||
[/then]
|
||||
[/if]
|
||||
{VARIABLE_OP x add 1}
|
||||
[/do]
|
||||
[/while]
|
||||
{RETURN ([false][/false])}
|
||||
[/event]
|
||||
[event]
|
||||
name=start
|
||||
{RETURN ({VARIABLE_CONDITIONAL x equals 6})}
|
||||
[/event]
|
||||
)}
|
||||
|
||||
{GENERIC_UNIT_TEST check_interrupts_continue (
|
||||
[event]
|
||||
name=start
|
||||
{VARIABLE x 0}
|
||||
[while]
|
||||
{VARIABLE_CONDITIONAL x less_than 1}
|
||||
[do]
|
||||
{VARIABLE_OP x add 1}
|
||||
[continue][/continue]
|
||||
{RETURN ([false][/false])}
|
||||
[/do]
|
||||
[/while]
|
||||
{RETURN ([true][/true])}
|
||||
[/event]
|
||||
)}
|
||||
|
||||
{GENERIC_UNIT_TEST check_interrupts_break_global (
|
||||
[event]
|
||||
name=start
|
||||
[break][/break]
|
||||
{RETURN ([false][/false])}
|
||||
[/event]
|
||||
[event]
|
||||
name=start
|
||||
{RETURN ([true][/true])}
|
||||
[/event]
|
||||
)}
|
||||
|
||||
{GENERIC_UNIT_TEST check_interrupts_continue_global (
|
||||
[event]
|
||||
name=start
|
||||
[lua]
|
||||
code=<<
|
||||
local H = wesnoth.require "lua/helper.lua"
|
||||
local A = H.set_wml_action_metatable{}
|
||||
local function continue()
|
||||
A.continue{}
|
||||
end
|
||||
-- Use pcall() to trap the WML error raised by continue in global scope
|
||||
local err, res = pcall(continue)
|
||||
if err then wesnoth.fire_event "success"
|
||||
else wesnoth.fire_event "fail" end
|
||||
>>
|
||||
[/lua]
|
||||
[/event]
|
||||
[event]
|
||||
name=success
|
||||
{RETURN ([true][/true])}
|
||||
[/event]
|
||||
[event]
|
||||
name=fail
|
||||
{RETURN ([false][/false])}
|
||||
[/event]
|
||||
)}
|
||||
|
||||
{GENERIC_UNIT_TEST check_interrupts_return_nested (
|
||||
[event]
|
||||
name=start
|
||||
[command]
|
||||
[return][/return]
|
||||
{RETURN ([false][/false])}
|
||||
[/command]
|
||||
{RETURN ([false][/false])}
|
||||
[/event]
|
||||
[event]
|
||||
name=start
|
||||
{RETURN ([true][/true])}
|
||||
[/event]
|
||||
)}
|
|
@ -143,3 +143,10 @@
|
|||
#
|
||||
0 check_conditionals_1
|
||||
0 check_conditionals_2
|
||||
# Interrupt tag tests
|
||||
0 check_interrupts_break
|
||||
0 check_interrupts_return
|
||||
0 check_interrupts_continue
|
||||
0 check_interrupts_break_global
|
||||
0 check_interrupts_return_nested
|
||||
0 check_interrupts_continue_global
|
||||
|
|
Loading…
Add table
Reference in a new issue