jaml 0.2.0

A Rust library for parsing and formatting JAML (Just Another Markup Language)
Documentation
# JAML - Just Another Markup Language

A Rust library for parsing and formatting JAML, a **YAML-inspired data serialization format** with explicit integer and binary types.

JAML shares the same data model as [JASN](../jasn/README.md) but uses indentation-based syntax for a cleaner, more readable appearance. Think "YAML done right" - no implicit type conversions, no ambiguous syntax, explicit types.

## Why JAML?

- **Clean YAML-like syntax** without the gotchas (no "Norway Problem")
- **Explicit types**: `42` is an integer, `42.0` is a float
- **Quoted strings**: No implicit type conversion, all string values must be quoted
- **Native binary data**: `b64"..."` and `hex"..."` literals
- **Timestamps**: First-class `ts"..."` support
- **Flexible indentation**: First indent defines base unit (2 spaces, 4 spaces, tabs, etc.)
- **Full serde support**: Serialize/deserialize any Rust type

## Quick Start

### Parsing JAML

```rust
use jaml::parse;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let value = parse(r#"
name: "Alice"
age: 30
balance: 1234.56
data: b64"SGVsbG8="
tags:
  - "rust"
  - "yaml"
  - "parser"
    "#)?;
    
    println!("{:#?}", value);
    Ok(())
}
```

### Serde Integration

```rust
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct Config {
    name: String,
    version: u32,
    enabled: bool,
    timeout_ms: Option<u64>,
    tags: Vec<String>,
}

let config = Config {
    name: "My App".to_string(),
    version: 1,
    enabled: true,
    timeout_ms: Some(5000),
    tags: vec!["rust".to_string(), "jaml".to_string()],
};

// Serialize to JAML
let jaml = jaml::to_string(&config)?;

// Deserialize from JAML
let parsed: Config = jaml::from_str(&jaml)?;
```

## Example

A comprehensive example showing all supported types:

```jaml
# Null
nothing: null

# Booleans
active: true
disabled: false

# Integers (distinct from floats)
count: 42
negative: -123
hex_value: 0xFF
binary_value: 0b1010
octal_value: 0o755
with_underscores: 1_000_000

# Floats
pi: 3.14159
scientific: 1.5e10
infinity: inf
not_a_number: nan

# Strings (must be quoted)
name: "Alice"
message: "Hello, World!"
unicode: "Hello \u4E16\u754C"

# Binary data
data_b64: b64"SGVsbG8gV29ybGQh"
data_hex: hex"48656c6c6f"

# Timestamps
created: ts"2024-01-15T12:30:45Z"
updated: ts"2024-01-15T12:30:45.123Z"

# Lists (block syntax)
numbers:
  - 1
  - 2
  - 3

# Nested lists
matrix:
  -
    - 1
    - 2
  -
    - 3
    - 4

# Maps (nested structures)
person:
  name: "Bob"
  age: 25
  address:
    street: "123 Main St"
    city: "Portland"
    zip: "97201"

# Inline lists and maps (compact single-line syntax)
inline_list: [1, 2, 3]
inline_map: {x: 10, y: 20}

# Lists of maps
users:
  - name: "Alice"
    role: "admin"
  - name: "Bob"
    role: "user"
```

## Features

### Data Types

- **Null**: `null`
- **Boolean**: `true`, `false`
- **Integer** (i64): `42`, `-123`, `0xFF`, `0b1010`, `0o755`
- **Float** (f64): `3.14`, `1e10`, `.5`, `5.`, `inf`, `nan`
- **String**: `"quoted"` or `'quoted'` (always quoted)
- **Binary**: `b64"base64..."` or `hex"hexdigits..."`
- **Timestamp**: `ts"2024-01-15T12:30:45Z"`
- **List**: Block syntax or inline `[...]`
- **Map**: Block syntax or inline `{...}`

### Syntax Features

- **Indentation-based**: Clean structure without braces
- **Flexible indentation**: First indent (2 spaces, 4 spaces, tabs) defines base unit
- **Line comments**: `#` starts a comment
- **Unquoted keys**: Map keys can be identifiers (`name: "value"`)
- **Quoted keys**: For special characters (`"key-with-dash": "value"`)
- **Inline syntax**: Compact `[...]` and `{...}` for single-line structures
- **Trailing commas**: Allowed in inline lists/maps

### No YAML Gotchas

JAML avoids common YAML pitfalls:

```yaml
# YAML 1.1 (problematic)
NO: Norway    # NO becomes false!
yes: Yemen    # yes becomes true!
```

```jaml
# JAML (explicit)
NO: "Norway"    # NO is an identifier key, "Norway" is a quoted string
yes: "Yemen"    # yes is an identifier key, "Yemen" is a quoted string
```

**All string values must be quoted** - no implicit type conversion!

## Installation

```toml
[dependencies]
jaml = "0.2"
```

For serde support (enabled by default):
```toml
[dependencies]
jaml = { version = "0.2", features = ["serde"] }
```

## API Overview

### Parsing

```rust
use jaml::{parse, Value};

// Parse to AST
let value: Value = jaml::parse(text)?;

// With serde
let config: MyStruct = jaml::from_str(text)?;
```

### Formatting

```rust
use jaml::{format, formatter::Options};

// Format a value
let text = jaml::format(&value);

// With custom options
let opts = Options::new()
    .with_sort_keys(true)
    .with_quote_style(formatter::QuoteStyle::PreferDouble);
let text = jaml::format_with_opts(&value, &opts);

// With serde
let text = jaml::to_string(&my_struct)?;
```

## Documentation

- **[Grammar Specification]GRAMMAR.md**: Complete EBNF grammar and rules
- **[Examples]examples/**: Valid and invalid example files
- **[JASN]../jasn/README.md**: Alternative JSON5-like syntax with same data model

## Differences from YAML

1. **Explicit string values**: All string values must be quoted
2. **Integer/Float distinction**: `42` and `42.0` are different types
3. **Binary type**: Native `b64"..."` and `hex"..."` literals
4. **Timestamp type**: Native `ts"..."` literals
5. **No implicit conversions**: No yes/no/on/off boolean conversion
6. **Flexible indentation**: First indent defines base unit
7. **Single document**: No multi-document support
8. **No anchors/aliases**: No `&anchor` or `*alias`
9. **No tags**: No `!!type` support
10. **Simpler, explicit syntax**: Focused subset with clear rules

## Differences from JASN

**Important:** JAML is **not a superset** of JASN. Unlike YAML/JSON (where JASN is a superset of JSON), JAML and JASN use intentionally **disjoint syntax styles**.

Both share the same data model (`jasn-core::Value`) and can represent identical data, but use different syntax:

| Feature | JASN | JAML |
|---------|------|------|
| Structure | Braces `{}` and brackets `[]` | Indentation-based |
| Multi-line structures | Allowed in `{}` and `[]` | Only via indentation (block syntax) |
| Inline structures | `[...]` and `{...}` with newlines allowed | `[...]` and `{...}` must be single-line |
| Comments | Block `/* */` | Line `#` only |
| String values | Optional quotes | Must be quoted |
| Trailing commas | Everywhere | Only in inline `[]` and `{}` |

**Design Philosophy:** The syntax incompatibility is intentional. Each format has its own consistent style:
- **JASN**: Braces/brackets everywhere, block comments, flexible quoting
- **JAML**: Indentation for structure, line comments, explicit quoting
- **Compatibility**: Via the shared `Value` type - convert between formats through the AST

```rust
// JASN text -> Value -> JAML text
let jasn = r#"{ name: "Alice", age: 30 }"#;
let value = jasn::parse(jasn)?;
let jaml = jaml::format(&value);

// JAML text -> Value -> JASN text
let jaml = "name: \"Alice\"\nage: 30";
let value = jaml::parse(jaml)?;
let jasn = jasn::format(&value);
```

## License

MIT License - see [LICENSE](../LICENSE) file for details.