Sage Vaillancourt f6f6a27689 | ||
---|---|---|
resources | ||
src | ||
.gitignore | ||
README.md | ||
package.json | ||
wscript |
README.md
A very basic LISP implementation with an interpreter and editor written for the Pebble.
Visit the Rebble page to download the full, compiled version!
Download the Android app to easily type up and send scripts to your Pebble.
Live REPL:
Table of contents
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)
lists all
useful objects that are
currently available in a pl
session, and (?)
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:
Thepl
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