Add help text for STANDALONE.
Pull most user functions into the new plfunc. Could integrate some tests directly into pl with --run-tests
This commit is contained in:
parent
30e8c87e66
commit
3bf65577c0
|
@ -1,4 +1,4 @@
|
||||||
files = pebblisp.c tokens.c object.c env.c web.c
|
files = pebblisp.c tokens.c object.c env.c web.c plfunc.c
|
||||||
libs = -lreadline -lmicrohttpd
|
libs = -lreadline -lmicrohttpd
|
||||||
exe = pl
|
exe = pl
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ SCRIPTDIR ?= /usr/local/share/pebblisp
|
||||||
mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
|
mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||||
mkfile_dir := $(dir $(mkfile_path))
|
mkfile_dir := $(dir $(mkfile_path))
|
||||||
|
|
||||||
GCC_COM ?= gcc -g -O0 -Wall -o $(exe) -D STANDALONE -DSCRIPTDIR=\"$(SCRIPTDIR)\"
|
GCC_COM ?= gcc -g -O0 -Wall -o $(exe) -D WEBSERVER -D STANDALONE -DSCRIPTDIR=\"$(SCRIPTDIR)\"
|
||||||
|
|
||||||
all:
|
all:
|
||||||
$(GCC_COM) $(files) $(libs) && echo && ./tests.sh
|
$(GCC_COM) $(files) $(libs) && echo && ./tests.sh
|
||||||
|
|
77
src/env.c
77
src/env.c
|
@ -5,6 +5,8 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "pebblisp.h"
|
#include "pebblisp.h"
|
||||||
|
#include "web.h"
|
||||||
|
#include "plfunc.h"
|
||||||
|
|
||||||
Object fetchFromEnvironment(const char* name, struct Environment* env)
|
Object fetchFromEnvironment(const char* name, struct Environment* env)
|
||||||
{
|
{
|
||||||
|
@ -223,16 +225,48 @@ struct symFunc {
|
||||||
Object (* func)(Object, Object, struct Environment*);
|
Object (* func)(Object, Object, struct Environment*);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Environment* _global;
|
struct Environment* globalEnv;
|
||||||
|
|
||||||
struct Environment* global()
|
struct Environment* global()
|
||||||
{
|
{
|
||||||
return _global;
|
return globalEnv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setGlobal(struct Environment* env)
|
void setGlobal(struct Environment* env)
|
||||||
{
|
{
|
||||||
_global = env;
|
globalEnv = env;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct helpText {
|
||||||
|
const char* symbol;
|
||||||
|
const char* help;
|
||||||
|
};
|
||||||
|
int currentHelp = 0;
|
||||||
|
struct helpText helpTexts[100];
|
||||||
|
|
||||||
|
#define pf(_name, _func) buildHelp(_name, &(_func), _func ## Doc)
|
||||||
|
|
||||||
|
struct symFunc buildHelp(const char* symbol, Object (* func)(Object, Object, struct Environment*), const char* help)
|
||||||
|
{
|
||||||
|
helpTexts[currentHelp].help = help;
|
||||||
|
helpTexts[currentHelp].symbol = symbol;
|
||||||
|
currentHelp += 1;
|
||||||
|
|
||||||
|
return (struct symFunc) {
|
||||||
|
.func = func,
|
||||||
|
.sym = symbol,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* getHelp(const char* symbol)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < currentHelp; i++) {
|
||||||
|
struct helpText h = helpTexts[i];
|
||||||
|
if (strcmp(symbol, h.symbol) == 0) {
|
||||||
|
return h.help;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "Help not found!";
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Environment defaultEnv()
|
struct Environment defaultEnv()
|
||||||
|
@ -263,25 +297,30 @@ struct Environment defaultEnv()
|
||||||
{"<", <h},
|
{"<", <h},
|
||||||
{"&", &and},
|
{"&", &and},
|
||||||
{"|", &or},
|
{"|", &or},
|
||||||
{"cat", &catObjects},
|
pf("cat", catObjects),
|
||||||
{"fil", &filter},
|
pf("fil", filter),
|
||||||
{"len", &len},
|
pf("len", len),
|
||||||
{"ap", &append},
|
pf("ap", append),
|
||||||
{"pre", &prepend},
|
pf("pre", prepend),
|
||||||
{"reduce", &reduce},
|
pf("reduce", reduce),
|
||||||
{"at", &at},
|
pf("at", at),
|
||||||
{"rest", &rest},
|
pf("rest", rest),
|
||||||
{"chat", &charAt},
|
pf("chat", charAt),
|
||||||
#ifndef LOW_MEM
|
#ifndef LOW_MEM
|
||||||
{"rev", &reverse},
|
pf("rev", reverse),
|
||||||
#endif
|
#endif
|
||||||
{"isnum", &isNum},
|
pf("isnum", isNum),
|
||||||
{"islist", &isList},
|
pf("islist", isList),
|
||||||
{"isstr", &isString},
|
pf("isstr", isString),
|
||||||
{"iserr", &isErr},
|
{"iserr", &isErr},
|
||||||
{"char", &charVal},
|
pf("char", charVal),
|
||||||
{"eval", &parseEvalO},
|
{"eval", &parseEvalO},
|
||||||
{"poss", &possessive},
|
{"poss", &possessive},
|
||||||
|
#ifdef WEBSERVER
|
||||||
|
pf("get", addGetRoute),
|
||||||
|
pf("post", addPostRoute),
|
||||||
|
pf("serve", startServer),
|
||||||
|
#endif
|
||||||
#ifdef STANDALONE
|
#ifdef STANDALONE
|
||||||
{"prn", &print},
|
{"prn", &print},
|
||||||
{"pch", &pChar},
|
{"pch", &pChar},
|
||||||
|
@ -289,9 +328,7 @@ struct Environment defaultEnv()
|
||||||
//{"sys", &systemCall},
|
//{"sys", &systemCall},
|
||||||
{"loadfile", &loadFile},
|
{"loadfile", &loadFile},
|
||||||
{"inp", &takeInput},
|
{"inp", &takeInput},
|
||||||
{"get", &addGetRoute},
|
pf("?", help)
|
||||||
{"post", &addPostRoute},
|
|
||||||
{"serve", &startServer}
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -42,4 +42,6 @@ struct StructDef getStructDef(struct Environment* env, const char* name);
|
||||||
|
|
||||||
int getStructIndex(struct Environment* env, const char* name);
|
int getStructIndex(struct Environment* env, const char* name);
|
||||||
|
|
||||||
|
const char* getHelp(const char* symbol);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -45,15 +45,6 @@ int listLength(const Object* listObj)
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object len(Object obj1, Object o_ignore, struct Environment* e_ignore)
|
|
||||||
{
|
|
||||||
Object o = numberObject(listLength(&obj1));
|
|
||||||
if (o.number < 0) {
|
|
||||||
return errorObject(NOT_A_LIST);
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a pointer to the Object at the given index in a given list
|
* Returns a pointer to the Object at the given index in a given list
|
||||||
* @param listObj The list to fetch an Object from
|
* @param listObj The list to fetch an Object from
|
||||||
|
|
|
@ -244,9 +244,6 @@ struct Error noError();
|
||||||
|
|
||||||
Object constructLambda(const Object* params, const Object* body, struct Environment* env);
|
Object constructLambda(const Object* params, const Object* body, struct Environment* env);
|
||||||
|
|
||||||
// Object version of listLength()
|
|
||||||
Object len(Object obj1, Object, struct Environment*);
|
|
||||||
|
|
||||||
int getAllocations();
|
int getAllocations();
|
||||||
|
|
||||||
size_t getBytes();
|
size_t getBytes();
|
||||||
|
|
408
src/pebblisp.c
408
src/pebblisp.c
|
@ -1,6 +1,5 @@
|
||||||
#include "pebblisp.h"
|
#include "pebblisp.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -8,6 +7,7 @@
|
||||||
#include <readline/history.h>
|
#include <readline/history.h>
|
||||||
|
|
||||||
#include "tokens.h"
|
#include "tokens.h"
|
||||||
|
#include "plfunc.h"
|
||||||
|
|
||||||
#ifdef STANDALONE
|
#ifdef STANDALONE
|
||||||
|
|
||||||
|
@ -414,371 +414,6 @@ Object eval(const Object* obj, struct Environment* env)
|
||||||
return errorObject(BAD_TYPE);
|
return errorObject(BAD_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* (reduce (list, initial) (fn (prev total) (+ prev total)))
|
|
||||||
*/
|
|
||||||
Object reduce(const Object listInitial, const Object func, struct Environment* env)
|
|
||||||
{
|
|
||||||
Object* list = itemAt(&listInitial, 0);
|
|
||||||
Object total = cloneObject(*list->forward); // From given initial value
|
|
||||||
|
|
||||||
if (list->type != TYPE_LIST) {
|
|
||||||
return simpleFuncEval(func, total, *list, env);
|
|
||||||
}
|
|
||||||
|
|
||||||
FOR_POINTER_IN_LIST(list) {
|
|
||||||
total = simpleFuncEval(func, total, *POINTER, env);
|
|
||||||
}
|
|
||||||
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CAT_MAX 1024
|
|
||||||
|
|
||||||
Object catObjects(const Object obj1, const Object obj2, struct Environment* env)
|
|
||||||
{
|
|
||||||
Object evalObj1 = eval(&obj1, env);
|
|
||||||
Object evalObj2 = eval(&obj2, env);
|
|
||||||
if (isError(evalObj2, ONLY_ONE_ARGUMENT)) {
|
|
||||||
return evalObj1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char str1[CAT_MAX] = "";
|
|
||||||
char str2[CAT_MAX] = "";
|
|
||||||
stringObj(str1, &evalObj1);
|
|
||||||
stringObj(str2, &evalObj2);
|
|
||||||
cleanObject(&evalObj1);
|
|
||||||
cleanObject(&evalObj2);
|
|
||||||
size_t length = strlen(str1) + strlen(str2) + 1;
|
|
||||||
|
|
||||||
Object o = newObject(TYPE_STRING);
|
|
||||||
o.string = calloc(sizeof(char), length);
|
|
||||||
strcat(o.string, str1);
|
|
||||||
strcat(o.string, str2);
|
|
||||||
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object listEquality(const Object* list1, const Object* list2)
|
|
||||||
{
|
|
||||||
FOR_POINTERS_IN_LISTS(list1, list2) {
|
|
||||||
if (P1->type != P2->type || P1->number != P2->number) {
|
|
||||||
return boolObject(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return boolObject(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object _basicOp(const Object* obj1, const Object* obj2, const char op,
|
|
||||||
struct Environment* env)
|
|
||||||
{
|
|
||||||
const int n1 = obj1->number;
|
|
||||||
const int n2 = obj2->number;
|
|
||||||
|
|
||||||
switch (op) {
|
|
||||||
case '+':
|
|
||||||
if (eitherIs(TYPE_STRING, obj1, obj2)) {
|
|
||||||
return catObjects(*obj1, *obj2, env);
|
|
||||||
}
|
|
||||||
return numberObject(n1 + n2);
|
|
||||||
case '-':
|
|
||||||
return numberObject(n1 - n2);
|
|
||||||
case '*':
|
|
||||||
return numberObject(n1 * n2);
|
|
||||||
case '/':
|
|
||||||
return numberObject(n1 / n2);
|
|
||||||
case '%':
|
|
||||||
return numberObject(n1 % n2);
|
|
||||||
|
|
||||||
case '&':
|
|
||||||
return boolObject(n1 != 0 && n2 != 0);
|
|
||||||
case '|':
|
|
||||||
return boolObject(n1 != 0 || n2 != 0);
|
|
||||||
|
|
||||||
case '=':
|
|
||||||
if (bothAre(TYPE_STRING, obj1, obj2)) {
|
|
||||||
return boolObject(!strcmp(obj1->string, obj2->string));
|
|
||||||
}
|
|
||||||
if (bothAre(TYPE_LIST, obj1, obj2)) {
|
|
||||||
return listEquality(obj1, obj2);
|
|
||||||
}
|
|
||||||
return boolObject(n1 == n2 && areSameType(obj1, obj2));
|
|
||||||
case '>':
|
|
||||||
return boolObject(n1 > n2);
|
|
||||||
case '<':
|
|
||||||
return boolObject(n1 < n2);
|
|
||||||
default:
|
|
||||||
return *obj1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Object basicOp(const Object* obj1, const Object* obj2, const char op,
|
|
||||||
struct Environment* env)
|
|
||||||
{
|
|
||||||
if (isError(*obj2, ONLY_ONE_ARGUMENT)) {
|
|
||||||
return *obj2;
|
|
||||||
}
|
|
||||||
|
|
||||||
int lists = (obj1->type == TYPE_LIST) + (obj2->type == TYPE_LIST);
|
|
||||||
if (lists == 0) {
|
|
||||||
return _basicOp(obj1, obj2, op, env);
|
|
||||||
|
|
||||||
} else if (lists ==
|
|
||||||
1) { // Single operand is applied to each element in list
|
|
||||||
const Object* listObj = (obj1->type == TYPE_LIST) ? obj1 : obj2;
|
|
||||||
const Object* singleObj = (obj1->type == TYPE_LIST) ? obj2 : obj1;
|
|
||||||
|
|
||||||
Object newList = listObject();
|
|
||||||
FOR_POINTER_IN_LIST(listObj) {
|
|
||||||
Object adding = eval(POINTER, env);
|
|
||||||
nf_addToList(&newList, _basicOp(&adding, singleObj, op, env));
|
|
||||||
}
|
|
||||||
return newList;
|
|
||||||
|
|
||||||
} else { // 2 lists with the op applied to matching indices of both lists
|
|
||||||
if (listLength(obj1) == listLength(obj2)) {
|
|
||||||
Object newList = listObject();
|
|
||||||
FOR_POINTERS_IN_LISTS(obj1, obj2) {
|
|
||||||
const Object ev1 = eval(P1, env);
|
|
||||||
const Object ev2 = eval(P2, env);
|
|
||||||
nf_addToList(&newList, _basicOp(&ev1, &ev2, op, env));
|
|
||||||
}
|
|
||||||
return newList;
|
|
||||||
} else {
|
|
||||||
return errorObject(LISTS_NOT_SAME_SIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define BASIC_OP(_name, _char) \
|
|
||||||
Object _name(Object obj1, Object obj2, struct Environment *env) \
|
|
||||||
{ \
|
|
||||||
return basicOp(&obj1, &obj2, _char, env); \
|
|
||||||
}
|
|
||||||
|
|
||||||
BASIC_OP(add, '+');
|
|
||||||
|
|
||||||
BASIC_OP(sub, '-');
|
|
||||||
|
|
||||||
BASIC_OP(mul, '*');
|
|
||||||
|
|
||||||
BASIC_OP(dvi, '/');
|
|
||||||
|
|
||||||
BASIC_OP(mod, '%');
|
|
||||||
|
|
||||||
BASIC_OP(equ, '=');
|
|
||||||
|
|
||||||
BASIC_OP(gth, '>');
|
|
||||||
|
|
||||||
BASIC_OP(lth, '<');
|
|
||||||
|
|
||||||
BASIC_OP(and, '&');
|
|
||||||
|
|
||||||
BASIC_OP(or, '|');
|
|
||||||
|
|
||||||
#undef BASIC_OP
|
|
||||||
|
|
||||||
Object filter(Object obj1, Object obj2, struct Environment* env)
|
|
||||||
{
|
|
||||||
Object filteredList = listObject();
|
|
||||||
Object* filteringList = &obj2;
|
|
||||||
FOR_POINTER_IN_LIST(filteringList) {
|
|
||||||
Object conditional = cloneObject(obj1);
|
|
||||||
nf_addToList(&conditional, *POINTER); // cloneObject()?
|
|
||||||
Object result = eval(&conditional, env);
|
|
||||||
cleanObject(&conditional);
|
|
||||||
if (result.number == 1) {
|
|
||||||
nf_addToList(&filteredList, *POINTER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return filteredList;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object append(Object list, Object newElement, struct Environment* env)
|
|
||||||
{
|
|
||||||
Object newList = cloneObject(list);
|
|
||||||
nf_addToList(&newList, cloneObject(newElement));
|
|
||||||
return newList;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object prepend(Object list, Object newElement, struct Environment* env)
|
|
||||||
{
|
|
||||||
Object newList = listObject();
|
|
||||||
nf_addToList(&newList, cloneObject(newElement));
|
|
||||||
appendList(&newList, &list);
|
|
||||||
return newList;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object at(Object index, Object list, struct Environment* env)
|
|
||||||
{
|
|
||||||
const Object* found = itemAt(&list, index.number);
|
|
||||||
if (found) {
|
|
||||||
return cloneObject(*found);
|
|
||||||
} else {
|
|
||||||
return errorObject(INDEX_PAST_END);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Object rest(Object list, Object ignore, struct Environment* env)
|
|
||||||
{
|
|
||||||
Object ret = listObject();
|
|
||||||
Object* l = &list;
|
|
||||||
FOR_POINTER_IN_LIST(l) {
|
|
||||||
if (POINTER == l->list) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
nf_addToList(&ret, cloneObject(*POINTER));
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object reverse(Object _list, Object ignore, struct Environment* ignore2)
|
|
||||||
{
|
|
||||||
const Object* list = &_list;
|
|
||||||
Object rev = listObject();
|
|
||||||
|
|
||||||
Object* tail = NULL;
|
|
||||||
FOR_POINTER_IN_LIST(list) {
|
|
||||||
Object* oldTail = tail;
|
|
||||||
allocObject(&tail, cloneObject(*POINTER));
|
|
||||||
if (oldTail) {
|
|
||||||
tail->forward = oldTail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rev.list = tail;
|
|
||||||
return rev;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object isNum(Object test, Object ignore, struct Environment* ignore2)
|
|
||||||
{
|
|
||||||
return test.type == TYPE_NUMBER ? boolObject(1) : boolObject(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object isList(Object test, Object ignore, struct Environment* ignore2)
|
|
||||||
{
|
|
||||||
return test.type == TYPE_LIST ? boolObject(1) : boolObject(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object isString(Object test, Object ignore, struct Environment* ignore2)
|
|
||||||
{
|
|
||||||
return test.type == TYPE_STRING ? boolObject(1) : boolObject(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the int value of a string's first character
|
|
||||||
Object charVal(Object test, Object ignore, struct Environment* ignore2)
|
|
||||||
{
|
|
||||||
return numberObject(test.string[0]);
|
|
||||||
// return test.type == TYPE_STRING && test.string[0] == '\0' ?
|
|
||||||
// boolObject(1) : boolObject(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object isErr(Object test, Object ignore, struct Environment* ignore2)
|
|
||||||
{
|
|
||||||
return test.type == TYPE_ERROR ? boolObject(1) : boolObject(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object charAt(Object string, Object at, struct Environment* ignore)
|
|
||||||
{
|
|
||||||
char* c = malloc(sizeof(char) * 2);
|
|
||||||
c[0] = string.string[at.number];
|
|
||||||
c[1] = '\0';
|
|
||||||
string.string = c;
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef STANDALONE
|
|
||||||
|
|
||||||
Object print(Object p, Object ignore, struct Environment* env)
|
|
||||||
{
|
|
||||||
Object c = cloneObject(p);
|
|
||||||
Object e = eval(&c, env);
|
|
||||||
_printObj(&e, 0);
|
|
||||||
cleanObject(&c);
|
|
||||||
cleanObject(&e);
|
|
||||||
return numberObject(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addRouteO(Object path, Object textFunc, struct Environment* env, enum RouteType type)
|
|
||||||
{
|
|
||||||
char* p = malloc(sizeof(char) * strlen(path.string) + 1);
|
|
||||||
strcpy(p, path.string);
|
|
||||||
|
|
||||||
struct Route r;
|
|
||||||
r.path = p;
|
|
||||||
r.routeAction = cloneObject(textFunc);
|
|
||||||
r.env = env;
|
|
||||||
r.type = type;
|
|
||||||
env->refs += 1;
|
|
||||||
//r.text = t;
|
|
||||||
addRoute(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object addGetRoute(Object path, Object textFunc, struct Environment* env)
|
|
||||||
{
|
|
||||||
addRouteO(path, textFunc, env, GET);
|
|
||||||
return numberObject(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object addPostRoute(Object path, Object textFunc, struct Environment* env)
|
|
||||||
{
|
|
||||||
addRouteO(path, textFunc, env, POST);
|
|
||||||
return numberObject(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object startServer(Object portObject, Object ignored, struct Environment* env)
|
|
||||||
{
|
|
||||||
int port = 8888;
|
|
||||||
if (portObject.type == TYPE_NUMBER) {
|
|
||||||
port = portObject.number;
|
|
||||||
}
|
|
||||||
return numberObject(start(port));
|
|
||||||
}
|
|
||||||
|
|
||||||
Object pChar(Object c, Object i1, struct Environment* i2)
|
|
||||||
{
|
|
||||||
if (c.type != TYPE_NUMBER) {
|
|
||||||
return errorObject(BAD_NUMBER);
|
|
||||||
}
|
|
||||||
printf("%c", c.number % 256);
|
|
||||||
return numberObject(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object printEnvO(Object i1, Object i2, struct Environment* env)
|
|
||||||
{
|
|
||||||
printEnv(global());
|
|
||||||
return numberObject(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Object parseEvalO(Object text, Object ignore, struct Environment* env)
|
|
||||||
{
|
|
||||||
if (text.type == TYPE_SYMBOL) {
|
|
||||||
Object string = eval(&text, env);
|
|
||||||
Object parsed = parseEval(string.string, env);
|
|
||||||
cleanObject(&string);
|
|
||||||
return parsed;
|
|
||||||
} else if (text.type != TYPE_STRING) {
|
|
||||||
return errorObject(CAN_ONLY_EVAL_STRINGS);
|
|
||||||
}
|
|
||||||
return parseEval(text.string, env);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef STANDALONE
|
|
||||||
|
|
||||||
Object takeInput(Object prompt, Object i2, struct Environment* i3)
|
|
||||||
{
|
|
||||||
if (prompt.type == TYPE_STRING) {
|
|
||||||
printf("%s", prompt.string);
|
|
||||||
}
|
|
||||||
char input[256] = "";
|
|
||||||
fgets(input, 256, stdin);
|
|
||||||
return stringFromSlice(input, strlen(input) - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void copySlice(char* dest, struct Slice* src)
|
void copySlice(char* dest, struct Slice* src)
|
||||||
{
|
{
|
||||||
if (!dest || !src) {
|
if (!dest || !src) {
|
||||||
|
@ -925,9 +560,9 @@ Result parseAtom(struct Slice* s)
|
||||||
if (c != '0' || s->length == 1) {
|
if (c != '0' || s->length == 1) {
|
||||||
return (Result) {parseDecimal(s), s};
|
return (Result) {parseDecimal(s), s};
|
||||||
#ifndef LOW_MEM
|
#ifndef LOW_MEM
|
||||||
} else if (c == '0' && s->text[1] == 'x') {
|
} else if (s->text[1] == 'x') {
|
||||||
return (Result) {parseHex(s), s};
|
return (Result) {parseHex(s), s};
|
||||||
} else if (c == '0' && s->text[1] == 'b') {
|
} else if (s->text[1] == 'b') {
|
||||||
return (Result) {parseBin(s), s};
|
return (Result) {parseBin(s), s};
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
|
@ -1021,6 +656,16 @@ Object parseEval(const char* input, struct Environment* env)
|
||||||
|
|
||||||
#ifdef STANDALONE
|
#ifdef STANDALONE
|
||||||
|
|
||||||
|
int readFile(const char* filename, struct Environment* env)
|
||||||
|
{
|
||||||
|
FILE* input = fopen(filename, "r");
|
||||||
|
if (!input) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
_readFile(input, env);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int _readFile(FILE* input, struct Environment* env)
|
int _readFile(FILE* input, struct Environment* env)
|
||||||
{
|
{
|
||||||
Object r = numberObject(0);
|
Object r = numberObject(0);
|
||||||
|
@ -1063,33 +708,6 @@ int _readFile(FILE* input, struct Environment* env)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int readFile(const char* filename, struct Environment* env)
|
|
||||||
{
|
|
||||||
FILE* input = fopen(filename, "r");
|
|
||||||
if (!input) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
_readFile(input, env);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object loadFile(Object filename, Object _, struct Environment* env)
|
|
||||||
{
|
|
||||||
if (isStringy(filename)) {
|
|
||||||
readFile(filename.string, env);
|
|
||||||
return numberObject(0);
|
|
||||||
}
|
|
||||||
return numberObject(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object systemCall(Object process, Object _, struct Environment* env)
|
|
||||||
{
|
|
||||||
if (isStringy(process)) {
|
|
||||||
return numberObject(system(process.string));
|
|
||||||
}
|
|
||||||
return numberObject(255);
|
|
||||||
}
|
|
||||||
|
|
||||||
void repl(struct Environment* env)
|
void repl(struct Environment* env)
|
||||||
{
|
{
|
||||||
char* buf;
|
char* buf;
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
#ifndef PEBBLISP_H
|
#ifndef PEBBLISP_H
|
||||||
#define PEBBLISP_H
|
#define PEBBLISP_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "env.h"
|
#include "env.h"
|
||||||
#include "object.h"
|
#include "object.h"
|
||||||
|
|
||||||
|
#define F(_name, _docs) static const char * const _name ## Doc = _docs; Object _name
|
||||||
|
|
||||||
struct Slice {
|
struct Slice {
|
||||||
const char* text;
|
const char* text;
|
||||||
unsigned char length;
|
unsigned char length;
|
||||||
|
@ -35,67 +39,7 @@ Object listEvalLambda(Object* lambda, const Object* remaining,
|
||||||
|
|
||||||
void debugSlice(struct Slice* s);
|
void debugSlice(struct Slice* s);
|
||||||
|
|
||||||
#define BASIC_OP(_name) \
|
Object simpleFuncEval(Object func, Object arg1, Object arg2, struct Environment* env);
|
||||||
Object _name(Object obj1, Object obj2, struct Environment *env);
|
|
||||||
|
|
||||||
BASIC_OP(add);
|
|
||||||
|
|
||||||
BASIC_OP(sub);
|
|
||||||
|
|
||||||
BASIC_OP(mul);
|
|
||||||
|
|
||||||
BASIC_OP(dvi);
|
|
||||||
|
|
||||||
BASIC_OP(mod);
|
|
||||||
|
|
||||||
BASIC_OP(equ);
|
|
||||||
|
|
||||||
BASIC_OP(gth);
|
|
||||||
|
|
||||||
BASIC_OP(lth);
|
|
||||||
|
|
||||||
BASIC_OP(and);
|
|
||||||
|
|
||||||
BASIC_OP(or);
|
|
||||||
#undef BASIC_OP
|
|
||||||
|
|
||||||
Object catObjects(Object obj1, Object obj2, struct Environment* env);
|
|
||||||
|
|
||||||
Object filter(Object obj1, Object obj2, struct Environment* env);
|
|
||||||
|
|
||||||
Object append(Object list, Object newElement, struct Environment* env);
|
|
||||||
|
|
||||||
Object prepend(Object list, Object newElement, struct Environment* env);
|
|
||||||
|
|
||||||
Object reduce(Object listInitial, Object func, struct Environment* env);
|
|
||||||
|
|
||||||
Object at(Object index, Object list, struct Environment* env);
|
|
||||||
|
|
||||||
Object rest(Object list, Object ignore, struct Environment* env);
|
|
||||||
|
|
||||||
Object reverse(Object _list, Object ignore, struct Environment* ignore2);
|
|
||||||
|
|
||||||
Object isNum(Object test, Object ignore, struct Environment* ignore2);
|
|
||||||
|
|
||||||
Object isList(Object test, Object ignore, struct Environment* ignore2);
|
|
||||||
|
|
||||||
Object isString(Object test, Object ignore, struct Environment* ignore2);
|
|
||||||
|
|
||||||
Object isErr(Object test, Object ignore, struct Environment* ignore2);
|
|
||||||
|
|
||||||
Object charAt(Object string, Object at, struct Environment* ignore);
|
|
||||||
|
|
||||||
Object charVal(Object test, Object ignore, struct Environment* ignore2);
|
|
||||||
|
|
||||||
Object print(Object p, Object ignore, struct Environment* ignore2);
|
|
||||||
|
|
||||||
Object pChar(Object c, Object i1, struct Environment* i2);
|
|
||||||
|
|
||||||
Object printEnvO(Object i1, Object i2, struct Environment* env);
|
|
||||||
|
|
||||||
Object parseEvalO(Object text, Object ignore, struct Environment* env);
|
|
||||||
|
|
||||||
Object possessive(Object structo, Object field, struct Environment* env);
|
|
||||||
|
|
||||||
#ifdef STANDALONE
|
#ifdef STANDALONE
|
||||||
|
|
||||||
|
@ -105,12 +49,10 @@ Object systemCall(Object call, Object _, struct Environment* i3);
|
||||||
|
|
||||||
Object loadFile(Object filename, Object _, struct Environment* env);
|
Object loadFile(Object filename, Object _, struct Environment* env);
|
||||||
|
|
||||||
Object startServer(Object path, Object textFunc, struct Environment* env);
|
int _readFile(FILE* input, struct Environment* env);
|
||||||
|
|
||||||
Object addGetRoute(Object path, Object textFunc, struct Environment* env);
|
int readFile(const char* filename, struct Environment* env);
|
||||||
|
|
||||||
Object addPostRoute(Object path, Object textFunc, struct Environment* env);
|
#endif /* STANDALONE */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
|
|
@ -0,0 +1,350 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "plfunc.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (reduce (list, initial) (fn (prev total) (+ prev total)))
|
||||||
|
*/
|
||||||
|
Object reduce(const Object listInitial, const Object func, struct Environment* env)
|
||||||
|
{
|
||||||
|
Object* list = itemAt(&listInitial, 0);
|
||||||
|
Object total = cloneObject(*list->forward); // From given initial value
|
||||||
|
|
||||||
|
if (list->type != TYPE_LIST) {
|
||||||
|
return simpleFuncEval(func, total, *list, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
FOR_POINTER_IN_LIST(list) {
|
||||||
|
total = simpleFuncEval(func, total, *POINTER, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object charAt(Object string, Object at, struct Environment* ignore)
|
||||||
|
{
|
||||||
|
char* c = malloc(sizeof(char) * 2);
|
||||||
|
c[0] = string.string[at.number];
|
||||||
|
c[1] = '\0';
|
||||||
|
string.string = c;
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object filter(Object condition, Object list, struct Environment* env)
|
||||||
|
{
|
||||||
|
Object filteredList = listObject();
|
||||||
|
FOR_POINTER_IN_LIST(&list) {
|
||||||
|
Object conditional = cloneObject(condition);
|
||||||
|
nf_addToList(&conditional, *POINTER);
|
||||||
|
Object result = eval(&conditional, env);
|
||||||
|
cleanObject(&conditional);
|
||||||
|
if (result.number == 1) {
|
||||||
|
nf_addToList(&filteredList, *POINTER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredList;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object append(Object list, Object newElement, struct Environment* env)
|
||||||
|
{
|
||||||
|
Object newList = cloneObject(list);
|
||||||
|
nf_addToList(&newList, cloneObject(newElement));
|
||||||
|
return newList;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object prepend(Object list, Object newElement, struct Environment* env)
|
||||||
|
{
|
||||||
|
Object newList = listObject();
|
||||||
|
nf_addToList(&newList, cloneObject(newElement));
|
||||||
|
appendList(&newList, &list);
|
||||||
|
return newList;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object at(Object index, Object list, struct Environment* env)
|
||||||
|
{
|
||||||
|
const Object* found = itemAt(&list, index.number);
|
||||||
|
if (found) {
|
||||||
|
return cloneObject(*found);
|
||||||
|
} else {
|
||||||
|
return errorObject(INDEX_PAST_END);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object rest(Object list, Object ignore, struct Environment* env)
|
||||||
|
{
|
||||||
|
Object ret = listObject();
|
||||||
|
Object* l = &list;
|
||||||
|
FOR_POINTER_IN_LIST(l) {
|
||||||
|
if (POINTER == l->list) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nf_addToList(&ret, cloneObject(*POINTER));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object reverse(Object _list, Object ignore, struct Environment* ignore2)
|
||||||
|
{
|
||||||
|
const Object* list = &_list;
|
||||||
|
Object rev = listObject();
|
||||||
|
|
||||||
|
Object* tail = NULL;
|
||||||
|
FOR_POINTER_IN_LIST(list) {
|
||||||
|
Object* oldTail = tail;
|
||||||
|
allocObject(&tail, cloneObject(*POINTER));
|
||||||
|
if (oldTail) {
|
||||||
|
tail->forward = oldTail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rev.list = tail;
|
||||||
|
return rev;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object isNum(Object test, Object ignore, struct Environment* ignore2)
|
||||||
|
{
|
||||||
|
return test.type == TYPE_NUMBER ? boolObject(1) : boolObject(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object isList(Object test, Object ignore, struct Environment* ignore2)
|
||||||
|
{
|
||||||
|
return test.type == TYPE_LIST ? boolObject(1) : boolObject(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object isString(Object test, Object ignore, struct Environment* ignore2)
|
||||||
|
{
|
||||||
|
return test.type == TYPE_STRING ? boolObject(1) : boolObject(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the int value of a string's first character
|
||||||
|
Object charVal(Object test, Object ignore, struct Environment* ignore2)
|
||||||
|
{
|
||||||
|
return numberObject(test.string[0]);
|
||||||
|
// return test.type == TYPE_STRING && test.string[0] == '\0' ?
|
||||||
|
// boolObject(1) : boolObject(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object isErr(Object test, Object ignore, struct Environment* ignore2)
|
||||||
|
{
|
||||||
|
return test.type == TYPE_ERROR ? boolObject(1) : boolObject(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object parseEvalO(Object text, Object ignore, struct Environment* env)
|
||||||
|
{
|
||||||
|
if (text.type == TYPE_SYMBOL) {
|
||||||
|
Object string = eval(&text, env);
|
||||||
|
Object parsed = parseEval(string.string, env);
|
||||||
|
cleanObject(&string);
|
||||||
|
return parsed;
|
||||||
|
} else if (text.type != TYPE_STRING) {
|
||||||
|
return errorObject(CAN_ONLY_EVAL_STRINGS);
|
||||||
|
}
|
||||||
|
return parseEval(text.string, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object listEquality(const Object* list1, const Object* list2)
|
||||||
|
{
|
||||||
|
FOR_POINTERS_IN_LISTS(list1, list2) {
|
||||||
|
if (P1->type != P2->type || P1->number != P2->number) {
|
||||||
|
return boolObject(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return boolObject(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CAT_MAX 1024
|
||||||
|
|
||||||
|
Object catObjects(const Object obj1, const Object obj2, struct Environment* env)
|
||||||
|
{
|
||||||
|
Object evalObj1 = eval(&obj1, env);
|
||||||
|
Object evalObj2 = eval(&obj2, env);
|
||||||
|
if (isError(evalObj2, ONLY_ONE_ARGUMENT)) {
|
||||||
|
return evalObj1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char str1[CAT_MAX] = "";
|
||||||
|
char str2[CAT_MAX] = "";
|
||||||
|
stringObj(str1, &evalObj1);
|
||||||
|
stringObj(str2, &evalObj2);
|
||||||
|
cleanObject(&evalObj1);
|
||||||
|
cleanObject(&evalObj2);
|
||||||
|
size_t length = strlen(str1) + strlen(str2) + 1;
|
||||||
|
|
||||||
|
Object o = newObject(TYPE_STRING);
|
||||||
|
o.string = calloc(sizeof(char), length);
|
||||||
|
strcat(o.string, str1);
|
||||||
|
strcat(o.string, str2);
|
||||||
|
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object _basicOp(const Object* obj1, const Object* obj2, const char op,
|
||||||
|
struct Environment* env)
|
||||||
|
{
|
||||||
|
const int n1 = obj1->number;
|
||||||
|
const int n2 = obj2->number;
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
case '+':
|
||||||
|
if (eitherIs(TYPE_STRING, obj1, obj2)) {
|
||||||
|
return catObjects(*obj1, *obj2, env);
|
||||||
|
}
|
||||||
|
return numberObject(n1 + n2);
|
||||||
|
case '-':
|
||||||
|
return numberObject(n1 - n2);
|
||||||
|
case '*':
|
||||||
|
return numberObject(n1 * n2);
|
||||||
|
case '/':
|
||||||
|
return numberObject(n1 / n2);
|
||||||
|
case '%':
|
||||||
|
return numberObject(n1 % n2);
|
||||||
|
|
||||||
|
case '&':
|
||||||
|
return boolObject(n1 != 0 && n2 != 0);
|
||||||
|
case '|':
|
||||||
|
return boolObject(n1 != 0 || n2 != 0);
|
||||||
|
|
||||||
|
case '=':
|
||||||
|
if (bothAre(TYPE_STRING, obj1, obj2)) {
|
||||||
|
return boolObject(!strcmp(obj1->string, obj2->string));
|
||||||
|
}
|
||||||
|
if (bothAre(TYPE_LIST, obj1, obj2)) {
|
||||||
|
return listEquality(obj1, obj2);
|
||||||
|
}
|
||||||
|
return boolObject(n1 == n2 && areSameType(obj1, obj2));
|
||||||
|
case '>':
|
||||||
|
return boolObject(n1 > n2);
|
||||||
|
case '<':
|
||||||
|
return boolObject(n1 < n2);
|
||||||
|
default:
|
||||||
|
return *obj1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object basicOp(const Object* obj1, const Object* obj2, const char op,
|
||||||
|
struct Environment* env)
|
||||||
|
{
|
||||||
|
if (isError(*obj2, ONLY_ONE_ARGUMENT)) {
|
||||||
|
return *obj2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lists = (obj1->type == TYPE_LIST) + (obj2->type == TYPE_LIST);
|
||||||
|
if (lists == 0) {
|
||||||
|
return _basicOp(obj1, obj2, op, env);
|
||||||
|
|
||||||
|
} else if (lists ==
|
||||||
|
1) { // Single operand is applied to each element in list
|
||||||
|
const Object* listObj = (obj1->type == TYPE_LIST) ? obj1 : obj2;
|
||||||
|
const Object* singleObj = (obj1->type == TYPE_LIST) ? obj2 : obj1;
|
||||||
|
|
||||||
|
Object newList = listObject();
|
||||||
|
FOR_POINTER_IN_LIST(listObj) {
|
||||||
|
Object adding = eval(POINTER, env);
|
||||||
|
nf_addToList(&newList, _basicOp(&adding, singleObj, op, env));
|
||||||
|
}
|
||||||
|
return newList;
|
||||||
|
|
||||||
|
} else { // 2 lists with the op applied to matching indices of both lists
|
||||||
|
if (listLength(obj1) == listLength(obj2)) {
|
||||||
|
Object newList = listObject();
|
||||||
|
FOR_POINTERS_IN_LISTS(obj1, obj2) {
|
||||||
|
const Object ev1 = eval(P1, env);
|
||||||
|
const Object ev2 = eval(P2, env);
|
||||||
|
nf_addToList(&newList, _basicOp(&ev1, &ev2, op, env));
|
||||||
|
}
|
||||||
|
return newList;
|
||||||
|
} else {
|
||||||
|
return errorObject(LISTS_NOT_SAME_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object len(Object obj1, Object o_ignore, struct Environment* e_ignore)
|
||||||
|
{
|
||||||
|
Object o = numberObject(listLength(&obj1));
|
||||||
|
if (o.number < 0) {
|
||||||
|
return errorObject(NOT_A_LIST);
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BASIC_OP(_name, _char) \
|
||||||
|
Object _name(Object obj1, Object obj2, struct Environment *env) \
|
||||||
|
{ \
|
||||||
|
return basicOp(&obj1, &obj2, _char, env); \
|
||||||
|
}
|
||||||
|
|
||||||
|
BASIC_OP(add, '+');
|
||||||
|
|
||||||
|
BASIC_OP(sub, '-');
|
||||||
|
|
||||||
|
BASIC_OP(mul, '*');
|
||||||
|
|
||||||
|
BASIC_OP(dvi, '/');
|
||||||
|
|
||||||
|
BASIC_OP(mod, '%');
|
||||||
|
|
||||||
|
BASIC_OP(equ, '=');
|
||||||
|
|
||||||
|
BASIC_OP(gth, '>');
|
||||||
|
|
||||||
|
BASIC_OP(lth, '<');
|
||||||
|
|
||||||
|
BASIC_OP(and, '&');
|
||||||
|
|
||||||
|
BASIC_OP(or, '|');
|
||||||
|
|
||||||
|
#undef BASIC_OP
|
||||||
|
|
||||||
|
#ifdef STANDALONE
|
||||||
|
|
||||||
|
Object pChar(Object c, Object i1, struct Environment* i2)
|
||||||
|
{
|
||||||
|
if (c.type != TYPE_NUMBER) {
|
||||||
|
return errorObject(BAD_NUMBER);
|
||||||
|
}
|
||||||
|
printf("%c", c.number % 256);
|
||||||
|
return numberObject(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object printEnvO(Object i1, Object i2, struct Environment* env)
|
||||||
|
{
|
||||||
|
printEnv(global());
|
||||||
|
return numberObject(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object takeInput(Object prompt, Object i2, struct Environment* i3)
|
||||||
|
{
|
||||||
|
if (prompt.type == TYPE_STRING) {
|
||||||
|
printf("%s", prompt.string);
|
||||||
|
}
|
||||||
|
char input[256] = "";
|
||||||
|
fgets(input, 256, stdin);
|
||||||
|
return stringFromSlice(input, strlen(input) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object loadFile(Object filename, Object _, struct Environment* env)
|
||||||
|
{
|
||||||
|
if (isStringy(filename)) {
|
||||||
|
readFile(filename.string, env);
|
||||||
|
return numberObject(0);
|
||||||
|
}
|
||||||
|
return numberObject(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object systemCall(Object process, Object _, struct Environment* env)
|
||||||
|
{
|
||||||
|
if (isStringy(process)) {
|
||||||
|
return numberObject(system(process.string));
|
||||||
|
}
|
||||||
|
return numberObject(255);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object help(Object symbol, Object ignore, struct Environment* ignore2)
|
||||||
|
{
|
||||||
|
const char* help = getHelp(symbol.string);
|
||||||
|
return objFromSlice(&help[-1], strlen(help) + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // STANDALONE
|
|
@ -0,0 +1,112 @@
|
||||||
|
#ifndef PEBBLISP_PLFUNC_H
|
||||||
|
#define PEBBLISP_PLFUNC_H
|
||||||
|
|
||||||
|
#include "pebblisp.h"
|
||||||
|
|
||||||
|
#define BASIC_OP(_name) \
|
||||||
|
Object _name(Object obj1, Object obj2, struct Environment *env);
|
||||||
|
|
||||||
|
BASIC_OP(add);
|
||||||
|
|
||||||
|
BASIC_OP(sub);
|
||||||
|
|
||||||
|
BASIC_OP(mul);
|
||||||
|
|
||||||
|
BASIC_OP(dvi);
|
||||||
|
|
||||||
|
BASIC_OP(mod);
|
||||||
|
|
||||||
|
BASIC_OP(equ);
|
||||||
|
|
||||||
|
BASIC_OP(gth);
|
||||||
|
|
||||||
|
BASIC_OP(lth);
|
||||||
|
|
||||||
|
BASIC_OP(and);
|
||||||
|
|
||||||
|
BASIC_OP(or);
|
||||||
|
|
||||||
|
#undef BASIC_OP
|
||||||
|
|
||||||
|
F(catObjects,
|
||||||
|
"Concatenate string versions of the given objects.\n"
|
||||||
|
"(cat \"Stuff: \" (1 2 3)) => \"Stuff: ( 1 2 3 )\""
|
||||||
|
)(Object obj1, Object obj2, struct Environment* env);
|
||||||
|
|
||||||
|
F(filter, "(filter (< 50) (25 60 100)) => ( 60 100 )")
|
||||||
|
(Object condition, Object list, struct Environment* env);
|
||||||
|
|
||||||
|
F(append, "(ap (1 2) 3) => ( 1 2 3 )")
|
||||||
|
(Object list, Object newElement, struct Environment* env);
|
||||||
|
|
||||||
|
F(prepend, "(pre (2 3) 1) => ( 1 2 3 )")
|
||||||
|
(Object list, Object newElement, struct Environment* env);
|
||||||
|
|
||||||
|
F(len, "(len (2 3)) => 2")
|
||||||
|
(Object obj1, Object o_ignore, struct Environment* e_ignore);
|
||||||
|
|
||||||
|
F(reduce, "(reduce ((1 2 3) 0) +) => 6")
|
||||||
|
(Object listInitial, Object func, struct Environment* env);
|
||||||
|
|
||||||
|
F(at, "(at 1 (1 2 3)) => 2")
|
||||||
|
(Object index, Object list, struct Environment* env);
|
||||||
|
|
||||||
|
F(rest, "(rest (1 2 3)) => ( 2 3 )")
|
||||||
|
(Object list, Object ignore, struct Environment* env);
|
||||||
|
|
||||||
|
F(reverse, "(rev (1 2 3)) => ( 3 2 1 )")
|
||||||
|
(Object _list, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
|
F(isNum, "(isnum 1) => T\n(isnum \"Hello\") => F")
|
||||||
|
(Object test, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
|
F(isList,
|
||||||
|
"(islist (1 2 3)) => T\n"
|
||||||
|
"(islist ()) => T\n"
|
||||||
|
"(islist \"Stringy\") => F"
|
||||||
|
)(Object test, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
|
F(isString,
|
||||||
|
"(isstr \"Heyo\") => T\n"
|
||||||
|
"(isstr \"\") => T\n"
|
||||||
|
"(isstr 10) => F"
|
||||||
|
)(Object test, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
|
F(isErr, "")
|
||||||
|
(Object test, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
|
F(charAt,
|
||||||
|
"(chat \"Hello\" 1) => \"e\"\n"
|
||||||
|
"(chat \"Hello\" 10) => \"\""
|
||||||
|
)(Object string, Object at, struct Environment* ignore);
|
||||||
|
|
||||||
|
F(charVal,
|
||||||
|
"(char \"h\") => 104\n"
|
||||||
|
"(char \"hello\") => 104\n"
|
||||||
|
"(char \"\") => 0"
|
||||||
|
)(Object test, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
|
F(parseEvalO, "(eval \"(1 2 3)\") => (1 2 3)")
|
||||||
|
(Object text, Object ignore, struct Environment* env);
|
||||||
|
|
||||||
|
F(possessive,
|
||||||
|
"(struct Post (title body)) (def p (Post \"TI\" \"BO\")) p's title => TI"
|
||||||
|
)(Object structo, Object field, struct Environment* env);
|
||||||
|
|
||||||
|
#ifdef STANDALONE
|
||||||
|
|
||||||
|
F(print, "Prints the string representation of the given object to stdout.")
|
||||||
|
(Object p, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
|
F(pChar, "Prints the ascii character for the given number value.")
|
||||||
|
(Object c, Object i1, struct Environment* i2);
|
||||||
|
|
||||||
|
F(printEnvO, "Prints out the current scoped environment.")
|
||||||
|
(Object i1, Object i2, struct Environment* env);
|
||||||
|
|
||||||
|
F(help, "(? +) => \"(+ 1 2) => 3\"")
|
||||||
|
(Object symbol, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //PEBBLISP_PLFUNC_H
|
|
@ -57,9 +57,9 @@ check() {
|
||||||
|
|
||||||
if $VALGRIND; then
|
if $VALGRIND; then
|
||||||
echo -ne "\n $1\r "
|
echo -ne "\n $1\r "
|
||||||
local output="$($VALCOM ./pl "(loadfile \"examples/lib.pbl\") $2")"
|
local output="$($VALCOM ./pl "(loadfile \"examples/lib.pbl\") $2" | grep -v PLT)"
|
||||||
else
|
else
|
||||||
local output="$(./pl "(loadfile \"examples/lib.pbl\") $2")"
|
local output="$(./pl "(loadfile \"examples/lib.pbl\") $2" | grep -v PLT)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$output" == "$3" ]; then
|
if [ "$output" == "$3" ]; then
|
||||||
|
@ -230,7 +230,7 @@ title "Environment"
|
||||||
check "EnvStressTestEarly" '(def a 1)(def b 20)(def c "yee")(def d "yeehunnid")(def e 3) (a)' "( 1 )"
|
check "EnvStressTestEarly" '(def a 1)(def b 20)(def c "yee")(def d "yeehunnid")(def e 3) (a)' "( 1 )"
|
||||||
check "EnvStressTestLate" "(def a 1)(def b 2)(def c 3)(def d 4)(def e 5)(def g 6)(def n 40) n" "40"
|
check "EnvStressTestLate" "(def a 1)(def b 2)(def c 3)(def d 4)(def e 5)(def g 6)(def n 40) n" "40"
|
||||||
hard_test_string="(def n 1000)"
|
hard_test_string="(def n 1000)"
|
||||||
for c in {0..200}; do hard_test_string="(def a$c 1)$hard_test_string"; done
|
for c in {0..50}; do hard_test_string="(def a$c 1)$hard_test_string"; done
|
||||||
check "HardEnvStressTest" "$hard_test_string n" "1000"
|
check "HardEnvStressTest" "$hard_test_string n" "1000"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
46
src/web.c
46
src/web.c
|
@ -13,7 +13,6 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "web.h"
|
#include "web.h"
|
||||||
#include "pebblisp.h"
|
|
||||||
#include "tokens.h"
|
#include "tokens.h"
|
||||||
|
|
||||||
#ifdef _MHD_FLAGS_ENUM
|
#ifdef _MHD_FLAGS_ENUM
|
||||||
|
@ -141,3 +140,48 @@ int start(int port)
|
||||||
// MHD_stop_daemon(daemon);
|
// MHD_stop_daemon(daemon);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object print(Object p, Object ignore, struct Environment* env)
|
||||||
|
{
|
||||||
|
Object c = cloneObject(p);
|
||||||
|
Object e = eval(&c, env);
|
||||||
|
_printObj(&e, 0);
|
||||||
|
cleanObject(&c);
|
||||||
|
cleanObject(&e);
|
||||||
|
return numberObject(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addRouteO(Object path, Object textFunc, struct Environment* env, enum RouteType type)
|
||||||
|
{
|
||||||
|
char* p = malloc(sizeof(char) * strlen(path.string) + 1);
|
||||||
|
strcpy(p, path.string);
|
||||||
|
|
||||||
|
struct Route r;
|
||||||
|
r.path = p;
|
||||||
|
r.routeAction = cloneObject(textFunc);
|
||||||
|
r.env = env;
|
||||||
|
r.type = type;
|
||||||
|
env->refs += 1;
|
||||||
|
addRoute(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object addGetRoute(Object path, Object textFunc, struct Environment* env)
|
||||||
|
{
|
||||||
|
addRouteO(path, textFunc, env, GET);
|
||||||
|
return numberObject(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object addPostRoute(Object path, Object textFunc, struct Environment* env)
|
||||||
|
{
|
||||||
|
addRouteO(path, textFunc, env, POST);
|
||||||
|
return numberObject(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object startServer(Object portObject, Object ignored, struct Environment* env)
|
||||||
|
{
|
||||||
|
int port = 8888;
|
||||||
|
if (portObject.type == TYPE_NUMBER) {
|
||||||
|
port = portObject.number;
|
||||||
|
}
|
||||||
|
return numberObject(start(port));
|
||||||
|
}
|
23
src/web.h
23
src/web.h
|
@ -1,4 +1,4 @@
|
||||||
#include "object.h"
|
#include "pebblisp.h"
|
||||||
|
|
||||||
enum RouteType {
|
enum RouteType {
|
||||||
GET,
|
GET,
|
||||||
|
@ -15,3 +15,24 @@ struct Route {
|
||||||
int addRoute(struct Route route);
|
int addRoute(struct Route route);
|
||||||
|
|
||||||
int start(int port);
|
int start(int port);
|
||||||
|
|
||||||
|
F(startServer,
|
||||||
|
"(serve) => 0\n"
|
||||||
|
"Starts a simple web server with routes that have been added with (get) and (post).\n"
|
||||||
|
"Note: This is a non-blocking call. It is recommended to wait for user input before exiting.\n"
|
||||||
|
" A simple way would be to use (inp) immediately after the (serve) call."
|
||||||
|
)(Object path, Object textFunc, struct Environment* env);
|
||||||
|
|
||||||
|
F(addGetRoute,
|
||||||
|
"Note: Implementation bugs currently prevent using an inline lambda.\n"
|
||||||
|
" (def homepage (fn () (\"Hello, world!\")));(get \"/\" homepage)\n"
|
||||||
|
" (def queryPage (fn (req) (req's queryParams)));(get \"/x\" queryPage)\n"
|
||||||
|
" (serve)\n"
|
||||||
|
)(Object path, Object textFunc, struct Environment* env);
|
||||||
|
|
||||||
|
F(addPostRoute,
|
||||||
|
"Note: Implementation bugs currently prevent using an inline lambda.\n"
|
||||||
|
" (def homepage (fn () (\"Hello, world!\")));(post \"/\" homepage)\n"
|
||||||
|
" (serve)\n"
|
||||||
|
)(Object path, Object textFunc, struct Environment* env);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue