You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Lua uses tables as its sole data-structuring mechanism — serving as
arrays, dictionaries, records, and objects. Configuration files for Lua-based applications (e.g., World of
Warcraft addons like ElvUI) store structured data as Lua table constructors. There is currently no PowerShell
module that provides native serialization of PowerShell objects into Lua table format or deserialization of Lua
table strings back into PowerShell objects.
Request
The module should provide two public functions — ConvertTo-Lua and ConvertFrom-Lua — that enable round-trip
data conversion between PowerShell objects and Lua table constructor
strings:
The functions are data serialization/deserialization tools — analogous to ConvertTo-Json / ConvertFrom-Json.
The parameter design follows those cmdlets where the Lua format permits, but Lua's own type system and
table constructor grammar take precedence over JSON alignment.
Lua data types and PowerShell mapping
According to the Lua 5.4 reference manual §2.1, Lua has eight basic
types. Only five are representable as data literals in table constructors:
Lua type
Lua literal examples
PowerShell type
Direction
table (key-value)
{ name = "x", size = 10 }
PSCustomObject (default) or [ordered] hashtable
both
table (sequence)
{ 1, 2, 3 }
[object[]]
both
table (mixed)
{ "a", name = "x" }
PSCustomObject or [ordered] hashtable (sequential values get integer keys)
both
string
"hello", 'hello', [[hello]]
[string]
both
number (integer)
42, 0xFF, -7
[int] or [long]
both
number (float)
3.14, 1e10, 0x1.fp10
[double]
both
boolean
true, false
[bool]
both
nil
nil
$null
both
The remaining three Lua types — function, userdata, and thread — cannot be represented as data
literals and are out of scope. This module treats Lua as a data serialization format, not as a programming
language.
Lua table constructor grammar
Per §3.4.9, the grammar for table constructors is:
tableconstructor ::= '{' [fieldlist] '}'
fieldlist ::= field {fieldsep field} [fieldsep]
field ::= '[' exp ']' '=' exp | Name '=' exp | exp
fieldsep ::= ',' | ';'
Key behaviors from the spec:
Name = exp is syntactic sugar for ["Name"] = exp
Fields of the form exp (no key) receive sequential integer keys starting at 1
Both , and ; are valid field separators — the parser must accept either
A trailing separator is allowed
Key order in non-sequence parts is not specified by Lua ("the order of the assignments in a
constructor is undefined")
ConvertTo-Lua — serialization
Parameter
Type
Default
Analogous JSON param
Notes
-InputObject
[object]
—
-InputObject
Mandatory, Position 0, pipeline. Any PowerShell object
-Depth
[int]
2
-Depth
Max recursion depth for nested object serialization. Range 0–100. Emits a warning when input exceeds this depth, matching ConvertTo-Json behavior
-Compress
[switch]
-Compress
Omit whitespace and indentation
-EnumsAsStrings
[switch]
-EnumsAsStrings
Serialize PowerShell enum values as their string name instead of numeric value
-AsArray
[switch]
-AsArray
Always wrap output in a Lua sequence table { ... }, even for a single value
Not applicable from ConvertTo-Json:
-EscapeHandling — Lua defines a fixed set of string escape sequences
(\n, \r, \t, \\, \", \', \a, \b, \f, \v, \xXX, \ddd, \u{XXX}). There is no
configurable escape model.
Serialization rules grounded in the Lua spec:
Lua identifiers ([a-zA-Z_][a-zA-Z0-9_]*) that are not reserved words
are emitted as bare keys: name = "value". All other keys use bracket-quote notation: ["key with spaces"] = "value".
This follows the Name = exp vs [exp] = exp distinction in the grammar.
Lua makes no distinction between arrays and dictionaries — both are tables. PowerShell arrays serialize as
sequence tables ({ 1, 2, 3 }); hashtables and PSCustomObject as key-value tables.
Per §2.1, Lua numbers have two subtypes: integer and float.
PowerShell integers serialize as Lua integers; PowerShell floats/doubles serialize as Lua floats. Numbers
always use invariant culture formatting.
$null serializes as nil. Per the Lua spec, "any key associated to the value nil is not considered part
of the table" — so properties with $null values are omitted from the output, consistent with how Lua
tables actually work.
Booleans serialize as true/false (lowercase, per Lua keywords).
Strings are double-quoted with escape sequences applied per §3.1.
Empty collections serialize as {}.
Uses 4-space indentation (Lua community convention). Not configurable — ConvertTo-Json also does not
expose indent control.
ConvertFrom-Lua — deserialization
Parameter
Type
Default
Analogous JSON param
Notes
-InputObject
[string]
—
-InputObject
Mandatory, Position 0, pipeline. A Lua table constructor string
-AsHashtable
[switch]
-AsHashtable
Output [ordered] hashtable instead of the default PSCustomObject. Matches ConvertFrom-Json -AsHashtable (which returns OrderedHashtable since PS 7.3)
-Depth
[int]
1024
-Depth
Max nesting depth allowed in input. Protects against excessively deep or crafted input. Throws a terminating error when exceeded
-NoEnumerate
[switch]
-NoEnumerate
Output arrays as a single [object[]] instead of enumerating elements through the pipeline. Required for round-trip fidelity of Lua sequences
Not applicable from ConvertFrom-Json:
-DateKind — Lua has no native date/time type (§2.1).
Date-like strings remain strings.
Deserialization rules grounded in the Lua spec:
Default output is PSCustomObject (matching ConvertFrom-Json). Use -AsHashtable for ordered hashtable
output.
Tables with only sequential values (the exp field form) deserialize as [object[]] arrays.
Tables with only key-value fields (Name = exp or [exp] = exp) deserialize as PSCustomObject
(or ordered hashtable with -AsHashtable).
Mixed tables (both sequential values and named keys) deserialize as PSCustomObject or ordered hashtable,
with sequential values assigned integer keys starting at 1 — matching the Lua spec behavior for the exp
field form.
Lua comments (-- single-line, --[[ ]] multi-line)
are ignored during parsing, matching how ConvertFrom-Json ignores JSON comments since PowerShell 6.
String parsing supports all three Lua string literal forms: double-quoted ("..."), single-quoted
('...'), and long strings ([[...]]), with escape sequences per §3.1.
Number parsing supports integers, floats, hexadecimal (0x/0X), and scientific notation (both e/E
for decimal and p/P for hex floats), per the numeral grammar in §3.1.
Integer values that fit in [int] return [int]; larger values return [long]; floats return [double].
nil deserializes as $null.
true/false deserialize as [bool].
Bare identifiers that are not true, false, or nil (i.e., variable references) are not valid in a
data-only context and should produce a parse error.
Example usage
# PowerShell object to Lua table constructor@{ name="ElvUI"; version="13.74"; enabled=$true } |ConvertTo-Lua# Output:# {# enabled = true,# name = "ElvUI",# version = "13.74"# }# Compressed output@(1,2,3) |ConvertTo-Lua-Compress
# Output: {1,2,3}# Depth-limited serialization$deep=@{ a=@{ b=@{ c="deep" } } }
$deep|ConvertTo-Lua-Depth 1# Serializes only one level deep, warning about truncation# Force array wrapping for single value"hello"|ConvertTo-Lua-AsArray
# Output:# {# "hello"# }# Lua table constructor to PSCustomObject (default)'{ server = "localhost", port = 8080 }'|ConvertFrom-Lua# Output: PSCustomObject with .server and .port properties# Lua table constructor to ordered hashtable'{ name = "ElvUI", enabled = true }'|ConvertFrom-Lua-AsHashtable
# Output: ordered hashtable# Parsing Lua sequences'{1, 2, 3}'|ConvertFrom-Lua# Output: 1, 2, 3 (enumerated through pipeline)# Round-trip fidelity with -NoEnumerate'{1, 2, 3}'|ConvertFrom-Lua-NoEnumerate |ConvertTo-Lua-Compress
# Output: {1,2,3}# Lua comments are ignored'{ -- config name = "test", --[[ block comment ]] enabled = true}'|ConvertFrom-Lua# Output: PSCustomObject with .name and .enabled properties# Bracket-key notation'{ ["key with spaces"] = "value" }'|ConvertFrom-Lua# Output: PSCustomObject with a "key with spaces" property
Acceptance criteria
ConvertTo-Lua serializes hashtables, ordered dictionaries, PSCustomObjects, arrays, and primitives into
valid Lua table constructor strings per §3.4.9
ConvertFrom-Lua parses Lua table constructor strings into PSCustomObject by default, with -AsHashtable
for ordered hashtable output
All Lua data literal types are supported: tables, strings (all three forms), numbers (integer, float, hex,
scientific notation), booleans, and nil
Lua comments (single-line and multi-line) are correctly ignored during parsing
-Depth on ConvertTo-Lua controls max recursion (default 2, range 0–100, warns on truncation)
-Depth on ConvertFrom-Lua limits max nesting (default 1024, throws on violation)
-Compress, -EnumsAsStrings, -AsArray, and -NoEnumerate behave consistently with their ConvertTo-Json/ConvertFrom-Json counterparts
Properties with $null values are omitted from serialized output (per Lua's nil-means-absent semantics)
Keys use bare identifiers when valid per Lua grammar; bracket-quote notation otherwise
Bare identifier references (variable names) in input produce a parse error
Technical decisions
Design principles:
The Lua 5.4 reference manual is the authoritative specification
for serialization/deserialization behavior — type mapping, string escaping, number formats, table
constructor grammar, and nil semantics.
Parameter names, defaults, and behavioral semantics follow ConvertTo-Json / ConvertFrom-Json
where those do not conflict with Lua's own rules — to lower cognitive burden for PowerShell developers.
Implementation approach: Pure PowerShell recursive-descent parser — no external DLL or .NET assembly
dependency. This keeps the module dependency-free and auditable.
Default output of ConvertFrom-Lua — PSCustomObject. Matching ConvertFrom-Json default behavior. Lua
tables are fundamentally associative arrays, but PSCustomObject provides idiomatic PowerShell dot-notation
access to fields. -AsHashtable returns [ordered]@{} for cases requiring duplicate-key tolerance or
case-sensitive keys (same rationale as ConvertFrom-Json -AsHashtable).
$null / nil handling. Per §2.1: "any key associated to the
value nil is not considered part of the table." Properties with $null values are omitted from serialized
output. On deserialization, explicit nil values in Lua input are preserved as $null in the PowerShell
object (since the user intentionally put them there), even though Lua itself would treat them as absent.
Serializer key formatting. Per §3.1, Lua identifiers are [a-zA-Z_][a-zA-Z0-9_]* and must not be reserved words (and, break, do, else, elseif, end, false, for, function, goto, if, in, local, nil, not, or, repeat, return, then, true, until, while). Keys matching this pattern use bare notation; all others use ["..."].
Number serialization. Lua 5.4 distinguishes integer and float subtypes
(§2.1). PowerShell [int], [long], and other integer types
serialize as Lua integers; [float], [double], and [decimal] serialize as Lua floats with invariant
culture formatting.
Indentation. Fixed 4-space indent (Lua community convention). ConvertTo-Json uses fixed 2-space indent
without exposing a parameter — the same approach is used here.
Function placement:
Public: src/functions/public/Lua/ConvertTo-Lua.ps1 and src/functions/public/Lua/ConvertFrom-Lua.ps1
Parser architecture:ConvertFrom-LuaTable.ps1 contains the main parser entry point plus internal helper
functions that form a recursive-descent parser using script-scoped state. The parser tracks current nesting
depth and throws when -Depth is exceeded.
Serializer architecture:ConvertTo-LuaTable.ps1 recursively walks PowerShell object graphs with
type-specific formatting. Format-LuaKey.ps1 determines whether a key is a valid bare Lua identifier or
needs bracket-quote notation (checking both the identifier pattern and the reserved word list).
Test approach: Pester tests covering:
Primitive type serialization/deserialization for every Lua data type
All three Lua string forms (double-quoted, single-quoted, long strings) with escape sequences
Number variants (integer, float, negative, hex, scientific notation, hex float)
Add ConvertTo-Lua public function in src/functions/public/Lua/ConvertTo-Lua.ps1 with parameters: -InputObject (mandatory, pipeline), -Depth (int, default 2, range 0–100), -Compress (switch), -EnumsAsStrings (switch), -AsArray (switch)
Add ConvertTo-LuaTable private function in src/functions/private/ConvertTo-LuaTable.ps1 — recursive serializer with depth tracking and 4-space indent
Add Format-LuaKey private function in src/functions/private/Format-LuaKey.ps1 — validate keys against Lua identifier pattern and reserved word list
Implement $null omission — skip properties with $null values per Lua nil-means-absent semantics
Implement -EnumsAsStrings — serialize enum values as string names instead of numeric values
Implement -AsArray — wrap single-object output in Lua sequence table
Core functions — ConvertFrom-Lua
Add ConvertFrom-Lua public function in src/functions/public/Lua/ConvertFrom-Lua.ps1 with parameters: -InputObject (mandatory, pipeline), -AsHashtable (switch), -Depth (int, default 1024), -NoEnumerate (switch)
Add ConvertFrom-LuaTable private function in src/functions/private/ConvertFrom-LuaTable.ps1 — recursive-descent parser with depth tracking
Implement default PSCustomObject output; -AsHashtable for [ordered]@{} output
Implement full Lua string parsing — double-quoted, single-quoted, long strings ([[...]]), all escape sequences per §3.1
Implement full Lua number parsing — integer, float, hex (0x), scientific notation (e/E), hex float exponent (p/P)
Context
Lua uses tables as its sole data-structuring mechanism — serving as
arrays, dictionaries, records, and objects. Configuration files for Lua-based applications (e.g., World of
Warcraft addons like ElvUI) store structured data as Lua table constructors. There is currently no PowerShell
module that provides native serialization of PowerShell objects into Lua table format or deserialization of Lua
table strings back into PowerShell objects.
Request
The module should provide two public functions —
ConvertTo-LuaandConvertFrom-Lua— that enable round-tripdata conversion between PowerShell objects and Lua table constructor
strings:
The functions are data serialization/deserialization tools — analogous to
ConvertTo-Json/ConvertFrom-Json.The parameter design follows those cmdlets where the Lua format permits, but Lua's own type system and
table constructor grammar take precedence over JSON alignment.
Lua data types and PowerShell mapping
According to the Lua 5.4 reference manual §2.1, Lua has eight basic
types. Only five are representable as data literals in table constructors:
{ name = "x", size = 10 }PSCustomObject(default) or[ordered]hashtable{ 1, 2, 3 }[object[]]{ "a", name = "x" }PSCustomObjector[ordered]hashtable (sequential values get integer keys)"hello",'hello',[[hello]][string]42,0xFF,-7[int]or[long]3.14,1e10,0x1.fp10[double]true,false[bool]nil$nullThe remaining three Lua types — function, userdata, and thread — cannot be represented as data
literals and are out of scope. This module treats Lua as a data serialization format, not as a programming
language.
Lua table constructor grammar
Per §3.4.9, the grammar for table constructors is:
Key behaviors from the spec:
Name = expis syntactic sugar for["Name"] = expexp(no key) receive sequential integer keys starting at 1,and;are valid field separators — the parser must accept eitherconstructor is undefined")
ConvertTo-Lua— serialization-InputObject[object]-InputObject-Depth[int]2-DepthConvertTo-Jsonbehavior-Compress[switch]-Compress-EnumsAsStrings[switch]-EnumsAsStrings-AsArray[switch]-AsArray{ ... }, even for a single valueNot applicable from
ConvertTo-Json:-EscapeHandling— Lua defines a fixed set of string escape sequences(
\n,\r,\t,\\,\",\',\a,\b,\f,\v,\xXX,\ddd,\u{XXX}). There is noconfigurable escape model.
Serialization rules grounded in the Lua spec:
[a-zA-Z_][a-zA-Z0-9_]*) that are not reserved wordsare emitted as bare keys:
name = "value". All other keys use bracket-quote notation:["key with spaces"] = "value".This follows the
Name = expvs[exp] = expdistinction in the grammar.sequence tables (
{ 1, 2, 3 }); hashtables andPSCustomObjectas key-value tables.PowerShell integers serialize as Lua integers; PowerShell floats/doubles serialize as Lua floats. Numbers
always use invariant culture formatting.
$nullserializes asnil. Per the Lua spec, "any key associated to the value nil is not considered partof the table" — so properties with
$nullvalues are omitted from the output, consistent with how Luatables actually work.
true/false(lowercase, per Lua keywords).{}.ConvertTo-Jsonalso does notexpose indent control.
ConvertFrom-Lua— deserialization-InputObject[string]-InputObject-AsHashtable[switch]-AsHashtable[ordered]hashtable instead of the defaultPSCustomObject. MatchesConvertFrom-Json -AsHashtable(which returnsOrderedHashtablesince PS 7.3)-Depth[int]1024-Depth-NoEnumerate[switch]-NoEnumerate[object[]]instead of enumerating elements through the pipeline. Required for round-trip fidelity of Lua sequencesNot applicable from
ConvertFrom-Json:-DateKind— Lua has no native date/time type (§2.1).Date-like strings remain strings.
Deserialization rules grounded in the Lua spec:
PSCustomObject(matchingConvertFrom-Json). Use-AsHashtablefor ordered hashtableoutput.
expfield form) deserialize as[object[]]arrays.Name = expor[exp] = exp) deserialize asPSCustomObject(or ordered hashtable with
-AsHashtable).PSCustomObjector ordered hashtable,with sequential values assigned integer keys starting at 1 — matching the Lua spec behavior for the
expfield form.
--single-line,--[[ ]]multi-line)are ignored during parsing, matching how
ConvertFrom-Jsonignores JSON comments since PowerShell 6."..."), single-quoted(
'...'), and long strings ([[...]]), with escape sequences per §3.1.0x/0X), and scientific notation (bothe/Efor decimal and
p/Pfor hex floats), per the numeral grammar in §3.1.Integer values that fit in
[int]return[int]; larger values return[long]; floats return[double].nildeserializes as$null.true/falsedeserialize as[bool].true,false, ornil(i.e., variable references) are not valid in adata-only context and should produce a parse error.
Example usage
Acceptance criteria
ConvertTo-Luaserializes hashtables, ordered dictionaries,PSCustomObjects, arrays, and primitives intovalid Lua table constructor strings per §3.4.9
ConvertFrom-Luaparses Lua table constructor strings intoPSCustomObjectby default, with-AsHashtablefor ordered hashtable output
scientific notation), booleans, and nil
-DepthonConvertTo-Luacontrols max recursion (default 2, range 0–100, warns on truncation)-DepthonConvertFrom-Lualimits max nesting (default 1024, throws on violation)-Compress,-EnumsAsStrings,-AsArray, and-NoEnumeratebehave consistently with theirConvertTo-Json/ConvertFrom-Jsoncounterparts$nullvalues are omitted from serialized output (per Lua's nil-means-absent semantics)$obj | ConvertTo-Lua | ConvertFrom-Luareturns equivalent dataTechnical decisions
Design principles:
for serialization/deserialization behavior — type mapping, string escaping, number formats, table
constructor grammar, and nil semantics.
ConvertTo-Json/ConvertFrom-Jsonwhere those do not conflict with Lua's own rules — to lower cognitive burden for PowerShell developers.
Implementation approach: Pure PowerShell recursive-descent parser — no external DLL or .NET assembly
dependency. This keeps the module dependency-free and auditable.
Default output of
ConvertFrom-Lua—PSCustomObject. MatchingConvertFrom-Jsondefault behavior. Luatables are fundamentally associative arrays, but
PSCustomObjectprovides idiomatic PowerShell dot-notationaccess to fields.
-AsHashtablereturns[ordered]@{}for cases requiring duplicate-key tolerance orcase-sensitive keys (same rationale as
ConvertFrom-Json -AsHashtable).$null/nilhandling. Per §2.1: "any key associated to thevalue nil is not considered part of the table." Properties with
$nullvalues are omitted from serializedoutput. On deserialization, explicit
nilvalues in Lua input are preserved as$nullin the PowerShellobject (since the user intentionally put them there), even though Lua itself would treat them as absent.
Serializer key formatting. Per §3.1, Lua identifiers are
[a-zA-Z_][a-zA-Z0-9_]*and must not be reserved words (and,break,do,else,elseif,end,false,for,function,goto,if,in,local,nil,not,or,repeat,return,then,true,until,while). Keys matching this pattern use bare notation; all others use["..."].Number serialization. Lua 5.4 distinguishes integer and float subtypes
(§2.1). PowerShell
[int],[long], and other integer typesserialize as Lua integers;
[float],[double], and[decimal]serialize as Lua floats with invariantculture formatting.
Indentation. Fixed 4-space indent (Lua community convention).
ConvertTo-Jsonuses fixed 2-space indentwithout exposing a parameter — the same approach is used here.
Function placement:
src/functions/public/Lua/ConvertTo-Lua.ps1andsrc/functions/public/Lua/ConvertFrom-Lua.ps1src/functions/private/ConvertTo-LuaTable.ps1,src/functions/private/ConvertFrom-LuaTable.ps1,src/functions/private/Format-LuaKey.ps1Parser architecture:
ConvertFrom-LuaTable.ps1contains the main parser entry point plus internal helperfunctions that form a recursive-descent parser using script-scoped state. The parser tracks current nesting
depth and throws when
-Depthis exceeded.Serializer architecture:
ConvertTo-LuaTable.ps1recursively walks PowerShell object graphs withtype-specific formatting.
Format-LuaKey.ps1determines whether a key is a valid bare Lua identifier orneeds bracket-quote notation (checking both the identifier pattern and the reserved word list).
Test approach: Pester tests covering:
--, multi-line--[[ ]])$null/nil omission behavior-Depth,-Compress,-EnumsAsStrings,-AsArray,-AsHashtable,-NoEnumerateImplementation plan
Core functions —
ConvertTo-LuaConvertTo-Luapublic function insrc/functions/public/Lua/ConvertTo-Lua.ps1with parameters:-InputObject(mandatory, pipeline),-Depth(int, default 2, range 0–100),-Compress(switch),-EnumsAsStrings(switch),-AsArray(switch)ConvertTo-LuaTableprivate function insrc/functions/private/ConvertTo-LuaTable.ps1— recursive serializer with depth tracking and 4-space indentFormat-LuaKeyprivate function insrc/functions/private/Format-LuaKey.ps1— validate keys against Lua identifier pattern and reserved word list$nullomission — skip properties with$nullvalues per Lua nil-means-absent semantics-Depth— emit warning when input object nesting exceeds specified depth-EnumsAsStrings— serialize enum values as string names instead of numeric values-AsArray— wrap single-object output in Lua sequence tableCore functions —
ConvertFrom-LuaConvertFrom-Luapublic function insrc/functions/public/Lua/ConvertFrom-Lua.ps1with parameters:-InputObject(mandatory, pipeline),-AsHashtable(switch),-Depth(int, default 1024),-NoEnumerate(switch)ConvertFrom-LuaTableprivate function insrc/functions/private/ConvertFrom-LuaTable.ps1— recursive-descent parser with depth trackingPSCustomObjectoutput;-AsHashtablefor[ordered]@{}output[[...]]), all escape sequences per §3.10x), scientific notation (e/E), hex float exponent (p/P)--) and multi-line (--[[ ]])-Depth— throw terminating error when nesting exceeds allowed depth-NoEnumerate— output arrays as single[object[]]without pipeline enumerationtrue/false/nil)Tests
tests/Lua.Tests.ps1with comprehensive test coveragetests/data/(Lua/JSON pairs for complex structure validation)$nullomission in serialization-Depthbehavior on both functions (warning onConvertTo-Lua, error onConvertFrom-Lua)-Compress,-EnumsAsStrings,-AsArray,-AsHashtable,-NoEnumerateDocumentation
README.mdwith module description, installation, and usage examplesexamples/General.ps1withConvertTo-LuaandConvertFrom-Luaexamplessrc/manifest.psd1as needed