Add ParseCSV as lua function
This commit is contained in:
@@ -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"}`
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user