1
0
Fork 0
mirror of https://github.com/Reuh/anselme.git synced 2025-10-28 00:59:31 +00:00

Add eventbuffer type

This commit is contained in:
Étienne Fildadut 2021-12-02 18:39:43 +01:00
parent 02d50fb79f
commit 721464218c
10 changed files with 211 additions and 121 deletions

View file

@ -219,84 +219,95 @@ common = {
--- event buffer management
-- i.e. only for text and choice events
events = {
--- add a new element to the event buffer
-- will flush if needed
-- returns true in case of success
-- returns nil, err in case of error
--- add a new element to the last event in the current buffer
-- will create new event if needed
append = function(self, state, type, data)
if state.interpreter.event_capture_stack[type] then
local r, e = state.interpreter.event_capture_stack[type][#state.interpreter.event_capture_stack[type]](data)
if not r then return r, e end
else
local r, e = self:make_space_for(state, type)
if not r then return r, e end
if not state.interpreter.event_buffer then
state.interpreter.event_type = type
state.interpreter.event_buffer = {}
end
table.insert(state.interpreter.event_buffer, data)
local buffer = self:current_buffer(state)
local last = buffer[#buffer]
if not last or last.type ~= type then
last = { type = type }
table.insert(buffer, last)
end
return true
end,
--- add a new item in the last element (a list of elements) of the event buffer
-- will flush if needed
-- will use default or a new list if buffer is empty
-- returns true in case of success
-- returns nil, err in case of error
append_in_last = function(self, state, type, data, default)
local r, e = self:make_space_for(state, type)
if not r then return r, e end
if not state.interpreter.event_buffer then
r, e = self:append(state, type, default or {})
if not r then return r, e end
end
table.insert(state.interpreter.event_buffer[#state.interpreter.event_buffer], data)
return true
table.insert(last, data)
end,
--- start capturing events of a certain type
-- when an event of the type is appended, fn will be called with this event data
-- and the event will not be added to the event buffer
-- fn returns nil, err in case of error
push_capture = function(self, state, type, fn)
if not state.interpreter.event_capture_stack[type] then
state.interpreter.event_capture_stack[type] = {}
end
table.insert(state.interpreter.event_capture_stack[type], fn)
--- new events will be collected in this event buffer (any table) until the next pop
-- this is handled by a stack so nesting is allowed
push_buffer = function(self, state, buffer)
table.insert(state.interpreter.event_buffer_stack, buffer)
end,
--- stop capturing events of a certain type.
-- must be called after a push_capture
-- this is handled by a stack so nested capturing is allowed.
pop_capture = function(self, state, type)
table.remove(state.interpreter.event_capture_stack[type])
if #state.interpreter.event_capture_stack[type] == 0 then
state.interpreter.event_capture_stack[type] = nil
end
-- must be called after a push_buffer
pop_buffer = function(self, state)
table.remove(state.interpreter.event_buffer_stack)
end,
--- returns the current buffer
current_buffer = function(self, state)
return state.interpreter.event_buffer_stack[#state.interpreter.event_buffer_stack]
end,
-- flush event buffer if it's neccessary to push an event of the given type
-- returns true in case of success
-- returns nil, err in case of error
make_space_for = function(self, state, type)
if state.interpreter.event_buffer and state.interpreter.event_type ~= type and not state.interpreter.event_capture_stack[type] then
return self:flush(state)
if #state.interpreter.event_buffer_stack == 0 and state.interpreter.current_event and state.interpreter.current_event.type ~= type then -- FIXME useful?
return self:manual_flush(state)
end
return true
end,
--- write all the data in a buffer into the current buffer, or to the game is no buffer is currently set
write_buffer = function(self, state, buffer)
for _, event in ipairs(buffer) do
if #state.interpreter.event_buffer_stack == 0 then
if event.type == "flush" then
local r, e = self:manual_flush(state)
if not r then return r, e end
elseif state.interpreter.current_event then
if state.interpreter.current_event.type == event.type then
for _, v in ipairs(event) do
table.insert(state.interpreter.current_event, v)
end
else
local r, e = self:manual_flush(state)
if not r then return r, e end
state.interpreter.current_event = event
end
else
state.interpreter.current_event = event
end
else
local current_buffer = self:current_buffer(state)
table.insert(current_buffer, event)
end
end
return true
end,
--- same as manual_flush but add the flush to the current buffer if one is set instead of directly to the game
flush = function(self, state)
if #state.interpreter.event_buffer_stack == 0 then
return self:manual_flush(state)
else
local current_buffer = self:current_buffer(state)
table.insert(current_buffer, { type = "flush" })
return true
end
end,
--- flush events and send them to the game if possible
-- returns true in case of success
-- returns nil, err in case of error
flush = function(self, state)
while state.interpreter.event_buffer do
local type, buffer = state.interpreter.event_type, state.interpreter.event_buffer
state.interpreter.event_type = nil
state.interpreter.event_buffer = nil
manual_flush = function(self, state)
while state.interpreter.current_event do
local event = state.interpreter.current_event
state.interpreter.current_event = nil
local type, buffer = event.type, event
buffer.type = nil
state.interpreter.skip_choices_until_flush = nil
-- choice processing
local choices
if type == "choice" then

View file

@ -23,7 +23,7 @@ local function eval(state, exp)
}
-- string
elseif exp.type == "string" then
local t, e = eval_text(state, exp.value)
local t, e = eval_text(state, exp.text)
if not t then return t, e end
return {
type = "string",
@ -32,7 +32,7 @@ local function eval(state, exp)
-- parentheses
elseif exp.type == "parentheses" then
return eval(state, exp.expression)
-- list parentheses
-- list defined in brackets
elseif exp.type == "list_brackets" then
if exp.expression then
local v, e = eval(state, exp.expression)
@ -52,7 +52,7 @@ local function eval(state, exp)
value = {}
}
end
-- list
-- list defined using , operator
elseif exp.type == "list" then
local flat = flatten_list(exp)
local l = {}
@ -65,17 +65,19 @@ local function eval(state, exp)
type = "list",
value = l
}
-- text: only triggered from choice/text lines
-- event buffer with from a text line
elseif exp.type == "text" then
local currentTags = tags:current(state)
local l = {}
events:push_buffer(state, l)
local current_tags = tags:current(state)
local v, e = eval_text_callback(state, exp.text, function(text)
local v2, e2 = events:append(state, "text", { text = text, tags = currentTags })
if not v2 then return v2, e2 end
events:append(state, "text", { text = text, tags = current_tags })
end)
events:pop_buffer(state)
if not v then return v, e end
return {
type = "nil",
value = nil
type = "eventbuffer",
value = l
}
-- assignment
elseif exp.type == ":=" then

View file

@ -33,17 +33,38 @@ run_line = function(state, line)
elseif line.type == "choice" then
local v, e = events:make_space_for(state, "choice")
if not v then return v, ("%s; in automatic event flush at %s"):format(e, line.source) end
local currentTags = tags:current(state)
local choice_block_state = { tags = currentTags, block = line.child }
v, e = events:append(state, "choice", { _state = choice_block_state }) -- new choice
if not v then return v, e end
events:push_capture(state, "text", function(event)
local v2, e2 = events:append_in_last(state, "choice", event, { _state = choice_block_state })
if not v2 then return v2, e2 end
end)
v, e = eval(state, line.text)
events:pop_capture(state, "text")
if not v then return v, ("%s; at %s"):format(e, line.source) end
-- convert text events to choices
if v.type == "eventbuffer" then
local current_tags = tags:current(state)
local choice_block_state = { tags = current_tags, block = line.child }
local final_buffer = {}
for _, event in ipairs(v.value) do
if event.type == "text" then
-- create new choice block if needed
local last_choice_block = final_buffer[#final_buffer]
if not last_choice_block or last_choice_block.type ~= "choice" then
last_choice_block = { type = "choice" }
table.insert(final_buffer, last_choice_block)
end
-- create new choice item in choice block if needed
local last_choice = last_choice_block[#last_choice_block]
if not last_choice then
last_choice = { _state = choice_block_state }
table.insert(last_choice_block, last_choice)
end
-- add text to last choice item
for _, txt in ipairs(event) do
table.insert(last_choice, txt)
end
else
table.insert(final_buffer, event)
end
end
v, e = events:write_buffer(state, final_buffer)
if not v then return v, ("%s; at %s"):format(e, line.source) end
end
elseif line.type == "tag" then
local v, e = eval(state, line.expression)
if not v then return v, ("%s; at %s"):format(e, line.source) end
@ -61,6 +82,10 @@ run_line = function(state, line)
if not v then return v, ("%s; in automatic event flush at %s"):format(e, line.source) end
v, e = eval(state, line.text)
if not v then return v, ("%s; at %s"):format(e, line.source) end
if v.type == "eventbuffer" then
v, e = events:write_buffer(state, v.value)
if not v then return v, ("%s; at %s"):format(e, line.source) end
end
elseif line.type == "flush_events" then
local v, e = events:flush(state)
if not v then return v, ("%s; in event flush at %s"):format(e, line.source) end