File I/O
Parsley uses file handles and I/O operators for reading and writing files. You create a handle that describes the file and its format, then use <== to read or ==> to write. The handle determines how data is serialized and deserialized.
I/O Operators
| Operator | Direction | Description |
|---|---|---|
<== |
Read | Read file contents into a variable |
==> |
Write | Write data to a file (overwrites) |
==>> |
Append | Append data to a file |
=/=> |
Remote Write | Write data to a network target (HTTP/SFTP) |
=/=>> |
Remote Append | Append data to a network target (SFTP) |
let config <== JSON(@./config.json) // read
{name: "Alice"} ==> JSON(@./output.json) // write
"log entry\n" ==>> text(@./app.log) // append
// Network targets use =/=> instead of ==>
{name: "Alice"} =/=> JSON(@https://api.example.com/users) // remote write
Note: For network targets (HTTP URLs and SFTP connections), use
=/=>and=/=>>instead of==>and==>>. The/in the operator visually signals that data crosses a network boundary. Both fetch (<=/=) and remote write (=/=>,=/=>>) can also be used as expressions β see HTTP & Networking for details.
File Handles
File handles are created by calling a format function with a path. They don't read or write anything on their own β the I/O operators do the actual work.
| Function | Format | Read type | Description |
|---|---|---|---|
JSON(path) |
JSON | dictionary/array | JSON file |
YAML(path) |
YAML | dictionary/array | YAML file |
CSV(path) |
CSV | table | CSV file (returns a table) |
PLN(path) |
PLN | any | Parsley Literal Notation |
text(path) |
Plain text | string | Raw text content |
lines(path) |
Lines | array | Array of strings (one per line) |
raw(path) |
Binary | array | Raw byte array |
MD(path) |
Markdown | dictionary | {html, md, raw} β rendered HTML, frontmatter, and source |
SVG(path) |
SVG | string | SVG content (strips XML prolog) |
file(path) |
Auto | varies | Auto-detect format from extension |
dir(path) |
Directory | array | Directory listing |
All file handles accept a path literal as the first argument:
let handle = JSON(@./data.json)
let data <== handle
Or inline:
let data <== JSON(@./data.json)
Reading Files
JSON
let config <== JSON(@./config.json)
config.database.host // "localhost"
Tip: For configuration files with Parsley-specific types (dates, money, paths), use PLN instead:
let config <== PLN(@./config.pln). PLN preserves all Parsley typesβsee Data Formats.
CSV
CSV reads return a table (not a raw array):
let sales <== CSV(@./sales.csv)
sales.count() // number of rows
for (row in sales) {
row.name + ": " + row.amount
}
Plain Text
let readme <== text(@./README.md)
readme.length() // character count
Lines
let items <== lines(@./todo.txt)
items.length() // number of lines
items[0] // first line
Markdown with Frontmatter
MD() reads a Markdown file, parses YAML frontmatter, and renders the content to HTML:
let doc <== MD(@./post.md)
doc.md.title // frontmatter field
doc.html // rendered HTML string
doc.raw // original Markdown (frontmatter stripped)
To parse Markdown already in a string, use the string method instead:
let result = source.parseMarkdown({ids: true})
// result.html, result.md, result.raw
Auto-detect
file() picks the format based on the file extension:
let data <== file(@./config.json) // reads as JSON
let text <== file(@./notes.txt) // reads as text
Writing Files
Use ==> to write (overwrite) and ==>> to append:
// Write PLN (preserves Parsley types like dates, money, paths)
{name: "Alice", joined: @2024-01-15, balance: $100.00} ==> PLN(@./user.pln)
// Write JSON (for external systems)
{name: "Alice", age: 30} ==> JSON(@./user.json)
// Write plain text
"Hello, world!" ==> text(@./greeting.txt)
// Append to a log
"New entry\n" ==>> text(@./app.log)
The write operator serializes the data according to the file handle's format. Writing a dictionary to a JSON handle produces formatted JSON; writing a string to a text handle writes it verbatim.
When to use PLN vs JSON: Use PLN for internal Parsley data (configs, caches, data files)βit preserves dates, money, paths, and other Parsley types. Use JSON for external interoperability (APIs, other languages). See Data Formats for details.
Directory Operations
dir()
Create a directory handle and read its contents:
let files <== dir(@./uploads)
for (f in files) {
f.name // filename
}
fileList()
List files matching a glob pattern. Takes a single path or string pattern and returns an array of file handles β read one with <==, or get its path via .path:
let sources = fileList("./src/*.pars")
sources.length() // number of matching files
for (f in sources) {
f.path.filename // "main.pars"
let content <== f // read the file
}
β οΈ
**matches files in subdirectories but not in the root of the pattern:fileList("./docs/**/*.md")misses./docs/index.md. List both patterns and join them with++if you need the whole tree.
Error Handling with Read
Use destructured read with {data, error} for safe file operations:
let {data, error} <== JSON(@./config.json)
let config = if (error) {
defaults // use defaults on error
} else {
data
}
When using the {data, error} pattern, read errors are captured instead of halting execution. Without it, a missing file or parse error produces an IO-class error.
Assets
The asset() builtin converts a file path to a web-accessible URL with cache-busting:
<img src={asset(@./logo.png)} alt="Logo"/>
// Produces: <img src="/assets/logo-a1b2c3d4.png" alt="Logo" />
Key Differences from Other Languages
- Operators instead of functions β
<==and==>replacereadFile()/writeFile(). The operator syntax makes the data flow direction visually clear. - Format-aware handles β the handle knows the file format, so you don't manually parse JSON or serialize CSV.
let data <== CSV(@./file.csv)returns a ready-to-use table. - PLN format β Parsley has its own serialization format (Parsley Literal Notation) that round-trips all Parsley types, including dates, money, and paths.
- Markdown is a first-class format β
MD()handles frontmatter parsing and HTML rendering in one step. - No streams β file I/O is synchronous and reads/writes the entire file at once.
See Also
- Paths β path literals and path manipulation
- URLs β URL literals used as file handle sources
- HTTP & Networking β
<=/=fetch,=/=>remote write, and=/=>>remote append operators - Error Handling β
{data, error}destructuring pattern - Data Formats β CSV and Markdown parsing details