# PebbLisp 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:
# 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, as in `((fn (a) (* a a)) 5)`, which also returns `25`. 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 useful on a low-memory device like the Pebble, where it may be useful 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. Partial function support in PebbLisp is nowhere near comprehensive, but `fil` operates on the bare notion that currently exists. The general form of a `fil` expression is ``` (fil (partial-condition) (candidate-list)) ``` Each element in the candidate list is compared against the partial condition. If the comparison returns true, it is added to the returned list. For example: ``` (fil (< 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