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.
This commit is contained in:
Sage Vaillancourt 2022-03-15 21:43:53 -04:00
parent b390e272a7
commit f37969418d
9 changed files with 203 additions and 47 deletions

View File

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

View File

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

View File

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

View File

@ -3,24 +3,60 @@
(struct post
(title body))
;(def element (fn (type) (fn (text) (cat "<" type ">" text "</" type ">"))))
;(def link (element "link"))
;(prnl (link "howdy"))
(def html (fn (text) (cat "<html>" text "</html>")))
(def head (fn (text) (cat "<head>" text "</head>")))
(def body (fn (text) (cat "<body>" text "</body>")))
(def link (fn (text) (cat "<link " text ">")))
(def h1 (fn (text) (cat "<h1>" text "</h1>
")))
(def h2 (fn (text) (cat "<h2>" text "</h2>")))
(def p (fn (text) (cat "<p>" text "</p>
")))
(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)

View File

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

View File

@ -1,5 +1,3 @@
#pragma ide diagnostic ignored "misc-no-recursion"
#include "pebblisp.h"
#include <stdio.h>
@ -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");

View File

@ -6,7 +6,7 @@
struct Slice {
const char* text;
char length;
unsigned char length;
};
typedef struct Result {

85
src/web.c Normal file
View File

@ -0,0 +1,85 @@
/* Feel free to use this example code in any way
you see fit (Public Domain) */
#include <sys/types.h>
//#ifndef _WIN32
#include <sys/select.h>
#include <sys/socket.h>
//#else
//#include <winsock2.h>
//#endif
#include <string.h>
#include <microhttpd.h>
#include <stdio.h>
#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 = "<html><body><h1>404, Dumbass.</h1></body></html>";
}
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;
}

12
src/web.h Normal file
View File

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