Skip to content

Commit

Permalink
Improve expression parsing
Browse files Browse the repository at this point in the history
Now uses the game's builtin expression evaluator
(`game.evaluate_expression`) instead of using Lua's `load` function.

Also improves behaviour when invalid expressions are confirmed in the
GUI, falling back to the previous valid value instead.

Fixes #18
  • Loading branch information
Sharparam committed Apr 16, 2023
1 parent 982c2d0 commit a12ba50
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 18 deletions.
6 changes: 6 additions & 0 deletions src/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
---------------------------------------------------------------------------------------------------
Version: 0.4.4
Date: 2023-04-16
Changes:
- Switch to using built-in expression evaluator in Factorio.
- Now falls back to previous valid value if invalid signal count is entered.
---------------------------------------------------------------------------------------------------
Version: 0.4.3
Date: 2023-04-15
Bugfixes:
Expand Down
22 changes: 10 additions & 12 deletions src/scripts/expression.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,21 @@ local NONMATH_PATTERN = "[^0-9%.+%-*/%%^()]"
local expr = {}

--- @param input string?
--- @param fallback number?
--- @return number
function expr.parse(input)
function expr.parse(input, fallback)
fallback = fallback or 0
log:debug("parsing expression: ", input)
if not input then return 0 end
local e = gsub(input, NONMATH_PATTERN, "")
local f, err = load("return " .. e, nil, "t", {})
if not f then
log:warn("The given input '", input, "' could not be parsed as a math expression: ", err)
return 0
end
local success, result = pcall(f)
if not input then return fallback end

local success, result = pcall(game.evaluate_expression, input)

if not success then
log:warn("The given input '", input, "' could not be evaluated as a math expression: ", result)
return 0
return fallback
end
log:debug("expression '", input, "' parsed as: ", result)
return tonumber(result) or 0

return result
end

return expr
14 changes: 8 additions & 6 deletions src/scripts/gui.lua
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,9 @@ end

--- @param element LuaGuiElement
--- @param player PlayerIdentification?
--- @param fallback number?
--- @return number?
local function resolve_textfield_number(element, player)
local function resolve_textfield_number(element, player, fallback)
if not element or not element.text then return nil end
local min = tonumber(element.tags.min) or constants.INT32_MIN
local max = tonumber(element.tags.max) or constants.INT32_MAX
Expand All @@ -115,9 +116,9 @@ local function resolve_textfield_number(element, player)
local value

if enable_expressions then
value = expression.parse(element.text)
value = expression.parse(element.text, fallback)
else
value = tonumber(element.text)
value = tonumber(element.text) or fallback
end

if not value then return nil end
Expand Down Expand Up @@ -245,7 +246,7 @@ local function set_cs_signal_value(element, player, update_element)
if not state then return end
local signal_name = element.tags.signal_name --[[@as string]]
local current = state.combinator:get_cs_value(signal_name)
local value = resolve_textfield_number(element, player)
local value = resolve_textfield_number(element, player, current)
if value then
log:debug("cs_signal_value_changed: ", signal_name, " changed to ", value)
else
Expand Down Expand Up @@ -437,7 +438,7 @@ local function handle_signal_value_confirmed(event)
local state = get_player_state(event.player_index)
if not state or not state.selected_slot then return end
local current = state.combinator:get_item_slot(state.selected_slot)
local value = resolve_textfield_number(state.signal_value_items, event.player_index)
local value = resolve_textfield_number(state.signal_value_items, event.player_index, current.count or 0)
if not value then
if current and current.count then
state.signal_value_items.text = tostring(current.count)
Expand All @@ -456,7 +457,8 @@ end
local function handle_signal_value_confirm(event)
local state = get_player_state(event.player_index)
if not state or not state.selected_slot then return end
local value = resolve_textfield_number(state.signal_value_items, event.player_index)
local current = state.combinator:get_item_slot(state.selected_slot)
local value = resolve_textfield_number(state.signal_value_items, event.player_index, current.count or 0)
if not value then
state.signal_value_confirm.enabled = false
return
Expand Down

0 comments on commit a12ba50

Please sign in to comment.