13 KiB
ArchieML-Veronica Language Reference
Veronica is a more specific dialect of ArchieML 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
- Objects
- Arrays
- Multiline Values
- Comments and Ignored Content
- Escaping
- 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:
{
"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 (
Titleandtitleare 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:
{
"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:
{
"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:
- Empty braces
{}- closes the current object - Opening a new object at the same level
- 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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
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:
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:
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:
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:
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:
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:
- Named closures -
{/name}and[/name]to exit specific nesting levels - Explicit multiline syntax -
key::/::keydelimiters for unambiguous multiline values - Flexible array items - Any redefined key starts a new array item, not just the first key
- Lifecycle hooks -
onEnterandonExitcallbacks for validation and transformation - Value transformation -
onFieldNameandonValuehooks 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.