mirror of
https://github.com/sbrl/Minetest-WorldEditAdditions.git
synced 2024-11-27 17:43:53 +01:00
Implement tokenise_commands utility function
This function is intended for use in //multi.
This commit is contained in:
parent
e111decd3a
commit
a937740275
@ -4,3 +4,4 @@ dofile(worldeditadditions.modpath.."/utils/parse/chance.lua")
|
|||||||
dofile(worldeditadditions.modpath.."/utils/parse/map.lua")
|
dofile(worldeditadditions.modpath.."/utils/parse/map.lua")
|
||||||
dofile(worldeditadditions.modpath.."/utils/parse/seed.lua")
|
dofile(worldeditadditions.modpath.."/utils/parse/seed.lua")
|
||||||
dofile(worldeditadditions.modpath.."/utils/parse/weighted_nodes.lua")
|
dofile(worldeditadditions.modpath.."/utils/parse/weighted_nodes.lua")
|
||||||
|
dofile(worldeditadditions.modpath.."/utils/parse/tokenise_commands.lua")
|
||||||
|
132
worldeditadditions/utils/parse/tokenise_commands.lua
Normal file
132
worldeditadditions/utils/parse/tokenise_commands.lua
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
--- Uncomment these 2 lines to run in standalone mode
|
||||||
|
-- worldeditadditions = {}
|
||||||
|
-- function worldeditadditions.trim(str) return (str:gsub("^%s*(.-)%s*$", "%1")) end
|
||||||
|
|
||||||
|
|
||||||
|
--- Tokenises a string of multiple commands into an array of individual commands.
|
||||||
|
-- Preserves the forward slash at the beginning of each command name.
|
||||||
|
-- Also supports arbitrarily nested and complex curly braces { } for grouping
|
||||||
|
-- commands together that would normally be split apart.
|
||||||
|
--
|
||||||
|
-- Simple example:
|
||||||
|
-- INPUT: //1 //2 //outset 25 //fixlight
|
||||||
|
-- OUTPUT: { "//1", "//2", "//outset 25", "//fixlight" }
|
||||||
|
--
|
||||||
|
-- Example with curly braces:
|
||||||
|
-- INPUT: //1 //2 //outset 50 {//many 5 //multi //fixlight //clearcut}
|
||||||
|
-- OUTPUT: { "//1", "//2", "//outset 50", "//many 5 //multi //fixlight //clearcut"}
|
||||||
|
--
|
||||||
|
-- @param command_str str The command string to operate on.
|
||||||
|
-- @returns bool,(string[]|string) If the operation was successful, then true followed by a table of strings is returned. If the operation was not successful, then false followed by an error message (as a single string) is returned instead.
|
||||||
|
function worldeditadditions.tokenise_commands(command_str)
|
||||||
|
local success, result = tokenise(command_str)
|
||||||
|
if not success then return success, result end
|
||||||
|
return true, recombine(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function tokenise(str)
|
||||||
|
if type(str) ~= "string" then return false, "Error: Expected input of type string." end
|
||||||
|
str = str:gsub("%s+", " ") -- Replace all runs of whitespace with a single space
|
||||||
|
|
||||||
|
-- The resulting tokens
|
||||||
|
local result = {}
|
||||||
|
|
||||||
|
local nested_depth = 0 -- The nested depth inside { and } we're currently at
|
||||||
|
local nested_stack = {} -- Stack of starting positions of curly brace { } blocks
|
||||||
|
local scanpos = 1 -- The current position we're scanning
|
||||||
|
while scanpos <= #str do
|
||||||
|
-- Find the next character of interest
|
||||||
|
local nextpos = str:find("[%s{}]", scanpos)
|
||||||
|
-- If it's nil, then cleanup and return
|
||||||
|
if nextpos == nil then
|
||||||
|
if nested_depth > 0 then
|
||||||
|
-- Handle unclosed brace groups
|
||||||
|
return false, "Error: Unclosed brace group detected."
|
||||||
|
else
|
||||||
|
-- Make sure we catch any trailing parts
|
||||||
|
local str_trailing = str:sub(scanpos)
|
||||||
|
if #str_trailing then table.insert(result, str_trailing) end
|
||||||
|
return true, result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Extract the character in question
|
||||||
|
local char = str:sub(nextpos, nextpos)
|
||||||
|
|
||||||
|
if char == "}" then
|
||||||
|
-- Decrease the nested depth
|
||||||
|
nested_depth = nested_depth - 1
|
||||||
|
-- Pop the start of this block off the stack and find this block's contents
|
||||||
|
block_start = table.remove(nested_stack, #nested_stack)
|
||||||
|
local substr = str:sub(block_start, nextpos - 1)
|
||||||
|
if #substr > 0 and nested_depth == 0 then table.insert(result, substr) end
|
||||||
|
elseif char == "{" then
|
||||||
|
-- Increase the nested depth, and store this position on the stack for later
|
||||||
|
nested_depth = nested_depth + 1
|
||||||
|
table.insert(nested_stack, nextpos + 1)
|
||||||
|
else
|
||||||
|
-- It's a space! Extract a part, but only if the nested depth is 0 (i.e. we're not inside any braces).
|
||||||
|
local substr = str:sub(scanpos, nextpos - 1)
|
||||||
|
if #substr > 0 and nested_depth == 0 then table.insert(result, substr) end
|
||||||
|
end
|
||||||
|
-- Move the scanning position up to just after the character we've just
|
||||||
|
-- found and handled
|
||||||
|
scanpos = nextpos + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle any trailing bits
|
||||||
|
local str_trailing = str:sub(scanpos)
|
||||||
|
if #str_trailing > 0 then table.insert(result, str_trailing) end
|
||||||
|
|
||||||
|
return true, result
|
||||||
|
end
|
||||||
|
|
||||||
|
local function recombine(parts)
|
||||||
|
local result = {}
|
||||||
|
local acc = {}
|
||||||
|
for i, value in ipairs(parts) do
|
||||||
|
value = worldeditadditions.trim(value)
|
||||||
|
if value:sub(1, 1) == "/" and #acc > 0 then
|
||||||
|
table.insert(result, table.concat(acc, " "))
|
||||||
|
acc = {}
|
||||||
|
end
|
||||||
|
table.insert(acc, value)
|
||||||
|
end
|
||||||
|
if #acc > 0 then table.insert(result, table.concat(acc, " ")) end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
----- Test harness code -----
|
||||||
|
-----------------------------
|
||||||
|
-- local function printparts(tbl)
|
||||||
|
-- for key,value in ipairs(tbl) do
|
||||||
|
-- print(key..": "..value)
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- local function test_input(input)
|
||||||
|
-- local success, result = worldeditadditions.tokenise_commands(input)
|
||||||
|
-- if success then
|
||||||
|
-- printparts(result)
|
||||||
|
--
|
||||||
|
-- -- print("RECOMBINED:")
|
||||||
|
-- -- printparts(recombine(result))
|
||||||
|
-- else
|
||||||
|
-- print(result)
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- print("\n\n\n*** 1 ***")
|
||||||
|
-- test_input("//multi //1 //cubeapply 10 set dirt")
|
||||||
|
-- print("\n\n\n*** 2 ***")
|
||||||
|
-- test_input("//multi //1 //2 //outset 50 {//many 5 //multi //fixlight //clearcut}")
|
||||||
|
-- print("\n\n\n*** 3 ***")
|
||||||
|
-- test_input("//multi //1 //2 //outset 50 {//many 5 //multi //ellipsoid 10 5 7 glass //clearcut}")
|
||||||
|
-- print("\n\n\n*** 4 ***")
|
||||||
|
-- test_input("//multi //1 //2 //outset 50 {//many 5 //multi //ellipsoid 10 5 7 glass //clearcut //many {//set dirt //fixlight}}")
|
||||||
|
-- print("\n\n\n*** 5 ***")
|
||||||
|
-- test_input("a { b c d { e f { g h i }j} k l m n}o p")
|
||||||
|
-- print("\n\n\n*** 6 ***")
|
||||||
|
-- test_input("a { b c d } e f {{ g h i }j k l m n}o p")
|
Loading…
Reference in New Issue
Block a user