345 lines
10 KiB
Markdown
345 lines
10 KiB
Markdown
# ![PebbLisp](https://git.sagev.space/sage/pebblisp/raw/branch/master/resources/images/pebblisp-logo-white.png)
|
|
|
|
A very basic LISP implementation with an interpreter and editor written for the Pebble.
|
|
|
|
[Visit the Rebble page](https://apps.rebble.io/en_US/application/5ec1f90f3dd31081fc98fce0) to download the full,
|
|
compiled version!
|
|
|
|
[Download the Android app](https://gitlab.com/sagev9000/pebblispandroid/-/wikis/Downloads) to easily type up and send
|
|
scripts to your Pebble.
|
|
|
|
Live REPL:
|
|
|
|
<div id="pebblisp-console-placeholder">Only functional on git.sagev.space</div>
|
|
|
|
# Table of contents
|
|
|
|
- [Coding in PebbLisp](#coding-in-pebblisp)
|
|
- [Def](#def)
|
|
- [If](#if)
|
|
- [Fn](#fn)
|
|
- [Cat](#cat)
|
|
- [Map](#map)
|
|
- [Fil](#fil)
|
|
- [Pebble-Specific Functions](#pebble-specific-functions)
|
|
- [Checking the Time](#checking-the-time)
|
|
- [Vibrating](#vibrating)
|
|
- [Window Manipulation](#window-manipulation)
|
|
- [Subscribing](#subscribing)
|
|
- [Non-Pebble Functionality](#non-pebble-functionality)
|
|
- [pl](#pl)
|
|
- [pebblisp.pbl](#pebblisppbl)
|
|
- [The Help Function `(?)`](#the-help-function-)
|
|
- [Viewing the `pl` Environment `(penv)`](#viewing-the-pl-environment-penv)
|
|
|
|
# Coding in PebbLisp
|
|
|
|
PebbLisp includes several built-ins functionalities.
|
|
|
|
## Def
|
|
|
|
`def` stores a given object into the environment with the given symbol. The general form is:
|
|
|
|
```
|
|
(def symbol object)
|
|
```
|
|
|
|
As an example, to store a long greeting message into the short symbol `greeting`:
|
|
|
|
```
|
|
(def greeting "Hello, my dear friend! How are you today?")
|
|
```
|
|
|
|
`greeting` will then evaluate to `"Hello, my dear friend! How are you today?"`.
|
|
|
|
> **Note:**\
|
|
> Defining an object with the same symbol as an object that already exists is possible, but deletes the
|
|
> original object from the environment!
|
|
|
|
## If
|
|
|
|
`if` checks a given condition. If the condition evaluates to true, the first given expression is returned. If false, the
|
|
second expression is returned. The general format is:
|
|
|
|
```
|
|
(if condition expr1 expr2)
|
|
```
|
|
|
|
The condition will typically involve a comparison operator. For example:
|
|
|
|
```
|
|
(if (< 5 10) "Yee" "haw")
|
|
```
|
|
|
|
This return `"Yee"`, as `(< 5 10)` (commonly written `5 < 10`) evaluates to true.
|
|
|
|
## Fn
|
|
|
|
`fn` creates a lambda with a (theoretically) arbitrary number of arguments to be evaluated on call. The general form is:
|
|
|
|
```
|
|
(fn (arg-list) (lambda-body))
|
|
```
|
|
|
|
A lambda will frequently be stored under a `def` symbol. For example, to define a simple square function, you might
|
|
write:
|
|
|
|
```
|
|
(def sq (fn (a) (* a a)))
|
|
```
|
|
|
|
Calling it is as simple as `(sq 5)`, which returns `25`.
|
|
|
|
Lambdas can also be applied anonymously. This is most useful when using something like `map`. For example, an anonymous lambda could be used to square each
|
|
element in a list:
|
|
|
|
```
|
|
(map (fn (a) (* a a)) (1 2 3 5 8 13 21 34))
|
|
```
|
|
|
|
This is particularly valuable on a low-memory device like the Pebble, where it may be wise to avoid storing the named
|
|
lambda object in the environment.
|
|
|
|
Lambdas may also have no arguments:
|
|
|
|
```
|
|
(fn () (uppercase (input)))
|
|
```
|
|
|
|
## Cat
|
|
|
|
`cat` returns its arguments concatenated as strings. It has the same general form as arithmetic operators
|
|
|
|
```
|
|
(cat expr1 expr2 ...)
|
|
```
|
|
|
|
For example, combining numbers and strings is quite simple:
|
|
|
|
```
|
|
(cat "There are " 5 " cats")
|
|
```
|
|
|
|
And this evaluates to `"There are 5 cats"`.
|
|
|
|
Previously, a `cat` operation was applied implicitly when using `+` with strings, but this resulted in confusing
|
|
behavior when combined with number objects (due to the left-associativity of operators, `(+ 1 2 "3")` would result
|
|
in `"33"`) and was thus removed.
|
|
|
|
## Map
|
|
|
|
`map` applies a given lambda to each element in a given list and returns a list composed of each result. The general
|
|
form of a `map` expression is
|
|
|
|
```
|
|
(map lambda (input-list))
|
|
```
|
|
|
|
For example, using a `sq` lambda:
|
|
|
|
```
|
|
(map sq (1 2 3 4 5 6 7 8 9 10))
|
|
```
|
|
|
|
would return `( 1 4 9 16 25 36 49 64 81 100 )`, with each element being the square of the corresponding element in the
|
|
input list.
|
|
|
|
## Fil
|
|
|
|
`fil` returns a filtered list, based on a given list and a given condition. The general form of a `fil`
|
|
expression is
|
|
|
|
```
|
|
(fil (condition) (candidate-list))
|
|
```
|
|
|
|
Each element in the candidate list is compared against the
|
|
condition. If the comparison returns true, it is added to the
|
|
returned list. For example:
|
|
|
|
```
|
|
(fil (fn (a) (> a 100)) (20 150 30 200))
|
|
```
|
|
|
|
would return `( 150 200 )`, as no other elements fit the condition `(< 100 n)`.
|
|
|
|
# Pebble-Specific Functions
|
|
|
|
There are several functions to access features of the Pebble itself.
|
|
Note that due to some over-simplicity of function-handling, all the following functions expect to receive two
|
|
arguments, regardless of how many they actually use.
|
|
|
|
## Checking the Time
|
|
|
|
There are several functions for fetching individual elements of the current time:
|
|
|
|
- `sec` the current seconds (0-59)
|
|
- `mnt` the current minutes (0-59)
|
|
- `hr` the current hour (0-23)
|
|
- `hrt` the current hour (1-12)
|
|
|
|
For example: `(mnt)` would return `16`, if called at 5:16
|
|
|
|
## Vibrating
|
|
|
|
`vibe` starts the vibration engine with a given pattern. The pattern should be a list, composed of
|
|
alternating on/off durations in milliseconds. For example:
|
|
|
|
```
|
|
(vibe (100 200 200 400 200 800))
|
|
```
|
|
|
|
would cause a sort of *Bz. Bzz. Bzzzz. Bzzzzzzzz.* pattern.
|
|
|
|
## Window Manipulation
|
|
|
|
Basic Window and TextLayer manipulations are enabled in PebbLisp.
|
|
|
|
`cw` creates a blank window that can be manipulated by other functions. Note that `cw` does not itself display the
|
|
window.
|
|
|
|
`pw` is the function responsible for pushing a window onto the stack. For example:
|
|
|
|
```
|
|
(def win (cw)) (pw win)
|
|
```
|
|
|
|
Creates and pushes to the screen a blank white window. Note that windows can be exited by tapping the back button.
|
|
Getting something useful to display requires the use of a TextLayer.
|
|
|
|
`atl` adds a text layer to the given window, and displays the given object as text. For example:
|
|
|
|
```
|
|
(def tl (atl win "Hello"))
|
|
```
|
|
|
|
Adds a TextLayer to `ww` with the text "Hello", where `ww` is a Window created with `cw`. It also stores a reference to
|
|
the TextLayer in `tl`, for later updates.
|
|
|
|
`utl` changes the text in a given TextLayer. For example:
|
|
|
|
```
|
|
(utl tl "Good-bye")
|
|
```
|
|
|
|
changes the text in `tl` to "Good-bye", where `tl` is an existing TextLayer.
|
|
|
|
## Subscribing
|
|
|
|
`sub` allows for a given lambda to be repeatedly called at a given time interval. More testing is needed, but it is
|
|
possible that this lambda needs to be defined beforehand, instead of directly passed to `sub`. The lambda will likely
|
|
fail if it requires arguments.
|
|
|
|
The first argument is the lambda to be called, and the second is an optional argument selecting the frequency of the
|
|
repetition. If the second argument is any number 1-6, it will repeat every second, minute, hour, day, month, or year,
|
|
respectively. If the argument is anything else, it will default to repeating every minute.
|
|
|
|
Subscribing currently has little use outside of window manipulation, as it's effects are hard to view outside of that
|
|
environment. As an example
|
|
|
|
```
|
|
(sub upwin 1)
|
|
```
|
|
|
|
would request that `upwin` be run every second, where `upwin` is a lambda that does not rely on arguments.
|
|
|
|
# Non-Pebble Functionality
|
|
|
|
PebbLisp has several features designed for use _outside_ of a Pebble device.\
|
|
The first was a simple command-line REPL, to more quickly test the language while developing.\
|
|
The number of extensions quickly grew, for the sake of dogfooding the language and purely for sport.
|
|
|
|
This is currently **not** an exhaustive list. However, calling [`(penv)`](#viewing-the-pl-environment-penv) lists all
|
|
useful objects that are
|
|
currently available in a `pl` session, and [`(?)`](#the-help-function-) can be used to read the built-in documentation
|
|
for available functions.
|
|
|
|
## pl
|
|
|
|
`pl` is a hybrid shell designed as both a PebbLisp REPL and a working user shell.
|
|
|
|
When a line is entered, it will be evaluated as PebbLisp code. If any error occurs, it will instead be evaluated
|
|
using stdlib's `system()` function.
|
|
|
|
> Note: It will also fall back to `system()` if the line evaluates to a function.
|
|
> This is because some PebbLisp functions overlap with common commands. E.g. `/bin/time` and `(time)`.\
|
|
> A quick way around this is to use `(cat your-fn)`, forcing a string representation.
|
|
|
|
Furthermore, `pl` offers tab-completion over the names of objects in its current environment, falling back to path
|
|
completion when appropriate.
|
|
|
|
## pebblisp.pbl
|
|
|
|
A plain pbl file read from `$HOME/.pebblisp.pbl`, by default.
|
|
Allows for setting default variables, aliases, and the `pl` prompt.
|
|
|
|
A simple take on this file can be found in `examples/`
|
|
|
|
## The Help Function `(?)`
|
|
|
|
Important for its usefulness in navigating all _other_ functions is `(?)`
|
|
|
|
`(?)` is used to pull up any available documentation for the given object, typically a function:
|
|
|
|
```
|
|
(? time)
|
|
```
|
|
|
|
Which provides help like:
|
|
|
|
```
|
|
Get a struct of the current time with fields (minute hour sec).
|
|
```
|
|
|
|
Notably, `(?)` can also be used on itself. I.e. `(? ?)`, which returns:
|
|
|
|
```
|
|
Gets a string with help text or a string representation for the object.
|
|
|
|
Function help:
|
|
(? islist) => "(islist (1 2 3)) => T"
|
|
|
|
Struct fields:
|
|
(? Output) => "{ stdout stderr }"
|
|
|
|
Other objects:
|
|
(? "Hello") => "Hello"
|
|
```
|
|
|
|
> **Note:**\
|
|
> The `pl` prompt has a hard-coded shorthand for these calls so that parentheses may be omitted.\
|
|
> Entering `? sys` at the prompt is equivalent to `(? sys)`
|
|
|
|
## Viewing the `pl` Environment `(penv)`
|
|
|
|
`(penv)` is a quick way to examine the objects in the current PebbLisp environment.
|
|
|
|
Example output:
|
|
|
|
```
|
|
env->capacity = 256
|
|
env->count = 92
|
|
x: "hello"
|
|
isnum: Native
|
|
min: Return the smallest of the given values
|
|
(min 2 1 6) => 1
|
|
( `...values` ) -> ( NATIVE_FUNC `values` `_min` ( `first` `values` ) )>
|
|
...
|
|
```
|
|
|
|
Note that the first two lines list the current capacity and number of objects in the environment.
|
|
|
|
Objects may be displayed a few ways, depending on their type. Seen here are:
|
|
|
|
* A simple object, `x`, displayed as a string
|
|
* A native function, `isnum`, denoted with the word Native (as its storage is opaque pointer data)
|
|
* A lambda, `min`, with a small docstring, a simple test, and the basic construction of the lambda.
|
|
|
|
`(penv)`, combined with `(?)` and tab-completion, makes it relatively easy to browse and test the PebbLisp interpreter.
|
|
|
|
# TODO
|
|
|
|
- Better script calling
|
|
- Wrap more Pebble SDK functions
|
|
- Maybe hard-code additional built-in functions to ease up on memory use
|