Something went wrong

Thank you for being patient! We're working hard on resolving the issue

Formulas - Lona Docs Log in

Formulas

Welcome to the Lona Lisp documentation. This guide covers the core concepts and features of the Lisp implementation used in Lona.

Introduction

Lona includes a built-in Lisp interpreter that allows you to write powerful automation scripts and extensions. This documentation provides a comprehensive reference for the language and its features.

All examples in this guide are interactive - click "Run" to execute them and see the results!

Getting Started

Try your first Lisp expression:

Basic Syntax

S-Expressions

Lisp code is written using S-expressions (symbolic expressions). An S-expression is either an atom or a list.

Lists

Lists are the fundamental data structure in Lisp. They can be created using the list function or quoted notation:

Data Types

Numbers

Lona Lisp supports both integers and floating-point numbers:

Strings

Strings are enclosed in double quotes:

You can get the length of a string:

Booleans

Symbols

Symbols are identifiers that can be quoted:

Variables

Defining Variables

Use define to create variables:

Using Variables

Variables can be used in expressions:

Functions

Defining Functions

Functions can be defined using the shorthand syntax:

Recursive Functions

Functions can call themselves recursively:

Lambda Functions

Lambda functions create anonymous functions:

Control Flow

If Expressions

The if expression evaluates a condition and returns one of two values:

If expressions can be nested:

Begin Blocks

Use begin to execute multiple expressions in sequence:

Let Bindings

Create local variable bindings with let:

Let bindings can be nested:

Match Expressions

Pattern matching with match:

Boolean Operations

Built-in Functions

Arithmetic

Addition with multiple values:

Subtraction:

Multiplication:

Division (returns decimals):

Modulo operation:

Complex nested arithmetic:

Comparison

Equality:

Inequality:

Less than and greater than:

Less than or equal, greater than or equal:

Collections

Vectors

Vectors are ordered collections created with square brackets or the vector function:

Get the count of elements:

Check if a vector is empty:

Get an element by index:

Add elements with conj:

Nested vectors:

Maps

Maps are key-value collections created with curly braces or the hash-map function:

Get the count of key-value pairs:

Get a value by key:

Add or update key-value pairs with assoc:

Remove keys with dissoc:

Get all keys:

Get all values:

Nested maps:

Higher-Order Functions

Map a function over a collection:

Advanced Examples

Mutually Recursive Functions

Functions can call each other recursively:

Working with Collections

Combining let bindings with collections:

Power Function

Calculate powers using recursion:

Sum from 1 to N

Maximum of Two Numbers

Identity Function

Sheet Lisp Functions

Sheet Lisp extends the standard Lisp library with functions for working with sheet data. These functions provide access to row values, date information, and cell styling.

Referencing Rows

You can reference other rows in formulas using bracket notation:

By row name: rows["Row Name"]

By row lookup key: rows["lookup-key"]

The referenced row returns its computed value for the current column (time period).

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, { id: "rid_expenses", label: "Expenses", lookupKey: ":expenses", timeScale: "week", data: [["yw-2024-1", 600]], type: "number" }, ], timeScale: "week", } { column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, { id: "rid_expenses", label: "Expenses", lookupKey: ":expenses", timeScale: "week", data: [["yw-2024-1", 600]], type: "number" }, ], timeScale: "week", }

Date Functions

Sheet Lisp provides access to the current column's date information:

date

Returns the date string for the current column:

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

date.dow

Returns the day of week as a short name (Mon, Tue, etc.):

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

date.dow.0

Returns the ISO day of week as a number (1 = Monday, 7 = Sunday):

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

date.dow.range

Returns a list of day-of-week short names for all days in the column range:

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

date.dow.range0

Returns a list of ISO day-of-week numbers for all days in the column range:

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

date.dom

Returns the day of month (1-31):

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

date.dom.range

Returns a list of day-of-month values for all days in the column range:

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

date.wse

Returns the week-since-epoch value:

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

date.wse.range

Returns a list of week-since-epoch values for all days in the column range:

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

date.dse

Returns the day-since-epoch value:

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

date.weekno

Returns the ISO week number (1-53):

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

date.hoursPerDay

Returns a list of hours per day for the column range (useful for DST transitions):

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

date.year

Returns the year of the column:

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

date.tz

Returns the timezone name:

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

Cell Styling

cell.bg

Sets the background color of the current cell:

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

Pass nil to clear the background:

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

With index for multi-value cells:

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

Example: Using Date Information

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, ], timeScale: "week", }

Example: Conditional Formatting

{ column: "yw-2024-1", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-1", 1000]], type: "number" }, { id: "rid_expenses", label: "Expenses", lookupKey: ":expenses", timeScale: "week", data: [["yw-2024-1", 600]], type: "number" }, ], timeScale: "week", }

Example: Days Until End of Year

Calculate the number of days remaining until the end of the year for the current column. Uses date.doy.end to get the day-of-year for the last day in the column range, which works correctly for days, weeks, months, or years:

{ column: "yw-2024-3", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-3", 1000]], type: "number" }, ], timeScale: "week", }

Example: Days Until End of Month

Calculate the number of days remaining until the end of the month for the current column. Uses date.dom.end to get the day-of-month for the last day in the column range, which works correctly for days, weeks, or months:

{ column: "yw-2024-3", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-3", 1000]], type: "number" }, ], timeScale: "week", }

Example: Workdays Until End of Year

Calculate the number of workdays (Monday-Friday) remaining until the end of the year. Uses date.dow.end to get the day of week for the last day in the column range (0=Mon, 6=Sun), then calculates full weeks × 5 plus partial week workdays:

{ column: "yw-2024-3", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "week", data: [["yw-2024-3", 1000]], type: "number" }, ], timeScale: "week", }

Example: Weekend Cell Background

Highlight weekend cells with an orange background. Uses date.dow.0 to check if the column falls on Saturday (5) or Sunday (6):

{ column: "ymd-2024-01-14", rows: [ { id: "rid_revenue", label: "Revenue", lookupKey: ":revenue", timeScale: "day", data: [["ymd-2024-01-14", 1000]], type: "number" }, ], timeScale: "day", }

Example: Extracting and Transforming JSON Data

Extract fields from a row containing JSON data (like weather data) and perform transformations. The weather API returns nested data with a day object containing temperature, humidity, and condition information. Use json-get-in to access nested properties:

{ column: "ymd-2024-07-15", rows: [ { id: "rid_weather", label: "Weather", lookupKey: ":~weather", timeScale: "day", data: [["ymd-2024-7-15", {"date": "2024-07-15", "day": {"avgtempF": 85, "maxtempF": 92, "mintempF": 78, "avghumidity": 65, "uv": 8, "condition": {"code": 1000, "text": "Sunny"}}, "astro": {"sunrise": "06:00 AM", "sunset": "08:30 PM"}}]], type: "json" }, ], timeScale: "day", }

Access deeply nested properties using json-get-in with a path array:

{ column: "ymd-2024-07-15", rows: [ { id: "rid_weather", label: "Weather", lookupKey: ":~weather", timeScale: "day", data: [["ymd-2024-7-15", {"date": "2024-07-15", "day": {"avgtempF": 85, "maxtempF": 92, "mintempF": 78, "avghumidity": 65, "uv": 8, "condition": {"code": 1000, "text": "Sunny"}}, "astro": {"sunrise": "06:00 AM", "sunset": "08:30 PM"}}]], type: "json" }, ], timeScale: "day", }