Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
a145b436c9
305
README.md
305
README.md
|
@ -1,150 +1,341 @@
|
|||
# PebbLisp
|
||||
|
||||
A very basic LISP implementation with an interpreter and editor written for the Pebble.
|
||||
|
||||
[Download the Android app](https://gitlab.com/sagev9000/pebblispandroid/-/wikis/Downloads) to easily type up and send scripts to your 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.
|
||||
|
||||
# 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
|
||||
|
||||
# Writing 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)`
|
||||
```
|
||||
(def symbol object)
|
||||
```
|
||||
|
||||
As an example, to store a long greeting message into the short symbol `g`:
|
||||
As an example, to store a long greeting message into the short symbol `greeting`:
|
||||
|
||||
`(def g "Hello, how are you today?")`
|
||||
```
|
||||
(def greeting "Hello, my dear friend! How are you today?")
|
||||
```
|
||||
|
||||
`g` will then evaluate to `"Hello, how are you today"`.
|
||||
`greeting` will then evaluate to `"Hello, my dear friend! How are you today?"`.
|
||||
|
||||
> Remember that defining an object with the same symbol as an object that already exists is possible, but deletes the original object from the environment!
|
||||
> **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)`
|
||||
`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:
|
||||
|
||||
The condition will typically be involve a comparison operator. For example:
|
||||
```
|
||||
(if condition expr1 expr2)
|
||||
```
|
||||
|
||||
`(if (< 5 10) "Yee" "haw")`
|
||||
The condition will typically involve a comparison operator. For example:
|
||||
|
||||
Would return `"Yee"`, as `(< 5 10)` aka `5 < 10` evaluates to true.
|
||||
```
|
||||
(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))`
|
||||
```
|
||||
(fn (arg-list) (lambda-body))
|
||||
```
|
||||
|
||||
A lambda will commonly be stored under a `def` symbol, for example:
|
||||
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)))`
|
||||
```
|
||||
(def sq (fn (a) (* a a)))
|
||||
```
|
||||
|
||||
Defines a simple lambda to square a given number. Calling it is as simple as `(sq 5)`, which returns `25`.
|
||||
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`, but this is most useful when using something like `map`. For example:
|
||||
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))`
|
||||
```
|
||||
(map (fn (a) (* a a)) (1 2 3 5 8 13 21 34))
|
||||
```
|
||||
|
||||
Uses an anonymous lambda to square each element in the list. 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.
|
||||
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 () 9001)`
|
||||
```
|
||||
(fn () (uppercase (input)))
|
||||
```
|
||||
|
||||
## Cat
|
||||
|
||||
`cat` returns its arguments concatenated as strings. It has the same general form as arithmetic operators
|
||||
|
||||
`(cat expr1 expr2 ...)`
|
||||
```
|
||||
(cat expr1 expr2 ...)
|
||||
```
|
||||
|
||||
For example, combining numbers and strings:
|
||||
For example, combining numbers and strings is quite simple:
|
||||
|
||||
`(cat "There are " 5 " cats")`
|
||||
```
|
||||
(cat "There are " 5 " cats")
|
||||
```
|
||||
|
||||
Would return `There are 5 cats`.
|
||||
And this evaluates to `"There are 5 cats"`.
|
||||
|
||||
A `cat` operation is applied implicitly when using `+` with strings, but this may result in confusing behavior when combined with number objects, due to the left-associativity of operators. For example, `(+ 5 10 " cats")` results in `15 cats` but `(+ "Cats: " 5 10)` results in `Cats: 510`.
|
||||
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))`
|
||||
`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)`
|
||||
```
|
||||
(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.
|
||||
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))`
|
||||
`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
|
||||
|
||||
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 (partial-condition) (candidate-list))
|
||||
```
|
||||
|
||||
`(fil (< 100) (20 150 30 200))`
|
||||
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 of the following functions expect to receive two arguments, regardless of how many they actually use.
|
||||
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 invidual elements of the current 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
|
||||
For example: `(mnt)` would return `16`, if called at 5:16
|
||||
|
||||
## Vibrating
|
||||
`vibe` calls the vibration engine to start, following 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))`
|
||||
`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.
|
||||
`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
|
||||
`pw` is the function responsible for pushing a window onto the stack. For example:
|
||||
|
||||
`(def win (cw)) (pw win)`
|
||||
```
|
||||
(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.
|
||||
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
|
||||
`atl` adds a text layer to the given window, and displays the given object as text. For example:
|
||||
|
||||
`(def tl (atl win "Hello"))`
|
||||
```
|
||||
(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.
|
||||
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` changes the text in a given TextLayer. For example:
|
||||
|
||||
`(utl tl "Good-bye")`
|
||||
```
|
||||
(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.
|
||||
`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.
|
||||
|
||||
Subscribing currently has little use outside of window manipulation, as it's effects are hard to view outside of that environment. As an example
|
||||
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.
|
||||
|
||||
`(sub upwin 1)`
|
||||
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
|
||||
- Even more pebble functions
|
||||
- Maybe hard-code more built-in functions to ease up on memory use
|
||||
- Wrap more Pebble SDK functions
|
||||
- Maybe hard-code additional built-in functions to ease up on memory use
|
||||
|
|
|
@ -21,7 +21,11 @@ struct Settings {
|
|||
Object getPrompt(struct Environment* env)
|
||||
{
|
||||
Object prompt = fetchFromEnvironment("prompt", env);
|
||||
if (!isError(prompt, DID_NOT_FIND_SYMBOL)) {
|
||||
prompt = cloneObject(prompt);
|
||||
} else {
|
||||
prompt = stringFromSlice("pl ~> ", 7);
|
||||
}
|
||||
if (prompt.type == TYPE_STRING) {
|
||||
return prompt;
|
||||
}
|
||||
|
|
|
@ -242,10 +242,10 @@ int stringNObj(struct string* s, const Object* obj)
|
|||
break;
|
||||
}
|
||||
case TYPE_STATIC_FUNC:
|
||||
appendf(s, "STATIC FUNC");
|
||||
appendf(s, "STATIC_FUNC");
|
||||
break;
|
||||
case TYPE_HASH_TABLE:
|
||||
appendf(s, "HASH TABLE");
|
||||
appendf(s, "HASH_TABLE");
|
||||
break;
|
||||
case TYPE_STRUCT:
|
||||
stringStruct(s, obj);
|
||||
|
@ -276,7 +276,7 @@ int stringNObj(struct string* s, const Object* obj)
|
|||
break;
|
||||
}
|
||||
case TYPE_FUNC:
|
||||
appendf(s, "F%ld", obj->number);
|
||||
appendf(s, "NATIVE_FUNC");
|
||||
break;
|
||||
case TYPE_LAMBDA: {
|
||||
#if defined(STANDALONE) && !defined(DEBUG)
|
||||
|
|
Loading…
Reference in New Issue