Compare commits
No commits in common. "8b9a351be11d8d0b2cdfc74c1977e303b809e002" and "ca16fa60ab5a4a95cfc54700ce7175294dcadc19" have entirely different histories.
8b9a351be1
...
ca16fa60ab
235
README.md
235
README.md
|
@ -1,132 +1,93 @@
|
|||
# PebbLisp
|
||||
|
||||
A very basic LISP implementation with an interpreter and editor written for the Pebble.
|
||||
|
||||
[Visit the Rebble page](https://apps.rebble.io/en_US/application/5ec1f90f3dd31081fc98fce0) to download the full,
|
||||
compiled version!
|
||||
[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
|
||||
[Download the Android app](https://gitlab.com/sagev9000/pebblispandroid/-/wikis/Downloads) to easily type up and send scripts to your Pebble.
|
||||
|
||||
# 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)
|
||||
```
|
||||
|
||||
As an example, to store a long greeting message into the short symbol `greeting`:
|
||||
As an example, to store a long greeting message into the short symbol `g`:
|
||||
|
||||
```
|
||||
(def greeting "Hello, my dear friend! How are you today?")
|
||||
(def g "Hello, how are you today?")
|
||||
```
|
||||
|
||||
`greeting` will then evaluate to `"Hello, my dear friend! How are you today?"`.
|
||||
`g` will then evaluate to `"Hello, how are you today"`.
|
||||
|
||||
> **Note:**\
|
||||
> Defining an object with the same symbol as an object that already exists is possible, but deletes the
|
||||
> original object from the environment!
|
||||
> 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!
|
||||
|
||||
## 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)
|
||||
```
|
||||
|
||||
The condition will typically involve a comparison operator. For example:
|
||||
The condition will typically be involve a comparison operator. For example:
|
||||
|
||||
```
|
||||
(if (< 5 10) "Yee" "haw")
|
||||
```
|
||||
|
||||
This return `"Yee"`, as `(< 5 10)` (commonly written `5 < 10`) evaluates to true.
|
||||
Would return `"Yee"`, as `(< 5 10)` aka `5 < 10` evaluates to true.
|
||||
|
||||
## Fn
|
||||
|
||||
`fn` creates a lambda with a (theoretically) arbitrary number of arguments to be evaluated on call. The general form is:
|
||||
|
||||
```
|
||||
(fn (arg-list) (lambda-body))
|
||||
```
|
||||
|
||||
A lambda will frequently be stored under a `def` symbol. For example, to define a simple square function, you might
|
||||
write:
|
||||
A lambda will commonly be stored under a `def` symbol, for example:
|
||||
|
||||
```
|
||||
(def sq (fn (a) (* a a)))
|
||||
```
|
||||
|
||||
Calling it is as simple as `(sq 5)`, which returns `25`.
|
||||
Defines a simple lambda to square a given number. Calling it is as simple as `(sq 5)`, which returns `25`.
|
||||
|
||||
Lambdas can also be applied anonymously, as in `((fn (a) (* a a)) 5)`, which also returns `25`.
|
||||
This is most useful when using something like `map`. For example, an anonymous lambda could be used to square each
|
||||
element in a list:
|
||||
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:
|
||||
|
||||
```
|
||||
(map (fn (a) (* a a)) (1 2 3 5 8 13 21 34))
|
||||
```
|
||||
|
||||
This is particularly useful on a low-memory device like the Pebble, where it may be useful to avoid storing the named
|
||||
lambda object in the environment.
|
||||
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.
|
||||
|
||||
Lambdas may also have no arguments:
|
||||
|
||||
```
|
||||
(fn () (uppercase (input)))
|
||||
(fn () 9001)
|
||||
```
|
||||
|
||||
## Cat
|
||||
|
||||
`cat` returns its arguments concatenated as strings. It has the same general form as arithmetic operators
|
||||
|
||||
```
|
||||
(cat expr1 expr2 ...)
|
||||
```
|
||||
|
||||
For example, combining numbers and strings is quite simple:
|
||||
For example, combining numbers and strings:
|
||||
|
||||
```
|
||||
(cat "There are " 5 " cats")
|
||||
```
|
||||
|
||||
And this evaluates to `"There are 5 cats"`.
|
||||
Would return `There are 5 cats`.
|
||||
|
||||
Previously, a `cat` operation was applied implicitly when using `+` with strings, but this resulted in confusing
|
||||
behavior when combined with number objects (due to the left-associativity of operators, `(+ 1 2 "3")` would result
|
||||
in `"33"`) and was thus removed.
|
||||
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`.
|
||||
|
||||
## 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))
|
||||
|
@ -135,24 +96,19 @@ form of a `map` expression is
|
|||
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` returns a filtered list, based on a given list and a given condition. Partial function support in PebbLisp is nowhere near comprehensive, but `fil` operates on the bare notion that currently exists. The general form of a `fil` expression is
|
||||
|
||||
```
|
||||
(fil (partial-condition) (candidate-list))
|
||||
```
|
||||
|
||||
Each element in the candidate list is compared against the partial condition. If the comparison returns true, it is
|
||||
added to the returned list. For example:
|
||||
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))
|
||||
|
@ -161,26 +117,26 @@ added to the returned list. For example:
|
|||
would return `( 150 200 )`, as no other elements fit the condition `(< 100 n)`.
|
||||
|
||||
# Pebble-Specific Functions
|
||||
|
||||
There are several functions to access features of the Pebble itself.
|
||||
Note that due to some over-simplicity of function-handling, all the following functions expect to receive two
|
||||
arguments, regardless of how many they actually use.
|
||||
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.
|
||||
|
||||
## 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)
|
||||
|
||||
There are several functions for fetching individual elements of the current time:
|
||||
For example:
|
||||
|
||||
- `sec` the current seconds (0-59)
|
||||
- `mnt` the current minutes (0-59)
|
||||
- `hr` the current hour (0-23)
|
||||
- `hrt` the current hour (1-12)
|
||||
```
|
||||
(mnt)
|
||||
```
|
||||
|
||||
For example: `(mnt)` would return `16`, if called at 5:16
|
||||
would return 16, if called at 5:16
|
||||
|
||||
## Vibrating
|
||||
|
||||
`vibe` starts the vibration engine with a given pattern. The pattern should be a list, composed of
|
||||
alternating on/off durations in milliseconds. For example:
|
||||
`vibe` 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))
|
||||
|
@ -189,11 +145,9 @@ alternating on/off durations in milliseconds. For example:
|
|||
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:
|
||||
|
||||
|
@ -201,8 +155,7 @@ window.
|
|||
(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:
|
||||
|
||||
|
@ -210,8 +163,7 @@ Getting something useful to display requires the use of a TextLayer.
|
|||
(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:
|
||||
|
||||
|
@ -222,17 +174,11 @@ the TextLayer in `tl`, for later updates.
|
|||
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.
|
||||
|
||||
`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.
|
||||
|
||||
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
|
||||
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)
|
||||
|
@ -240,102 +186,7 @@ environment. As an example
|
|||
|
||||
would request that `upwin` be run every second, where `upwin` is a lambda that does not rely on arguments.
|
||||
|
||||
# Non-Pebble Functionality
|
||||
|
||||
PebbLisp has several features designed for use _outside_ of a Pebble device.\
|
||||
The first was a simple command-line REPL, to more quickly test the language while developing.\
|
||||
The number of extensions quickly grew, for the sake of dogfooding the language and purely for sport.
|
||||
|
||||
This is currently **not** an exhaustive list. However, calling [`(penv)`](#viewing-the-pl-environment-penv) lists all
|
||||
useful objects that are
|
||||
currently available in a `pl` session, and [`(?)`](#the-help-function-) can be used to read the built-in documentation
|
||||
for available functions.
|
||||
|
||||
## pl
|
||||
|
||||
`pl` is a hybrid shell designed as both a PebbLisp REPL and a working user shell.
|
||||
|
||||
When a line is entered, it will be evaluated as PebbLisp code. If any error occurs, it will instead be evaluated
|
||||
using stdlib's `system()` function.
|
||||
|
||||
> Note: It will also fall back to `system()` if the line evaluates to a function.
|
||||
> This is because some PebbLisp functions overlap with common commands. E.g. `/bin/time` and `(time)`.\
|
||||
> A quick way around this is to use `(cat your-fn)`, forcing a string representation.
|
||||
|
||||
Furthermore, `pl` offers tab-completion over the names of objects in its current environment, falling back to path
|
||||
completion when appropriate.
|
||||
|
||||
## pebblisp.pbl
|
||||
|
||||
A plain pbl file read from `$HOME/.pebblisp.pbl`, by default.
|
||||
Allows for setting default variables, aliases, and the `pl` prompt.
|
||||
|
||||
A simple take on this file can be found in `examples/`
|
||||
|
||||
## The Help Function `(?)`
|
||||
|
||||
Important for its usefulness in navigating all _other_ functions is `(?)`
|
||||
|
||||
`(?)` is used to pull up any available documentation for the given object, typically a function:
|
||||
|
||||
```
|
||||
(? time)
|
||||
```
|
||||
|
||||
Which provides help like:
|
||||
|
||||
```
|
||||
Get a struct of the current time with fields (minute hour sec).
|
||||
```
|
||||
|
||||
Notably, `(?)` can also be used on itself. I.e. `(? ?)`, which returns:
|
||||
|
||||
```
|
||||
Gets a string with help text or a string representation for the object.
|
||||
|
||||
Function help:
|
||||
(? islist) => "(islist (1 2 3)) => T"
|
||||
|
||||
Struct fields:
|
||||
(? Output) => "{ stdout stderr }"
|
||||
|
||||
Other objects:
|
||||
(? "Hello") => "Hello"
|
||||
```
|
||||
|
||||
> **Note:**\
|
||||
> The `pl` prompt has a hard-coded shorthand for these calls so that parentheses may be omitted.\
|
||||
> Entering `? sys` at the prompt is equivalent to `(? sys)`
|
||||
|
||||
## Viewing the `pl` Environment `(penv)`
|
||||
|
||||
`(penv)` is a quick way to examine the objects in the current PebbLisp environment.
|
||||
|
||||
Example output:
|
||||
|
||||
```
|
||||
env->capacity = 256
|
||||
env->count = 92
|
||||
x: "hello"
|
||||
isnum: Native
|
||||
min: Return the smallest of the given values
|
||||
(min 2 1 6) => 1
|
||||
( `...values` ) -> ( NATIVE_FUNC `values` `_min` ( `first` `values` ) )>
|
||||
...
|
||||
```
|
||||
|
||||
Note that the first two lines list the current capacity and number of objects in the environment.
|
||||
|
||||
Objects may be displayed a few ways, depending on their type. Seen here are:
|
||||
|
||||
* A simple object, `x`, displayed as a string
|
||||
* A native function, `isnum`, denoted with the word Native (as its storage is opaque pointer data)
|
||||
* A lambda, `min`, with a small docstring, a simple test, and the basic construction of the lambda.
|
||||
|
||||
`(penv)`, combined with `(?)` and tab-completion, makes it relatively easy to browse and test the PebbLisp interpreter.
|
||||
|
||||
# TODO
|
||||
|
||||
- Better script calling
|
||||
- Wrap more Pebble SDK functions
|
||||
- Maybe hard-code additional built-in functions to ease up on memory use
|
||||
- Even more pebble functions
|
||||
- Maybe hard-code more built-in functions to ease up on memory use
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
exe = pl
|
||||
|
||||
base_files = pcmain.c pebblisp.c tokens.c object.c env.c hash.c
|
||||
base_files = main.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
|
||||
files:= $(base_files) $(func_files)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#define HASHLESS_ENV
|
||||
#include "object.h"
|
||||
#include "plfunc/pebbleobject.h"
|
||||
#include "pebblemain.h"
|
||||
#include "calc.h"
|
||||
|
||||
#define RESULT_LENGTH 128
|
||||
|
||||
|
@ -379,7 +379,7 @@ static Object run_script(Object* params, int length,
|
|||
free(code);
|
||||
return o;
|
||||
}
|
||||
throw(SCRIPT_NOT_FOUND, "");
|
||||
return errorObject(SCRIPT_NOT_FOUND);
|
||||
}
|
||||
|
||||
static void code_window_unload(Window* window)
|
||||
|
@ -527,7 +527,7 @@ static void inbox_received_callback(DictionaryIterator* iter, void* context)
|
|||
}
|
||||
|
||||
/** General **/
|
||||
void addFunction(const char* name, Object (* func)(Object*, int, struct Environment*), struct Environment* env)
|
||||
void af(const char* name, Object (* func)(Object*, int, struct Environment*), struct Environment* env)
|
||||
{
|
||||
Object o = newObject(TYPE_FUNC);
|
||||
o.func = func;
|
||||
|
@ -538,15 +538,15 @@ static struct Environment pebbleEnv()
|
|||
{
|
||||
struct Environment e = defaultEnv();
|
||||
setGlobal(&e);
|
||||
addFunction("window", &add_window, &e);
|
||||
addFunction("sc", &run_script, &e);
|
||||
addFunction("cw", &createWindow, &e);
|
||||
addFunction("pw", &pushWindow, &e);
|
||||
addFunction("rw", &deleteWindow, &e);
|
||||
addFunction("atl", &addTextLayer, &e);
|
||||
addFunction("utl", &updateTextLayer, &e);
|
||||
addFunction("vibe", &doVibe, &e);
|
||||
addFunction("sub", &subscribe, &e);
|
||||
af("window", &add_window, &e);
|
||||
af("sc", &run_script, &e);
|
||||
af("cw", &createWindow, &e);
|
||||
af("pw", &pushWindow, &e);
|
||||
af("rw", &deleteWindow, &e);
|
||||
af("atl", &addTextLayer, &e);
|
||||
af("utl", &updateTextLayer, &e);
|
||||
af("vibe", &doVibe, &e);
|
||||
af("sub", &subscribe, &e);
|
||||
return e;
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef PEBBLE_MAIN_H
|
||||
#define PEBBLE_MAIN_H
|
||||
#ifndef CALC_H
|
||||
#define CALC_H
|
||||
|
||||
#include <pebble.h>
|
||||
#include "pebblisp.h"
|
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
|
||||
const Object* param = params->list;
|
||||
const Object* argument = arguments;
|
||||
for (; param; param = param->forward) {
|
||||
for (int i = 0; i < paramCount && param; i++) {
|
||||
const char* paramName = param->string;
|
||||
if (paramName[0] == '.' && paramName[1] == '.' && paramName[2] == '.' && paramName[3] != '\0') {
|
||||
paramName = ¶mName[3];
|
||||
|
@ -144,9 +144,6 @@ struct Environment envForLambda(const Object* params, const Object* arguments, i
|
|||
cleanObject(&varargs);
|
||||
break;
|
||||
}
|
||||
if (!argument) {
|
||||
break;
|
||||
}
|
||||
|
||||
Object newEnvObj = eval(argument, outer);
|
||||
|
||||
|
@ -154,7 +151,8 @@ struct Environment envForLambda(const Object* params, const Object* arguments, i
|
|||
|
||||
cleanObject(&newEnvObj);
|
||||
|
||||
argument = argument->forward;
|
||||
argument = argument ? argument->forward : NULL;
|
||||
param = param->forward;
|
||||
}
|
||||
env.outer = outer;
|
||||
|
||||
|
@ -242,7 +240,6 @@ struct Environment defaultEnv()
|
|||
#endif
|
||||
#ifdef STANDALONE
|
||||
pf(segfault),
|
||||
pf(typeOf),
|
||||
pf(print),
|
||||
pf(numToChar),
|
||||
pf(printEnvO),
|
||||
|
@ -257,8 +254,7 @@ struct Environment defaultEnv()
|
|||
pf(help),
|
||||
pf(buildHashTable),
|
||||
pf(addToHashTable),
|
||||
pf(getFromHashTable),
|
||||
pf(randomO)
|
||||
pf(getFromHashTable)
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -480,7 +476,7 @@ Object help(Object* params, int length, struct Environment* env)
|
|||
int runTest(const char* test, const char* expected, int detailed)
|
||||
{
|
||||
struct Environment env = defaultEnv();
|
||||
Object o = parseEval(test, "testCode", &env);
|
||||
Object o = parseEval(test, &env);
|
||||
size_t length;
|
||||
char* result = stringObj(&o, &length);
|
||||
cleanObject(&o);
|
||||
|
|
|
@ -174,14 +174,6 @@
|
|||
(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) (
|
||||
(matches text (cat pattern "*"))
|
||||
)))
|
||||
|
|
|
@ -6,65 +6,48 @@
|
|||
" " " " " "
|
||||
))
|
||||
|
||||
(def is-empty (fn (tile) (= " " tile)))
|
||||
|
||||
(def print-board (fn (board) (
|
||||
(sys "clear")
|
||||
(def '(a b c
|
||||
d e f
|
||||
g h i) board)
|
||||
(prnl)
|
||||
(prnl (cat " " a " | " b " | " c))
|
||||
(prnl " -----------")
|
||||
(prnl "-----------")
|
||||
(prnl (cat " " d " | " e " | " f))
|
||||
(prnl " -----------")
|
||||
(prnl "-----------")
|
||||
(prnl (cat " " g " | " h " | " i))
|
||||
)))
|
||||
|
||||
(def do-move (fn (board player warn) (
|
||||
(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 do-move (fn (board player) (
|
||||
(def input (inp))
|
||||
(def spot (eval input))
|
||||
(def i 0)
|
||||
(if (& (is-empty (at (- spot 1) board)) (> spot 0) (< spot 10))
|
||||
(map (fn (piece) (
|
||||
(set i (+ 1 i))
|
||||
(if (= i spot) player piece)
|
||||
)) board)
|
||||
(
|
||||
(print-board board)
|
||||
(do-move board player T)
|
||||
)
|
||||
)
|
||||
)))
|
||||
|
||||
(def is-winning-row (fn (row) (
|
||||
(def winning-row (fn (row) (
|
||||
(def '(a b c) row)
|
||||
(& (not (is-empty a)) (= a b c))
|
||||
(& (not (= " " a)) (= a b c))
|
||||
)))
|
||||
|
||||
(def is-full-board (fn (board)
|
||||
(none-in board is-empty)
|
||||
))
|
||||
|
||||
(def get-winner (fn (board) (
|
||||
(sys "clear")
|
||||
(def '(a b c
|
||||
d e f
|
||||
g h i) board)
|
||||
(if (is-winning-row (a b c)) a
|
||||
(if (is-winning-row (d e f)) d
|
||||
(if (is-winning-row (g h i)) g
|
||||
(if (is-winning-row (a d g)) a
|
||||
(if (is-winning-row (b e h)) b
|
||||
(if (is-winning-row (c f i)) c
|
||||
(if (is-winning-row (a e i)) a
|
||||
(if (is-winning-row (c e g)) c
|
||||
(if (is-full-board board) "Nobody"
|
||||
(if (winning-row (a b c)) a
|
||||
(if (winning-row (d e f)) d
|
||||
(if (winning-row (g h i)) g
|
||||
(if (winning-row (a d g)) a
|
||||
(if (winning-row (b e h)) b
|
||||
(if (winning-row (c f i)) c
|
||||
(if (winning-row (a e i)) a
|
||||
(if (winning-row (c e g)) c
|
||||
" "
|
||||
)))))))))
|
||||
))))))))
|
||||
)))
|
||||
|
||||
(def next-player (fn (current) (
|
||||
|
@ -72,15 +55,15 @@
|
|||
)))
|
||||
|
||||
(def game (fn (board player) (
|
||||
(if (= " " (get-winner board)) (
|
||||
(print-board board)
|
||||
(def winner (get-winner board))
|
||||
(if (is-empty winner) (
|
||||
(def b (do-move board player F))
|
||||
(def b (do-move board player))
|
||||
(game b (next-player player))
|
||||
) (
|
||||
(prnl)
|
||||
(prnl "Game over! " winner " wins!")
|
||||
(print-board board)
|
||||
(prnl "")
|
||||
(prnl "Game over! " (next-player player) " wins!")
|
||||
))
|
||||
)))
|
||||
|
||||
(game empty-board (if (= 0 (rand 2)) "X" "O"))
|
||||
(game empty-board "X")
|
||||
|
|
|
@ -185,7 +185,7 @@ Object buildHashTable(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 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)
|
||||
{
|
||||
checkTypes(getFromHashTable);
|
||||
checkTypes(getFromHashTable)
|
||||
|
||||
struct ObjectTable* table = ¶ms[0].table->table;
|
||||
struct StrippedObject* fetched = getFromTable(table, params[1].string);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#define _GNU_SOURCE // For segfault handling
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "pebblisp.h"
|
||||
|
||||
|
@ -21,11 +21,7 @@ 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;
|
||||
}
|
||||
|
@ -120,7 +116,7 @@ void repl(struct Environment* env)
|
|||
free(oldBuf);
|
||||
}
|
||||
|
||||
Object o = parseEval(buf, "REPL", env);
|
||||
Object o = parseEval(buf, env);
|
||||
if (isFuncy(o) || isError(o, DID_NOT_FIND_SYMBOL)) {
|
||||
system(buf);
|
||||
} else {
|
||||
|
@ -250,9 +246,9 @@ int main(int argc, const char* argv[])
|
|||
readFile(SCRIPTDIR "/lib.pbl", &env);
|
||||
}
|
||||
|
||||
Object o = parseEval("(def prompt %%)", "[INTERNAL]", &env);
|
||||
Object o = parseEval("(def prompt %%)", &env);
|
||||
cleanObject(&o);
|
||||
o = parseEval("(def preprocess (fn (text) (text)))", "[INTERNAL]", &env);
|
||||
o = parseEval("(def preprocess (fn (text) (text)))", &env);
|
||||
cleanObject(&o);
|
||||
|
||||
if (!settings.ignoreConfig) {
|
||||
|
@ -272,10 +268,10 @@ int main(int argc, const char* argv[])
|
|||
if (file) {
|
||||
// Execute a file
|
||||
loadArgsIntoEnv(argc, argv, &env);
|
||||
_readFile(file, argv[argc - 1], &env);
|
||||
_readFile(file, &env);
|
||||
} else {
|
||||
// Run arguments directly as pl code
|
||||
Object r = parseEval(argv[argc - 1], "[PL ARGUMENTS]", &env);
|
||||
Object r = parseEval(argv[argc - 1], &env);
|
||||
printAndClean(&r);
|
||||
}
|
||||
} else {
|
|
@ -0,0 +1,4 @@
|
|||
#ifndef PEBBLISP_MAIN_H
|
||||
#define PEBBLISP_MAIN_H
|
||||
|
||||
#endif // PEBBLISP_MAIN_H
|
|
@ -122,7 +122,6 @@ static const char* errorText[] = {
|
|||
"NULL_MAP_ARGS",
|
||||
"LAMBDA_ARGS_NOT_LIST",
|
||||
"DID_NOT_FIND_SYMBOL",
|
||||
"BAD_SYMBOL",
|
||||
"BAD_TYPE",
|
||||
"BAD_PARAMS",
|
||||
"BAD_NUMBER",
|
||||
|
@ -243,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);
|
||||
|
@ -277,7 +276,7 @@ int stringNObj(struct string* s, const Object* obj)
|
|||
break;
|
||||
}
|
||||
case TYPE_FUNC:
|
||||
appendf(s, "NATIVE_FUNC");
|
||||
appendf(s, "F%ld", obj->number);
|
||||
break;
|
||||
case TYPE_LAMBDA: {
|
||||
#if defined(STANDALONE) && !defined(DEBUG)
|
||||
|
@ -734,7 +733,7 @@ inline Object withLen(size_t len, enum Type type)
|
|||
inline Object constructLambda(const Object* params, const Object* body, struct Environment* env)
|
||||
{
|
||||
if (!params || !body) {
|
||||
throw(NULL_LAMBDA_LIST, "fn params and body cannot be null");
|
||||
return errorObject(NULL_LAMBDA_LIST);
|
||||
}
|
||||
|
||||
if (params->type != TYPE_LIST) {
|
||||
|
|
16
src/object.h
16
src/object.h
|
@ -22,13 +22,7 @@
|
|||
#ifdef RELEASE
|
||||
#define assert(...) do { } while (0)
|
||||
#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
|
||||
|
||||
#define MAX_TOK_CNT 2048
|
||||
|
@ -100,7 +94,6 @@ enum errorCode {
|
|||
NULL_MAP_ARGS,
|
||||
LAMBDA_ARGS_NOT_LIST,
|
||||
DID_NOT_FIND_SYMBOL,
|
||||
BAD_SYMBOL,
|
||||
BAD_TYPE,
|
||||
BAD_PARAMS,
|
||||
BAD_NUMBER,
|
||||
|
@ -322,11 +315,8 @@ Object errorWithAllocatedContextLineNo(enum errorCode code, char* context, int l
|
|||
#define errorAddContext(x, y, z, a) ;
|
||||
#define throw(_code, ...) return errorObject(_code)
|
||||
#else
|
||||
#define throw(_code, ...) do { \
|
||||
char* ERROR_CONTEXT = malloc(sizeof(char) * ERR_LEN); \
|
||||
sprintf(ERROR_CONTEXT, __VA_ARGS__); \
|
||||
return errorWithAllocatedContextLineNo(_code, ERROR_CONTEXT, __LINE__, __FILE__); \
|
||||
} while (0)
|
||||
#define throw(_code, ...) do { 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__)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
#ifndef PC_MAIN_H
|
||||
#define PC_MAIN_H
|
||||
|
||||
#endif
|
|
@ -54,17 +54,15 @@ Object listDef(Object* nameList, Object* valueList, struct Environment* env)
|
|||
* @param env The environment to add the new definition to
|
||||
* @return The symbol(s) defined
|
||||
*/
|
||||
Object def(Object* params, int length, struct Environment* env)
|
||||
Object def(Object* params, unused int length, struct Environment* env)
|
||||
{
|
||||
if (length == 2) {
|
||||
if (isStringy(params[0])) {
|
||||
return singleDef(params[0].string, ¶ms[1], env);
|
||||
}
|
||||
|
||||
if (isListy(params[0]) && isListy(params[1])) {
|
||||
if (length == 2 && isListy(params[0]) && isListy(params[1])) {
|
||||
return listDef(¶ms[0], ¶ms[1], env);
|
||||
}
|
||||
}
|
||||
|
||||
throw(BAD_TYPE, "Poorly constructed (def)");
|
||||
}
|
||||
|
@ -363,7 +361,7 @@ Object eval(const Object* obj, struct Environment* env)
|
|||
|
||||
Object structAccess(Object* params, unused int length, unused struct Environment* env)
|
||||
{
|
||||
checkTypes(structAccess);
|
||||
checkTypes(structAccess)
|
||||
|
||||
Object structo = params[0];
|
||||
Object field = params[1];
|
||||
|
@ -567,13 +565,13 @@ struct Slice* getLastOpen()
|
|||
return lastOpen;
|
||||
}
|
||||
|
||||
Object parseEval(const char* input, const char* codeSource, struct Environment* env)
|
||||
Object parseEval(const char* input, struct Environment* env)
|
||||
{
|
||||
struct Error err = noError();
|
||||
|
||||
struct Slice* tokens = nf_tokenize(input, &err);
|
||||
if (err.context != NULL) {
|
||||
Object o = errorWithContextLineNo(err.code, err.context, err.plContext->lineNumber, codeSource);
|
||||
Object o = errorWithContext(err.code, err.context);
|
||||
free(err.context);
|
||||
return o;
|
||||
}
|
||||
|
@ -683,21 +681,21 @@ char* readFileToString(FILE* input)
|
|||
}
|
||||
|
||||
/// 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) {
|
||||
return 1;
|
||||
}
|
||||
_readFile(input, fileName, env);
|
||||
_readFile(input, env);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _readFile(FILE* input, const char* fileName, struct Environment* env)
|
||||
int _readFile(FILE* input, struct Environment* env)
|
||||
{
|
||||
char* fileText = readFileToString(input);
|
||||
fclose(input);
|
||||
Object r = parseEval(fileText, fileName, env);
|
||||
Object r = parseEval(fileText, env);
|
||||
cleanObject(&r);
|
||||
free(fileText - 1);
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ Result readSeq(struct Slice* slices);
|
|||
|
||||
Result parseAtom(struct Slice* slice);
|
||||
|
||||
Object parseEval(const char* input, const char* fileName, struct Environment* env);
|
||||
Object parseEval(const char* input, struct Environment* env);
|
||||
|
||||
Object evalList(const Object* obj, struct Environment* env);
|
||||
|
||||
|
@ -46,24 +46,20 @@ Object typeCheck(const char* funcName, Object* params, int length,
|
|||
|
||||
#ifndef DISABLE_TYPE_CHECKS
|
||||
#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) { \
|
||||
return ERROR; \
|
||||
}
|
||||
#else
|
||||
#define checkTypes(FUNC) do { } while (0)
|
||||
#define checkTypes(FUNC) ;
|
||||
#endif
|
||||
|
||||
#ifdef STANDALONE
|
||||
|
||||
char* readFileToString(FILE* input);
|
||||
|
||||
int _readFile(FILE* input, const char* fileName, struct Environment* env);
|
||||
int _readFile(FILE* input, struct Environment* env);
|
||||
|
||||
int readFile(const char* fileName, struct Environment* env);
|
||||
int readFile(const char* filename, struct Environment* env);
|
||||
|
||||
struct Slice* getLastOpen();
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
Object reduce(Object* params, unused int length, struct Environment* env)
|
||||
{
|
||||
checkTypes(reduce);
|
||||
checkTypes(reduce)
|
||||
Object list = params[0];
|
||||
Object func = params[1];
|
||||
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)
|
||||
{
|
||||
checkTypes(filter);
|
||||
checkTypes(filter)
|
||||
Object condition = params[0];
|
||||
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)
|
||||
{
|
||||
checkTypes(append);
|
||||
checkTypes(append)
|
||||
Object list = params[0];
|
||||
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)
|
||||
{
|
||||
checkTypes(prepend);
|
||||
checkTypes(prepend)
|
||||
Object list = cloneObject(params[0]);
|
||||
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)
|
||||
{
|
||||
checkTypes(at);
|
||||
checkTypes(at)
|
||||
Object index = params[0];
|
||||
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)
|
||||
{
|
||||
checkTypes(rest);
|
||||
checkTypes(rest)
|
||||
Object list = params[0];
|
||||
|
||||
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)
|
||||
{
|
||||
checkTypes(reverse);
|
||||
checkTypes(reverse)
|
||||
const Object* list = ¶ms[0];
|
||||
|
||||
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)
|
||||
{
|
||||
checkTypes(isNum);
|
||||
checkTypes(isNum)
|
||||
Object test = params[0];
|
||||
|
||||
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)
|
||||
{
|
||||
checkTypes(isList);
|
||||
checkTypes(isList)
|
||||
Object test = params[0];
|
||||
|
||||
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)
|
||||
{
|
||||
checkTypes(isString);
|
||||
checkTypes(isString)
|
||||
Object test = params[0];
|
||||
|
||||
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)
|
||||
{
|
||||
checkTypes(isErr);
|
||||
checkTypes(isErr)
|
||||
Object test = params[0];
|
||||
|
||||
return boolObject(test.type == TYPE_ERROR);
|
||||
|
@ -176,14 +176,14 @@ Object parseEvalO(Object* params, unused int length, struct Environment* env)
|
|||
switch (text.type) {
|
||||
case TYPE_SYMBOL: {
|
||||
Object string = eval(&text, env);
|
||||
Object parsed = parseEval(string.string, "(eval)", env);
|
||||
Object parsed = parseEval(string.string, env);
|
||||
cleanObject(&string);
|
||||
return parsed;
|
||||
}
|
||||
case TYPE_SLIST:
|
||||
return evalList(&text, env);
|
||||
case TYPE_STRING:
|
||||
return parseEval(text.string, "(eval)", env);
|
||||
return parseEval(text.string, env);
|
||||
default:
|
||||
throw(CAN_ONLY_EVAL_STRINGS, "Tried to (eval) a %s, instead of a string or symbol list",
|
||||
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)
|
||||
{
|
||||
checkTypes(catObjects);
|
||||
checkTypes(catObjects)
|
||||
|
||||
if (length == 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)
|
||||
{
|
||||
checkTypes(len);
|
||||
checkTypes(len)
|
||||
|
||||
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)
|
||||
{
|
||||
if (length < 2) {
|
||||
throw(NOT_ENOUGH_ARGUMENTS, "expected at least 2");
|
||||
return errorObject(NOT_ENOUGH_ARGUMENTS);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (timeStructDefinition == -1) {
|
||||
parseEval("(struct Time (minute hour sec))", "[INTERNAL]", env);
|
||||
parseEval("(struct Time (minute hour sec))", env);
|
||||
timeStructDefinition = getStructIndex("Time");
|
||||
}
|
||||
time_t t = time(NULL);
|
||||
|
|
|
@ -12,15 +12,9 @@ Object print(Object* params, int length, unused struct Environment* env)
|
|||
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)
|
||||
{
|
||||
checkTypes(numToChar);
|
||||
checkTypes(numToChar)
|
||||
Object c = params[0];
|
||||
|
||||
if (c.number > 255 || c.number < 0) {
|
||||
|
@ -45,7 +39,7 @@ Object takeInput(Object* params, 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));
|
||||
}
|
||||
|
@ -58,7 +52,7 @@ Object cwd(unused Object* params, unused int length, unused struct Environment*
|
|||
|
||||
Object systemCall(Object* params, unused int length, unused struct Environment* env)
|
||||
{
|
||||
checkTypes(systemCall);
|
||||
checkTypes(systemCall)
|
||||
Object process = params[0];
|
||||
|
||||
if (isStringy(process)) {
|
||||
|
@ -69,7 +63,7 @@ Object systemCall(Object* params, unused int length, unused struct Environment*
|
|||
|
||||
Object readFileToObject(Object* params, unused int length, unused struct Environment* env)
|
||||
{
|
||||
checkTypes(readFileToObject);
|
||||
checkTypes(readFileToObject)
|
||||
Object filename = params[0];
|
||||
|
||||
FILE* file = fopen(filename.string, "r");
|
||||
|
@ -85,27 +79,11 @@ Object readFileToObject(Object* params, unused int length, unused struct Environ
|
|||
|
||||
Object getEnvVar(Object* params, unused int length, unused struct Environment* env)
|
||||
{
|
||||
checkTypes(getEnvVar);
|
||||
checkTypes(getEnvVar)
|
||||
const char* envVar = getenv(params[0].string);
|
||||
if (envVar) {
|
||||
return nullTerminated(envVar);
|
||||
}
|
||||
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
|
|
@ -7,14 +7,6 @@
|
|||
|
||||
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",
|
||||
({ expect(isNumber), returns(isStringy) }),
|
||||
"Gets a string containing the ascii character for the given number value.",
|
||||
|
@ -68,12 +60,6 @@ tfn(getEnvVar, "env",
|
|||
"(env HOME) => /home/sagevaillancourt"
|
||||
);
|
||||
|
||||
tfn(randomO, "rand",
|
||||
({ returns(isNumber) }),
|
||||
"Returns a semi-random integer\n"
|
||||
"(rand) => 2394568"
|
||||
);
|
||||
|
||||
#endif //PEBBLISP_PC_H
|
||||
|
||||
#endif // STANDALONE
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "pebbleobject.h"
|
||||
|
||||
#include "../pebblemain.h"
|
||||
#include "../calc.h"
|
||||
#include "../object.h"
|
||||
|
||||
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 text = params[1];
|
||||
if (getPebbleType(window) != WINDOW) {
|
||||
throw(BAD_TYPE, "Expected a pebble window, but received %s", getTypeName(&window));
|
||||
return errorObject(0);
|
||||
}
|
||||
Layer* window_layer =
|
||||
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});
|
||||
return trueObject();
|
||||
} else {
|
||||
throw(NOT_A_LIST, "(vibe) requires a non-empty list!");
|
||||
return errorObject(NOT_A_LIST);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
Object charVal(Object* params, unused int length, unused struct Environment* env)
|
||||
{
|
||||
checkTypes(charVal);
|
||||
checkTypes(charVal)
|
||||
Object test = params[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)
|
||||
{
|
||||
checkTypes(chars);
|
||||
checkTypes(chars)
|
||||
char c[2];
|
||||
c[1] = '\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)
|
||||
{
|
||||
checkTypes(matches);
|
||||
checkTypes(matches)
|
||||
return boolObject(stringComp(params[0].string, params[1].string));
|
||||
}
|
||||
|
||||
Object slen(Object* params, int length, unused struct Environment* env)
|
||||
{
|
||||
checkTypes(slen);
|
||||
checkTypes(slen)
|
||||
return numberObject(strlen(params[0].string));
|
||||
}
|
||||
|
||||
Object substring(Object* params, int length, unused struct Environment* env)
|
||||
{
|
||||
checkTypes(substring);
|
||||
checkTypes(substring)
|
||||
Object start = params[0]; // First char to include
|
||||
Object end = params[1]; // First char to exclude
|
||||
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)
|
||||
{
|
||||
checkTypes(charAt);
|
||||
checkTypes(charAt)
|
||||
Object string = params[0];
|
||||
Object at = params[1];
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ void cleanPromise(struct Promise* promise)
|
|||
|
||||
Object await(Object* params, int length, struct Environment* env)
|
||||
{
|
||||
checkTypes(await);
|
||||
checkTypes(await)
|
||||
struct Promise* promise = params[0].promise;
|
||||
if (!promise->done) { // TODO: Does `done` need a mutex or other lock?
|
||||
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 parsed;
|
||||
if (isDigit(value[0])) {
|
||||
parsed = parseEval(value, "[INTERNAL]", NULL);
|
||||
parsed = parseEval(value, NULL);
|
||||
} else {
|
||||
parsed = stringFromSlice(value, strlen(value));
|
||||
}
|
||||
|
@ -127,7 +127,8 @@ void initialize()
|
|||
if (!initialized) {
|
||||
initialized = 1;
|
||||
}
|
||||
Object o = parseEval("(struct Request (queryParams username password))", "[INTERNAL]", global());
|
||||
eprintf("Initializing...\n");
|
||||
Object o = parseEval("(struct Request (queryParams username password))", global());
|
||||
cleanObject(&o);
|
||||
requestDefinition = getStructIndex("Request");
|
||||
}
|
||||
|
|
|
@ -75,9 +75,7 @@ check() {
|
|||
fi
|
||||
|
||||
|
||||
local expected="$3"
|
||||
if [ "$3" == "$regex" ]; then
|
||||
expected="$4"
|
||||
if [[ "$output" =~ ^$4$ ]]; then
|
||||
pass "$1" $exit_code
|
||||
return
|
||||
|
@ -88,7 +86,7 @@ check() {
|
|||
fi
|
||||
|
||||
fail "$1" "$2"
|
||||
FAIL_OUTPUT="${FAIL_OUTPUT}\n [31m expected '$expected' but received '$output'\n"
|
||||
FAIL_OUTPUT="${FAIL_OUTPUT}\n [31m expected '$3' but received '$output'\n"
|
||||
}
|
||||
|
||||
echo "[1;34m::SHELL TESTS::[0;m"
|
||||
|
|
112
src/tokens.c
112
src/tokens.c
|
@ -4,55 +4,17 @@
|
|||
|
||||
/*
|
||||
* Grammar:
|
||||
* Number:
|
||||
* Hex:
|
||||
* 0x[0-9a-f]+
|
||||
* 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
|
||||
* token
|
||||
* expr
|
||||
* (op expr expr)
|
||||
* (list expr expr ... )
|
||||
*/
|
||||
|
||||
// Is the char a standalone token?
|
||||
static const char singleTokens[] = "()'?";
|
||||
|
||||
int isSingle(const char *c)
|
||||
{
|
||||
static const char singleTokens[] = "()'?";
|
||||
|
||||
int i = 0;
|
||||
while (singleTokens[i] != '\0') {
|
||||
if (singleTokens[i] == c[0]) {
|
||||
|
@ -77,18 +39,18 @@ int isWhitespace(const char c)
|
|||
return c == ' ' || c == '\t' || c == '\n';
|
||||
}
|
||||
|
||||
void buildErrFromInput(struct Error* err, enum errorCode code, int i, const char* input, struct Slice* slice)
|
||||
void buildErrFromInput(struct Error* err, enum errorCode code, int i, const char* input)
|
||||
{
|
||||
err->context = malloc(sizeof(char) * ERR_LEN + 1);
|
||||
err->code = code;
|
||||
err->plContext = slice;
|
||||
int start = i > ERR_LEN ? i - ERR_LEN : 0;
|
||||
strncpy(err->context, &input[start], ERR_LEN);
|
||||
}
|
||||
|
||||
void collectSymbol(const char* input, int* i, int* length);
|
||||
|
||||
void processString(const char* input, struct Error* err, struct Slice* slice, int* i, int* lineNumber, int* length);
|
||||
void processString(const char* input, struct Error* err, struct Slice* slices, int slice, int* i, int* lineNumber,
|
||||
int* length);
|
||||
|
||||
struct Slice* nf_tokenize(const char* input, struct Error* err)
|
||||
{
|
||||
|
@ -98,7 +60,6 @@ struct Slice* nf_tokenize(const char* input, struct Error* err)
|
|||
}
|
||||
|
||||
struct Slice slices[MAX_TOK_CNT];
|
||||
struct Slice* slice = &slices[0];
|
||||
//int token_count = MAX_TOK_CNT;
|
||||
// do {
|
||||
// slices = malloc(sizeof(struct Slice) * token_count);
|
||||
|
@ -106,6 +67,7 @@ struct Slice* nf_tokenize(const char* input, struct Error* err)
|
|||
// } while (slices == NULL);
|
||||
|
||||
int i = 0;
|
||||
int slice = 0;
|
||||
int lineNumber = 1;
|
||||
|
||||
int parens = 0;
|
||||
|
@ -124,14 +86,14 @@ struct Slice* nf_tokenize(const char* input, struct Error* err)
|
|||
} else if (input[i] == ')') {
|
||||
parens--;
|
||||
if (parens < 0) {
|
||||
buildErrFromInput(err, MISMATCHED_PARENS, i, input, slice);
|
||||
buildErrFromInput(err, MISMATCHED_PARENS, i, input);
|
||||
//free(slices);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
slice->text = &input[i];
|
||||
slice->lineNumber = lineNumber;
|
||||
slices[slice].text = &input[i];
|
||||
slices[slice].lineNumber = lineNumber;
|
||||
|
||||
if (isSingle(&input[i])) {
|
||||
i++;
|
||||
|
@ -141,31 +103,28 @@ struct Slice* nf_tokenize(const char* input, struct Error* err)
|
|||
}
|
||||
continue;
|
||||
} else if (input[i] == '"') {
|
||||
processString(input, err, slice, &i, &lineNumber, &length);
|
||||
processString(input, err, slices, slice, &i, &lineNumber, &length);
|
||||
} else {
|
||||
collectSymbol(input, &i, &length);
|
||||
}
|
||||
|
||||
if (err->context != NULL) {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
collectSymbol(input, &i, &length);
|
||||
if (length == 0) {
|
||||
buildErrFromInput(err, BAD_SYMBOL, i, input, slice);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
slice->length = length;
|
||||
slices[slice].length = length;
|
||||
slice++;
|
||||
}
|
||||
|
||||
if (parens != 0) {
|
||||
buildErrFromInput(err, MISMATCHED_PARENS, i, input, slice);
|
||||
buildErrFromInput(err, MISMATCHED_PARENS, i, input);
|
||||
//free(slices);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
slice->text = NULL;
|
||||
slice->length = 0;
|
||||
size_t size = sizeof(struct Slice) * ((slice - &slices[0]) + 1);
|
||||
slices[slice].text = NULL;
|
||||
slices[slice].length = 0;
|
||||
size_t size = sizeof(struct Slice) * (slice + 1);
|
||||
struct Slice* allocated = malloc(size);
|
||||
memcpy(allocated, slices, size);
|
||||
|
||||
|
@ -177,28 +136,20 @@ 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,
|
||||
int* length);
|
||||
|
||||
void processString(const char* input, struct Error* err, struct Slice* slice, int* i, int* lineNumber, int* length)
|
||||
void processString(const char* input, struct Error* err, struct Slice* slices, int slice, int* i, int* lineNumber,
|
||||
int* length)
|
||||
{
|
||||
if (input[(*i) + 1] == '"' && input[(*i) + 2] == '"') {
|
||||
tripleQuotedString(input, err, slice, i, lineNumber, length);
|
||||
tripleQuotedString(input, err, &slices[slice], i, lineNumber, length);
|
||||
} else {
|
||||
singleQuotedString(input, i, lineNumber, length);
|
||||
}
|
||||
(*i)++;
|
||||
}
|
||||
|
||||
int validSymbolChar(const char* c)
|
||||
{
|
||||
return !isWhitespace(*c)
|
||||
&& !isSingle(c)
|
||||
&& *c != '"'
|
||||
&& *c != '\0';
|
||||
}
|
||||
|
||||
void collectSymbol(const char* input, int* i, int* length)
|
||||
{
|
||||
// TODO: Error if length is 0?
|
||||
while (validSymbolChar(&input[++(*i)])) {
|
||||
while (!isWhitespace(input[++(*i)]) && !isSingle(&input[(*i)]) && input[(*i)] != '"' && input[(*i)] != '\0') {
|
||||
(*length)++;
|
||||
}
|
||||
}
|
||||
|
@ -220,7 +171,7 @@ void tripleQuotedString(const char* input, struct Error* err, struct Slice* slic
|
|||
}
|
||||
(*length)++;
|
||||
if (input[c] == '\0' || input[c + 1] == '\0' || input[c + 2] == '\0') {
|
||||
buildErrFromInput(err, UNEXPECTED_EOF, c, input, slice);
|
||||
buildErrFromInput(err, UNEXPECTED_EOF, c, input);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -229,10 +180,9 @@ void tripleQuotedString(const char* input, struct Error* err, struct Slice* slic
|
|||
void singleQuotedString(const char* input, int* i, int* lineNumber, int* length)
|
||||
{
|
||||
while (input[++(*i)] != '\0') {
|
||||
const int c = *i;
|
||||
if (input[c] == '"') {
|
||||
if (input[(*i)] == '"') {
|
||||
int backslashes = 0;
|
||||
while (input[(c - 1) - backslashes] == '\\') {
|
||||
while (input[((*i) - 1) - backslashes] == '\\') {
|
||||
backslashes++;
|
||||
}
|
||||
// \\\" => Odd number of backslashes, quote IS escaped
|
||||
|
@ -240,7 +190,7 @@ void singleQuotedString(const char* input, int* i, int* lineNumber, int* length)
|
|||
if (backslashes % 2 == 0) {
|
||||
break;
|
||||
}
|
||||
} else if (input[c] == '\n') {
|
||||
} else if (input[(*i)] == '\n') {
|
||||
(*lineNumber)++;
|
||||
}
|
||||
(*length)++;
|
||||
|
|
Loading…
Reference in New Issue