Compare commits
10 Commits
ca16fa60ab
...
8b9a351be1
Author | SHA1 | Date |
---|---|---|
Sage Vaillancourt | 8b9a351be1 | |
Sage Vaillancourt | c37a12e244 | |
Sage Vaillancourt | 6176e9eb4b | |
Sage Vaillancourt | 9b6b80f204 | |
Sage Vaillancourt | a145b436c9 | |
Sage Vaillancourt | 672cdb692a | |
Sage Vaillancourt | 6503bd7dcc | |
Sage Vaillancourt | aae322e7fc | |
Sage Vaillancourt | cba73e45b7 | |
Sage Vaillancourt | e05b5648ae |
235
README.md
235
README.md
|
@ -1,93 +1,132 @@
|
||||||
# PebbLisp
|
# PebbLisp
|
||||||
|
|
||||||
A very basic LISP implementation with an interpreter and editor written for the Pebble.
|
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!
|
[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.
|
[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.
|
PebbLisp includes several built-ins functionalities.
|
||||||
|
|
||||||
## Def
|
## Def
|
||||||
|
|
||||||
`def` stores a given object into the environment with the given symbol. The general form is:
|
`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
|
||||||
`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` 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 condition expr1 expr2)
|
||||||
```
|
```
|
||||||
|
|
||||||
The condition will typically be involve a comparison operator. For example:
|
The condition will typically involve a comparison operator. For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
(if (< 5 10) "Yee" "haw")
|
(if (< 5 10) "Yee" "haw")
|
||||||
```
|
```
|
||||||
|
|
||||||
Would return `"Yee"`, as `(< 5 10)` aka `5 < 10` evaluates to true.
|
This return `"Yee"`, as `(< 5 10)` (commonly written `5 < 10`) evaluates to true.
|
||||||
|
|
||||||
## Fn
|
## Fn
|
||||||
|
|
||||||
`fn` creates a lambda with a (theoretically) arbitrary number of arguments to be evaluated on call. The general form is:
|
`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:
|
Lambdas may also have no arguments:
|
||||||
|
|
||||||
```
|
```
|
||||||
(fn () 9001)
|
(fn () (uppercase (input)))
|
||||||
```
|
```
|
||||||
|
|
||||||
## Cat
|
## Cat
|
||||||
|
|
||||||
`cat` returns its arguments concatenated as strings. It has the same general form as arithmetic operators
|
`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
|
||||||
`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` 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 lambda (input-list))
|
||||||
|
@ -96,19 +135,24 @@ A `cat` operation is applied implicitly when using `+` with strings, but this ma
|
||||||
For example, using a `sq` lambda:
|
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
|
||||||
`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` 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 (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:
|
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))
|
(fil (< 100) (20 150 30 200))
|
||||||
|
@ -117,26 +161,26 @@ Each element in the candidate list is compared against the partial condition. If
|
||||||
would return `( 150 200 )`, as no other elements fit the condition `(< 100 n)`.
|
would return `( 150 200 )`, as no other elements fit the condition `(< 100 n)`.
|
||||||
|
|
||||||
# Pebble-Specific Functions
|
# Pebble-Specific Functions
|
||||||
|
|
||||||
There are several functions to access features of the Pebble itself.
|
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
|
## Checking the Time
|
||||||
There are several functions for fetching invidual 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:
|
There are several functions for fetching individual elements of the current time:
|
||||||
|
|
||||||
```
|
- `sec` the current seconds (0-59)
|
||||||
(mnt)
|
- `mnt` the current minutes (0-59)
|
||||||
```
|
- `hr` the current hour (0-23)
|
||||||
|
- `hrt` the current hour (1-12)
|
||||||
|
|
||||||
would return 16, if called at 5:16
|
For example: `(mnt)` would return `16`, if called at 5:16
|
||||||
|
|
||||||
## Vibrating
|
## 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` 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))
|
(vibe (100 200 200 400 200 800))
|
||||||
|
@ -145,9 +189,11 @@ would return 16, if called at 5:16
|
||||||
would cause a sort of *Bz. Bzz. Bzzzz. Bzzzzzzzz.* pattern.
|
would cause a sort of *Bz. Bzz. Bzzzz. Bzzzzzzzz.* pattern.
|
||||||
|
|
||||||
## Window Manipulation
|
## Window Manipulation
|
||||||
|
|
||||||
Basic Window and TextLayer manipulations are enabled in PebbLisp.
|
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:
|
||||||
|
|
||||||
|
@ -155,7 +201,8 @@ Basic Window and TextLayer manipulations are enabled in PebbLisp.
|
||||||
(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:
|
||||||
|
|
||||||
|
@ -163,7 +210,8 @@ Creates and pushes to the screen a blank white window. Note that windows can be
|
||||||
(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:
|
||||||
|
|
||||||
|
@ -174,11 +222,17 @@ Adds a TextLayer to `ww` with the text "Hello", where `ww` is a Window created w
|
||||||
changes the text in `tl` to "Good-bye", where `tl` is an existing TextLayer.
|
changes the text in `tl` to "Good-bye", where `tl` is an existing TextLayer.
|
||||||
|
|
||||||
## Subscribing
|
## 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.
|
||||||
|
|
||||||
|
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)
|
(sub upwin 1)
|
||||||
|
@ -186,7 +240,102 @@ Subscribing currently has little use outside of window manipulation, as it's eff
|
||||||
|
|
||||||
would request that `upwin` be run every second, where `upwin` is a lambda that does not rely on arguments.
|
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
|
# TODO
|
||||||
|
|
||||||
- Better script calling
|
- Better script calling
|
||||||
- Even more pebble functions
|
- Wrap more Pebble SDK functions
|
||||||
- Maybe hard-code more built-in functions to ease up on memory use
|
- Maybe hard-code additional built-in functions to ease up on memory use
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
exe = pl
|
exe = pl
|
||||||
|
|
||||||
base_files = main.c pebblisp.c tokens.c object.c env.c hash.c
|
base_files = pcmain.c pebblisp.c tokens.c object.c env.c hash.c
|
||||||
func_files = plfunc/web.c plfunc/general.c plfunc/threads.c plfunc/plstring.c plfunc/pc.c
|
func_files = plfunc/web.c plfunc/general.c plfunc/threads.c plfunc/plstring.c plfunc/pc.c
|
||||||
files:= $(base_files) $(func_files)
|
files:= $(base_files) $(func_files)
|
||||||
|
|
||||||
|
|
14
src/env.c
14
src/env.c
|
@ -131,7 +131,7 @@ struct Environment envForLambda(const Object* params, const Object* arguments, i
|
||||||
// Evaluate the `argument` list
|
// Evaluate the `argument` list
|
||||||
const Object* param = params->list;
|
const Object* param = params->list;
|
||||||
const Object* argument = arguments;
|
const Object* argument = arguments;
|
||||||
for (int i = 0; i < paramCount && param; i++) {
|
for (; param; param = param->forward) {
|
||||||
const char* paramName = param->string;
|
const char* paramName = param->string;
|
||||||
if (paramName[0] == '.' && paramName[1] == '.' && paramName[2] == '.' && paramName[3] != '\0') {
|
if (paramName[0] == '.' && paramName[1] == '.' && paramName[2] == '.' && paramName[3] != '\0') {
|
||||||
paramName = ¶mName[3];
|
paramName = ¶mName[3];
|
||||||
|
@ -144,6 +144,9 @@ struct Environment envForLambda(const Object* params, const Object* arguments, i
|
||||||
cleanObject(&varargs);
|
cleanObject(&varargs);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!argument) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
Object newEnvObj = eval(argument, outer);
|
Object newEnvObj = eval(argument, outer);
|
||||||
|
|
||||||
|
@ -151,8 +154,7 @@ struct Environment envForLambda(const Object* params, const Object* arguments, i
|
||||||
|
|
||||||
cleanObject(&newEnvObj);
|
cleanObject(&newEnvObj);
|
||||||
|
|
||||||
argument = argument ? argument->forward : NULL;
|
argument = argument->forward;
|
||||||
param = param->forward;
|
|
||||||
}
|
}
|
||||||
env.outer = outer;
|
env.outer = outer;
|
||||||
|
|
||||||
|
@ -240,6 +242,7 @@ struct Environment defaultEnv()
|
||||||
#endif
|
#endif
|
||||||
#ifdef STANDALONE
|
#ifdef STANDALONE
|
||||||
pf(segfault),
|
pf(segfault),
|
||||||
|
pf(typeOf),
|
||||||
pf(print),
|
pf(print),
|
||||||
pf(numToChar),
|
pf(numToChar),
|
||||||
pf(printEnvO),
|
pf(printEnvO),
|
||||||
|
@ -254,7 +257,8 @@ struct Environment defaultEnv()
|
||||||
pf(help),
|
pf(help),
|
||||||
pf(buildHashTable),
|
pf(buildHashTable),
|
||||||
pf(addToHashTable),
|
pf(addToHashTable),
|
||||||
pf(getFromHashTable)
|
pf(getFromHashTable),
|
||||||
|
pf(randomO)
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -476,7 +480,7 @@ Object help(Object* params, int length, struct Environment* env)
|
||||||
int runTest(const char* test, const char* expected, int detailed)
|
int runTest(const char* test, const char* expected, int detailed)
|
||||||
{
|
{
|
||||||
struct Environment env = defaultEnv();
|
struct Environment env = defaultEnv();
|
||||||
Object o = parseEval(test, &env);
|
Object o = parseEval(test, "testCode", &env);
|
||||||
size_t length;
|
size_t length;
|
||||||
char* result = stringObj(&o, &length);
|
char* result = stringObj(&o, &length);
|
||||||
cleanObject(&o);
|
cleanObject(&o);
|
||||||
|
|
|
@ -174,6 +174,14 @@
|
||||||
(eval (rf file-name))
|
(eval (rf file-name))
|
||||||
)))
|
)))
|
||||||
|
|
||||||
|
(def any-in (fn (list f)
|
||||||
|
(> (len (fil f list)) 0)
|
||||||
|
))
|
||||||
|
|
||||||
|
(def none-in (fn (list f)
|
||||||
|
(not (any-in list f))
|
||||||
|
))
|
||||||
|
|
||||||
(def startsWith (fn (text pattern) (
|
(def startsWith (fn (text pattern) (
|
||||||
(matches text (cat pattern "*"))
|
(matches text (cat pattern "*"))
|
||||||
)))
|
)))
|
||||||
|
@ -184,4 +192,4 @@
|
||||||
|
|
||||||
(def endsWith (fn (text pattern) (
|
(def endsWith (fn (text pattern) (
|
||||||
(matches text (cat "*" pattern))
|
(matches text (cat "*" pattern))
|
||||||
)))
|
)))
|
||||||
|
|
|
@ -6,48 +6,65 @@
|
||||||
" " " " " "
|
" " " " " "
|
||||||
))
|
))
|
||||||
|
|
||||||
|
(def is-empty (fn (tile) (= " " tile)))
|
||||||
|
|
||||||
(def print-board (fn (board) (
|
(def print-board (fn (board) (
|
||||||
(sys "clear")
|
(sys "clear")
|
||||||
(def '(a b c
|
(def '(a b c
|
||||||
d e f
|
d e f
|
||||||
g h i) board)
|
g h i) board)
|
||||||
(prnl (cat " " a " | " b " | " c))
|
(prnl)
|
||||||
(prnl "-----------")
|
(prnl (cat " " a " | " b " | " c))
|
||||||
(prnl (cat " " d " | " e " | " f))
|
(prnl " -----------")
|
||||||
(prnl "-----------")
|
(prnl (cat " " d " | " e " | " f))
|
||||||
(prnl (cat " " g " | " h " | " i))
|
(prnl " -----------")
|
||||||
|
(prnl (cat " " g " | " h " | " i))
|
||||||
)))
|
)))
|
||||||
|
|
||||||
(def do-move (fn (board player) (
|
(def do-move (fn (board player warn) (
|
||||||
(def input (inp))
|
(prnl)
|
||||||
|
(if warn (prnl "You can't go there!") ())
|
||||||
|
(prnl "Your turn, " player "!")
|
||||||
|
(prnl "Where do you want to play?")
|
||||||
|
(def input (inp "1-9: "))
|
||||||
(def spot (eval input))
|
(def spot (eval input))
|
||||||
(def i 0)
|
(def i 0)
|
||||||
(map (fn (piece) (
|
(if (& (is-empty (at (- spot 1) board)) (> spot 0) (< spot 10))
|
||||||
(set i (+ 1 i))
|
(map (fn (piece) (
|
||||||
(if (= i spot) player piece)
|
(set i (+ 1 i))
|
||||||
)) board)
|
(if (= i spot) player piece)
|
||||||
|
)) board)
|
||||||
|
(
|
||||||
|
(print-board board)
|
||||||
|
(do-move board player T)
|
||||||
|
)
|
||||||
|
)
|
||||||
)))
|
)))
|
||||||
|
|
||||||
(def winning-row (fn (row) (
|
(def is-winning-row (fn (row) (
|
||||||
(def '(a b c) row)
|
(def '(a b c) row)
|
||||||
(& (not (= " " a)) (= a b c))
|
(& (not (is-empty a)) (= a b c))
|
||||||
)))
|
)))
|
||||||
|
|
||||||
|
(def is-full-board (fn (board)
|
||||||
|
(none-in board is-empty)
|
||||||
|
))
|
||||||
|
|
||||||
(def get-winner (fn (board) (
|
(def get-winner (fn (board) (
|
||||||
(sys "clear")
|
|
||||||
(def '(a b c
|
(def '(a b c
|
||||||
d e f
|
d e f
|
||||||
g h i) board)
|
g h i) board)
|
||||||
(if (winning-row (a b c)) a
|
(if (is-winning-row (a b c)) a
|
||||||
(if (winning-row (d e f)) d
|
(if (is-winning-row (d e f)) d
|
||||||
(if (winning-row (g h i)) g
|
(if (is-winning-row (g h i)) g
|
||||||
(if (winning-row (a d g)) a
|
(if (is-winning-row (a d g)) a
|
||||||
(if (winning-row (b e h)) b
|
(if (is-winning-row (b e h)) b
|
||||||
(if (winning-row (c f i)) c
|
(if (is-winning-row (c f i)) c
|
||||||
(if (winning-row (a e i)) a
|
(if (is-winning-row (a e i)) a
|
||||||
(if (winning-row (c e g)) c
|
(if (is-winning-row (c e g)) c
|
||||||
|
(if (is-full-board board) "Nobody"
|
||||||
" "
|
" "
|
||||||
))))))))
|
)))))))))
|
||||||
)))
|
)))
|
||||||
|
|
||||||
(def next-player (fn (current) (
|
(def next-player (fn (current) (
|
||||||
|
@ -55,15 +72,15 @@
|
||||||
)))
|
)))
|
||||||
|
|
||||||
(def game (fn (board player) (
|
(def game (fn (board player) (
|
||||||
(if (= " " (get-winner board)) (
|
(print-board board)
|
||||||
(print-board board)
|
(def winner (get-winner board))
|
||||||
(def b (do-move board player))
|
(if (is-empty winner) (
|
||||||
|
(def b (do-move board player F))
|
||||||
(game b (next-player player))
|
(game b (next-player player))
|
||||||
) (
|
) (
|
||||||
(print-board board)
|
(prnl)
|
||||||
(prnl "")
|
(prnl "Game over! " winner " wins!")
|
||||||
(prnl "Game over! " (next-player player) " wins!")
|
|
||||||
))
|
))
|
||||||
)))
|
)))
|
||||||
|
|
||||||
(game empty-board "X")
|
(game empty-board (if (= 0 (rand 2)) "X" "O"))
|
||||||
|
|
|
@ -185,7 +185,7 @@ Object buildHashTable(Object* params, int length, struct Environment* env)
|
||||||
|
|
||||||
Object addToHashTable(Object* params, int length, struct Environment* env)
|
Object addToHashTable(Object* params, int length, struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(addToHashTable)
|
checkTypes(addToHashTable);
|
||||||
|
|
||||||
Object name = params[1];
|
Object name = params[1];
|
||||||
Object add = params[2];
|
Object add = params[2];
|
||||||
|
@ -197,7 +197,7 @@ Object addToHashTable(Object* params, int length, struct Environment* env)
|
||||||
|
|
||||||
Object getFromHashTable(Object* params, int length, struct Environment* env)
|
Object getFromHashTable(Object* params, int length, struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(getFromHashTable)
|
checkTypes(getFromHashTable);
|
||||||
|
|
||||||
struct ObjectTable* table = ¶ms[0].table->table;
|
struct ObjectTable* table = ¶ms[0].table->table;
|
||||||
struct StrippedObject* fetched = getFromTable(table, params[1].string);
|
struct StrippedObject* fetched = getFromTable(table, params[1].string);
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
#ifndef PEBBLISP_MAIN_H
|
|
||||||
#define PEBBLISP_MAIN_H
|
|
||||||
|
|
||||||
#endif // PEBBLISP_MAIN_H
|
|
|
@ -122,6 +122,7 @@ static const char* errorText[] = {
|
||||||
"NULL_MAP_ARGS",
|
"NULL_MAP_ARGS",
|
||||||
"LAMBDA_ARGS_NOT_LIST",
|
"LAMBDA_ARGS_NOT_LIST",
|
||||||
"DID_NOT_FIND_SYMBOL",
|
"DID_NOT_FIND_SYMBOL",
|
||||||
|
"BAD_SYMBOL",
|
||||||
"BAD_TYPE",
|
"BAD_TYPE",
|
||||||
"BAD_PARAMS",
|
"BAD_PARAMS",
|
||||||
"BAD_NUMBER",
|
"BAD_NUMBER",
|
||||||
|
@ -242,10 +243,10 @@ int stringNObj(struct string* s, const Object* obj)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TYPE_STATIC_FUNC:
|
case TYPE_STATIC_FUNC:
|
||||||
appendf(s, "STATIC FUNC");
|
appendf(s, "STATIC_FUNC");
|
||||||
break;
|
break;
|
||||||
case TYPE_HASH_TABLE:
|
case TYPE_HASH_TABLE:
|
||||||
appendf(s, "HASH TABLE");
|
appendf(s, "HASH_TABLE");
|
||||||
break;
|
break;
|
||||||
case TYPE_STRUCT:
|
case TYPE_STRUCT:
|
||||||
stringStruct(s, obj);
|
stringStruct(s, obj);
|
||||||
|
@ -276,7 +277,7 @@ int stringNObj(struct string* s, const Object* obj)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TYPE_FUNC:
|
case TYPE_FUNC:
|
||||||
appendf(s, "F%ld", obj->number);
|
appendf(s, "NATIVE_FUNC");
|
||||||
break;
|
break;
|
||||||
case TYPE_LAMBDA: {
|
case TYPE_LAMBDA: {
|
||||||
#if defined(STANDALONE) && !defined(DEBUG)
|
#if defined(STANDALONE) && !defined(DEBUG)
|
||||||
|
@ -733,7 +734,7 @@ inline Object withLen(size_t len, enum Type type)
|
||||||
inline Object constructLambda(const Object* params, const Object* body, struct Environment* env)
|
inline Object constructLambda(const Object* params, const Object* body, struct Environment* env)
|
||||||
{
|
{
|
||||||
if (!params || !body) {
|
if (!params || !body) {
|
||||||
return errorObject(NULL_LAMBDA_LIST);
|
throw(NULL_LAMBDA_LIST, "fn params and body cannot be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params->type != TYPE_LIST) {
|
if (params->type != TYPE_LIST) {
|
||||||
|
|
16
src/object.h
16
src/object.h
|
@ -22,7 +22,13 @@
|
||||||
#ifdef RELEASE
|
#ifdef RELEASE
|
||||||
#define assert(...) do { } while (0)
|
#define assert(...) do { } while (0)
|
||||||
#else
|
#else
|
||||||
#define assert(...) do {if (!(__VA_ARGS__)) {eprintf("\n[31;1massertion '" # __VA_ARGS__ "' at %s:%d failed!\nBailing![0m\n", __FILE__, __LINE__); int* X = NULL; unused int Y = *X;}} while (0)
|
#define assert(...) do { \
|
||||||
|
if (!(__VA_ARGS__)) { \
|
||||||
|
eprintf("\n[31;1massertion '" # __VA_ARGS__ "' at %s:%d failed!\nBailing![0m\n", __FILE__, __LINE__); \
|
||||||
|
int* X = NULL; \
|
||||||
|
unused int Y = *X; \
|
||||||
|
}\
|
||||||
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MAX_TOK_CNT 2048
|
#define MAX_TOK_CNT 2048
|
||||||
|
@ -94,6 +100,7 @@ enum errorCode {
|
||||||
NULL_MAP_ARGS,
|
NULL_MAP_ARGS,
|
||||||
LAMBDA_ARGS_NOT_LIST,
|
LAMBDA_ARGS_NOT_LIST,
|
||||||
DID_NOT_FIND_SYMBOL,
|
DID_NOT_FIND_SYMBOL,
|
||||||
|
BAD_SYMBOL,
|
||||||
BAD_TYPE,
|
BAD_TYPE,
|
||||||
BAD_PARAMS,
|
BAD_PARAMS,
|
||||||
BAD_NUMBER,
|
BAD_NUMBER,
|
||||||
|
@ -315,8 +322,11 @@ Object errorWithAllocatedContextLineNo(enum errorCode code, char* context, int l
|
||||||
#define errorAddContext(x, y, z, a) ;
|
#define errorAddContext(x, y, z, a) ;
|
||||||
#define throw(_code, ...) return errorObject(_code)
|
#define throw(_code, ...) return errorObject(_code)
|
||||||
#else
|
#else
|
||||||
#define throw(_code, ...) do { char* ERROR_CONTEXT = malloc(sizeof(char) * ERR_LEN); sprintf(ERROR_CONTEXT, __VA_ARGS__); \
|
#define throw(_code, ...) do { \
|
||||||
return errorWithAllocatedContextLineNo(_code, ERROR_CONTEXT, __LINE__, __FILE__); } while (0)
|
char* ERROR_CONTEXT = malloc(sizeof(char) * ERR_LEN); \
|
||||||
|
sprintf(ERROR_CONTEXT, __VA_ARGS__); \
|
||||||
|
return errorWithAllocatedContextLineNo(_code, ERROR_CONTEXT, __LINE__, __FILE__); \
|
||||||
|
} while (0)
|
||||||
#define errorWithContext(_code, _context) errorWithContextLineNo(_code, _context, __LINE__, __FILE__)
|
#define errorWithContext(_code, _context) errorWithContextLineNo(_code, _context, __LINE__, __FILE__)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE // For segfault handling
|
||||||
|
|
||||||
#include "pebblisp.h"
|
#include "pebblisp.h"
|
||||||
|
|
||||||
|
@ -21,7 +21,11 @@ struct Settings {
|
||||||
Object getPrompt(struct Environment* env)
|
Object getPrompt(struct Environment* env)
|
||||||
{
|
{
|
||||||
Object prompt = fetchFromEnvironment("prompt", env);
|
Object prompt = fetchFromEnvironment("prompt", env);
|
||||||
prompt = cloneObject(prompt);
|
if (!isError(prompt, DID_NOT_FIND_SYMBOL)) {
|
||||||
|
prompt = cloneObject(prompt);
|
||||||
|
} else {
|
||||||
|
prompt = stringFromSlice("pl ~> ", 7);
|
||||||
|
}
|
||||||
if (prompt.type == TYPE_STRING) {
|
if (prompt.type == TYPE_STRING) {
|
||||||
return prompt;
|
return prompt;
|
||||||
}
|
}
|
||||||
|
@ -116,7 +120,7 @@ void repl(struct Environment* env)
|
||||||
free(oldBuf);
|
free(oldBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object o = parseEval(buf, env);
|
Object o = parseEval(buf, "REPL", env);
|
||||||
if (isFuncy(o) || isError(o, DID_NOT_FIND_SYMBOL)) {
|
if (isFuncy(o) || isError(o, DID_NOT_FIND_SYMBOL)) {
|
||||||
system(buf);
|
system(buf);
|
||||||
} else {
|
} else {
|
||||||
|
@ -246,9 +250,9 @@ int main(int argc, const char* argv[])
|
||||||
readFile(SCRIPTDIR "/lib.pbl", &env);
|
readFile(SCRIPTDIR "/lib.pbl", &env);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object o = parseEval("(def prompt %%)", &env);
|
Object o = parseEval("(def prompt %%)", "[INTERNAL]", &env);
|
||||||
cleanObject(&o);
|
cleanObject(&o);
|
||||||
o = parseEval("(def preprocess (fn (text) (text)))", &env);
|
o = parseEval("(def preprocess (fn (text) (text)))", "[INTERNAL]", &env);
|
||||||
cleanObject(&o);
|
cleanObject(&o);
|
||||||
|
|
||||||
if (!settings.ignoreConfig) {
|
if (!settings.ignoreConfig) {
|
||||||
|
@ -268,10 +272,10 @@ int main(int argc, const char* argv[])
|
||||||
if (file) {
|
if (file) {
|
||||||
// Execute a file
|
// Execute a file
|
||||||
loadArgsIntoEnv(argc, argv, &env);
|
loadArgsIntoEnv(argc, argv, &env);
|
||||||
_readFile(file, &env);
|
_readFile(file, argv[argc - 1], &env);
|
||||||
} else {
|
} else {
|
||||||
// Run arguments directly as pl code
|
// Run arguments directly as pl code
|
||||||
Object r = parseEval(argv[argc - 1], &env);
|
Object r = parseEval(argv[argc - 1], "[PL ARGUMENTS]", &env);
|
||||||
printAndClean(&r);
|
printAndClean(&r);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
|
@ -0,0 +1,4 @@
|
||||||
|
#ifndef PC_MAIN_H
|
||||||
|
#define PC_MAIN_H
|
||||||
|
|
||||||
|
#endif
|
|
@ -4,7 +4,7 @@
|
||||||
#define HASHLESS_ENV
|
#define HASHLESS_ENV
|
||||||
#include "object.h"
|
#include "object.h"
|
||||||
#include "plfunc/pebbleobject.h"
|
#include "plfunc/pebbleobject.h"
|
||||||
#include "calc.h"
|
#include "pebblemain.h"
|
||||||
|
|
||||||
#define RESULT_LENGTH 128
|
#define RESULT_LENGTH 128
|
||||||
|
|
||||||
|
@ -379,7 +379,7 @@ static Object run_script(Object* params, int length,
|
||||||
free(code);
|
free(code);
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
return errorObject(SCRIPT_NOT_FOUND);
|
throw(SCRIPT_NOT_FOUND, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void code_window_unload(Window* window)
|
static void code_window_unload(Window* window)
|
||||||
|
@ -527,7 +527,7 @@ static void inbox_received_callback(DictionaryIterator* iter, void* context)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** General **/
|
/** General **/
|
||||||
void af(const char* name, Object (* func)(Object*, int, struct Environment*), struct Environment* env)
|
void addFunction(const char* name, Object (* func)(Object*, int, struct Environment*), struct Environment* env)
|
||||||
{
|
{
|
||||||
Object o = newObject(TYPE_FUNC);
|
Object o = newObject(TYPE_FUNC);
|
||||||
o.func = func;
|
o.func = func;
|
||||||
|
@ -538,15 +538,15 @@ static struct Environment pebbleEnv()
|
||||||
{
|
{
|
||||||
struct Environment e = defaultEnv();
|
struct Environment e = defaultEnv();
|
||||||
setGlobal(&e);
|
setGlobal(&e);
|
||||||
af("window", &add_window, &e);
|
addFunction("window", &add_window, &e);
|
||||||
af("sc", &run_script, &e);
|
addFunction("sc", &run_script, &e);
|
||||||
af("cw", &createWindow, &e);
|
addFunction("cw", &createWindow, &e);
|
||||||
af("pw", &pushWindow, &e);
|
addFunction("pw", &pushWindow, &e);
|
||||||
af("rw", &deleteWindow, &e);
|
addFunction("rw", &deleteWindow, &e);
|
||||||
af("atl", &addTextLayer, &e);
|
addFunction("atl", &addTextLayer, &e);
|
||||||
af("utl", &updateTextLayer, &e);
|
addFunction("utl", &updateTextLayer, &e);
|
||||||
af("vibe", &doVibe, &e);
|
addFunction("vibe", &doVibe, &e);
|
||||||
af("sub", &subscribe, &e);
|
addFunction("sub", &subscribe, &e);
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef CALC_H
|
#ifndef PEBBLE_MAIN_H
|
||||||
#define CALC_H
|
#define PEBBLE_MAIN_H
|
||||||
|
|
||||||
#include <pebble.h>
|
#include <pebble.h>
|
||||||
#include "pebblisp.h"
|
#include "pebblisp.h"
|
|
@ -54,14 +54,16 @@ Object listDef(Object* nameList, Object* valueList, struct Environment* env)
|
||||||
* @param env The environment to add the new definition to
|
* @param env The environment to add the new definition to
|
||||||
* @return The symbol(s) defined
|
* @return The symbol(s) defined
|
||||||
*/
|
*/
|
||||||
Object def(Object* params, unused int length, struct Environment* env)
|
Object def(Object* params, int length, struct Environment* env)
|
||||||
{
|
{
|
||||||
if (isStringy(params[0])) {
|
if (length == 2) {
|
||||||
return singleDef(params[0].string, ¶ms[1], env);
|
if (isStringy(params[0])) {
|
||||||
}
|
return singleDef(params[0].string, ¶ms[1], env);
|
||||||
|
}
|
||||||
|
|
||||||
if (length == 2 && isListy(params[0]) && isListy(params[1])) {
|
if (isListy(params[0]) && isListy(params[1])) {
|
||||||
return listDef(¶ms[0], ¶ms[1], env);
|
return listDef(¶ms[0], ¶ms[1], env);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw(BAD_TYPE, "Poorly constructed (def)");
|
throw(BAD_TYPE, "Poorly constructed (def)");
|
||||||
|
@ -361,7 +363,7 @@ Object eval(const Object* obj, struct Environment* env)
|
||||||
|
|
||||||
Object structAccess(Object* params, unused int length, unused struct Environment* env)
|
Object structAccess(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(structAccess)
|
checkTypes(structAccess);
|
||||||
|
|
||||||
Object structo = params[0];
|
Object structo = params[0];
|
||||||
Object field = params[1];
|
Object field = params[1];
|
||||||
|
@ -565,13 +567,13 @@ struct Slice* getLastOpen()
|
||||||
return lastOpen;
|
return lastOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object parseEval(const char* input, struct Environment* env)
|
Object parseEval(const char* input, const char* codeSource, struct Environment* env)
|
||||||
{
|
{
|
||||||
struct Error err = noError();
|
struct Error err = noError();
|
||||||
|
|
||||||
struct Slice* tokens = nf_tokenize(input, &err);
|
struct Slice* tokens = nf_tokenize(input, &err);
|
||||||
if (err.context != NULL) {
|
if (err.context != NULL) {
|
||||||
Object o = errorWithContext(err.code, err.context);
|
Object o = errorWithContextLineNo(err.code, err.context, err.plContext->lineNumber, codeSource);
|
||||||
free(err.context);
|
free(err.context);
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
@ -681,21 +683,21 @@ char* readFileToString(FILE* input)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns 1 if the file could not be opened. Otherwise, 0
|
/// Returns 1 if the file could not be opened. Otherwise, 0
|
||||||
int readFile(const char* filename, struct Environment* env)
|
int readFile(const char* fileName, struct Environment* env)
|
||||||
{
|
{
|
||||||
FILE* input = fopen(filename, "r");
|
FILE* input = fopen(fileName, "r");
|
||||||
if (!input) {
|
if (!input) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
_readFile(input, env);
|
_readFile(input, fileName, env);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _readFile(FILE* input, struct Environment* env)
|
int _readFile(FILE* input, const char* fileName, struct Environment* env)
|
||||||
{
|
{
|
||||||
char* fileText = readFileToString(input);
|
char* fileText = readFileToString(input);
|
||||||
fclose(input);
|
fclose(input);
|
||||||
Object r = parseEval(fileText, env);
|
Object r = parseEval(fileText, fileName, env);
|
||||||
cleanObject(&r);
|
cleanObject(&r);
|
||||||
free(fileText - 1);
|
free(fileText - 1);
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ Result readSeq(struct Slice* slices);
|
||||||
|
|
||||||
Result parseAtom(struct Slice* slice);
|
Result parseAtom(struct Slice* slice);
|
||||||
|
|
||||||
Object parseEval(const char* input, struct Environment* env);
|
Object parseEval(const char* input, const char* fileName, struct Environment* env);
|
||||||
|
|
||||||
Object evalList(const Object* obj, struct Environment* env);
|
Object evalList(const Object* obj, struct Environment* env);
|
||||||
|
|
||||||
|
@ -46,20 +46,24 @@ Object typeCheck(const char* funcName, Object* params, int length,
|
||||||
|
|
||||||
#ifndef DISABLE_TYPE_CHECKS
|
#ifndef DISABLE_TYPE_CHECKS
|
||||||
#define checkTypes(FUNC) int FAILED; Object ERROR = typeCheck(FUNC ## Symbol, params, length, FUNC ## TypeChecks, array_length(FUNC ## TypeChecks), &FAILED); \
|
#define checkTypes(FUNC) int FAILED; Object ERROR = typeCheck(FUNC ## Symbol, params, length, FUNC ## TypeChecks, array_length(FUNC ## TypeChecks), &FAILED); \
|
||||||
|
if (FAILED) { \
|
||||||
|
return ERROR; \
|
||||||
|
} else do { } while (0)
|
||||||
|
#define verifyTypes(FUNC, TYPE_CHECKS) int FAILED; Object ERROR = typeCheck(FUNC ## Symbol, params, length, TYPE_CHECKS, length, &FAILED); \
|
||||||
if (FAILED) { \
|
if (FAILED) { \
|
||||||
return ERROR; \
|
return ERROR; \
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define checkTypes(FUNC) ;
|
#define checkTypes(FUNC) do { } while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef STANDALONE
|
#ifdef STANDALONE
|
||||||
|
|
||||||
char* readFileToString(FILE* input);
|
char* readFileToString(FILE* input);
|
||||||
|
|
||||||
int _readFile(FILE* input, struct Environment* env);
|
int _readFile(FILE* input, const char* fileName, struct Environment* env);
|
||||||
|
|
||||||
int readFile(const char* filename, struct Environment* env);
|
int readFile(const char* fileName, struct Environment* env);
|
||||||
|
|
||||||
struct Slice* getLastOpen();
|
struct Slice* getLastOpen();
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
Object reduce(Object* params, unused int length, struct Environment* env)
|
Object reduce(Object* params, unused int length, struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(reduce)
|
checkTypes(reduce);
|
||||||
Object list = params[0];
|
Object list = params[0];
|
||||||
Object func = params[1];
|
Object func = params[1];
|
||||||
Object total = params[2];
|
Object total = params[2];
|
||||||
|
@ -33,7 +33,7 @@ Object reduce(Object* params, unused int length, struct Environment* env)
|
||||||
|
|
||||||
Object filter(Object* params, unused int length, struct Environment* env)
|
Object filter(Object* params, unused int length, struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(filter)
|
checkTypes(filter);
|
||||||
Object condition = params[0];
|
Object condition = params[0];
|
||||||
Object list = params[1];
|
Object list = params[1];
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ Object filter(Object* params, unused int length, struct Environment* env)
|
||||||
|
|
||||||
Object append(Object* params, unused int length, unused struct Environment* env)
|
Object append(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(append)
|
checkTypes(append);
|
||||||
Object list = params[0];
|
Object list = params[0];
|
||||||
Object newElement = params[1];
|
Object newElement = params[1];
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ Object append(Object* params, unused int length, unused struct Environment* env)
|
||||||
|
|
||||||
Object prepend(Object* params, unused int length, unused struct Environment* env)
|
Object prepend(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(prepend)
|
checkTypes(prepend);
|
||||||
Object list = cloneObject(params[0]);
|
Object list = cloneObject(params[0]);
|
||||||
Object newElement = cloneObject(params[1]);
|
Object newElement = cloneObject(params[1]);
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ Object prepend(Object* params, unused int length, unused struct Environment* env
|
||||||
|
|
||||||
Object at(Object* params, unused int length, unused struct Environment* env)
|
Object at(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(at)
|
checkTypes(at);
|
||||||
Object index = params[0];
|
Object index = params[0];
|
||||||
Object list = params[1];
|
Object list = params[1];
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ Object at(Object* params, unused int length, unused struct Environment* env)
|
||||||
|
|
||||||
Object rest(Object* params, unused int length, unused struct Environment* env)
|
Object rest(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(rest)
|
checkTypes(rest);
|
||||||
Object list = params[0];
|
Object list = params[0];
|
||||||
|
|
||||||
Object BuildListNamed(ret);
|
Object BuildListNamed(ret);
|
||||||
|
@ -108,7 +108,7 @@ Object rest(Object* params, unused int length, unused struct Environment* env)
|
||||||
|
|
||||||
Object reverse(Object* params, unused int length, unused struct Environment* ignore2)
|
Object reverse(Object* params, unused int length, unused struct Environment* ignore2)
|
||||||
{
|
{
|
||||||
checkTypes(reverse)
|
checkTypes(reverse);
|
||||||
const Object* list = ¶ms[0];
|
const Object* list = ¶ms[0];
|
||||||
|
|
||||||
Object rev = listObject();
|
Object rev = listObject();
|
||||||
|
@ -128,7 +128,7 @@ Object reverse(Object* params, unused int length, unused struct Environment* ign
|
||||||
|
|
||||||
Object isNum(Object* params, unused int length, unused struct Environment* env)
|
Object isNum(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(isNum)
|
checkTypes(isNum);
|
||||||
Object test = params[0];
|
Object test = params[0];
|
||||||
|
|
||||||
return boolObject(test.type == TYPE_NUMBER);
|
return boolObject(test.type == TYPE_NUMBER);
|
||||||
|
@ -136,7 +136,7 @@ Object isNum(Object* params, unused int length, unused struct Environment* env)
|
||||||
|
|
||||||
Object isList(Object* params, unused int length, unused struct Environment* env)
|
Object isList(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(isList)
|
checkTypes(isList);
|
||||||
Object test = params[0];
|
Object test = params[0];
|
||||||
|
|
||||||
return boolObject(test.type == TYPE_LIST);
|
return boolObject(test.type == TYPE_LIST);
|
||||||
|
@ -144,7 +144,7 @@ Object isList(Object* params, unused int length, unused struct Environment* env)
|
||||||
|
|
||||||
Object isString(Object* params, unused int length, unused struct Environment* env)
|
Object isString(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(isString)
|
checkTypes(isString);
|
||||||
Object test = params[0];
|
Object test = params[0];
|
||||||
|
|
||||||
return boolObject(test.type == TYPE_STRING);
|
return boolObject(test.type == TYPE_STRING);
|
||||||
|
@ -152,7 +152,7 @@ Object isString(Object* params, unused int length, unused struct Environment* en
|
||||||
|
|
||||||
Object isErr(Object* params, unused int length, unused struct Environment* env)
|
Object isErr(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(isErr)
|
checkTypes(isErr);
|
||||||
Object test = params[0];
|
Object test = params[0];
|
||||||
|
|
||||||
return boolObject(test.type == TYPE_ERROR);
|
return boolObject(test.type == TYPE_ERROR);
|
||||||
|
@ -176,14 +176,14 @@ Object parseEvalO(Object* params, unused int length, struct Environment* env)
|
||||||
switch (text.type) {
|
switch (text.type) {
|
||||||
case TYPE_SYMBOL: {
|
case TYPE_SYMBOL: {
|
||||||
Object string = eval(&text, env);
|
Object string = eval(&text, env);
|
||||||
Object parsed = parseEval(string.string, env);
|
Object parsed = parseEval(string.string, "(eval)", env);
|
||||||
cleanObject(&string);
|
cleanObject(&string);
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
case TYPE_SLIST:
|
case TYPE_SLIST:
|
||||||
return evalList(&text, env);
|
return evalList(&text, env);
|
||||||
case TYPE_STRING:
|
case TYPE_STRING:
|
||||||
return parseEval(text.string, env);
|
return parseEval(text.string, "(eval)", env);
|
||||||
default:
|
default:
|
||||||
throw(CAN_ONLY_EVAL_STRINGS, "Tried to (eval) a %s, instead of a string or symbol list",
|
throw(CAN_ONLY_EVAL_STRINGS, "Tried to (eval) a %s, instead of a string or symbol list",
|
||||||
getTypeName(&text));
|
getTypeName(&text));
|
||||||
|
@ -192,7 +192,7 @@ Object parseEvalO(Object* params, unused int length, struct Environment* env)
|
||||||
|
|
||||||
Object catObjects(Object* params, int length, unused struct Environment* env)
|
Object catObjects(Object* params, int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(catObjects)
|
checkTypes(catObjects);
|
||||||
|
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
return stringFromSlice("", 0);
|
return stringFromSlice("", 0);
|
||||||
|
@ -217,7 +217,7 @@ Object catObjects(Object* params, int length, unused struct Environment* env)
|
||||||
|
|
||||||
Object len(Object* params, unused int length, unused struct Environment* env)
|
Object len(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(len)
|
checkTypes(len);
|
||||||
|
|
||||||
return numberObject(listLength(¶ms[0]));
|
return numberObject(listLength(¶ms[0]));
|
||||||
}
|
}
|
||||||
|
@ -277,7 +277,7 @@ int areEqual(const Object* obj1, const Object* obj2)
|
||||||
Object equ(Object* params, int length, unused struct Environment* env)
|
Object equ(Object* params, int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
if (length < 2) {
|
if (length < 2) {
|
||||||
return errorObject(NOT_ENOUGH_ARGUMENTS);
|
throw(NOT_ENOUGH_ARGUMENTS, "expected at least 2");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < length - 1; i++) {
|
for (int i = 0; i < length - 1; i++) {
|
||||||
|
@ -329,7 +329,7 @@ int timeStructDefinition = -1;
|
||||||
Object getTime(unused Object* params, unused int length, struct Environment* env)
|
Object getTime(unused Object* params, unused int length, struct Environment* env)
|
||||||
{
|
{
|
||||||
if (timeStructDefinition == -1) {
|
if (timeStructDefinition == -1) {
|
||||||
parseEval("(struct Time (minute hour sec))", env);
|
parseEval("(struct Time (minute hour sec))", "[INTERNAL]", env);
|
||||||
timeStructDefinition = getStructIndex("Time");
|
timeStructDefinition = getStructIndex("Time");
|
||||||
}
|
}
|
||||||
time_t t = time(NULL);
|
time_t t = time(NULL);
|
||||||
|
|
|
@ -12,9 +12,15 @@ Object print(Object* params, int length, unused struct Environment* env)
|
||||||
return numberObject(0);
|
return numberObject(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object typeOf(Object* params, unused int length, unused struct Environment* env)
|
||||||
|
{
|
||||||
|
const char* typeString = getTypeName(&(params[0]));
|
||||||
|
return nullTerminated(typeString);
|
||||||
|
}
|
||||||
|
|
||||||
Object numToChar(Object* params, unused int length, unused struct Environment* env)
|
Object numToChar(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(numToChar)
|
checkTypes(numToChar);
|
||||||
Object c = params[0];
|
Object c = params[0];
|
||||||
|
|
||||||
if (c.number > 255 || c.number < 0) {
|
if (c.number > 255 || c.number < 0) {
|
||||||
|
@ -39,7 +45,7 @@ Object takeInput(Object* params, int length, unused struct Environment* env)
|
||||||
|
|
||||||
Object cd(Object* params, unused int length, unused struct Environment* env)
|
Object cd(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(cd)
|
checkTypes(cd);
|
||||||
|
|
||||||
return numberObject(chdir(params[0].string));
|
return numberObject(chdir(params[0].string));
|
||||||
}
|
}
|
||||||
|
@ -52,7 +58,7 @@ Object cwd(unused Object* params, unused int length, unused struct Environment*
|
||||||
|
|
||||||
Object systemCall(Object* params, unused int length, unused struct Environment* env)
|
Object systemCall(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(systemCall)
|
checkTypes(systemCall);
|
||||||
Object process = params[0];
|
Object process = params[0];
|
||||||
|
|
||||||
if (isStringy(process)) {
|
if (isStringy(process)) {
|
||||||
|
@ -63,7 +69,7 @@ Object systemCall(Object* params, unused int length, unused struct Environment*
|
||||||
|
|
||||||
Object readFileToObject(Object* params, unused int length, unused struct Environment* env)
|
Object readFileToObject(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(readFileToObject)
|
checkTypes(readFileToObject);
|
||||||
Object filename = params[0];
|
Object filename = params[0];
|
||||||
|
|
||||||
FILE* file = fopen(filename.string, "r");
|
FILE* file = fopen(filename.string, "r");
|
||||||
|
@ -79,11 +85,27 @@ Object readFileToObject(Object* params, unused int length, unused struct Environ
|
||||||
|
|
||||||
Object getEnvVar(Object* params, unused int length, unused struct Environment* env)
|
Object getEnvVar(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(getEnvVar)
|
checkTypes(getEnvVar);
|
||||||
const char* envVar = getenv(params[0].string);
|
const char* envVar = getenv(params[0].string);
|
||||||
if (envVar) {
|
if (envVar) {
|
||||||
return nullTerminated(envVar);
|
return nullTerminated(envVar);
|
||||||
}
|
}
|
||||||
return stringFromSlice("", 0);
|
return stringFromSlice("", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
int initializedRand = 0;
|
||||||
|
Object randomO(Object* params, unused int length, unused struct Environment* env)
|
||||||
|
{
|
||||||
|
if (!initializedRand) {
|
||||||
|
srand(time(0));
|
||||||
|
initializedRand = 1;
|
||||||
|
}
|
||||||
|
int num = rand();
|
||||||
|
if (length > 0 && params[0].type == TYPE_NUMBER) {
|
||||||
|
num = num % params[0].number;
|
||||||
|
}
|
||||||
|
return numberObject(num);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // STANDALONE
|
#endif // STANDALONE
|
|
@ -7,6 +7,14 @@
|
||||||
|
|
||||||
fn(print, "prn", "Prints the string representation of the given object to stdout.");
|
fn(print, "prn", "Prints the string representation of the given object to stdout.");
|
||||||
|
|
||||||
|
tfn(typeOf, "type-of",
|
||||||
|
({ anyType, returns(isStringy) }),
|
||||||
|
"Gets a string indicating the type of the given object",
|
||||||
|
"(type-of 10)", "TYPE_NUMBER",
|
||||||
|
"(type-of \"string\")", "TYPE_STRING",
|
||||||
|
"(type-of (fn () ()))", "TYPE_LAMBDA",
|
||||||
|
);
|
||||||
|
|
||||||
tfn(numToChar, "ch",
|
tfn(numToChar, "ch",
|
||||||
({ expect(isNumber), returns(isStringy) }),
|
({ expect(isNumber), returns(isStringy) }),
|
||||||
"Gets a string containing the ascii character for the given number value.",
|
"Gets a string containing the ascii character for the given number value.",
|
||||||
|
@ -60,6 +68,12 @@ tfn(getEnvVar, "env",
|
||||||
"(env HOME) => /home/sagevaillancourt"
|
"(env HOME) => /home/sagevaillancourt"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
tfn(randomO, "rand",
|
||||||
|
({ returns(isNumber) }),
|
||||||
|
"Returns a semi-random integer\n"
|
||||||
|
"(rand) => 2394568"
|
||||||
|
);
|
||||||
|
|
||||||
#endif //PEBBLISP_PC_H
|
#endif //PEBBLISP_PC_H
|
||||||
|
|
||||||
#endif // STANDALONE
|
#endif // STANDALONE
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "pebbleobject.h"
|
#include "pebbleobject.h"
|
||||||
|
|
||||||
#include "../calc.h"
|
#include "../pebblemain.h"
|
||||||
#include "../object.h"
|
#include "../object.h"
|
||||||
|
|
||||||
Object pebbleOther(enum PebbleType type, void* data, void (* cleanup)(Object*),
|
Object pebbleOther(enum PebbleType type, void* data, void (* cleanup)(Object*),
|
||||||
|
@ -94,7 +94,7 @@ Object addTextLayer(Object* params, int length, struct Environment* env)
|
||||||
Object window = params[0];
|
Object window = params[0];
|
||||||
Object text = params[1];
|
Object text = params[1];
|
||||||
if (getPebbleType(window) != WINDOW) {
|
if (getPebbleType(window) != WINDOW) {
|
||||||
return errorObject(0);
|
throw(BAD_TYPE, "Expected a pebble window, but received %s", getTypeName(&window));
|
||||||
}
|
}
|
||||||
Layer* window_layer =
|
Layer* window_layer =
|
||||||
window_get_root_layer(accessPebbleObject(window)->window);
|
window_get_root_layer(accessPebbleObject(window)->window);
|
||||||
|
@ -179,6 +179,6 @@ Object doVibe(Object* params, int length, struct Environment* env)
|
||||||
(VibePattern) {.durations = pattern, .num_segments = l});
|
(VibePattern) {.durations = pattern, .num_segments = l});
|
||||||
return trueObject();
|
return trueObject();
|
||||||
} else {
|
} else {
|
||||||
return errorObject(NOT_A_LIST);
|
throw(NOT_A_LIST, "(vibe) requires a non-empty list!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
Object charVal(Object* params, unused int length, unused struct Environment* env)
|
Object charVal(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(charVal)
|
checkTypes(charVal);
|
||||||
Object test = params[0];
|
Object test = params[0];
|
||||||
|
|
||||||
return numberObject(test.string[0]);
|
return numberObject(test.string[0]);
|
||||||
|
@ -12,7 +12,7 @@ Object charVal(Object* params, unused int length, unused struct Environment* env
|
||||||
|
|
||||||
Object chars(Object* params, unused int length, unused struct Environment* env)
|
Object chars(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(chars)
|
checkTypes(chars);
|
||||||
char c[2];
|
char c[2];
|
||||||
c[1] = '\0';
|
c[1] = '\0';
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -62,19 +62,19 @@ int stringComp(const char* string, const char* pattern)
|
||||||
|
|
||||||
Object matches(Object* params, int length, unused struct Environment* env)
|
Object matches(Object* params, int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(matches)
|
checkTypes(matches);
|
||||||
return boolObject(stringComp(params[0].string, params[1].string));
|
return boolObject(stringComp(params[0].string, params[1].string));
|
||||||
}
|
}
|
||||||
|
|
||||||
Object slen(Object* params, int length, unused struct Environment* env)
|
Object slen(Object* params, int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(slen)
|
checkTypes(slen);
|
||||||
return numberObject(strlen(params[0].string));
|
return numberObject(strlen(params[0].string));
|
||||||
}
|
}
|
||||||
|
|
||||||
Object substring(Object* params, int length, unused struct Environment* env)
|
Object substring(Object* params, int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(substring)
|
checkTypes(substring);
|
||||||
Object start = params[0]; // First char to include
|
Object start = params[0]; // First char to include
|
||||||
Object end = params[1]; // First char to exclude
|
Object end = params[1]; // First char to exclude
|
||||||
Object string = params[2];
|
Object string = params[2];
|
||||||
|
@ -99,7 +99,7 @@ Object substring(Object* params, int length, unused struct Environment* env)
|
||||||
|
|
||||||
Object charAt(Object* params, unused int length, unused struct Environment* env)
|
Object charAt(Object* params, unused int length, unused struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(charAt)
|
checkTypes(charAt);
|
||||||
Object string = params[0];
|
Object string = params[0];
|
||||||
Object at = params[1];
|
Object at = params[1];
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ void cleanPromise(struct Promise* promise)
|
||||||
|
|
||||||
Object await(Object* params, int length, struct Environment* env)
|
Object await(Object* params, int length, struct Environment* env)
|
||||||
{
|
{
|
||||||
checkTypes(await)
|
checkTypes(await);
|
||||||
struct Promise* promise = params[0].promise;
|
struct Promise* promise = params[0].promise;
|
||||||
if (!promise->done) { // TODO: Does `done` need a mutex or other lock?
|
if (!promise->done) { // TODO: Does `done` need a mutex or other lock?
|
||||||
pthread_join(promise->thread, NULL);
|
pthread_join(promise->thread, NULL);
|
||||||
|
|
|
@ -59,7 +59,7 @@ add_query_param(void* queryParamsV, enum MHD_ValueKind kind, const char* key,
|
||||||
Object pair = startList(nullTerminated(key));
|
Object pair = startList(nullTerminated(key));
|
||||||
Object parsed;
|
Object parsed;
|
||||||
if (isDigit(value[0])) {
|
if (isDigit(value[0])) {
|
||||||
parsed = parseEval(value, NULL);
|
parsed = parseEval(value, "[INTERNAL]", NULL);
|
||||||
} else {
|
} else {
|
||||||
parsed = stringFromSlice(value, strlen(value));
|
parsed = stringFromSlice(value, strlen(value));
|
||||||
}
|
}
|
||||||
|
@ -127,8 +127,7 @@ void initialize()
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
initialized = 1;
|
initialized = 1;
|
||||||
}
|
}
|
||||||
eprintf("Initializing...\n");
|
Object o = parseEval("(struct Request (queryParams username password))", "[INTERNAL]", global());
|
||||||
Object o = parseEval("(struct Request (queryParams username password))", global());
|
|
||||||
cleanObject(&o);
|
cleanObject(&o);
|
||||||
requestDefinition = getStructIndex("Request");
|
requestDefinition = getStructIndex("Request");
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,9 @@ check() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
local expected="$3"
|
||||||
if [ "$3" == "$regex" ]; then
|
if [ "$3" == "$regex" ]; then
|
||||||
|
expected="$4"
|
||||||
if [[ "$output" =~ ^$4$ ]]; then
|
if [[ "$output" =~ ^$4$ ]]; then
|
||||||
pass "$1" $exit_code
|
pass "$1" $exit_code
|
||||||
return
|
return
|
||||||
|
@ -86,7 +88,7 @@ check() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fail "$1" "$2"
|
fail "$1" "$2"
|
||||||
FAIL_OUTPUT="${FAIL_OUTPUT}\n [31m expected '$3' but received '$output'\n"
|
FAIL_OUTPUT="${FAIL_OUTPUT}\n [31m expected '$expected' but received '$output'\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "[1;34m::SHELL TESTS::[0;m"
|
echo "[1;34m::SHELL TESTS::[0;m"
|
||||||
|
|
112
src/tokens.c
112
src/tokens.c
|
@ -4,17 +4,55 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Grammar:
|
* Grammar:
|
||||||
* token
|
* Number:
|
||||||
* expr
|
* Hex:
|
||||||
* (op expr expr)
|
* 0x[0-9a-f]+
|
||||||
* (list expr expr ... )
|
* Bin:
|
||||||
|
* 0b[01]+
|
||||||
|
* Dec:
|
||||||
|
* -?[0-9]+
|
||||||
|
*
|
||||||
|
* String:
|
||||||
|
* "([^\n"])|(\\")*"
|
||||||
|
* """.*"""
|
||||||
|
*
|
||||||
|
* Boolean:
|
||||||
|
* T
|
||||||
|
* F
|
||||||
|
*
|
||||||
|
* List:
|
||||||
|
* (...Expression)
|
||||||
|
*
|
||||||
|
* SList:
|
||||||
|
* '(...Expression)
|
||||||
|
*
|
||||||
|
* Symbol:
|
||||||
|
* [a-z][a-z0-9]*
|
||||||
|
*
|
||||||
|
* Primitive:
|
||||||
|
* Symbol
|
||||||
|
* Number
|
||||||
|
* String
|
||||||
|
* Boolean
|
||||||
|
* List
|
||||||
|
*
|
||||||
|
* Function Call:
|
||||||
|
* (Symbol ...Expression)
|
||||||
|
*
|
||||||
|
* Struct Access:
|
||||||
|
* Symbol.Symbol
|
||||||
|
*
|
||||||
|
* Expression:
|
||||||
|
* Primitive
|
||||||
|
* Function Call
|
||||||
|
* Struct Access
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Is the char a standalone token?
|
// Is the char a standalone token?
|
||||||
static const char singleTokens[] = "()'?";
|
|
||||||
|
|
||||||
int isSingle(const char *c)
|
int isSingle(const char *c)
|
||||||
{
|
{
|
||||||
|
static const char singleTokens[] = "()'?";
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (singleTokens[i] != '\0') {
|
while (singleTokens[i] != '\0') {
|
||||||
if (singleTokens[i] == c[0]) {
|
if (singleTokens[i] == c[0]) {
|
||||||
|
@ -39,18 +77,18 @@ int isWhitespace(const char c)
|
||||||
return c == ' ' || c == '\t' || c == '\n';
|
return c == ' ' || c == '\t' || c == '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
void buildErrFromInput(struct Error* err, enum errorCode code, int i, const char* input)
|
void buildErrFromInput(struct Error* err, enum errorCode code, int i, const char* input, struct Slice* slice)
|
||||||
{
|
{
|
||||||
err->context = malloc(sizeof(char) * ERR_LEN + 1);
|
err->context = malloc(sizeof(char) * ERR_LEN + 1);
|
||||||
err->code = code;
|
err->code = code;
|
||||||
|
err->plContext = slice;
|
||||||
int start = i > ERR_LEN ? i - ERR_LEN : 0;
|
int start = i > ERR_LEN ? i - ERR_LEN : 0;
|
||||||
strncpy(err->context, &input[start], ERR_LEN);
|
strncpy(err->context, &input[start], ERR_LEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void collectSymbol(const char* input, int* i, int* length);
|
void collectSymbol(const char* input, int* i, int* length);
|
||||||
|
|
||||||
void processString(const char* input, struct Error* err, struct Slice* slices, int slice, int* i, int* lineNumber,
|
void processString(const char* input, struct Error* err, struct Slice* slice, int* i, int* lineNumber, int* length);
|
||||||
int* length);
|
|
||||||
|
|
||||||
struct Slice* nf_tokenize(const char* input, struct Error* err)
|
struct Slice* nf_tokenize(const char* input, struct Error* err)
|
||||||
{
|
{
|
||||||
|
@ -60,6 +98,7 @@ struct Slice* nf_tokenize(const char* input, struct Error* err)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Slice slices[MAX_TOK_CNT];
|
struct Slice slices[MAX_TOK_CNT];
|
||||||
|
struct Slice* slice = &slices[0];
|
||||||
//int token_count = MAX_TOK_CNT;
|
//int token_count = MAX_TOK_CNT;
|
||||||
// do {
|
// do {
|
||||||
// slices = malloc(sizeof(struct Slice) * token_count);
|
// slices = malloc(sizeof(struct Slice) * token_count);
|
||||||
|
@ -67,7 +106,6 @@ struct Slice* nf_tokenize(const char* input, struct Error* err)
|
||||||
// } while (slices == NULL);
|
// } while (slices == NULL);
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int slice = 0;
|
|
||||||
int lineNumber = 1;
|
int lineNumber = 1;
|
||||||
|
|
||||||
int parens = 0;
|
int parens = 0;
|
||||||
|
@ -86,14 +124,14 @@ struct Slice* nf_tokenize(const char* input, struct Error* err)
|
||||||
} else if (input[i] == ')') {
|
} else if (input[i] == ')') {
|
||||||
parens--;
|
parens--;
|
||||||
if (parens < 0) {
|
if (parens < 0) {
|
||||||
buildErrFromInput(err, MISMATCHED_PARENS, i, input);
|
buildErrFromInput(err, MISMATCHED_PARENS, i, input, slice);
|
||||||
//free(slices);
|
//free(slices);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slices[slice].text = &input[i];
|
slice->text = &input[i];
|
||||||
slices[slice].lineNumber = lineNumber;
|
slice->lineNumber = lineNumber;
|
||||||
|
|
||||||
if (isSingle(&input[i])) {
|
if (isSingle(&input[i])) {
|
||||||
i++;
|
i++;
|
||||||
|
@ -103,28 +141,31 @@ struct Slice* nf_tokenize(const char* input, struct Error* err)
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
} else if (input[i] == '"') {
|
} else if (input[i] == '"') {
|
||||||
processString(input, err, slices, slice, &i, &lineNumber, &length);
|
processString(input, err, slice, &i, &lineNumber, &length);
|
||||||
|
if (err->context != NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
collectSymbol(input, &i, &length);
|
collectSymbol(input, &i, &length);
|
||||||
|
if (length == 0) {
|
||||||
|
buildErrFromInput(err, BAD_SYMBOL, i, input, slice);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err->context != NULL) {
|
slice->length = length;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
slices[slice].length = length;
|
|
||||||
slice++;
|
slice++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parens != 0) {
|
if (parens != 0) {
|
||||||
buildErrFromInput(err, MISMATCHED_PARENS, i, input);
|
buildErrFromInput(err, MISMATCHED_PARENS, i, input, slice);
|
||||||
//free(slices);
|
//free(slices);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
slices[slice].text = NULL;
|
slice->text = NULL;
|
||||||
slices[slice].length = 0;
|
slice->length = 0;
|
||||||
size_t size = sizeof(struct Slice) * (slice + 1);
|
size_t size = sizeof(struct Slice) * ((slice - &slices[0]) + 1);
|
||||||
struct Slice* allocated = malloc(size);
|
struct Slice* allocated = malloc(size);
|
||||||
memcpy(allocated, slices, size);
|
memcpy(allocated, slices, size);
|
||||||
|
|
||||||
|
@ -136,20 +177,28 @@ void singleQuotedString(const char* input, int* i, int* lineNumber, int* length)
|
||||||
void tripleQuotedString(const char* input, struct Error* err, struct Slice* slice, int* i, int* lineNumber,
|
void tripleQuotedString(const char* input, struct Error* err, struct Slice* slice, int* i, int* lineNumber,
|
||||||
int* length);
|
int* length);
|
||||||
|
|
||||||
void processString(const char* input, struct Error* err, struct Slice* slices, int slice, int* i, int* lineNumber,
|
void processString(const char* input, struct Error* err, struct Slice* slice, int* i, int* lineNumber, int* length)
|
||||||
int* length)
|
|
||||||
{
|
{
|
||||||
if (input[(*i) + 1] == '"' && input[(*i) + 2] == '"') {
|
if (input[(*i) + 1] == '"' && input[(*i) + 2] == '"') {
|
||||||
tripleQuotedString(input, err, &slices[slice], i, lineNumber, length);
|
tripleQuotedString(input, err, slice, i, lineNumber, length);
|
||||||
} else {
|
} else {
|
||||||
singleQuotedString(input, i, lineNumber, length);
|
singleQuotedString(input, i, lineNumber, length);
|
||||||
}
|
}
|
||||||
(*i)++;
|
(*i)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int validSymbolChar(const char* c)
|
||||||
|
{
|
||||||
|
return !isWhitespace(*c)
|
||||||
|
&& !isSingle(c)
|
||||||
|
&& *c != '"'
|
||||||
|
&& *c != '\0';
|
||||||
|
}
|
||||||
|
|
||||||
void collectSymbol(const char* input, int* i, int* length)
|
void collectSymbol(const char* input, int* i, int* length)
|
||||||
{
|
{
|
||||||
while (!isWhitespace(input[++(*i)]) && !isSingle(&input[(*i)]) && input[(*i)] != '"' && input[(*i)] != '\0') {
|
// TODO: Error if length is 0?
|
||||||
|
while (validSymbolChar(&input[++(*i)])) {
|
||||||
(*length)++;
|
(*length)++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,7 +220,7 @@ void tripleQuotedString(const char* input, struct Error* err, struct Slice* slic
|
||||||
}
|
}
|
||||||
(*length)++;
|
(*length)++;
|
||||||
if (input[c] == '\0' || input[c + 1] == '\0' || input[c + 2] == '\0') {
|
if (input[c] == '\0' || input[c + 1] == '\0' || input[c + 2] == '\0') {
|
||||||
buildErrFromInput(err, UNEXPECTED_EOF, c, input);
|
buildErrFromInput(err, UNEXPECTED_EOF, c, input, slice);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,9 +229,10 @@ void tripleQuotedString(const char* input, struct Error* err, struct Slice* slic
|
||||||
void singleQuotedString(const char* input, int* i, int* lineNumber, int* length)
|
void singleQuotedString(const char* input, int* i, int* lineNumber, int* length)
|
||||||
{
|
{
|
||||||
while (input[++(*i)] != '\0') {
|
while (input[++(*i)] != '\0') {
|
||||||
if (input[(*i)] == '"') {
|
const int c = *i;
|
||||||
|
if (input[c] == '"') {
|
||||||
int backslashes = 0;
|
int backslashes = 0;
|
||||||
while (input[((*i) - 1) - backslashes] == '\\') {
|
while (input[(c - 1) - backslashes] == '\\') {
|
||||||
backslashes++;
|
backslashes++;
|
||||||
}
|
}
|
||||||
// \\\" => Odd number of backslashes, quote IS escaped
|
// \\\" => Odd number of backslashes, quote IS escaped
|
||||||
|
@ -190,7 +240,7 @@ void singleQuotedString(const char* input, int* i, int* lineNumber, int* length)
|
||||||
if (backslashes % 2 == 0) {
|
if (backslashes % 2 == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (input[(*i)] == '\n') {
|
} else if (input[c] == '\n') {
|
||||||
(*lineNumber)++;
|
(*lineNumber)++;
|
||||||
}
|
}
|
||||||
(*length)++;
|
(*length)++;
|
||||||
|
|
Loading…
Reference in New Issue