Improved buildscript and itemscript. Still thinking about itemscript filters...

This commit is contained in:
Andrew Lalis 2023-04-26 10:37:17 +02:00
parent a1dd7a850a
commit cfe8cc8a20
3 changed files with 85 additions and 41 deletions

View File

@ -22,67 +22,94 @@ function buildscript.runWithItem(ms_script, filterExpr)
end end
end end
local function parseArgValue(argSpec, str) -- Parses a value for an argument specification from a raw value.
local function parseArgValue(argSpec, arg)
if argSpec.required and (not arg or #arg < 1) then
return false, "Missing required value."
end
if argSpec.type == "string" then
return true, arg
elseif argSpec.type == "number" then
local num = tonumber(arg)
if not num and argSpec.required then
return false, "Invalid number."
end
return true, num
elseif argSpec.type == "bool" then
local txt = string.lower(arg)
if txt == "true" or txt == "t" or txt == "yes" or txt == "y" then
return true, true
else
return true, false
end
else
return false, "Unknown type: " .. argSpec.type
end
end end
-- Parses arguments according to a specification table, for common building -- Parses arguments according to a specification table, for common building
-- scripts, and returns a table with key-value pairs for each arg. -- scripts, and returns a table with key-value pairs for each arg.
-- The specification table should be formatted like so: -- The specification table should be formatted like so:
-- { -- {
-- argName = { type = "string", required = true, idx = 1 } -- argName = { type = "string", required = true, idx = 1 },
-- namedArg = { name = "-f", required = true, type = "bool" }
-- } -- }
-- Supported types: string, number, bool
function buildscript.parseArgs(args, spec) function buildscript.parseArgs(args, spec)
local idxArgSpecs = {}
local namedArgSpecs = {}
for name, argSpec in pairs(spec) do for name, argSpec in pairs(spec) do
if argSpec.idx ~= nil then if argSpec.idx ~= nil then
-- Add this argSpec to the list of indexed arg specs for parsing first. if type(argSpec.idx) ~= "number" or argSpec.idx < 1 then
if type(argSpec.idx) ~= "number" or argSpec.idx < 1 do
return false, "Invalid argument specification: " .. name .. " does not have a valid numeric index." return false, "Invalid argument specification: " .. name .. " does not have a valid numeric index."
end end
idxArgSpecs[name] = argSpec
elseif argSpec.name ~= nil then elseif argSpec.name ~= nil then
-- Otherwise, ensure that this argSpec has a name. if type(argSpec.name) ~= "string" or #argSpec.name < 3 then
if type(argSpec.name) ~= "string" or #argSpec.name < 3 do
return false, "Invalid argument specification: " .. name .. " does not have a valid string name." return false, "Invalid argument specification: " .. name .. " does not have a valid string name."
end end
namedArgSpecs[name] = argSpec
else else
return false, "Invalid argument specification: " .. name .. " doesn't have idx or name." return false, "Invalid argument specification: " .. name .. " doesn't have idx or name."
end end
if not argSpec.type then argSpec.type = "string" end
end end
local results = {} local results = {}
local idx = 1
while idx <= #args do -- Iterate over each argument specification, and try and find a value for it.
local parsed = false for name, argSpec in pairs(spec) do
-- Try and see if there's an idx arg spec for this index first. if argSpec.idx then
for name, argSpec in pairs(idxArgSpecs) do -- Parse a positional argument.
if argSpec.idx == idx then if argSpec.idx > #args and argSpec.required then
local success, value = parseArgValue(argSpec, args[idx]) return false, "Missing required positional argument " .. name .. " at index " .. argSpec.idx
if success then end
if argSpec.idx > #args then
results[name] = nil
else
local success, value = parseArgValue(argSpec, args[argSpec.idx])
if not success then
return false, "Failed to parse value for argument " .. name .. ": " .. value
end
results[name] = value results[name] = value
idxArgSpecs[name] = nil end
parsed = true else
-- Parse a named argument by iterating over all args until we find one matching the name.
local valueFound = false
for idx, arg in pairs(args) do
if arg == argSpec.name then
if idx >= #args and argSpec.required then
return false, "Missing value for required argument " .. name
end
local success, value = parseArgValue(argSpec, args[idx + 1])
if not success then
return false, "Failed to parse value for argument " .. name .. ": " .. value
end
results[name] = value
valueFound = true
break break
elseif not success and argSpec.required then
return false, "Failed to parse value for " .. name .. " argument: " .. value
end end
end end
end if argSpec.required and not valueFound then
return false, "Missing argument: " .. name
-- If no idx arg spec could parse the argument, try a named one.
if not parsed then
if idx == #args then
return false, "Missing value for argument " .. args[idx]
end
for name, argSpec in pairs(idxArgSpecs) do
end end
end end
idx = idx + 1
end end
return true, results return true, results

View File

@ -59,6 +59,12 @@ end
Prepending ! will match any item stack whose name does not match. Prepending ! will match any item stack whose name does not match.
Prepending # will do a fuzzy match using string.find. Prepending # will do a fuzzy match using string.find.
Examples:
"coal" matches only "minecraft:coal" items
"!#wood" matches all items except any that contain the phrase "wood"
"#iron" matches all items that contain the phrase "iron"
]]-- ]]--
local function parseItemFilterExpression(expr) local function parseItemFilterExpression(expr)
local prefixIdx, prefixIdxEnd = string.find(expr, "^[!#]+") local prefixIdx, prefixIdxEnd = string.find(expr, "^[!#]+")
@ -137,11 +143,12 @@ function itemscript.select(filterExpr)
return false return false
end end
-- Selects a slot containing at least one of the given item type, or waits for -- Selects a slot containing at least minCount (or 1) of an item type matching
-- the user to add an item otherwise. -- the given filter expression.
function itemscript.selectOrWait(filterExpr) function itemscript.selectOrWait(filterExpr, minCount)
while not itemscript.select(filterExpr) do minCount = minCount or 1
print("Couldn't find at least one item matching the filter expression: \"" .. filterExpr .. "\". Please add it.") while itemscript.totalCount(filterExpr) < minCount do
print("Couldn't find at least " .. minCount .. " item(s) matching the filter expression: \"" .. filterExpr .. "\". Please add it.")
os.pullEvent("turtle_inventory") os.pullEvent("turtle_inventory")
end end
end end

View File

@ -21,5 +21,15 @@ function print_r (t, name, indent)
table_r(t,name or '__unnamed__',indent or '','') table_r(t,name or '__unnamed__',indent or '','')
end end
local ms = require("src/movescript") -- local ms = require("src/movescript")
print_r(ms.parse("35(2F(safe=false)R 3(L(delay=0.25, file=file.txt)UB))", {debug=true})) -- print_r(ms.parse("35(2F(safe=false)R 3(L(delay=0.25, file=file.txt)UB))", {debug=true}))
local bs = require("src/buildscript")
local args = {...}
local spec = {
num = { type = "number", required = true, idx = 1 },
name = { name = "name", type = "bool", required = true }
}
local success, result = bs.parseArgs(args, spec)
print(success)
print_r(result)