Add ParseCSV as lua function

This commit is contained in:
2025-11-03 16:32:14 +01:00
parent 91ad9006fa
commit e8d6613ac8

View File

@@ -189,24 +189,122 @@ end
---@param table table ---@param table table
---@param depth number? ---@param depth number?
function DumpTable(table, depth) function dump(table, depth)
if depth == nil then if depth == nil then
depth = 0 depth = 0
end end
if (depth > 200) then if (depth > 200) then
print("Error: Depth > 200 in dumpTable()") print("Error: Depth > 200 in dump()")
return return
end end
for k, v in pairs(table) do for k, v in pairs(table) do
if (type(v) == "table") then if (type(v) == "table") then
print(string.rep(" ", depth) .. k .. ":") print(string.rep(" ", depth) .. k .. ":")
DumpTable(v, depth + 1) dump(v, depth + 1)
else else
print(string.rep(" ", depth) .. k .. ": ", v) print(string.rep(" ", depth) .. k .. ": ", v)
end end
end end
end end
--- Parses CSV text into rows and fields using a minimal RFC 4180 state machine.
---
--- Requirements/assumptions:
--- - Input is a single string containing the entire CSV content.
--- - Field separators are commas (,).
--- - Newlines between rows may be "\n" or "\r\n". "\r\n" is treated as one line break.
--- - Fields may be quoted with double quotes (").
--- - Inside quoted fields, doubled quotes ("") represent a literal quote character.
--- - No backslash escaping is supported (not part of RFC 4180).
--- - Newlines inside quoted fields are preserved as part of the field.
--- - Leading/trailing spaces are preserved; no trimming is performed.
--- - Empty fields and empty rows are preserved.
--- - The final row is emitted even if the text does not end with a newline.
---
--- Returns:
--- - A table (array) of rows; each row is a table (array) of string fields.
local function parseCSV(csv)
local rows = {}
local fields = {}
local field = {}
local STATE_DEFAULT = 1
local STATE_IN_QUOTES = 2
local STATE_QUOTE_IN_QUOTES = 3
local state = STATE_DEFAULT
local i = 1
local len = #csv
while i <= len do
local c = csv:sub(i, i)
if state == STATE_DEFAULT then
if c == '"' then
state = STATE_IN_QUOTES
i = i + 1
elseif c == ',' then
table.insert(fields, table.concat(field))
field = {}
i = i + 1
elseif c == '\r' or c == '\n' then
table.insert(fields, table.concat(field))
field = {}
table.insert(rows, fields)
fields = {}
if c == '\r' and i < len and csv:sub(i + 1, i + 1) == '\n' then
i = i + 2
else
i = i + 1
end
else
table.insert(field, c)
i = i + 1
end
elseif state == STATE_IN_QUOTES then
if c == '"' then
state = STATE_QUOTE_IN_QUOTES
i = i + 1
else
table.insert(field, c)
i = i + 1
end
else -- STATE_QUOTE_IN_QUOTES
if c == '"' then
table.insert(field, '"')
state = STATE_IN_QUOTES
i = i + 1
elseif c == ',' then
table.insert(fields, table.concat(field))
field = {}
state = STATE_DEFAULT
i = i + 1
elseif c == '\r' or c == '\n' then
table.insert(fields, table.concat(field))
field = {}
table.insert(rows, fields)
fields = {}
state = STATE_DEFAULT
if c == '\r' and i < len and csv:sub(i + 1, i + 1) == '\n' then
i = i + 2
else
i = i + 1
end
else
state = STATE_DEFAULT
-- Don't increment i, reprocess character in DEFAULT state
end
end
end
if #field > 0 or #fields > 0 then
table.insert(fields, table.concat(field))
table.insert(rows, fields)
end
return rows
end
-- String to number conversion helper -- String to number conversion helper
function num(str) function num(str)
return tonumber(str) or 0 return tonumber(str) or 0
@@ -519,25 +617,26 @@ func GetLuaFunctionsHelp() string {
return `Lua Functions Available in Global Environment: return `Lua Functions Available in Global Environment:
MATH FUNCTIONS: MATH FUNCTIONS:
min(a, b) - Returns the minimum of two numbers min(a, b) - Returns the minimum of two numbers
max(a, b) - Returns the maximum of two numbers max(a, b) - Returns the maximum of two numbers
round(x, n) - Rounds x to n decimal places (default 0) round(x, n) - Rounds x to n decimal places (default 0)
floor(x) - Returns the floor of x floor(x) - Returns the floor of x
ceil(x) - Returns the ceiling of x ceil(x) - Returns the ceiling of x
STRING FUNCTIONS: STRING FUNCTIONS:
upper(s) - Converts string to uppercase upper(s) - Converts string to uppercase
lower(s) - Converts string to lowercase lower(s) - Converts string to lowercase
format(s, ...) - Formats string using Lua string.format format(s, ...) - Formats string using Lua string.format
trim(s) - Removes leading/trailing whitespace trim(s) - Removes leading/trailing whitespace
strsplit(inputstr, sep) - Splits string by separator (default: whitespace) strsplit(inputstr, sep) - Splits string by separator (default: whitespace)
num(str) - Converts string to number (returns 0 if invalid) parseCSV(csv) - Parses CSV text into rows of fields
str(num) - Converts number to string num(str) - Converts string to number (returns 0 if invalid)
is_number(str) - Returns true if string is numeric str(num) - Converts number to string
is_number(str) - Returns true if string is numeric
TABLE FUNCTIONS: TABLE FUNCTIONS:
DumpTable(table, depth) - Prints table structure recursively dump(table, depth) - Prints table structure recursively
isArray(t) - Returns true if table is a sequential array isArray(t) - Returns true if table is a sequential array
HTTP FUNCTIONS: HTTP FUNCTIONS:
fetch(url, options) - Makes HTTP request, returns response table fetch(url, options) - Makes HTTP request, returns response table
@@ -552,12 +651,12 @@ UTILITY FUNCTIONS:
print(...) - Prints arguments to Go logger print(...) - Prints arguments to Go logger
EXAMPLES: EXAMPLES:
round(3.14159, 2) -> 3.14 round(3.14159, 2) -> 3.14
strsplit("a,b,c", ",") -> {"a", "b", "c"} strsplit("a,b,c", ",") -> {"a", "b", "c"}
upper("hello") -> "HELLO" upper("hello") -> "HELLO"
min(5, 3) -> 3 min(5, 3) -> 3
num("123") -> 123 num("123") -> 123
is_number("abc") -> false is_number("abc") -> false
fetch("https://api.example.com/data") fetch("https://api.example.com/data")
re("(\\w+)@(\\w+)", "user@domain.com") -> {"user@domain.com", "user", "domain.com"}` re("(\\w+)@(\\w+)", "user@domain.com") -> {"user@domain.com", "user", "domain.com"}`
} }