Syntax
Base Syntax (JSON5)
Section titled “Base Syntax (JSON5)”LPML is built on JSON5, which extends JSON with:
Comments
Section titled “Comments”// Single-line comments/* Multi-line comments */Unquoted Keys
Section titled “Unquoted Keys”{ name: "Gesslar", // Simple identifier my cool key: "value", // Spaces are fine (YAML-style) admin-heal: "sound.wav", // So are hyphens café: "value", // ...and UTF-8 letters additional ids: [1, 2, 3], // Works great! "key: with colon": "value" // Quote only for a literal ':'}Spacey Keys: LPML borrows YAML’s unquoted-key feel — write the key naturally and end it with :. The parser takes everything up to the first : as the key name and trims surrounding whitespace, so spaces, hyphens, dots, digits, and UTF-8 letters are all valid unquoted. Reach for quotes only when the key must contain a literal : — the one character that always ends the key.
Trailing Commas
Section titled “Trailing Commas”{ foo: "bar", baz: 42, // ← trailing comma is fine}Single-Quoted Strings
Section titled “Single-Quoted Strings”name: 'single quotes work too'Number Formats
Section titled “Number Formats”{ hex: 0xFF, // Hexadecimal octal: 0o77, // Octal binary: 0b1010, // Binary decimal: 3.14, // Standard leadingDot: .5, // Leading decimal point trailingDot: 5., // Trailing decimal point positive: +42 // Plus sign allowed}LPML Extensions
Section titled “LPML Extensions”Spacey Keys (YAML-style)
Section titled “Spacey Keys (YAML-style)”Unlike JSON5 — which only allows identifier keys to go unquoted — LPML lets you write almost any key without quotes, YAML-style:
{ // All of these are valid, unquoted: simple: "value", two words: "value", multiple word key: "value", crafting material: "leather", admin-heal: "sound.wav", // hyphens level-2: "value", // hyphens and digits café: "value", // UTF-8 letters
// Quote a key only when it must contain a literal colon: "key: with colon": "value", 'single quoted': "value"}How it works:
- The parser reads everything from the start of the key until the first
:, then trims surrounding whitespace - Any character is allowed in between — spaces, hyphens, dots, digits, UTF-8 letters
- The first
:always ends the key. This is where LPML differs from YAML, which ends a key only at a colon followed by whitespace; to put a literal:inside a key, quote it - Quoted keys are the escape hatch for anything the bare form can’t express
Examples:
{ // Character data with natural keys hit points: 100, mana points: 50, experience points: 1500,
// Crafting materials crafting material: "yes", material type: "leather", quality level: "high"}String Concatenation
Section titled “String Concatenation”Adjacent strings are automatically concatenated with intelligent spacing:
// Concatenation with spaces (default)bio: "A seasoned adventurer" "from the West."// Result: "A seasoned adventurer from the West."
// Preserve newlines when strings end with \npoem: "Line 1\n" "Line 2\n" "Line 3"// Result: "Line 1\nLine 2\nLine 3"
// Mixed modetext: "First paragraph." "Second sentence.\n" "New paragraph."// Result: "First paragraph. Second sentence.\nNew paragraph."Rules:
- If a string ends with
\n, the next string concatenates without adding a space - Otherwise, strings are joined with a single space
- Works with any quote style:
"...",'...'
Multiline Strings
Section titled “Multiline Strings”Strings can span multiple lines in the source:
// Source spans multiple linesdescription: "This is a long description that spans multiple lines."// Result: "This is a long description that spans multiple lines."Folding behavior:
- Actual newlines in the source (pressing Enter) are converted to spaces
- Escape sequences like
\nare preserved as actual newlines
File Includes
Section titled “File Includes”A string value beginning with # followed by a path denotes a file include:
{ // Absolute path config: "#/home/user/config.lpml",
// Relative path stats: "#./stats.lpml", parent: "#../shared.lpml",}Specified behavior:
- A conforming implementation SHOULD replace the
#pathvalue with the parsed contents of the referenced file - Included files SHOULD be recursively processed (supporting nested includes)
- How relative paths are resolved is implementation-defined
- If a file cannot be found, an implementation SHOULD keep the include string as its literal value (graceful degradation)
- Include syntax works with both double and single quotes
Escaping includes:
{ channel: "\#general" // → "#general" (literal)}The \# escape prevents include processing and produces a literal # in the resulting string.