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:
parent
b390e272a7
commit
f37969418d
|
@ -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
|
||||
|
||||
|
|
11
src/env.c
11
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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
(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
|
||||
(def homepage (fn () (html (cat
|
||||
(head (link "rel='stylesheet' href='styles.css'"))
|
||||
(body (cat
|
||||
(h1 "This is Sage's Blog")
|
||||
(htmlize p1)
|
||||
(htmlize p2)
|
||||
))))
|
||||
(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)
|
||||
|
|
35
src/object.c
35
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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
struct Slice {
|
||||
const char* text;
|
||||
char length;
|
||||
unsigned char length;
|
||||
};
|
||||
|
||||
typedef struct Result {
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue