https://github.com/1amageek/swift-yaml
A pure Swift YAML parser with no external dependencies
https://github.com/1amageek/swift-yaml
parser swift swift-package-manager yaml
Last synced: 4 months ago
JSON representation
A pure Swift YAML parser with no external dependencies
- Host: GitHub
- URL: https://github.com/1amageek/swift-yaml
- Owner: 1amageek
- License: mit
- Created: 2026-03-06T10:28:26.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-03-11T05:27:07.000Z (4 months ago)
- Last Synced: 2026-03-11T05:54:40.252Z (4 months ago)
- Topics: parser, swift, swift-package-manager, yaml
- Language: Swift
- Size: 21.5 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# swift-yaml
A pure Swift YAML 1.2.2 parser with zero dependencies. Parses YAML strings into a typed `Node` tree with full spec compliance, verified by 569 tests.
## Installation
Add to your `Package.swift`:
```swift
dependencies: [
.package(url: "https://github.com/1amageek/swift-yaml.git", from: "0.3.0")
]
```
Then add `"YAML"` to your target's dependencies:
```swift
.target(name: "YourTarget", dependencies: ["YAML"])
```
## Usage
```swift
import YAML
let node = try compose(yaml: """
server:
host: localhost
port: 8080
features: [auth, logging]
""")
if case .mapping(let root) = node,
case .mapping(let server) = root["server"] {
print(server["host"]) // Optional(.scalar("localhost"))
print(server["port"]) // Optional(.scalar("8080"))
print(server["features"]) // Optional(.sequence([...]))
}
```
### Node Types
The parser produces a recursive `Node` tree with three cases:
| Case | Type | Description |
|---|---|---|
| `.scalar(Scalar)` | `Node.Scalar` | String value with optional source position |
| `.mapping(Mapping)` | `Node.Mapping` | Ordered key-value pairs (`RandomAccessCollection`) |
| `.sequence(Sequence)` | `Node.Sequence` | Ordered list of nodes (`RandomAccessCollection`) |
Access values using computed properties:
```swift
node.scalar?.string // "value" or nil
node.mapping?["key"] // Node? or nil
node.sequence?[0] // Node or nil
```
### Supported YAML Features
| Feature | Example |
|---|---|
| Block mapping | `key: value` |
| Block sequence | `- item` |
| Flow sequence | `[a, b, c]` |
| Flow mapping | `{key: value}` |
| Literal block scalar | |, |-, |+ |
| Folded block scalar | `>`, `>-`, `>+` |
| Single-quoted string | `'it''s escaped'` |
| Double-quoted string | `"hello\nworld"` |
| Anchors & aliases | `&anchor` / `*anchor` |
| Tags | `!!str`, `!custom`, `!` |
| Multi-document | `---` / `...` |
| Directives | `%YAML 1.2`, `%TAG` |
| Comments | `# comment` |
| Complex mapping keys | `{flow: map}: value`, `[seq]: value` |
| Escape sequences | All 18 YAML escapes + `\xNN`, `\uNNNN`, `\UNNNNNNNN` |
| Nested structures | Arbitrary depth |
### Error Handling
All parse errors are reported as `YAMLError` with source position:
```swift
do {
let node = try compose(yaml: invalidYAML)
} catch let error as YAMLError {
print(error) // "3:12: scanner error: unterminated double-quoted string"
}
```
### Type Safety
All public types conform to `Sendable`, `Hashable`, and `Equatable`:
```swift
// Use nodes as dictionary keys or set elements
var seen: Set = []
seen.insert(node)
// Compare nodes
if node == .scalar(Node.Scalar("expected")) { ... }
```
## Architecture
The parser uses a classic three-stage pipeline:
```
YAML String → Scanner → Parser → Node Tree
(tokens) (recursive descent)
```
1. **Scanner** — Tokenizes YAML input with indentation tracking, flow/block context switching, and block scalar processing
2. **Parser** — Recursive descent parser that consumes tokens and builds the `Node` tree with anchor resolution
3. **Compose** — Public entry point (`compose(yaml:)`) that wires Scanner and Parser together
## API Reference
### Entry Point
```swift
public func compose(yaml: String) throws -> Node?
```
Returns `nil` for empty documents. Throws `YAMLError` on invalid input.
### Node
```swift
public enum Node: Sendable, Hashable {
case scalar(Scalar)
case mapping(Mapping)
case sequence(Sequence)
var scalar: Scalar? { get }
var mapping: Mapping? { get }
var sequence: Sequence? { get }
}
```
### Node.Scalar
```swift
public struct Scalar: Sendable, Hashable {
public let string: String
public let mark: Mark?
public init(_ string: String, mark: Mark? = nil)
}
```
### Node.Mapping
```swift
public struct Mapping: Sendable, Hashable, RandomAccessCollection {
// Element = (key: Node, value: Node)
public subscript(key: String) -> Node? // lookup by string key
public subscript(position: Int) -> (Node, Node) // access by index
public var first: (key: Node, value: Node)?
public var isEmpty: Bool
}
```
### Node.Sequence
```swift
public struct Sequence: Sendable, Hashable, RandomAccessCollection {
// Element = Node
public subscript(position: Int) -> Node
}
```
### Mark
```swift
public struct Mark: Sendable, Hashable, CustomStringConvertible {
public let line: Int // 1-based
public let column: Int // 1-based
}
```
### YAMLError
```swift
public enum YAMLError: Error, Sendable, CustomStringConvertible {
case scanner(message: String, mark: Mark)
case parser(message: String, mark: Mark)
case unexpectedEndOfInput(mark: Mark)
}
```
## Requirements
- Swift 6.2+
## License
MIT License. See [LICENSE](LICENSE) for details.