# ArchieML-Veronica Language Reference Veronica is a more specific dialect of [ArchieML](https://archieml.org) designed for newsrooms and content editors. While ArchieML is "forgiving," Veronica makes some syntax more explicit to prevent common parsing errors, especially when working with multiline content and nested structures. ## Table of Contents - [Keys and Values](#keys-and-values) - [Objects](#objects) - [Nested Objects](#nested-objects) - [Named Closures](#named-closures-veronica-extension) - [Arrays](#arrays) - [Arrays of Objects](#arrays-of-objects) - [Arrays of Strings (Simple Arrays)](#arrays-of-strings-simple-arrays) - [Freeform Arrays](#freeform-arrays) - [Nested Arrays](#nested-arrays) - [Named Array Closures](#named-array-closures-veronica-extension) - [Repeating Keys in Arrays](#repeating-keys-in-arrays-veronica-extension) - [Multiline Values](#multiline-values) - [Veronica Multiline Syntax](#veronica-multiline-syntax-veronica-extension) - [ArchieML Multiline Syntax](#archieml-multiline-syntax) - [Comments and Ignored Content](#comments-and-ignored-content) - [Escaping](#escaping) - [Hooks and Customization](#hooks-and-customization) ## Keys and Values The simplest form of ArchieML is a key-value pair. Keys are defined by a colon, with the key on the left and the value on the right. ``` title: My Article author: Jane Smith published: 2024-01-15 ``` **Result:** ```json { "title": "My Article", "author": "Jane Smith", "published": "2024-01-15" } ``` ### Key Rules - Keys can contain letters, numbers, underscores, hyphens, and Unicode characters - Keys **cannot** contain: whitespace, `{`, `}`, `[`, `]`, `:`, `.`, or `+` - Keys are case-sensitive (`Title` and `title` are different) - Whitespace around keys and values is automatically trimmed ### Ignored Content Any text that doesn't match ArchieML syntax is ignored, allowing you to include notes and comments freely: ``` This is just a note that will be ignored. title: My Article Here's another note about the article. author: Jane Smith ``` ## Objects ### Dot Notation You can create nested objects using dot notation: ``` colors.red: #ff0000 colors.green: #00ff00 colors.blue: #0000ff ``` **Result:** ```json { "colors": { "red": "#ff0000", "green": "#00ff00", "blue": "#0000ff" } } ``` ### Object Blocks For more complex objects, use curly braces: ``` {colors} red: #ff0000 green: #00ff00 blue: #0000ff {} ``` **Result:** Same as above. ### Nested Objects Prepend a period (`.`) to a block name to nest it within the current object: ``` {author} name: Jane Smith email: jane@example.com {.social} twitter: @janesmith github: janesmith {} bio: Award-winning journalist {} ``` **Result:** ```json { "author": { "name": "Jane Smith", "email": "jane@example.com", "social": { "twitter": "@janesmith", "github": "janesmith" }, "bio": "Award-winning journalist" } } ``` ### Closing Objects You can close an object in several ways: 1. **Empty braces** `{}` - closes the current object 2. **Opening a new object** at the same level 3. **Named closure** (Veronica extension) - see below ### Named Closures (Veronica Extension) Veronica extends ArchieML with named closures, allowing you to close a specific object by name, even if you're nested several levels deep: ``` {outer} value: test {.middle} value: nested {.inner} value: deep {/middle} This closes middle and inner, returning to outer scope. stillOuter: yes {} ``` **Result:** ```json { "outer": { "value": "test", "middle": { "value": "nested", "inner": { "value": "deep" } }, "stillOuter": "yes" } } ``` **Important:** The slash must be flush with the opening brace: `{/name}` works, but `{ /name }` does not. ## Arrays ### Arrays of Objects Arrays are defined using square brackets. The first key that repeats signals the start of a new item: ``` [people] name: Alice age: 30 name: Bob age: 25 [] ``` **Result:** ```json { "people": [ {"name": "Alice", "age": 30}, {"name": "Bob", "age": 25} ] } ``` ### Arrays of Strings (Simple Arrays) For simple lists of strings, use asterisks: ``` [tags] * news * technology * AI [] ``` **Result:** ```json { "tags": ["news", "technology", "AI"] } ``` ### Freeform Arrays Freeform arrays (marked with `[+arrayName]`) preserve the order of different types of content. They're useful for mixed content like articles with text, images, and pull quotes: ``` [+content] This is a paragraph of text. Another paragraph here. {.image} src: photo.jpg caption: A beautiful photo {} More text after the image. {.quote} text: An inspiring quotation author: Famous Person {} [] ``` **Result:** ```json { "content": [ {"type": "text", "value": "This is a paragraph of text."}, {"type": "text", "value": "Another paragraph here."}, {"type": "image", "value": {"src": "photo.jpg", "caption": "A beautiful photo"}}, {"type": "text", "value": "More text after the image."}, {"type": "quote", "value": {"text": "An inspiring quotation", "author": "Famous Person"}} ] } ``` ### Nested Arrays Prepend a period to an array name to nest it: ``` [sections] title: Introduction [.subsections] heading: Background content: Context here [] heading: Methodology content: How we did it [] [/sections] ``` **Result:** ```json { "sections": [ { "title": "Introduction", "subsections": [ {"heading": "Background", "content": "Context here"}, {"heading": "Methodology", "content": "How we did it"} ] } ] } ``` ### Named Array Closures (Veronica Extension) Like objects, arrays support named closures to jump out of nested structures: ``` [parent] value: test [.nested] * one * two [/parent] This is outside the parent array. ``` This is especially useful when you have deeply nested arrays and want to exit multiple levels at once. ### Repeating Keys in Arrays (Veronica Extension) **This is a key difference from standard ArchieML.** In Veronica, arrays start a new item when **any** key is redefined, not just the first key: ``` [items] name: First description: First item color: red description: Second item name: Second color: blue [] ``` **Result:** ```json { "items": [ {"name": "First", "description": "First item", "color": "red"}, {"name": "Second", "description": "Second item", "color": "blue"} ] } ``` In standard ArchieML, the second item would need to redefine `name` (the first key) to start a new item. Veronica is more flexible: redefining `description` also triggers a new item. ## Multiline Values ### Veronica Multiline Syntax (Veronica Extension) Veronica introduces an explicit multiline syntax that's less ambiguous than ArchieML's `:end` syntax: ``` description:: This is the first line of my description. This is the second paragraph. This can contain [brackets] and {braces} safely. ::description ``` **Result:** ```json { "description": "This is the first line of my description.\n\nThis is the second paragraph.\n\nThis can contain [brackets] and {braces} safely." } ``` **Syntax:** - Open with `key::` (key followed by double colon) - Write your content on following lines - Close with `::key` (double colon followed by the same key name) This syntax is more explicit and helps prevent accidentally consuming subsequent content. ### ArchieML Multiline Syntax Veronica still supports the traditional ArchieML multiline syntax with `:end`: ``` description: This is a multiline value. It continues until :end is found. :end ``` **Important:** Within multiline blocks, ArchieML syntax is **not** parsed. To include literal backslashes or the `:end` marker, use a backslash escape: ``` code: To end a multiline, use \:end A literal backslash: \\ :end ``` ## Comments and Ignored Content ### Block Comments Use `:skip` and `:endskip` to comment out entire sections: ``` title: My Article :skip This entire section is ignored. author: Will Not Parse {test} {} :endskip published: 2024-01-15 ``` **Result:** ```json { "title": "My Article", "published": "2024-01-15" } ``` ### Stop Parsing Use `:ignore` to immediately stop parsing. Everything after `:ignore` is ignored: ``` title: My Article author: Jane Smith :ignore This and everything below is completely ignored. Nothing here will be parsed. ``` **Result:** ```json { "title": "My Article", "author": "Jane Smith" } ``` ## Escaping Use a backslash (`\`) to escape special ArchieML characters when they appear at the start of a line in a multiline context: ``` description: \[This is not an array] \{This is not an object} \:end is not the end marker The real end: :end ``` **Note:** Escaping only works in multiline contexts and only for characters at the start of a line. Outside of multiline blocks, special characters are generally ignored if they don't form valid syntax. ## Hooks and Customization Veronica provides several hooks for customizing parsing behavior: ### `onFieldName(name: string) => string` Transform field names during parsing. Useful for normalizing keys: ```javascript const result = parse(text, { onFieldName: (name) => name.toLowerCase() }); ``` This is particularly helpful when working with Google Docs, which may capitalize the first word of a line. ### `onValue(value: any, key: string) => any` Transform values during parsing. Useful for type coercion: ```javascript const result = parse(text, { onValue: (value, key) => { // Auto-convert boolean strings if (value === "true") return true; if (value === "false") return false; // Auto-convert numbers if (/^\d+$/.test(value)) return parseInt(value); if (/^\d+\.\d+$/.test(value)) return parseFloat(value); // Auto-parse ISO dates if (/^\d{4}-\d{2}-\d{2}/.test(value)) { return new Date(value); } return value; } }); ``` ### `onEnter(keypath: string[], item: any) => void` Called when entering an object or array. The `keypath` is an array of strings representing the path from the root: ```javascript const result = parse(text, { onEnter: (keypath, item) => { console.log(`Entering: ${keypath.join('.')}`); console.log(`Type: ${Array.isArray(item) ? 'array' : 'object'}`); } }); ``` ### `onExit(keypath: string[], item: any) => any` Called when exiting an object or array. This is ideal for validation and adding computed properties. If you return a value, it replaces the item in the output: ```javascript const result = parse(text, { onExit: (keypath, item) => { // Validate required fields if (keypath[0] === "person" && !item.name) { throw new Error("Person must have a name"); } // Add computed properties if (keypath[0] === "person" && item.firstName && item.lastName) { item.fullName = `${item.firstName} ${item.lastName}`; } return item; // Return the modified item } }); ``` **Example with validation:** ```javascript const text = ` {author} firstName: Jane lastName: Smith {/author} `; const result = parse(text, { onExit: (keypath, item) => { if (keypath[0] === "author") { if (!item.firstName || !item.lastName) { throw new Error("Author must have both firstName and lastName"); } // Add computed fullName item.fullName = `${item.firstName} ${item.lastName}`; } return item; } }); // Result: { author: { firstName: "Jane", lastName: "Smith", fullName: "Jane Smith" } } ``` ### `verbose: boolean` Enable verbose logging to see detailed parsing information: ```javascript const result = parse(text, { verbose: true }); ``` ## Complete Example Here's a comprehensive example showing many Veronica features: ``` title: Understanding Veronica subtitle: A Guide to Structured Content published: 2024-01-15 {metadata} tags.primary: archieml tags.secondary: parsing wordCount: 1500 {/metadata} intro:: This is a multiline introduction to Veronica. It supports multiple paragraphs and preserves formatting. ::intro [sections] heading: Introduction body: Welcome to Veronica, a dialect of ArchieML. heading: Features body: Veronica adds several improvements over standard ArchieML. [.examples] * Named closures * Explicit multiline syntax * Better array handling [/sections] [+mixedContent] This is a text paragraph. {.callout} type: warning message: Remember to close your arrays! {} Another text paragraph here. [] {author} firstName: Jane lastName: Smith email: jane@example.com {.social} twitter: @janesmith github: janesmith {/author} footer: Copyright 2024 ``` This example demonstrates: - Simple key-value pairs - Object blocks with nested objects - Dot notation for nested keys - Veronica multiline syntax (`key::` / `::key`) - Arrays of objects with nested arrays - Freeform arrays with mixed content - Named closures (`{/author}`, `[/sections]`) ## Summary of Veronica Extensions Veronica extends ArchieML with these key features: 1. **Named closures** - `{/name}` and `[/name]` to exit specific nesting levels 2. **Explicit multiline syntax** - `key::` / `::key` delimiters for unambiguous multiline values 3. **Flexible array items** - Any redefined key starts a new array item, not just the first key 4. **Lifecycle hooks** - `onEnter` and `onExit` callbacks for validation and transformation 5. **Value transformation** - `onFieldName` and `onValue` hooks for custom processing These changes make Veronica more predictable and robust when working with complex structured content, especially in collaborative editing environments like Google Docs.