From 3bf65577c0d024bfe8686ffdac4481e98aff3ccd Mon Sep 17 00:00:00 2001 From: Sage Vaillancourt Date: Sun, 20 Mar 2022 03:50:43 -0400 Subject: [PATCH] Add help text for STANDALONE. Pull most user functions into the new plfunc. Could integrate some tests directly into pl with --run-tests --- src/Makefile | 4 +- src/env.c | 77 +++++++--- src/env.h | 2 + src/object.c | 9 -- src/object.h | 3 - src/pebblisp.c | 408 ++----------------------------------------------- src/pebblisp.h | 76 ++------- src/plfunc.c | 350 ++++++++++++++++++++++++++++++++++++++++++ src/plfunc.h | 112 ++++++++++++++ src/tests.sh | 6 +- src/web.c | 46 +++++- src/web.h | 23 ++- 12 files changed, 615 insertions(+), 501 deletions(-) create mode 100644 src/plfunc.c create mode 100644 src/plfunc.h diff --git a/src/Makefile b/src/Makefile index a4d7681..3c8d26d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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 exe = pl @@ -9,7 +9,7 @@ SCRIPTDIR ?= /usr/local/share/pebblisp mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) 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: $(GCC_COM) $(files) $(libs) && echo && ./tests.sh diff --git a/src/env.c b/src/env.c index ecb3b2b..150e97a 100644 --- a/src/env.c +++ b/src/env.c @@ -5,6 +5,8 @@ #include #include "pebblisp.h" +#include "web.h" +#include "plfunc.h" Object fetchFromEnvironment(const char* name, struct Environment* env) { @@ -223,16 +225,48 @@ struct symFunc { Object (* func)(Object, Object, struct Environment*); }; -struct Environment* _global; +struct Environment* globalEnv; struct Environment* global() { - return _global; + return globalEnv; } 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() @@ -263,25 +297,30 @@ struct Environment defaultEnv() {"<", <h}, {"&", &and}, {"|", &or}, - {"cat", &catObjects}, - {"fil", &filter}, - {"len", &len}, - {"ap", &append}, - {"pre", &prepend}, - {"reduce", &reduce}, - {"at", &at}, - {"rest", &rest}, - {"chat", &charAt}, + pf("cat", catObjects), + pf("fil", filter), + pf("len", len), + pf("ap", append), + pf("pre", prepend), + pf("reduce", reduce), + pf("at", at), + pf("rest", rest), + pf("chat", charAt), #ifndef LOW_MEM - {"rev", &reverse}, + pf("rev", reverse), #endif - {"isnum", &isNum}, - {"islist", &isList}, - {"isstr", &isString}, + pf("isnum", isNum), + pf("islist", isList), + pf("isstr", isString), {"iserr", &isErr}, - {"char", &charVal}, + pf("char", charVal), {"eval", &parseEvalO}, {"poss", &possessive}, +#ifdef WEBSERVER + pf("get", addGetRoute), + pf("post", addPostRoute), + pf("serve", startServer), +#endif #ifdef STANDALONE {"prn", &print}, {"pch", &pChar}, @@ -289,9 +328,7 @@ struct Environment defaultEnv() //{"sys", &systemCall}, {"loadfile", &loadFile}, {"inp", &takeInput}, - {"get", &addGetRoute}, - {"post", &addPostRoute}, - {"serve", &startServer} + pf("?", help) #endif }; diff --git a/src/env.h b/src/env.h index 97d2b99..eccef68 100644 --- a/src/env.h +++ b/src/env.h @@ -42,4 +42,6 @@ struct StructDef getStructDef(struct Environment* env, const char* name); int getStructIndex(struct Environment* env, const char* name); +const char* getHelp(const char* symbol); + #endif diff --git a/src/object.c b/src/object.c index 37c2df1..c57be72 100644 --- a/src/object.c +++ b/src/object.c @@ -45,15 +45,6 @@ int listLength(const Object* listObj) 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 * @param listObj The list to fetch an Object from diff --git a/src/object.h b/src/object.h index 4b4bbfb..5640f10 100644 --- a/src/object.h +++ b/src/object.h @@ -244,9 +244,6 @@ struct Error noError(); Object constructLambda(const Object* params, const Object* body, struct Environment* env); -// Object version of listLength() -Object len(Object obj1, Object, struct Environment*); - int getAllocations(); size_t getBytes(); diff --git a/src/pebblisp.c b/src/pebblisp.c index 1de7a6e..9eb9613 100644 --- a/src/pebblisp.c +++ b/src/pebblisp.c @@ -1,6 +1,5 @@ #include "pebblisp.h" -#include #include #include @@ -8,6 +7,7 @@ #include #include "tokens.h" +#include "plfunc.h" #ifdef STANDALONE @@ -414,371 +414,6 @@ Object eval(const Object* obj, struct Environment* env) 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) { if (!dest || !src) { @@ -925,9 +560,9 @@ Result parseAtom(struct Slice* s) if (c != '0' || s->length == 1) { return (Result) {parseDecimal(s), s}; #ifndef LOW_MEM - } else if (c == '0' && s->text[1] == 'x') { + } else if (s->text[1] == 'x') { return (Result) {parseHex(s), s}; - } else if (c == '0' && s->text[1] == 'b') { + } else if (s->text[1] == 'b') { return (Result) {parseBin(s), s}; #endif } else { @@ -1021,6 +656,16 @@ Object parseEval(const char* input, struct Environment* env) #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) { Object r = numberObject(0); @@ -1063,33 +708,6 @@ int _readFile(FILE* input, struct Environment* env) 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) { char* buf; diff --git a/src/pebblisp.h b/src/pebblisp.h index 1a95238..906feeb 100644 --- a/src/pebblisp.h +++ b/src/pebblisp.h @@ -1,9 +1,13 @@ #ifndef PEBBLISP_H #define PEBBLISP_H +#include + #include "env.h" #include "object.h" +#define F(_name, _docs) static const char * const _name ## Doc = _docs; Object _name + struct Slice { const char* text; unsigned char length; @@ -35,67 +39,7 @@ Object listEvalLambda(Object* lambda, const Object* remaining, void debugSlice(struct Slice* s); -#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 - -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); +Object simpleFuncEval(Object func, Object arg1, Object arg2, struct Environment* env); #ifdef STANDALONE @@ -105,12 +49,10 @@ Object systemCall(Object call, Object _, struct Environment* i3); 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 \ No newline at end of file diff --git a/src/plfunc.c b/src/plfunc.c new file mode 100644 index 0000000..deecedd --- /dev/null +++ b/src/plfunc.c @@ -0,0 +1,350 @@ +#include +#include + +#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 \ No newline at end of file diff --git a/src/plfunc.h b/src/plfunc.h new file mode 100644 index 0000000..3023949 --- /dev/null +++ b/src/plfunc.h @@ -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 diff --git a/src/tests.sh b/src/tests.sh index 1463139..f7fdabd 100755 --- a/src/tests.sh +++ b/src/tests.sh @@ -57,9 +57,9 @@ check() { if $VALGRIND; then 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 - local output="$(./pl "(loadfile \"examples/lib.pbl\") $2")" + local output="$(./pl "(loadfile \"examples/lib.pbl\") $2" | grep -v PLT)" fi 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 "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)" -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" echo "" diff --git a/src/web.c b/src/web.c index fb86158..e525bb4 100644 --- a/src/web.c +++ b/src/web.c @@ -13,7 +13,6 @@ #include #include "web.h" -#include "pebblisp.h" #include "tokens.h" #ifdef _MHD_FLAGS_ENUM @@ -141,3 +140,48 @@ int start(int port) // MHD_stop_daemon(daemon); 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)); +} \ No newline at end of file diff --git a/src/web.h b/src/web.h index ab778eb..14619bb 100644 --- a/src/web.h +++ b/src/web.h @@ -1,4 +1,4 @@ -#include "object.h" +#include "pebblisp.h" enum RouteType { GET, @@ -15,3 +15,24 @@ struct Route { int addRoute(struct Route route); 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); +