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:
Sage Vaillancourt 2022-03-20 03:50:43 -04:00
parent 30e8c87e66
commit 3bf65577c0
12 changed files with 615 additions and 501 deletions

View File

@ -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

View File

@ -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()
{"<", &lth}, {"<", &lth},
{"&", &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
}; };

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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

350
src/plfunc.c Normal file
View File

@ -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

112
src/plfunc.h Normal file
View File

@ -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

View File

@ -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 ""

View File

@ -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));
}

View File

@ -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);