From f37969418df3aba46669fe9f41f3fc3996c9b395 Mon Sep 17 00:00:00 2001 From: Sage Vaillancourt Date: Tue, 15 Mar 2022 21:43:53 -0400 Subject: [PATCH] Add simple web server implementation. Adjust global Environment implementation. Example `webby.pl` is now a simple server program. Only limit stringNObj outside of STANDALONE implementations. Larger catObjects sizes. Parsing files ignores semicolons inside of quotes. --- src/Makefile | 2 +- src/env.c | 11 ++++++ src/env.h | 4 +- src/examples/webby.pl | 56 +++++++++++++++++++++++----- src/object.c | 35 ++++++++++-------- src/pebblisp.c | 43 ++++++++++++---------- src/pebblisp.h | 2 +- src/web.c | 85 +++++++++++++++++++++++++++++++++++++++++++ src/web.h | 12 ++++++ 9 files changed, 203 insertions(+), 47 deletions(-) create mode 100644 src/web.c create mode 100644 src/web.h diff --git a/src/Makefile b/src/Makefile index d0f4a73..a4d7681 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,4 +1,4 @@ -files = pebblisp.c tokens.c object.c env.c +files = pebblisp.c tokens.c object.c env.c web.c libs = -lreadline -lmicrohttpd exe = pl diff --git a/src/env.c b/src/env.c index b323792..f6ae16d 100644 --- a/src/env.c +++ b/src/env.c @@ -215,6 +215,17 @@ struct symFunc { Object (* func)(Object, Object, struct Environment*); }; +struct Environment* _global; +struct Environment* global() +{ + return _global; +} + +void setGlobal(struct Environment *env) +{ + _global = env; +} + struct Environment defaultEnv() { char** strings = calloc(sizeof(char*), MAX_ENV_ELM); diff --git a/src/env.h b/src/env.h index a431d63..3a43718 100644 --- a/src/env.h +++ b/src/env.h @@ -16,7 +16,9 @@ struct Environment { int refs; }; -struct Environment* global; + +struct Environment* global(); +void setGlobal(struct Environment *env); Object fetchFromEnvironment(const char* name, struct Environment* env); diff --git a/src/examples/webby.pl b/src/examples/webby.pl index cf174c2..36f2f83 100755 --- a/src/examples/webby.pl +++ b/src/examples/webby.pl @@ -3,24 +3,60 @@ (struct post (title body)) +;(def element (fn (type) (fn (text) (cat "<" type ">" text "")))) +;(def link (element "link")) +;(prnl (link "howdy")) + (def html (fn (text) (cat "" text ""))) +(def head (fn (text) (cat "" text ""))) (def body (fn (text) (cat "" text ""))) +(def link (fn (text) (cat ""))) (def h1 (fn (text) (cat "

" text "

"))) (def h2 (fn (text) (cat "

" text "

"))) (def p (fn (text) (cat "

" text "

"))) -(def htmlize (fn (po) - (cat - (h2 po's title) - (p po's body)))) + +(def htmlize (fn (po) (cat + (h2 po's title) + (p po's body)))) (def p1 (post "Hey" "This is a post")) -(def p2 (post "This" "Is ALSO a post")) +(def p2 (post "This" +"Is ALSO a post. And frankly, what a great post! It's so long and flowing, +and the interpreter won't even instantly crash over it! It's truly astounding +stuff, when you think about it." +)) -(prnl (html (body (cat - (h1 "This is Sage's Blog") - (htmlize p1) - (htmlize p2) - )))) +(def homepage (fn () (html (cat + (head (link "rel='stylesheet' href='styles.css'")) + (body (cat + (h1 "This is Sage's Blog") + (htmlize p1) + (htmlize p2)))))) ) + +(addroute "/" homepage) + +(addroute "/styles.css" (fn () ( +"html { + background-color: #ddddff; +} +body { + width: 40%; + margin-left: auto; + margin-right: auto; + font-size: 200%; + font-family: sans; +} +" +))) + +(def PORT 9090) +(serve PORT) +(prnl (cat "Hosting server on " PORT ". Press enter to exit.")) +(def repl (fn () ( + (eval (inp)) + (repl) + ))) +(repl) diff --git a/src/object.c b/src/object.c index 4ef693e..8facbe6 100644 --- a/src/object.c +++ b/src/object.c @@ -267,9 +267,9 @@ void stringStruct(char* dest, const Object* obj) dest[0] = '{'; dest[1] = '\0'; - for (int i = 0; i < global->structDefs[so->definition].fieldCount; i++) { + for (int i = 0; i < global()->structDefs[so->definition].fieldCount; i++) { strcat(dest, " "); - strcat(dest, global->structDefs[so->definition].names[i]); + strcat(dest, global()->structDefs[so->definition].names[i]); strcat(dest, ": "); int isString = so->fields[i].type == TYPE_STRING; if (isString) { @@ -309,6 +309,11 @@ void stringStruct(char* dest, const Object* obj) * @param dest The string to copy the list text into * @param obj The list Object to make a string from */ +#ifdef STANDALONE +#define stringf(_dest, _len, _format, args...) sprintf(_dest, _format, ## args) +#else +#define stringf(_dest, _len, _format, args...) snprintf(_dest, _len, _format, ## args) +#endif char* stringNObj(char* dest, const Object* obj, const size_t len) { if (!dest || !obj) { @@ -317,16 +322,16 @@ char* stringNObj(char* dest, const Object* obj, const size_t len) switch (obj->type) { case TYPE_NUMBER: - snprintf(dest, len, "%d", obj->number); + stringf(dest, len, "%d", obj->number); break; case TYPE_BOOL: - snprintf(dest, len, "%s", obj->number ? "T" : "F"); + stringf(dest, len, "%s", obj->number ? "T" : "F"); break; case TYPE_STRING: - snprintf(dest, len, "%s", obj->string); + stringf(dest, len, "%s", obj->string); break; case TYPE_SYMBOL: - snprintf(dest, len, "`%s`", obj->string); + stringf(dest, len, "`%s`", obj->string); break; case TYPE_STRUCT: //snprintf(dest, len, "{%s}", obj->structObject->definition->names[0]); @@ -339,28 +344,28 @@ char* stringNObj(char* dest, const Object* obj, const size_t len) case TYPE_ERROR: { int code = getErrorCode(*obj); #ifdef SIMPLE_ERRORS - snprintf(dest, len, "E[%d]", (int)code); + stringf(dest, len, "E[%d]", (int)code); #else if (obj->error->context && obj->error->context[0] != '\0') { - snprintf(dest, len, "'%s': %s", errorText[code], + stringf(dest, len, "'%s': %s", errorText[code], obj->error->context); } else { - snprintf(dest, len, "%s", errorText[code]); + stringf(dest, len, "%s", errorText[code]); } #endif break; } case TYPE_FUNC: case TYPE_LAMBDA: - snprintf(dest, len, "X%d", obj->number); + stringf(dest, len, "X%d", obj->number); break; case TYPE_OTHER: - snprintf(dest, len, "%p", obj->other->data); + stringf(dest, len, "%p", obj->other->data); break; } if (!isValidType(*obj)) { - snprintf(dest, len, "BAD_TYPE(%d) X%d", obj->type, obj->number); + stringf(dest, len, "BAD_TYPE(%d) X%d", obj->type, obj->number); } return dest; @@ -506,7 +511,7 @@ void cleanObject(Object* target) free(target->lambda); break; case TYPE_STRUCT: - for (int i = 0; i < global->structDefs[target->structObject->definition].fieldCount; i++) { + for (int i = 0; i < global()->structDefs[target->structObject->definition].fieldCount; i++) { cleanObject(&target->structObject->fields[i]); } free(target->structObject->fields); @@ -657,7 +662,7 @@ inline Object structObject(int definition) Object structo = newObject(TYPE_STRUCT); structo.structObject = malloc(sizeof(struct StructObject)); structo.structObject->definition = definition; - structo.structObject->fields = malloc(sizeof(Object) * global->structDefs[definition].fieldCount); + structo.structObject->fields = malloc(sizeof(Object) * global()->structDefs[definition].fieldCount); return structo; } @@ -761,7 +766,7 @@ Object cloneStruct(const Object src) { Object structo = structObject(src.structObject->definition); struct StructObject* so = structo.structObject; - for (int i = 0; i < global->structDefs[so->definition].fieldCount; i++) { + for (int i = 0; i < global()->structDefs[so->definition].fieldCount; i++) { so->fields[i] = cloneObject(src.structObject->fields[i]); } return structo; diff --git a/src/pebblisp.c b/src/pebblisp.c index a22743f..10e14e6 100644 --- a/src/pebblisp.c +++ b/src/pebblisp.c @@ -1,5 +1,3 @@ -#pragma ide diagnostic ignored "misc-no-recursion" - #include "pebblisp.h" #include @@ -411,12 +409,12 @@ Object eval(const Object* obj, struct Environment* env) return errorObject(BAD_TYPE); } +#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); -#define CAT_MAX 200 char str1[CAT_MAX] = ""; char str2[CAT_MAX] = ""; stringObj(str1, &evalObj1); @@ -668,25 +666,29 @@ Object print(Object p, Object ignore, struct Environment* env) Object addRouteO(Object path, Object textFunc, struct Environment* env) { - // const char* p = malloc(sizeof(char) * strlen(path.string) + 1); - // strcpy(p, path.string); - // //const char* t = malloc(sizeof(char) * strlen(textFunc.string) + 1); - // //strcpy(t, textFunc.string); + char* p = malloc(sizeof(char) * strlen(path.string) + 1); + strcpy(p, path.string); + //const char* t = malloc(sizeof(char) * strlen(textFunc.string) + 1); + //strcpy(t, textFunc.string); - // struct Route r; - // r.path = p; - // r.routeAction = cloneObject(textFunc); - // r.env = env; - // env->refs += 1; - // //r.text = t; - // addRoute(r); + struct Route r; + r.path = p; + r.routeAction = cloneObject(textFunc); + r.env = env; + env->refs += 1; + //r.text = t; + addRoute(r); return numberObject(1); } -Object startServer(Object path, Object textFunc, struct Environment* env) +Object startServer(Object portObject, Object ignored, struct Environment* env) { - return numberObject(1/*start(8888)*/); + int port = 8888; + if (portObject.type == TYPE_NUMBER) { + port = portObject.number; + } + return numberObject(start(port)); } Object pChar(Object c, Object i1, struct Environment* i2) @@ -776,7 +778,7 @@ Object possessive(Object structo, Object field, struct Environment* env) return errorObject(NULL_PARSE); } - struct StructDef structDef = global->structDefs[structo.structObject->definition]; + struct StructDef structDef = global()->structDefs[structo.structObject->definition]; for (int i = 0; i < structDef.fieldCount; i++) { if (strcmp(field.string, structDef.names[i]) == 0) { return cloneObject(structo.structObject->fields[i]); @@ -987,6 +989,7 @@ int _readFile(FILE* input, struct Environment* env) strncat(page, line, strlen(line) - 1); } } + int isQuote = 0; while (fgets(line, LINE_MAX, input)) { int i; for (i = 0; i < LINE_MAX; i++) { @@ -996,7 +999,9 @@ int _readFile(FILE* input, struct Environment* env) } else { int j = 0; for (j = i; j < LINE_MAX; j++) { - if (line[j] == ';' || line[j] == '\0') { + if (line[j] == '"') { + isQuote = !isQuote; + } else if (line[j] == '\0' || (!isQuote && line[j] == ';')) { break; } } @@ -1068,7 +1073,7 @@ void loadArgsIntoEnv(int argc, const char* argv[], struct Environment* env) int main(int argc, const char* argv[]) { struct Environment env = defaultEnv(); - global = &env; + setGlobal(&env); readFile(SCRIPTDIR "/lib.pbl", &env); if (argc >= 2) { FILE* file = fopen(argv[1], "r"); diff --git a/src/pebblisp.h b/src/pebblisp.h index 52c4e26..90a2631 100644 --- a/src/pebblisp.h +++ b/src/pebblisp.h @@ -6,7 +6,7 @@ struct Slice { const char* text; - char length; + unsigned char length; }; typedef struct Result { diff --git a/src/web.c b/src/web.c new file mode 100644 index 0000000..96e5cb9 --- /dev/null +++ b/src/web.c @@ -0,0 +1,85 @@ +/* Feel free to use this example code in any way + you see fit (Public Domain) */ + +#include +//#ifndef _WIN32 +#include +#include +//#else +//#include +//#endif +#include +#include +#include + +#include "web.h" +#include "pebblisp.h" + +int routeCount = 0; +struct Route routes[10]; + +int addRoute(struct Route route) { + routes[routeCount] = route; + routeCount += 1; + return 0; +} + +static enum MHD_Result +answer_to_connection(void *cls, struct MHD_Connection *connection, + const char *url, const char *method, + const char *version, const char *upload_data, + size_t *upload_data_size, void **con_cls) +{ + const char *page = NULL; + printf("%s URL: '%s' :: ", method, url); + for (int i = 0; i < routeCount; i++) { + if (strcmp(url, routes[i].path) == 0) { + printf("route[%d]\n", i); + //page = routes[i].routeAction(url); + Object emptyList = listObject(); + nf_addToList(&emptyList, numberObject(0)); + Object route = cloneObject(routes[i].routeAction); + Object result = listEvalLambda(&route, &emptyList, routes[i].env); + page = result.string; + break; + } + } + + if (!page) { + printf("no route found!\n"); + page = "

404, Dumbass.

"; + } + + struct MHD_Response *response; + enum MHD_Result ret; + (void) cls; /* Unused. Silent compiler warning. */ + (void) url; /* Unused. Silent compiler warning. */ + (void) method; /* Unused. Silent compiler warning. */ + (void) version; /* Unused. Silent compiler warning. */ + (void) upload_data; /* Unused. Silent compiler warning. */ + (void) upload_data_size; /* Unused. Silent compiler warning. */ + (void) con_cls; /* Unused. Silent compiler warning. */ + + response = + MHD_create_response_from_buffer(strlen(page), (void *) page, + MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + + return ret; +} + +int start(int port) +{ + struct MHD_Daemon *daemon = MHD_start_daemon( + MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD, + port, NULL, NULL, &answer_to_connection, NULL, MHD_OPTION_END); + + if (NULL == daemon) + return 1; + + // (void) getchar(); + + // MHD_stop_daemon(daemon); + return 0; +} diff --git a/src/web.h b/src/web.h new file mode 100644 index 0000000..99cd8ac --- /dev/null +++ b/src/web.h @@ -0,0 +1,12 @@ +#include "object.h" + +struct Route { + const char* path; + Object routeAction; + struct Environment* env; + //const char* text; +}; + +int addRoute(struct Route route); + +int start(int port);