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
|
libs = -lreadline -lmicrohttpd
|
||||||
exe = pl
|
exe = pl
|
||||||
|
|
||||||
|
|
11
src/env.c
11
src/env.c
|
@ -215,6 +215,17 @@ struct symFunc {
|
||||||
Object (* func)(Object, Object, struct Environment*);
|
Object (* func)(Object, Object, struct Environment*);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Environment* _global;
|
||||||
|
struct Environment* global()
|
||||||
|
{
|
||||||
|
return _global;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setGlobal(struct Environment *env)
|
||||||
|
{
|
||||||
|
_global = env;
|
||||||
|
}
|
||||||
|
|
||||||
struct Environment defaultEnv()
|
struct Environment defaultEnv()
|
||||||
{
|
{
|
||||||
char** strings = calloc(sizeof(char*), MAX_ENV_ELM);
|
char** strings = calloc(sizeof(char*), MAX_ENV_ELM);
|
||||||
|
|
|
@ -16,7 +16,9 @@ struct Environment {
|
||||||
|
|
||||||
int refs;
|
int refs;
|
||||||
};
|
};
|
||||||
struct Environment* global;
|
|
||||||
|
struct Environment* global();
|
||||||
|
void setGlobal(struct Environment *env);
|
||||||
|
|
||||||
Object fetchFromEnvironment(const char* name, struct Environment* env);
|
Object fetchFromEnvironment(const char* name, struct Environment* env);
|
||||||
|
|
||||||
|
|
|
@ -3,24 +3,60 @@
|
||||||
(struct post
|
(struct post
|
||||||
(title body))
|
(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 html (fn (text) (cat "<html>" text "</html>")))
|
||||||
|
(def head (fn (text) (cat "<head>" text "</head>")))
|
||||||
(def body (fn (text) (cat "<body>" text "</body>")))
|
(def body (fn (text) (cat "<body>" text "</body>")))
|
||||||
|
(def link (fn (text) (cat "<link " text ">")))
|
||||||
(def h1 (fn (text) (cat "<h1>" text "</h1>
|
(def h1 (fn (text) (cat "<h1>" text "</h1>
|
||||||
")))
|
")))
|
||||||
(def h2 (fn (text) (cat "<h2>" text "</h2>")))
|
(def h2 (fn (text) (cat "<h2>" text "</h2>")))
|
||||||
(def p (fn (text) (cat "<p>" text "</p>
|
(def p (fn (text) (cat "<p>" text "</p>
|
||||||
")))
|
")))
|
||||||
|
|
||||||
(def htmlize (fn (po)
|
|
||||||
(cat
|
(def htmlize (fn (po) (cat
|
||||||
(h2 po's title)
|
(h2 po's title)
|
||||||
(p po's body))))
|
(p po's body))))
|
||||||
|
|
||||||
(def p1 (post "Hey" "This is a post"))
|
(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")
|
(h1 "This is Sage's Blog")
|
||||||
(htmlize p1)
|
(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[0] = '{';
|
||||||
dest[1] = '\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, " ");
|
||||||
strcat(dest, global->structDefs[so->definition].names[i]);
|
strcat(dest, global()->structDefs[so->definition].names[i]);
|
||||||
strcat(dest, ": ");
|
strcat(dest, ": ");
|
||||||
int isString = so->fields[i].type == TYPE_STRING;
|
int isString = so->fields[i].type == TYPE_STRING;
|
||||||
if (isString) {
|
if (isString) {
|
||||||
|
@ -309,6 +309,11 @@ void stringStruct(char* dest, const Object* obj)
|
||||||
* @param dest The string to copy the list text into
|
* @param dest The string to copy the list text into
|
||||||
* @param obj The list Object to make a string from
|
* @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)
|
char* stringNObj(char* dest, const Object* obj, const size_t len)
|
||||||
{
|
{
|
||||||
if (!dest || !obj) {
|
if (!dest || !obj) {
|
||||||
|
@ -317,16 +322,16 @@ char* stringNObj(char* dest, const Object* obj, const size_t len)
|
||||||
|
|
||||||
switch (obj->type) {
|
switch (obj->type) {
|
||||||
case TYPE_NUMBER:
|
case TYPE_NUMBER:
|
||||||
snprintf(dest, len, "%d", obj->number);
|
stringf(dest, len, "%d", obj->number);
|
||||||
break;
|
break;
|
||||||
case TYPE_BOOL:
|
case TYPE_BOOL:
|
||||||
snprintf(dest, len, "%s", obj->number ? "T" : "F");
|
stringf(dest, len, "%s", obj->number ? "T" : "F");
|
||||||
break;
|
break;
|
||||||
case TYPE_STRING:
|
case TYPE_STRING:
|
||||||
snprintf(dest, len, "%s", obj->string);
|
stringf(dest, len, "%s", obj->string);
|
||||||
break;
|
break;
|
||||||
case TYPE_SYMBOL:
|
case TYPE_SYMBOL:
|
||||||
snprintf(dest, len, "`%s`", obj->string);
|
stringf(dest, len, "`%s`", obj->string);
|
||||||
break;
|
break;
|
||||||
case TYPE_STRUCT:
|
case TYPE_STRUCT:
|
||||||
//snprintf(dest, len, "{%s}", obj->structObject->definition->names[0]);
|
//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: {
|
case TYPE_ERROR: {
|
||||||
int code = getErrorCode(*obj);
|
int code = getErrorCode(*obj);
|
||||||
#ifdef SIMPLE_ERRORS
|
#ifdef SIMPLE_ERRORS
|
||||||
snprintf(dest, len, "E[%d]", (int)code);
|
stringf(dest, len, "E[%d]", (int)code);
|
||||||
#else
|
#else
|
||||||
if (obj->error->context && obj->error->context[0] != '\0') {
|
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);
|
obj->error->context);
|
||||||
} else {
|
} else {
|
||||||
snprintf(dest, len, "%s", errorText[code]);
|
stringf(dest, len, "%s", errorText[code]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TYPE_FUNC:
|
case TYPE_FUNC:
|
||||||
case TYPE_LAMBDA:
|
case TYPE_LAMBDA:
|
||||||
snprintf(dest, len, "X%d", obj->number);
|
stringf(dest, len, "X%d", obj->number);
|
||||||
break;
|
break;
|
||||||
case TYPE_OTHER:
|
case TYPE_OTHER:
|
||||||
snprintf(dest, len, "%p", obj->other->data);
|
stringf(dest, len, "%p", obj->other->data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValidType(*obj)) {
|
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;
|
return dest;
|
||||||
|
@ -506,7 +511,7 @@ void cleanObject(Object* target)
|
||||||
free(target->lambda);
|
free(target->lambda);
|
||||||
break;
|
break;
|
||||||
case TYPE_STRUCT:
|
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]);
|
cleanObject(&target->structObject->fields[i]);
|
||||||
}
|
}
|
||||||
free(target->structObject->fields);
|
free(target->structObject->fields);
|
||||||
|
@ -657,7 +662,7 @@ inline Object structObject(int definition)
|
||||||
Object structo = newObject(TYPE_STRUCT);
|
Object structo = newObject(TYPE_STRUCT);
|
||||||
structo.structObject = malloc(sizeof(struct StructObject));
|
structo.structObject = malloc(sizeof(struct StructObject));
|
||||||
structo.structObject->definition = definition;
|
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;
|
return structo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -761,7 +766,7 @@ Object cloneStruct(const Object src)
|
||||||
{
|
{
|
||||||
Object structo = structObject(src.structObject->definition);
|
Object structo = structObject(src.structObject->definition);
|
||||||
struct StructObject* so = structo.structObject;
|
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]);
|
so->fields[i] = cloneObject(src.structObject->fields[i]);
|
||||||
}
|
}
|
||||||
return structo;
|
return structo;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#pragma ide diagnostic ignored "misc-no-recursion"
|
|
||||||
|
|
||||||
#include "pebblisp.h"
|
#include "pebblisp.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -411,12 +409,12 @@ Object eval(const Object* obj, struct Environment* env)
|
||||||
return errorObject(BAD_TYPE);
|
return errorObject(BAD_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define CAT_MAX 1024
|
||||||
Object catObjects(const Object obj1, const Object obj2, struct Environment* env)
|
Object catObjects(const Object obj1, const Object obj2, struct Environment* env)
|
||||||
{
|
{
|
||||||
Object evalObj1 = eval(&obj1, env);
|
Object evalObj1 = eval(&obj1, env);
|
||||||
Object evalObj2 = eval(&obj2, env);
|
Object evalObj2 = eval(&obj2, env);
|
||||||
|
|
||||||
#define CAT_MAX 200
|
|
||||||
char str1[CAT_MAX] = "";
|
char str1[CAT_MAX] = "";
|
||||||
char str2[CAT_MAX] = "";
|
char str2[CAT_MAX] = "";
|
||||||
stringObj(str1, &evalObj1);
|
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)
|
Object addRouteO(Object path, Object textFunc, struct Environment* env)
|
||||||
{
|
{
|
||||||
// const char* p = malloc(sizeof(char) * strlen(path.string) + 1);
|
char* p = malloc(sizeof(char) * strlen(path.string) + 1);
|
||||||
// strcpy(p, path.string);
|
strcpy(p, path.string);
|
||||||
// //const char* t = malloc(sizeof(char) * strlen(textFunc.string) + 1);
|
//const char* t = malloc(sizeof(char) * strlen(textFunc.string) + 1);
|
||||||
// //strcpy(t, textFunc.string);
|
//strcpy(t, textFunc.string);
|
||||||
|
|
||||||
// struct Route r;
|
struct Route r;
|
||||||
// r.path = p;
|
r.path = p;
|
||||||
// r.routeAction = cloneObject(textFunc);
|
r.routeAction = cloneObject(textFunc);
|
||||||
// r.env = env;
|
r.env = env;
|
||||||
// env->refs += 1;
|
env->refs += 1;
|
||||||
// //r.text = t;
|
//r.text = t;
|
||||||
// addRoute(r);
|
addRoute(r);
|
||||||
|
|
||||||
return numberObject(1);
|
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)
|
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);
|
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++) {
|
for (int i = 0; i < structDef.fieldCount; i++) {
|
||||||
if (strcmp(field.string, structDef.names[i]) == 0) {
|
if (strcmp(field.string, structDef.names[i]) == 0) {
|
||||||
return cloneObject(structo.structObject->fields[i]);
|
return cloneObject(structo.structObject->fields[i]);
|
||||||
|
@ -987,6 +989,7 @@ int _readFile(FILE* input, struct Environment* env)
|
||||||
strncat(page, line, strlen(line) - 1);
|
strncat(page, line, strlen(line) - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
int isQuote = 0;
|
||||||
while (fgets(line, LINE_MAX, input)) {
|
while (fgets(line, LINE_MAX, input)) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < LINE_MAX; i++) {
|
for (i = 0; i < LINE_MAX; i++) {
|
||||||
|
@ -996,7 +999,9 @@ int _readFile(FILE* input, struct Environment* env)
|
||||||
} else {
|
} else {
|
||||||
int j = 0;
|
int j = 0;
|
||||||
for (j = i; j < LINE_MAX; j++) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1068,7 +1073,7 @@ void loadArgsIntoEnv(int argc, const char* argv[], struct Environment* env)
|
||||||
int main(int argc, const char* argv[])
|
int main(int argc, const char* argv[])
|
||||||
{
|
{
|
||||||
struct Environment env = defaultEnv();
|
struct Environment env = defaultEnv();
|
||||||
global = &env;
|
setGlobal(&env);
|
||||||
readFile(SCRIPTDIR "/lib.pbl", &env);
|
readFile(SCRIPTDIR "/lib.pbl", &env);
|
||||||
if (argc >= 2) {
|
if (argc >= 2) {
|
||||||
FILE* file = fopen(argv[1], "r");
|
FILE* file = fopen(argv[1], "r");
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
struct Slice {
|
struct Slice {
|
||||||
const char* text;
|
const char* text;
|
||||||
char length;
|
unsigned char length;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct Result {
|
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