Start switch to array-based functions.

No more mandatory two-params, though several things are still busted.
This commit is contained in:
Sage Vaillancourt 2022-03-24 16:56:52 -04:00 committed by Sage Vaillancourt
parent e00d706522
commit 0a649095ab
9 changed files with 197 additions and 111 deletions

View File

@ -179,7 +179,7 @@ void printEnv(struct Environment* env)
}
void addFunc(const char* name,
Object (* func)(Object, Object, struct Environment*),
Object (* func)(Object*, int, struct Environment*),
struct Environment* env)
{
Object o = newObject(TYPE_FUNC);
@ -223,7 +223,7 @@ void deleteEnv(struct Environment* e)
struct symFunc {
const char* sym;
Object (* func)(Object, Object, struct Environment*);
Object (* func)(Object*, int, struct Environment*);
};
struct Environment* globalEnv;
@ -252,7 +252,7 @@ struct helpText helpTexts[100];
int helpInitialized = 0;
struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object, Object, 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)
{
if (!helpInitialized) {
@ -324,7 +324,6 @@ char* getHelp(const char* symbol)
}
fnn(segfault, "seg", "Induces a segfault.")
(Object ignore1, Object ignore2, struct Environment* env)
{
int* p = NULL;
return numberObject(*p);
@ -434,6 +433,7 @@ struct Environment defaultEnv()
pf("sys", systemCall),
pf("loadfile", loadFile),
pf("inp", takeInput),
pf("rf", readFileToObject),
pf("?", help)
#endif
};

View File

@ -32,7 +32,7 @@ void addToEnv(struct Environment* env, const char* name, Object obj);
void printEnv(struct Environment* env);
void addFunc(const char* name,
Object (* func)(Object, Object, struct Environment*),
Object (* func)(Object*, int, struct Environment*),
struct Environment* env);
void deleteEnv(struct Environment* e);

View File

@ -100,7 +100,7 @@ struct Object {
Object* list;
char* string;
Object (* func)(Object, Object, struct Environment*);
Object (* func)(Object*, int, struct Environment*);
struct StructObject* structObject;
struct Lambda* lambda;

View File

@ -247,40 +247,26 @@ Object simpleFuncEval(const Object func, Object arg1, Object arg2, struct Enviro
Object listEvalFunc(const Object* list, const Object* function,
const int length, struct Environment* env)
{
if (length == 0) {
return function->func(boolObject(0), boolObject(0), env);
}
//printf("Using new funcEval.\n");
//printf("list: ");
//printObj(list);
//printf("\nfunction:");
//printObj(function);
//printf("\n");
Object rest[length];
evalForms(rest, list->list->forward, env);
// for (int i = 0; i < length; i++) {
// printf("rest[%d]: ", i);
// printObj(&rest[i]);
// printf("\n");
// }
Object func_result = rest[0];
if (length == 1) {
Object oneArg = errorObject(ONLY_ONE_ARGUMENT);
func_result = function->func(func_result, oneArg, env);
// Return a partial function if more parameters are required
// Otherwise, return the function result
cleanObject(&rest[0]);
if (isError(func_result, ONLY_ONE_ARGUMENT)) {
// These functions modify their second argument,
// so we don't clean oneArg here
cleanObject(&func_result);
return cloneObject(*list);
} else {
cleanObject(&oneArg);
return func_result;
}
} else {
// With two args, will apply function once
// With more than two args, apply the function repeatedly
for (int i = 1; i < length; i++) {
Object toClean = func_result;
func_result = function->func(func_result, rest[i], env);
cleanObject(&toClean);
cleanObject(&rest[i]);
}
return func_result;
Object result = function->func(rest, length, env);
for (int i = 0; i < length; i++) {
cleanObject(&rest[i]);
}
return result;
}
/**
@ -370,7 +356,7 @@ Object evalList(const Object* obj, struct Environment* env)
Object first_eval = eval(first_form, env);
switch (first_eval.type) {
case TYPE_FUNC:
// Passes evalLength - 1, because we skip the first form
// Uses evalLength - 1 because we skip the first form
return listEvalFunc(obj, &first_eval, evalLength - 1, env);
case TYPE_LAMBDA:
@ -445,8 +431,11 @@ void debugSlice(struct Slice* s)
#endif
}
Object possessive(Object structo, Object field, struct Environment* env)
Object possessive(Object* params, int length, struct Environment* env)
{
Object structo = params[0];
Object field = params[1];
if (structo.type != TYPE_STRUCT) {
printf("`'s` must be used on a struct!\n");
return errorObject(NULL_PARSE);

View File

@ -13,14 +13,14 @@
#define fn(_name, _docs, ...) static const char * const _name ## Doc = _docs; \
static const char * const _name ## Tests[] = {__VA_ARGS__}; \
static_assert(array_length(_name ## Tests) % 2 == 0, "Array of test strings must have exactly one expected result for each test."); \
Object _name
Object _name(Object* params, int length, struct Environment* env)
#define fnn(_name, _symbol, _docs, ...)\
static const char * const _name ## Doc = _docs;\
static const char * const _name ## Symbol = _symbol;\
static const char * const _name ## Tests[] = {__VA_ARGS__}; \
static_assert(array_length(_name ## Tests) % 2 == 0, "Array of test strings must have exactly one expected result for each test."); \
Object _name
Object _name(Object* params, int length, struct Environment* env)
struct Slice {
const char* text;

View File

@ -6,8 +6,11 @@
/**
* (reduce (list, initial) (fn (prev total) (+ prev total)))
*/
Object reduce(const Object listInitial, const Object func, struct Environment* env)
Object reduce(Object* params, int length, struct Environment* env)
{
const Object listInitial = params[0];
const Object func = params[1];
Object* list = itemAt(&listInitial, 0);
Object total = cloneObject(*list->forward); // From given initial value
@ -22,8 +25,11 @@ Object reduce(const Object listInitial, const Object func, struct Environment* e
return total;
}
Object charAt(Object string, Object at, struct Environment* ignore)
Object charAt(Object* params, int length, struct Environment* env)
{
Object string = params[0];
Object at = params[1];
char* c = malloc(sizeof(char) * 2);
c[0] = string.string[at.number];
c[1] = '\0';
@ -31,8 +37,11 @@ Object charAt(Object string, Object at, struct Environment* ignore)
return string;
}
Object filter(Object condition, Object list, struct Environment* env)
Object filter(Object* params, int length, struct Environment* env)
{
Object condition = params[0];
Object list = params[1];
Object filteredList = listObject();
FOR_POINTER_IN_LIST(&list) {
Object conditional = cloneObject(condition);
@ -46,23 +55,32 @@ Object filter(Object condition, Object list, struct Environment* env)
return filteredList;
}
Object append(Object list, Object newElement, struct Environment* env)
Object append(Object* params, int length, struct Environment* env)
{
Object list = params[0];
Object newElement = params[1];
Object newList = cloneObject(list);
nf_addToList(&newList, cloneObject(newElement));
return newList;
}
Object prepend(Object list, Object newElement, struct Environment* env)
Object prepend(Object* params, int length, struct Environment* env)
{
Object list = params[0];
Object newElement = params[1];
Object newList = listObject();
nf_addToList(&newList, cloneObject(newElement));
appendList(&newList, &list);
return newList;
}
Object at(Object index, Object list, struct Environment* env)
Object at(Object* params, int length, struct Environment* env)
{
Object index = params[0];
Object list = params[1];
const Object* found = itemAt(&list, index.number);
if (found) {
return cloneObject(*found);
@ -71,8 +89,10 @@ Object at(Object index, Object list, struct Environment* env)
}
}
Object rest(Object list, Object ignore, struct Environment* env)
Object rest(Object* params, int length, struct Environment* env)
{
Object list = params[0];
Object ret = listObject();
Object* l = &list;
FOR_POINTER_IN_LIST(l) {
@ -84,8 +104,10 @@ Object rest(Object list, Object ignore, struct Environment* env)
return ret;
}
Object reverse(Object _list, Object ignore, struct Environment* ignore2)
Object reverse(Object* params, int length, struct Environment* ignore2)
{
Object _list = params[0];
if (!isListy(_list)) {
return errorObject(NOT_A_LIST);
}
@ -105,43 +127,55 @@ Object reverse(Object _list, Object ignore, struct Environment* ignore2)
return rev;
}
Object isNum(Object test, Object ignore, struct Environment* ignore2)
Object isNum(Object* params, int length, struct Environment* env)
{
Object test = params[0];
return test.type == TYPE_NUMBER ? boolObject(1) : boolObject(0);
}
Object isList(Object test, Object ignore, struct Environment* ignore2)
Object isList(Object* params, int length, struct Environment* env)
{
Object test = params[0];
return test.type == TYPE_LIST ? boolObject(1) : boolObject(0);
}
Object isString(Object test, Object ignore, struct Environment* ignore2)
Object isString(Object* params, int length, struct Environment* env)
{
Object test = params[0];
return test.type == TYPE_STRING ? boolObject(1) : boolObject(0);
}
// Get the int value of a string's first character
Object charVal(Object test, Object ignore, struct Environment* ignore2)
Object charVal(Object* params, int length, struct Environment* env)
{
Object test = params[0];
return numberObject(test.string[0]);
// return test.type == TYPE_STRING && test.string[0] == '\0' ?
// boolObject(1) : boolObject(0);
}
Object isErr(Object test, Object ignore, struct Environment* ignore2)
Object isErr(Object* params, int length, struct Environment* env)
{
Object test = params[0];
return test.type == TYPE_ERROR ? boolObject(1) : boolObject(0);
}
Object parseEvalO(Object text, Object ignore, struct Environment* env)
Object parseEvalO(Object* params, int length, struct Environment* env)
{
Object text = params[0];
if (text.type == TYPE_SYMBOL) {
Object string = eval(&text, env);
Object parsed = parseEval(string.string, env);
cleanObject(&string);
return parsed;
} else if (text.type == TYPE_SLIST) {
return evalList(&text, env);
return evalList(&text, env, 0);
} else if (text.type != TYPE_STRING) {
return errorObject(CAN_ONLY_EVAL_STRINGS);
}
@ -160,7 +194,7 @@ Object listEquality(const Object* list1, const Object* list2)
#define CAT_MAX 1024
Object catObjects(const Object obj1, const Object obj2, struct Environment* env)
Object _catObjects(Object obj1, Object obj2, struct Environment* env)
{
Object evalObj1 = eval(&obj1, env);
Object evalObj2 = eval(&obj2, env);
@ -174,16 +208,30 @@ Object catObjects(const Object obj1, const Object obj2, struct Environment* env)
stringObj(str2, &evalObj2);
cleanObject(&evalObj1);
cleanObject(&evalObj2);
size_t length = strlen(str1) + strlen(str2) + 1;
size_t strLength = strlen(str1) + strlen(str2) + 1;
Object o = newObject(TYPE_STRING);
o.string = calloc(sizeof(char), length);
o.string = calloc(sizeof(char), strLength);
strcat(o.string, str1);
strcat(o.string, str2);
return o;
}
Object catObjects(Object* params, int length, struct Environment* env)
{
Object string = stringFromSlice("", 0);
if (length == 0) {
return string;
}
for (int i = 0; i < length; i++) {
Object newString = _catObjects(string, params[i], env);
cleanObject(&string);
string = newString;
}
return string;
}
Object _basicOp(const Object* obj1, const Object* obj2, const char op,
struct Environment* env)
{
@ -193,7 +241,8 @@ Object _basicOp(const Object* obj1, const Object* obj2, const char op,
switch (op) {
case '+':
if (eitherIsNot(TYPE_NUMBER, obj1, obj2)) {
return catObjects(*obj1, *obj2, env);
Object objects[2] = {*obj1, *obj2};
return catObjects(objects, 2, env);
}
return numberObject(n1 + n2);
case '-':
@ -264,8 +313,10 @@ Object basicOp(const Object* obj1, const Object* obj2, const char op,
}
}
Object len(Object obj1, Object o_ignore, struct Environment* e_ignore)
Object len(Object* params, int length, struct Environment* env)
{
Object obj1 = params[0];
Object o = numberObject(listLength(&obj1));
if (o.number < 0) {
return errorObject(NOT_A_LIST);
@ -274,20 +325,47 @@ Object len(Object obj1, Object o_ignore, struct Environment* e_ignore)
}
#define BASIC_OP(_name, _char) \
Object _name(Object obj1, Object obj2, struct Environment *env) \
{ \
return basicOp(&obj1, &obj2, _char, env); \
Object _name(Object* params, int length, struct Environment* env) \
{ \
Object obj1 = params[0]; \
Object obj2 = params[1]; \
return basicOp(&obj1, &obj2, _char, env); \
}
BASIC_OP(add, '+')
BASIC_OP(sub, '-')
#define PLUS +
BASIC_OP(mul, '*')
// Object add(Object* params, int length, struct Environment* env)
// {
// Object sum = numberObject(0);
// for (int i = 0; i < length; i++) {
// sum.number += params[i].number;
// }
// return sum;
// }
BASIC_OP(dvi, '/')
#define BASIC_MATH(_name, _op) \
Object _name(Object* params, int length, struct Environment* env) \
{ \
if (length == 0) { \
return numberObject(0); \
} \
Object sum = numberObject(params[0].number); \
for (int i = 1; i < length; i++) { \
sum.number _op params[i].number; \
} \
return sum; \
}
BASIC_OP(mod, '%')
BASIC_MATH(add, +=)
BASIC_MATH(sub, -=)
BASIC_MATH(mul, *=)
BASIC_MATH(dvi, /=)
BASIC_MATH(mod, %=)
BASIC_OP(equ, '=')
@ -303,8 +381,10 @@ BASIC_OP(or, '|')
#ifdef STANDALONE
Object pChar(Object c, Object i1, struct Environment* i2)
Object pChar(Object* params, int length, struct Environment* env)
{
Object c = params[0];
if (c.type != TYPE_NUMBER) {
return errorObject(BAD_NUMBER);
}
@ -312,14 +392,15 @@ Object pChar(Object c, Object i1, struct Environment* i2)
return numberObject(0);
}
Object printEnvO(Object i1, Object i2, struct Environment* env)
Object printEnvO(Object* params, int length, struct Environment* env)
{
printEnv(global());
return numberObject(0);
}
Object takeInput(Object prompt, Object i2, struct Environment* i3)
Object takeInput(Object* params, int length, struct Environment* env)
{
Object prompt = params[0];
if (prompt.type == TYPE_STRING) {
printf("%s", prompt.string);
}
@ -328,8 +409,10 @@ Object takeInput(Object prompt, Object i2, struct Environment* i3)
return stringFromSlice(input, strlen(input) - 1);
}
Object loadFile(Object filename, Object _, struct Environment* env)
Object loadFile(Object* params, int length, struct Environment* env)
{
Object filename = params[0];
if (isStringy(filename)) {
readFile(filename.string, env);
return numberObject(0);
@ -337,16 +420,20 @@ Object loadFile(Object filename, Object _, struct Environment* env)
return numberObject(1);
}
Object systemCall(Object process, Object _, struct Environment* env)
Object systemCall(Object* params, int length, struct Environment* env)
{
Object process = params[0];
if (isStringy(process)) {
return numberObject(system(process.string));
}
return numberObject(255);
}
Object help(Object symbol, Object ignore, struct Environment* ignore2)
Object help(Object* params, int length, struct Environment* env)
{
Object symbol = params[0];
char* help = getHelp(symbol.string);
Object helpText = newObject(TYPE_STRING);
helpText.string = help;
@ -375,8 +462,10 @@ char* readFileToString(FILE* input)
return string;
}
Object readFileToObject(Object filename, Object ignore, struct Environment* ignore2)
Object readFileToObject(Object* params, int length, struct Environment* env)
{
Object filename = params[0];
if (filename.type != TYPE_STRING) {
return errorObject(NULL_PARSE);
}

View File

@ -4,7 +4,7 @@
#include "pebblisp.h"
#define BASIC_OP(_name) \
Object _name(Object obj1, Object obj2, struct Environment *env)
Object _name(Object* params, int length, struct Environment* env)
BASIC_OP(add);
@ -31,29 +31,29 @@ BASIC_OP(or);
fn(catObjects,
"Concatenate string versions of the given objects.",
"(cat \"Stuff: \" (1 2 3))", "Stuff: ( 1 2 3 )",
)(Object obj1, Object obj2, struct Environment* env);
); // (Object obj1, Object obj2, struct Environment* env);
fn(filter,
"Filter a list based on the given condition.",
"(fil (< 50) (25 60 100))", "( 60 100 )",
)(Object condition, Object list, struct Environment* env);
); // (Object condition, Object list, struct Environment* env);
fn(append,
"Append the given element. Creates a new list.",
"(ap (1 2) 3)", "( 1 2 3 )",
)(Object list, Object newElement, struct Environment* env);
); // (Object list, Object newElement, struct Environment* env);
fn(prepend,
"Prepend the given element. Creates a new list",
"(pre (2 3) 1)", "( 1 2 3 )",
)(Object list, Object newElement, struct Environment* env);
); // (Object list, Object newElement, struct Environment* env);
fn(len,
"Returns the length of the given list, or a NOT_A_LIST error if the expression is not a list.",
"(len (2 3))", "2",
"(len ())", "0",
"(len \"string\")", "NOT_A_LIST",
)(Object obj1, Object o_ignore, struct Environment* e_ignore);
); // (Object obj1, Object o_ignore, struct Environment* e_ignore);
fn(reduce,
"Performs a simple reduction. Does not currently work with lambdas.\n"
@ -62,27 +62,27 @@ fn(reduce,
" - A function to apply to each value.",
"(reduce (5 6) +)", "11",
"(reduce ((1 2 3) 0) +)", "6",
)(Object listInitial, Object func, struct Environment* env);
); // (Object listInitial, Object func, struct Environment* env);
fn(at,
"Get item at the given index in the given list.",
"(at 1 (1 2 3))", "2",
"(at 99 (1 2 3))", "INDEX_PAST_END",
"(at 99 \"string\")", "INDEX_PAST_END",
)(Object index, Object list, struct Environment* env);
); // (Object index, Object list, struct Environment* env);
fn(rest,
"Get the tail of a list. All but the first element.",
"(rest (1 2 3))", "( 2 3 )",
"(rest ())", "( )",
"(rest \"string\")", "( )",
)(Object list, Object ignore, struct Environment* env);
); // (Object list, Object ignore, struct Environment* env);
fn(reverse,
"Reverse a list.",
"(rev (1 2 3))", "( 3 2 1 )",
"(rev \"string\")", "NOT_A_LIST",
)(Object _list, Object ignore, struct Environment* ignore2);
); // (Object _list, Object ignore, struct Environment* ignore2);
fn(isNum,
"Returns `T` only if the argument evaluates to a number.",
@ -90,14 +90,14 @@ fn(isNum,
"(isnum (+ 5 5))", "T",
"(isnum '(+ 5 5))", "F",
"(isnum \"Hello\")", "F",
)(Object test, Object ignore, struct Environment* ignore2);
); // (Object test, Object ignore, struct Environment* ignore2);
fn(isList,
"Returns `T` only if the argument is a list.",
"(islist (1 2 3))", "T",
"(islist ())", "T",
"(islist \"Stringy\")", "F",
)(Object test, Object ignore, struct Environment* ignore2);
); // (Object test, Object ignore, struct Environment* ignore2);
fn(isString,
"Returns `T` only if the argument is a string.",
@ -105,56 +105,56 @@ fn(isString,
"(isstr \"\")", "T",
"(isstr (cat 5 5))", "T",
"(isstr 10)", "F",
)(Object test, Object ignore, struct Environment* ignore2);
); // (Object test, Object ignore, struct Environment* ignore2);
fn(isErr,
"Check if the argument is an error.",
"(iserr (at 10 ()))", "T",
"(iserr 5)", "F",
)(Object test, Object ignore, struct Environment* ignore2);
); // (Object test, Object ignore, struct Environment* ignore2);
fn(charAt,
"Get the char in the given string at the given index.",
"(chat \"Hello\" 1)", "e",
"(chat \"Hello\" 10)", "",
)(Object string, Object at, struct Environment* ignore);
); // (Object string, Object at, struct Environment* ignore);
fn(charVal,
"Get the ascii integer representaton of the given character.",
"(char \"h\")", "104",
"(char \"hello\")", "104",
"(char \"\")", "0",
)(Object test, Object ignore, struct Environment* ignore2);
); // (Object test, Object ignore, struct Environment* ignore2);
fn(parseEvalO,
"Evaluate the given string or quoted list.",
"(eval \"(1 2 3)\")", "( 1 2 3 )",
"(eval '(+ 5 5))", "10",
)(Object text, Object ignore, struct Environment* env);
); // (Object text, Object ignore, struct Environment* env);
fn(possessive,
"(struct Post (title body))\n"
"(def p (Post \"TI\" \"BO\"))\n"
"p's title => TI"
)(Object structo, Object field, struct Environment* env);
); // (Object structo, Object field, struct Environment* env);
#ifdef STANDALONE
fn(print, "Prints the string representation of the given object to stdout.")
(Object p, Object ignore, struct Environment* ignore2);
fn(print, "Prints the string representation of the given object to stdout.");
//(Object p, Object ignore, struct Environment* ignore2);
fn(pChar, "Prints the ascii character for the given number value.")
(Object c, Object i1, struct Environment* i2);
fn(pChar, "Prints the ascii character for the given number value.");
//(Object c, Object i1, struct Environment* i2);
fn(printEnvO, "Prints out the current scoped environment.")
(Object i1, Object i2, struct Environment* env);
fn(printEnvO, "Prints out the current scoped environment.");
//(Object i1, Object i2, struct Environment* env);
fn(systemCall,
"Opens a shell and runs the given command, returning the command's exit code.\n"
"(sys \"echo yee\")\n"
"yee\n"
"=> 0"
)(Object process, Object _, struct Environment* env);
); // (Object process, Object _, struct Environment* env);
fn(loadFile,
"Loads and parses the given file.\n"
@ -162,24 +162,24 @@ fn(loadFile,
"(loadfile \"printdate.pl\")\n"
"Mon 21 Mar 2022 10:35:03 AM EDT\n"
"=> 0"
)(Object filename, Object _, struct Environment* env);
); // (Object filename, Object _, struct Environment* env);
fn(takeInput,
"Take console input with an optional prompt. For example:\n"
"`(def x (input))` will wait for user input with no prompt.\n"
"`(def x (input \">> \"))` wait for input, but prompt the user with '>> '.\n"
)(Object i1, Object i2, struct Environment* i3);
); // (Object i1, Object i2, struct Environment* i3);
fn(help,
"Displays help text for the given function.\n"
"Currently requires the function name as a string, but future syntactic sugar may\n"
"loosen this requirement.\n"
"(? \"+\") => \"(+ 1 2) => 3\""
)(Object symbol, Object ignore, struct Environment* ignore2);
); // (Object symbol, Object ignore, struct Environment* ignore2);
fn(readFileToObject,
"Read a file into a string object."
)(Object filename, Object ignore, struct Environment* ignore2);
); // (Object filename, Object ignore, struct Environment* ignore2);
#endif // STANDALONE

View File

@ -141,8 +141,10 @@ int start(int port)
return 0;
}
Object print(Object p, Object ignore, struct Environment* env)
Object print(Object* params, int length, struct Environment* env)
{
Object p = params[0];
Object ignore = params[1];
Object c = cloneObject(p);
Object e = eval(&c, env);
_printObj(&e, 0);
@ -165,20 +167,26 @@ void addRouteO(Object path, Object textFunc, struct Environment* env, enum Route
addRoute(r);
}
Object addGetRoute(Object path, Object textFunc, struct Environment* env)
Object addGetRoute(Object* params, int length, struct Environment* env)
{
Object path = params[0];
Object textFunc = params[1];
addRouteO(path, textFunc, env, GET);
return numberObject(1);
}
Object addPostRoute(Object path, Object textFunc, struct Environment* env)
Object addPostRoute(Object* params, int length, struct Environment* env)
{
Object path = params[0];
Object textFunc = params[1];
addRouteO(path, textFunc, env, POST);
return numberObject(1);
}
Object startServer(Object portObject, Object ignored, struct Environment* env)
Object startServer(Object* params, int length, struct Environment* env)
{
Object portObject = params[0];
int port = 8888;
if (portObject.type == TYPE_NUMBER) {
port = portObject.number;

View File

@ -21,18 +21,18 @@ fn(startServer,
"Starts a simple web server with routes that have been added with (get) and (post).\n"
"Note: This is a non-blocking call. It is recommended to wait for user input before exiting.\n"
" A simple way would be to use (inp) immediately after the (serve) call."
)(Object path, Object textFunc, struct Environment* env);
);
fn(addGetRoute,
"Note: Implementation bugs currently prevent using an inline lambda.\n"
" (def homepage (fn () (\"Hello, world!\")));(get \"/\" homepage)\n"
" (def queryPage (fn (req) (req's queryParams)));(get \"/x\" queryPage)\n"
" (serve)\n"
)(Object path, Object textFunc, struct Environment* env);
);
fn(addPostRoute,
"Note: Implementation bugs currently prevent using an inline lambda.\n"
" (def homepage (fn () (\"Hello, world!\")));(post \"/\" homepage)\n"
" (serve)\n"
)(Object path, Object textFunc, struct Environment* env);
);