A Human-Friendly Data Serialization Format
Quick reference for UP syntax rules and common patterns.
UP is whitespace-delimited. The basic unit is:
key value
Option 1: Use quotes (traditional)
name "John Doe"
description "This is a description"
Option 2: Use : suffix (line-oriented)
name: John Doe
description: This is a complete sentence with spaces.
The : suffix after a key enables line-oriented value mode, capturing everything after the colon as the value.
❌ WRONG (without quotes or colon):
name John Doe
description This is a description
This parses as:
name: "John"
Doe: (new key with no value)
description: "This"
is: "a"
description: (duplicate key)
name Alice
status active
environment production
Type annotations work with all value syntaxes:
# With whitespace-delimited values
port!int 8080
enabled!bool true
timeout!dur 30s
version "1.2.3"
# With line-oriented values (: suffix)
count!int: 42
url!uri: https://example.com/path?query=value
name!string: John Doe
# Special annotation: !quoted preserves or adds literal quotes
title!quoted: "Senior Engineer" # value is: "Senior Engineer" (with quotes)
name!quoted: Alice # value is: "Alice" (quotes added)
Note: The !quoted annotation is used with line-oriented values (: suffix) to preserve or add literal quote characters in the value, since quotes are normally stripped in line-oriented mode.
email alice@example.com
repository https://github.com/user/repo
website https://example.com/path/to/page
The : in URLs is treated as a literal character, not a key suffix, because it’s not at the end of the key token.
: SuffixThe : suffix enables capturing the entire rest of the line as a value:
message: Hello, World!
description: This is a complete sentence.
url: https://example.com/path?query=value
note: Values can contain any characters, including "quotes" and :colons
Important: In line-oriented mode, surrounding quotes are automatically stripped by default:
# Line-oriented: surrounding quotes are stripped
title: "Senior Engineer" # value is: Senior Engineer (no quotes)
# Traditional: quotes are delimiters (same result)
title "Senior Engineer" # value is: Senior Engineer (no quotes)
# To preserve literal quotes, use !quoted annotation
title!quoted: "Senior Engineer" # value is: "Senior Engineer" (with quotes)
title!quoted: Engineer # value is: "Engineer" (quotes added)
Comments and Line-Oriented Values:
In line-oriented mode, # starts a comment, ending the value:
# The value ends at #, comment begins
message: Hello, World! # This is a comment
# Result: value is "Hello, World!" (comment not included)
# To include literal # in value, use traditional quoted syntax
tag "Use #hashtags freely" # value is: Use #hashtags freely
tag: Use #hashtags freely # value is: Use (comment starts at #)
# Or escape within multiline strings
description ```
Use #hashtags and # symbols freely
in multiline strings
**Rule:** Use traditional quoted syntax when you need literal `#` characters in values without triggering comments.
This works with **any type annotation**:
```up
name!string: John Doe
count!int: 42
enabled!bool: true
url!uri: https://api.example.com/v1/endpoint
# With !quoted to preserve literal quotes
note!quoted: "Important" # value is: "Important" (with quotes)
With dedented multiline strings:
description!4:
This is a dedented
multiline value
with consistent indentation
server {
host localhost
port!int 8080
name "My Server"
}
# Multiline
tags [
production
web
api
]
# Inline
tags [production, web, api]
description ```
This is a multiline string.
Whitespace is preserved.
No quotes needed inside.
notes ``` Multi-line notes can contain anything including “quotes”
For structured documents, use dedent to maintain indentation:
❌ BAD (ruins indentation):
metadata {
notes ```
This breaks the visual structure
because it's not indented
}
✅ **GOOD (maintains structure):**
```up
metadata {
notes!2 ```
This maintains the visual structure
by using dedent to remove leading spaces
```
}
Dedent removes N spaces from each line:
# !4 removes 4 spaces from each line
description!4 ```
Line 1 has 4 leading spaces
Line 2 also has 4 leading spaces
Result: both lines flush left in output
```
# Choose dedent based on your indentation level
block {
field!2 ```
Content indented 2 spaces
```
nested {
field!4 ```
Content indented 4 spaces
```
}
}
The : character has different meanings depending on its context:
: as Key Suffix → Line-Oriented Value ModeWhen : appears immediately after a key (with optional type annotation), it enables line-oriented value capture:
message: Hello, World!
name!string: John Doe
url!uri: https://example.com/path
The entire rest of the line becomes the value.
: Within Values → Literal CharacterWhen : appears within a value (not as a key suffix), it’s a literal character:
website https://example.com # value is "https://example.com"
time 10:30:45 # value is "10:30:45"
ratio 3:4 # value is "3:4"
URLs work naturally without quotes because the : is part of the value, not a key suffix.
: Within Keys (Not Suffix) → Literal CharacterKeys can contain : as long as it’s not the final character:
http://example.com some-value # key="http://example.com", value="some-value"
my:key:with:colons value # key="my:key:with:colons", value="value"
: → Must Be QuotedTo use a key that ends with a literal : character, quote the key:
"key:" full line value # key="key:", value="full line value"
"note:" This is the value # key="note:", value="This is the value"
"http://example.com:" value # key="http://example.com:", value="value"
When using : suffix (line-oriented mode), surrounding quotes are automatically stripped by default:
# Line-oriented mode: surrounding quotes are stripped
title: "Senior Software Engineer" # value is: Senior Software Engineer (no quotes)
note: This has "embedded" quotes # value is: This has "embedded" quotes
# Traditional mode: quotes are delimiters (same result)
title "Senior Software Engineer" # value is: Senior Software Engineer (no quotes)
# To preserve literal quotes, use !quoted annotation
title!quoted: "Senior Engineer" # value is: "Senior Engineer" (with quotes)
title!quoted: Engineer # value is: "Engineer" (quotes added)
note!quoted: Message # value is: "Message" (quotes added)
Why this design?
title: "Value" and title "Value" produce the same result!quoted when you explicitly need literal quote characters in the valueIn line-oriented mode, # starts a comment, which ends the value capture:
# Comment starts at #
message: Hello, World! # This is a comment
# Result: value is "Hello, World!" (comment excluded)
line: Text before # text after
# Result: value is "Text before " (everything after # is a comment)
# Quotes are stripped before comment processing
title: "Senior Engineer" # This is a comment
# Result: value is "Senior Engineer" (quotes stripped, comment excluded)
# To include literal # in values, use traditional quoted syntax
hashtag "Use #hashtags" # value is: Use #hashtags
hashtag: Use #hashtags # value is: Use (comment starts at #)
# Alternative: use multiline strings for content with # symbols
content ```
This can contain #hashtags
and # symbols freely
**When to use each approach:**
| Need | Solution | Example |
|------|----------|---------|
| Multi-word value, no `#` | Line-oriented | `title: Senior Engineer` |
| Multi-word value with `#` | Quoted string | `tag "Use #hashtags"` |
| Multi-word with `#` and `"` | Multiline | See above |
**Why this design?**
- Line-oriented mode captures the **raw text** after the colon
- Quotes lose their special meaning in line-oriented mode
- This is consistent with "entire rest of line becomes the value"
- If you want quotes as delimiters, use traditional syntax without `:`
**When to use each:**
| Syntax | Use Case | Result |
|--------|----------|--------|
| `key: value with spaces` | Natural text without quotes | `value with spaces` |
| `key "value with spaces"` | Traditional quoted string | `value with spaces` |
| `key: "value"` | Literal quotes in value | `"value"` (quotes included) |
### Summary Table
| Context | Example | Meaning |
|---------|---------|---------|
| After key (suffix) | `name: John Doe` | Line-oriented value mode |
| Within value | `https://example.com` | Literal `:` character |
| Within key (not suffix) | `my:key value` | Literal `:` character |
| Key ending in `:` | `"key:" value` | Must quote key |
## Common Patterns
### Schema Definitions
✅ **CORRECT:**
```up
namespace greeting
version 1.2.3
description "Simple greeting generator"
functions {
hello {
description "Generate a greeting"
params {
name!string {
description "Name to greet"
default World
required!bool false
}
}
returns!string {
description "Greeting message"
example "Hello, Alice!"
}
}
}
metadata {
author "UP Team"
license MIT
safe!bool true
}
✅ CORRECT:
policy {
verify_hashes!bool true
require_signatures!bool true
}
trusted_signers {
up_core {
name "UP Core Team"
email security@uplang.org
trust_level official
}
}
✅ CORRECT:
namespaces {
greeting {
version 1.2.3
files {
executable {
path ./up-namespaces/greeting
hash sha256:abc123...
}
}
verified!bool true
source local
}
}
✅ CORRECT:
users [
{
name "Alice Johnson"
email alice@example.com
role admin
}
{
name "Bob Smith"
email bob@example.com
role user
}
]
Simple identifiers:
name Alice
port 8080
enabled true
Hyphenated:
app-name MyApp
max-connections 100
user-id 12345
Underscored:
app_name MyApp
max_connections 100
user_id 12345
URLs as keys (unquoted):
https://example.com some-value
http://api.example.com/endpoint config-data
Quoted keys (special characters or ending with :):
"key with spaces" value
"key!with!bangs" value
"key:" full line value after colon
"note:" This is captured as the value
Single-word (no quotes needed):
name Alice
status active
environment production
Quoted multi-word (traditional):
name "John Doe"
description "This is a description"
message "Hello, World!"
Line-oriented with : suffix (modern):
name: John Doe
description: This is a complete sentence with multiple words.
message: Hello, World!
note: Values can contain "quotes" and special chars (quotes stripped)
With !quoted to preserve literal quotes:
title!quoted: "Senior Engineer" # value is: "Senior Engineer" (with quotes)
name!quoted: Alice # value is: "Alice" (quotes added)
note!quoted: Important # value is: "Important" (quotes added)
URLs (no quotes needed):
website https://example.com
api https://api.example.com/v1/endpoint?key=value
repository https://github.com/user/repo
With type annotations (whitespace-delimited):
port!int 8080
enabled!bool true
timeout!dur 30s
score!float 98.6
With type annotations and : suffix:
name!string: John Doe
count!int: 42
url!uri: https://example.com/path
enabled!bool: true
message!quoted: "Important" # combines type annotation with !quoted
With type annotations and multiline/blocks:
description!4 ```
This is a dedented
multiline value
```
config!json {
host localhost
port!int 8080
}
items!list [
one
two
three
]
name "Hello World" (or use name: Hello World)value "!@#$%"#: tag "Use #hashtags" (can’t use : suffix)code "123" (string 123, not number)value ""cmd "$variable"status productionport!int 8080enabled!bool truewebsite https://example.com: suffix instead): name: John Doeversion "1.2.3" or version 1.2.3path "./my file.txt" or path ./myfile.txtIn line-oriented mode (with : suffix), surrounding quotes are automatically stripped:
# These produce the SAME result:
title "Senior Engineer" # value: Senior Engineer (no quotes)
title: Senior Engineer # value: Senior Engineer (no quotes)
title: "Senior Engineer" # value: Senior Engineer (quotes stripped)
# To preserve literal quotes, use !quoted annotation:
title!quoted: "Senior Engineer" # value: "Senior Engineer" (with quotes)
title!quoted: Engineer # value: "Engineer" (quotes added)
Rule: Use !quoted annotation when you need literal quote characters in the value.
In line-oriented mode, # starts a comment, ending the value:
# Comment starts at #
message: Hello, World! # This is a comment
# Result: value is "Hello, World!"
# Quotes are stripped before comment processing
title: "Value" # comment
# Result: value is "Value" (quotes stripped, comment excluded)
# To include # in values, use quoted strings
tag "Use #hashtags" # value: Use #hashtags
tag: Use #hashtags # value: Use (# starts comment)
Rule: Use traditional quoted syntax when values contain # characters.
"key with spaces" value"key!with!bangs" value, "key#hash" value:: "key:" value, "note:" This is the valuename, port, enabledapp-name, max-connectionsapp_name, max_connections: not as suffix): https://example.com value❌ WRONG:
description This is wrong
name John Doe
message Hello World
✅ CORRECT (with quotes):
description "This is correct"
name "John Doe"
message "Hello World"
✅ CORRECT (with colon suffix):
description: This is correct
name: John Doe
message: Hello World
⚠️ Works but unnecessary:
environment "production"
status "active"
✅ Better (simpler):
environment production
status active
❌ WRONG (parsed as strings):
port 8080
enabled true
✅ CORRECT:
port!int 8080
enabled!bool true
❌ WRONG:
description "Line 1
Line 2
Line 3"
✅ CORRECT:
description ```
Line 1
Line 2
Line 3
## Quick Reference Table
| Value Type | Needs Quotes? | Example |
|------------|---------------|---------|
| Single word | No | `status active` |
| Multi-word (quoted) | **YES** | `name "John Doe"` |
| Multi-word (colon) | No | `name: John Doe` |
| With `#` symbol | **YES** | `tag "Use #hashtags"` |
| Colon + quotes | No (quotes stripped) | `name: "John"` → value is `John` |
| Colon + `#` | Ends at `#` | `msg: Hi # comment` → value is `Hi ` |
| With !quoted | Preserves/adds quotes | `name!quoted: John` → value is `"John"` |
| URL/Email | No | `email user@example.com` |
| Number | No (+ type) | `port!int 8080` |
| Boolean | No (+ type) | `enabled!bool true` |
| Version | Optional | `version "1.2.3"` or `version 1.2.3` |
| Path (no spaces) | No | `path ./file.txt` |
| Path (with spaces) | **YES** | `path "./my file.txt"` |
| Multiline | Use ` ``` ` | See multiline syntax |
| Empty string | **YES** | `value ""` |
| With `:` suffix | No | `message: Any text here` |
## Document Lint Directives
UP supports document-level lint directives to enforce coding standards and catch potential errors. These directives are specified at the top of the document using the `!lint` annotation.
### Syntax
```up
!lint {
rule-name!level error
another-rule!level warning
}
# Your document content follows
name: John Doe
| Rule | Default | Description |
|---|---|---|
require-line-oriented |
false |
All multi-word values must use : suffix syntax |
require-quoted |
false |
All multi-word values must use quoted syntax |
no-empty-values |
false |
Keys must have non-empty values |
no-ambiguous-keys |
true |
Warn on keys that could be values (e.g., name John Doe) |
require-type-annotations |
false |
Non-string values must have explicit type annotations |
no-trailing-whitespace |
false |
Lines must not have trailing whitespace |
consistent-indentation |
false |
Blocks must use consistent indentation (2 or 4 spaces) |
prefer-line-oriented |
false |
Suggest : suffix for multi-word values instead of quotes |
no-literal-quotes |
false |
Warn on key: "value" (suggests removing quotes or using !quoted) |
Enforce line-oriented values:
!lint {
require-line-oriented!level error
}
# ✅ Valid
title: Senior Software Engineer
description: This is a complete sentence.
# ❌ Invalid - must use : suffix
name "John Doe"
Prevent empty values:
!lint {
no-empty-values!level error
}
# ✅ Valid
status active
# ❌ Invalid - empty value
status
Require type annotations:
!lint {
require-type-annotations!level warning
}
# ✅ Valid
port!int 8080
enabled!bool true
# ❌ Invalid - missing type annotation
timeout 30
Catch ambiguous syntax:
!lint {
no-ambiguous-keys!level error
}
# ❌ Warning - "Doe" appears to be a key with no value
# Suggestion: use `name "John Doe"` or `name: John Doe`
name John Doe
Prevent literal quotes in line-oriented mode:
!lint {
no-literal-quotes!level warning
}
# ❌ Warning - quotes will be stripped, use !quoted if intentional
title: "Senior Engineer"
# ✅ Valid - explicitly using !quoted
title!quoted: "Senior Engineer"
# ✅ Valid - no quotes
title: Senior Engineer
!lint {
no-empty-values!level error
no-ambiguous-keys!level error
require-type-annotations!level warning
consistent-indentation!level error
}
# Document content
server {
host!string: localhost
port!int: 8080
enabled!bool: true
}
Lint rules can have different enforcement levels using the !level annotation (scoped within !lint blocks):
!lint {
# Error: stops processing
no-empty-values!level error
# Warning: shows warning but continues
prefer-line-oriented!level warning
# Info: informational message
no-literal-quotes!level info
}
Available levels:
error - Validation fails, stops processingwarning - Shows warning but continuesinfo - Informational message onlyNote: Within a !lint block, the !level annotation is automatically scoped to lint.level, providing cleaner syntax while maintaining explicit semantics.
# Validate with lint rules
up validate -i yourfile.up --lint
# Show lint warnings
up lint -i yourfile.up
# Fix auto-fixable lint issues
up lint -i yourfile.up --fix
Lint directives use the existing !annotation syntax and block structure, so no grammar changes are needed. The !lint annotation is semantically interpreted by the validator/linter.
To check if your UP is valid:
# Parse and validate
up validate -i yourfile.up
# Parse and validate with linting
up validate -i yourfile.up --lint
# Parse and output as JSON (will fail if invalid)
up parse -i yourfile.up --pretty
The Golden Rule:
For values containing whitespace, you have two options:
name "John Doe": suffix: name: John DoeType Safety: Remember that strings are default, so only use quotes when needed for parsing, and use type annotations (!int, !bool, etc.) when you want non-string types.
Colon Rules:
: as key suffix → captures full line as value: within values → literal character (URLs work naturally): within keys → literal character: → must be quoted