Getting Started with Parsley

This tutorial walks you through Parsley's core concepts with hands-on examples. By the end, you'll be able to write variables, functions, loops, and HTML templates โ€” everything you need to start building with Basil.

Prerequisites: You need pars (the Parsley CLI) installed. Run pars to start an interactive session, or pars myfile.pars to run a script.


Your First Program

Create a file called hello.pars:

let name = "world"
"Hello, " + name + "!"

Run it:

$ pars hello.pars
Hello, world!

let creates a variable. The + operator concatenates strings. Parsley outputs the result of the last expression โ€” no print() needed.


Variables and Expressions

Parsley has two ways to declare variables:

let name = "Alice"              // immutable
var count = 0                   // mutable
count = count + 1               // OK โ€” var allows this
// name = "Bob"                 // Error โ€” let doesn't allow reassignment

Parsley has numbers, strings, booleans, and null:

let age = 30
let price = 9.99
let active = true
let missing = null

Arithmetic works as you'd expect:

let x = 10
let y = 3
x + y       // 13
x * y       // 30
x / y       // 3.333...
x % y       // 1 (remainder)

See: Variables & Binding ยท Types ยท Operators


Strings

Strings can be written with double quotes, single quotes, or backticks:

let s1 = "double quoted"
let s2 = `backtick โ€” {1 + 1} interpolation`
let s3 = 'single quoted โ€” @{1 + 1} interpolation'

Backtick strings use {expr} for interpolation. Single-quoted strings use @{expr}:

let user = "Alice"
`Welcome, {user}!`    // Welcome, Alice!

Having three types of interpolation will make sense, when you start making real-world scripts, especially for the web.

Strings have many useful methods:

"hello".toUpper()        // HELLO
"  hi  ".trim()          // hi
"hello world".split(" ") // ["hello", "world"]
"banana".includes("nan") // true

See: Strings


Arrays

Arrays are ordered collections. Create them with square brackets:

let fruits = ["apple", "banana", "cherry"]
fruits[0]           // apple
fruits[-1]          // cherry (last element)
fruits.length()     // 3

Use .map() to transform and .filter() to select:

let nums = [1, 2, 3, 4, 5]

nums.map(fn(n) { n * 2 })      // [2, 4, 6, 8, 10]

nums.filter(fn(n) { n > 3 })   // [4, 5]

Concatenate arrays with ++:

[1, 2] ++ [3, 4]   // [1, 2, 3, 4]

See: Arrays


Dictionaries

Dictionaries are key-value pairs โ€” Parsley's equivalent of objects or maps:

let person = {name: "Alice", age: 30, city: "London"}
person.name     // Alice
person.age      // 30

Merge dictionaries with ++:

let defaults = {theme: "light", lang: "en"}
let prefs = {theme: "dark"}
defaults ++ prefs   // {theme: "dark", lang: "en"}

Destructure to extract values:

let {name, age} = {name: "Bob", age: 25, role: "admin"}
name    // Bob
age     // 25

See: Dictionaries


Functions

Functions are created with fn. The last expression is the return value:

let double = fn(x) { x * 2 }
double(5)       // 10

let add = fn(a, b) { a + b }
add(3, 4)       // 7

Use dictionary destructuring with ?? for optional parameters:

let greet = fn({name, greeting}) {
    let g = greeting ?? "Hello"
    `{g}, {name}!`
}
greet({name: "Alice"})                    // Hello, Alice!
greet({name: "Bob", greeting: "Hey"})     // Hey, Bob!

Functions are values โ€” you can pass them to other functions:

let apply = fn(f, x) { f(x) }
apply(double, 21)   // 42

See: Functions


Control Flow

if is an Expression

if returns a value, so you can use it inline:

let age = 20
let status = if (age >= 18) "adult" else "minor"
status      // adult

Or with blocks:

if (age >= 18) {
    "Welcome!"
} else {
    "Too young"
}

for Returns an Array

for loops are expressions that return arrays โ€” like map in other languages:

let nums = [1, 2, 3, 4, 5]

let squares = for (n in nums) { n * n }
squares     // [1, 4, 9, 16, 25]

Filter by returning values only from an if:

let evens = for (n in nums) {
    if (n % 2 == 0) { n }
}
evens       // [2, 4]

Iterate over dictionaries:

let scores = {alice: 95, bob: 87}
for (name, score in scores) {
    `{name}: {score}`
}
// ["alice: 95", "bob: 87"]

See: Control Flow


CSV to Table in One Line

Parsley makes data processing simple. Parse a CSV string, and you get a table with typed columns. Chain .toBox() to see it formatted:

let csv = "name,age,role\nAlice,30,Engineer\nBob,25,Designer\nCarol,35,Manager"
csv.parseCSV().toBox()

Result:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ name  โ”‚ age โ”‚ role     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Alice โ”‚ 30  โ”‚ Engineer โ”‚
โ”‚ Bob   โ”‚ 25  โ”‚ Designer โ”‚
โ”‚ Carol โ”‚ 35  โ”‚ Manager  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Tables support SQL-like operations, so you can filter, sort, and aggregate before displaying:

csv.parseCSV()
    .where(fn(row) { row.age >= 30 })
    .orderBy("name")
    .toBox()
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ name  โ”‚ age โ”‚ role     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Alice โ”‚ 30  โ”‚ Engineer โ”‚
โ”‚ Carol โ”‚ 35  โ”‚ Manager  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

See: Data Formats ยท Tables


Building HTML with Tags

Parsley has first-class HTML tag syntax. Tags are values, not strings:

<h1>"Hello!"</h1>      // <h1>Hello!</h1>

Important: Text content inside tags must be quoted. Tag attributes don't need quotes for simple values.

Embed expressions directly inside tags:

let user = "Alice"
<p>"Welcome, " user "!"</p>

Result: <p>Welcome, Alice!</p>

Use for to generate lists:

let items = ["Apples", "Bananas", "Cherries"]
<ul>
    for (item in items) {
        <li>item</li>
    }
</ul>

Result: <ul><li>Apples</li><li>Bananas</li><li>Cherries</li></ul>

Singleton tags MUST be self-closing: Write <br/>, <hr/>, <img src="photo.jpg"/> โ€” never <br> or <img>.

See: Tags


Components

Components are functions that return tags. Use them like custom HTML elements:

let Card = fn({title, body}) {
    <div class=card>
        <h2>title</h2>
        <p>body</p>
    </div>
}

<Card title="Hello" body="Welcome to Parsley!"/>

Result: <div class=card><h2>Hello</h2><p>Welcome to Parsley!</p></div>

Components compose naturally:

let Page = fn({title, contents}) {
    <html>
    <head><title>title</title></head>
    <body>contents</body>
    </html>
}

<Page title="Home">
    <h1>"Welcome!"</h1>
    <p>"This is my page."</p>
</Page>

See: Tags ยท Modules


A Simple Web Page Handler

In Basil, each route is a .pars file that exports a handler. Here's a complete example:

// components/Layout.pars
export Layout = fn({title, contents}) {
    <html>
    <head>
        <title>title</title>
        <link rel="stylesheet" href="/style.css"/>
    </head>
    <body>
        <nav><a href="/">"Home"</a></nav>
        <main>contents</main>
    </body>
    </html>
}
// routes/index.pars
let {Layout} = import @./components/Layout.pars

let items = ["Learn Parsley", "Build with Basil", "Ship it!"]

<Layout title="My App">
    <h1>"My To-Do List"</h1>
    <ul>
        for (item in items) {
            <li>item</li>
        }
    </ul>
</Layout>

The handler returns HTML automatically โ€” Basil detects the leading < tag and sets the content type.


Error Handling

try wraps a function call and returns {result, error} โ€” there are no catch blocks:

let risky = fn() { fail("oops") }
let {result, error} = try risky()
if (error) {
    "Failed: " + error
} else {
    "Got: " + result
}

Use check as a guard โ€” it's like an assertion that returns early:

let process = fn(input) {
    check input != null else "No input"
    check input.length() > 0 else "Empty input"
    `Processing: {input}`
}

Note: log() is Parsley's debug output function โ€” use it when you need to inspect values mid-execution (inside loops, conditionals, or complex expressions). For normal output, just let the expression be the result.

See: Error Handling


Where to Go Next

You now know the fundamentals. Here are paths forward depending on what you're building:

Goal Read
Learn all the built-in types Types, then the Built-in Types section
Build web pages Tags โ†’ Modules โ†’ Strings
Work with databases Database โ†’ Query DSL โ†’ Schemas
Build REST APIs @basil/api โ†’ HTTP & Networking โ†’ Session Management
Process files and data File I/O โ†’ Data Formats โ†’ Arrays
Explore the full manual Manual Index

Key Differences from Other Languages

If you're coming from JavaScript, Python, or similar languages, watch out for these:

Concept Parsley Other languages
Variables let (immutable), var (mutable) const/let in JS, let/var in Swift
Output Last expression is the result print(), console.log()
Debug output log() (for debugging only) Same as output in most languages
Comments // only # in Python
String interpolation `Hello {name}` ${} in JS, f"" in Python
For loops Return arrays (like map) Statements (no return value)
If/else Expression (returns a value) Statement in most languages
Tags First-class syntax: <p>"hi"</p> Strings: "<p>hi</p>"
Self-closing tags Required: <br/> Optional in HTML5
Paths Literals: @./file.txt Strings: "./file.txt"

See: Parsley Cheatsheet