Add simple Promise type.

A bit unstable. Almost certainly needs some locking on struct Promise fields.
This commit is contained in:
Sage Vaillancourt 2022-04-05 23:53:17 -04:00
parent fc4f0bef28
commit b54b93c7d1
8 changed files with 137 additions and 8 deletions

View File

@ -1,5 +1,5 @@
files = main.c pebblisp.c tokens.c object.c env.c web.c plfunc.c hash.c files = main.c pebblisp.c tokens.c object.c env.c web.c plfunc.c hash.c threads.c
libs = -lreadline -lmicrohttpd libs = -lreadline -lmicrohttpd -lpthread
exe = pl exe = pl
file_libs := $(files) $(libs) file_libs := $(files) $(libs)

View File

@ -6,6 +6,7 @@
#include "pebblisp.h" #include "pebblisp.h"
#include "web.h" #include "web.h"
#include "threads.h"
#include "plfunc.h" #include "plfunc.h"
struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*), const char* help, struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*), const char* help,
@ -57,6 +58,7 @@ struct symFunc {
#endif #endif
#ifndef HASHLESS_ENV // Hash-based env #ifndef HASHLESS_ENV // Hash-based env
Object* fetch(const char* name, struct Environment* env) Object* fetch(const char* name, struct Environment* env)
{ {
while (env) { while (env) {
@ -207,6 +209,8 @@ struct Environment defaultEnv()
pf(takeInput), pf(takeInput),
pf(readFileToObject), pf(readFileToObject),
pf(getEnvVar), pf(getEnvVar),
pf(async),
pf(await),
pf(help) pf(help)
#endif #endif
}; };
@ -471,6 +475,8 @@ struct Environment defaultEnvPreAllocated(struct EnvElement* elements)
pf(takeInput), pf(takeInput),
pf(readFileToObject), pf(readFileToObject),
pf(getEnvVar), pf(getEnvVar),
pf(async),
pf(await),
pf(help) pf(help)
#endif #endif
}; };
@ -554,6 +560,7 @@ void shredDictionary()
} }
#ifdef STANDALONE #ifdef STANDALONE
struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*), const char* help, struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*), const char* help,
const char* const* tests, size_t testLength) const char* const* tests, size_t testLength)
{ {
@ -570,6 +577,7 @@ struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, st
.sym = symbol, .sym = symbol,
}; };
} }
#else #else
struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*)) struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*))
{ {
@ -699,7 +707,7 @@ int runTests(int detailed)
char* result = stringObj(&o, &length); char* result = stringObj(&o, &length);
cleanObject(&o); cleanObject(&o);
int expectedLen = 0; int expectedLen = 0;
while(expected[expectedLen] && expected[expectedLen] != ';') { while (expected[expectedLen] && expected[expectedLen] != ';') {
expectedLen++; expectedLen++;
} }
if (strncmp(result, expected, expectedLen) != 0) { if (strncmp(result, expected, expectedLen) != 0) {
@ -733,6 +741,7 @@ int runTests(int detailed)
// fprintf(stderr, "TOTAL BYTES: %zu\n", getBytes()); // fprintf(stderr, "TOTAL BYTES: %zu\n", getBytes());
return failureCount; return failureCount;
} }
#endif #endif
int getStructIndex(const char* name) int getStructIndex(const char* name)

View File

@ -6,6 +6,9 @@
#include <string.h> #include <string.h>
#ifdef STANDALONE #ifdef STANDALONE
#include "threads.h"
size_t bytes = 0; size_t bytes = 0;
size_t getBytes() size_t getBytes()
@ -148,7 +151,7 @@ static const char* errorText[] = {
"NO_CLONE_SPECIFIED", "NO_CLONE_SPECIFIED",
"CAN_ONLY_EVAL_STRINGS", "CAN_ONLY_EVAL_STRINGS",
"UNEXPECTED_EOF", "UNEXPECTED_EOF",
"INDEX_PAST_END"}; "INDEX_PAST_END" };
#endif #endif
size_t inflate(struct string* s, size_t additional) size_t inflate(struct string* s, size_t additional)
@ -172,7 +175,7 @@ size_t inflate(struct string* s, size_t additional)
return s->capacity; return s->capacity;
} }
int stringNObj(struct string *s, const Object* obj); int stringNObj(struct string* s, const Object* obj);
/** /**
* Creates a string from a given list Object * Creates a string from a given list Object
@ -263,6 +266,9 @@ int stringNObj(struct string* s, const Object* obj)
case TYPE_LIST: case TYPE_LIST:
stringList(s, obj); stringList(s, obj);
break; break;
case TYPE_PROMISE:
s->cursor += sprintf(s->cursor, isDone(obj->promise) ? "<DONE>" : "<PENDING>");
break;
case TYPE_ERROR: { case TYPE_ERROR: {
int code = getErrorCode(*obj); int code = getErrorCode(*obj);
#ifdef SIMPLE_ERRORS #ifdef SIMPLE_ERRORS
@ -344,6 +350,7 @@ void printType(const Object* obj)
SIMPLE_TYPE(TYPE_FUNC); SIMPLE_TYPE(TYPE_FUNC);
SIMPLE_TYPE(TYPE_SYMBOL); SIMPLE_TYPE(TYPE_SYMBOL);
SIMPLE_TYPE(TYPE_STRING); SIMPLE_TYPE(TYPE_STRING);
SIMPLE_TYPE(TYPE_PROMISE);
SIMPLE_TYPE(TYPE_OTHER); SIMPLE_TYPE(TYPE_OTHER);
SIMPLE_TYPE(TYPE_ERROR); SIMPLE_TYPE(TYPE_ERROR);
case TYPE_LAMBDA: case TYPE_LAMBDA:
@ -426,6 +433,9 @@ void cleanObject(Object* target)
free(target->structObject->fields); free(target->structObject->fields);
free(target->structObject); free(target->structObject);
break; break;
case TYPE_PROMISE:
cleanPromise(target->promise);
break;
case TYPE_ERROR: case TYPE_ERROR:
#ifndef SIMPLE_ERRORS #ifndef SIMPLE_ERRORS
free(target->error->plContext); free(target->error->plContext);
@ -603,6 +613,7 @@ inline int isValidType(const Object test)
case TYPE_SYMBOL: case TYPE_SYMBOL:
case TYPE_LAMBDA: case TYPE_LAMBDA:
case TYPE_STRING: case TYPE_STRING:
case TYPE_PROMISE:
case TYPE_OTHER: case TYPE_OTHER:
case TYPE_ERROR: case TYPE_ERROR:
return 1; return 1;
@ -648,6 +659,8 @@ inline Object cloneObject(const Object src)
case TYPE_STRING: case TYPE_STRING:
case TYPE_SYMBOL: case TYPE_SYMBOL:
return cloneString(src); return cloneString(src);
case TYPE_PROMISE:
return clonePromise(src);
case TYPE_ERROR: case TYPE_ERROR:
return errorWithContext(getErrorCode(src), src.error->context); return errorWithContext(getErrorCode(src), src.error->context);
case TYPE_OTHER: case TYPE_OTHER:

View File

@ -9,6 +9,7 @@
#define sprintf(_dest, args...) snprintf(_dest, 999, args) #define sprintf(_dest, args...) snprintf(_dest, 999, args)
#else #else
#ifdef DEBUG #ifdef DEBUG
#define printd(...) printf(__VA_ARGS__) #define printd(...) printf(__VA_ARGS__)
#else #else
@ -67,6 +68,7 @@ typedef enum Type {
TYPE_SYMBOL, TYPE_SYMBOL,
TYPE_LAMBDA, TYPE_LAMBDA,
TYPE_STRING, TYPE_STRING,
TYPE_PROMISE,
TYPE_OTHER, TYPE_OTHER,
TYPE_ERROR TYPE_ERROR
} Type; } Type;
@ -75,6 +77,7 @@ typedef struct Object Object;
struct Lambda; struct Lambda;
struct Environment; struct Environment;
struct Promise;
struct Other; struct Other;
struct Error { struct Error {
enum errorCode code; enum errorCode code;
@ -95,6 +98,7 @@ struct Object {
struct StructObject* structObject; struct StructObject* structObject;
struct Lambda* lambda; struct Lambda* lambda;
struct Other* other; struct Other* other;
struct Promise* promise;
#ifdef SIMPLE_ERRORS #ifdef SIMPLE_ERRORS
enum errorCode error; enum errorCode error;
#else #else
@ -246,6 +250,7 @@ struct Error noError();
Object constructLambda(const Object* params, const Object* body, struct Environment* env); Object constructLambda(const Object* params, const Object* body, struct Environment* env);
#ifdef STANDALONE #ifdef STANDALONE
int getAllocations(); int getAllocations();
size_t getBytes(); size_t getBytes();

View File

@ -287,6 +287,7 @@ Object eval(const Object* obj, struct Environment* env)
case TYPE_STRING: case TYPE_STRING:
case TYPE_STRUCT: case TYPE_STRUCT:
case TYPE_SLIST: case TYPE_SLIST:
case TYPE_PROMISE:
return cloneObject(*obj); return cloneObject(*obj);
case TYPE_SYMBOL: case TYPE_SYMBOL:

View File

@ -187,7 +187,7 @@ Object chars(Object* params, unused int length, unused struct Environment* env)
c[1] = '\0'; c[1] = '\0';
int i = 0; int i = 0;
Object list = listObject(); Object list = listObject();
while(params[0].string[i]) { while (params[0].string[i]) {
c[0] = params[0].string[i]; c[0] = params[0].string[i];
nf_addToList(&list, stringFromSlice(c, 1)); nf_addToList(&list, stringFromSlice(c, 1));
i++; i++;
@ -198,7 +198,7 @@ Object chars(Object* params, unused int length, unused struct Environment* env)
size_t literalLen(const char* pattern) size_t literalLen(const char* pattern)
{ {
const char* cursor = pattern; const char* cursor = pattern;
while(*cursor != '*' && *cursor != '\0') { while (*cursor != '*' && *cursor != '\0') {
cursor++; cursor++;
} }
return cursor - pattern; return cursor - pattern;
@ -457,7 +457,7 @@ Object numToChar(Object* params, unused int length, unused struct Environment* e
checkTypes(numToChar) checkTypes(numToChar)
Object c = params[0]; Object c = params[0];
if (c.type != TYPE_NUMBER) { if (c.type != TYPE_NUMBER || c.number > 255 || c.number < 0) {
return errorObject(BAD_NUMBER); return errorObject(BAD_NUMBER);
} }
char ch[1] = { c.number }; char ch[1] = { c.number };
@ -561,4 +561,5 @@ Object getEnvVar(Object* params, unused int length, unused struct Environment* e
checkTypes(getEnvVar) checkTypes(getEnvVar)
return nullTerminated(getenv(params[0].string)); return nullTerminated(getenv(params[0].string));
} }
#endif // STANDALONE #endif // STANDALONE

74
src/threads.c Normal file
View File

@ -0,0 +1,74 @@
#include "threads.h"
#include "object.h"
#include "pebblisp.h"
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
struct Promise {
int done;
int refs;
struct Environment* env;
Object object;
pthread_t thread;
};
Object clonePromise(Object src)
{
src.promise->refs += 1;
return src;
}
int isDone(struct Promise* promise)
{
return promise->done;
}
void cleanPromise(struct Promise* promise)
{
promise->refs -= 1;
if (promise->refs == 0) {
free(promise);
}
}
Object await(Object* params, int length, struct Environment* env)
{
struct Promise* promise = params[0].promise;
if (!promise->done) { // TODO: Does `done` need a mutex or other lock?
pthread_join(promise->thread, NULL);
}
return cloneObject(promise->object);
}
void* doAsync(void* args)
{
struct Promise* promise = args;
Object cloned = cloneObject(promise->object);
Object first_eval = eval(&cloned, promise->env);
Object e = listEvalLambda(&first_eval, NULL, 1, promise->env);
promise->object = e;
promise->done = 1;
cleanPromise(promise);
cleanObject(&cloned);
return NULL;
}
Object async(Object* params, int length, struct Environment* env)
{
Object promise = newObject(TYPE_PROMISE);
promise.promise = malloc(sizeof(struct Promise));
*promise.promise = (struct Promise) {
.refs = 2,
.done = 0,
.env = env,
.object = params[0], // TODO: Clone?
};
pthread_create(&promise.promise->thread, NULL, doAsync, promise.promise);
return promise;
}

26
src/threads.h Normal file
View File

@ -0,0 +1,26 @@
#ifdef STANDALONE
#ifndef THREADS_H
#define THREADS_H
#include "pebblisp.h"
Object clonePromise(Object src);
void cleanPromise(struct Promise* promise);
int isDone(struct Promise* promise);
fn(async, "async",
"Run the given lambda on a separate thread.",
"(def sleepy (fn () ((sys \"sleep 0.1\") \"Hiya\"))) (def x (async sleepy)) x", "<PENDING>",
"(def sleepy (fn () ((sys \"sleep 0.1\") \"Hiya\"))) (def x (async sleepy)) (await x)", "Hiya",
);
fn(await, "await",
""
);
#endif // PEBBLISP_THREADS_H
#endif // STANDALONE