Define function symbols at their declaration.
This commit is contained in:
parent
52c928846d
commit
5779ad5427
71
src/env.c
71
src/env.c
|
@ -215,8 +215,7 @@ struct helpText {
|
|||
int currentHelp = 0;
|
||||
struct helpText helpTexts[100];
|
||||
|
||||
#define pfn(_func) buildFuncSym(_func ## Symbol, &(_func), _func ## Doc, NULL)
|
||||
#define pf(_name, _func) buildFuncSym(_name, &(_func), _func ## Doc, _func ## Tests, array_length(_func ## Tests))
|
||||
#define pf(_func) buildFuncSym(_func ## Symbol, &(_func), _func ## Doc, _func ## Tests, array_length(_func ## Tests))
|
||||
|
||||
/// For any instances (e.g. segfault recovery) where defaultEnv() may be called
|
||||
/// multiple times.
|
||||
|
@ -282,11 +281,12 @@ void printColored(const char* code)
|
|||
}
|
||||
}
|
||||
|
||||
fn(help,
|
||||
fn(help, "?",
|
||||
"Gets a string with help text for the given function.\n"
|
||||
"For example:\n"
|
||||
" (? islist) => \"(islist (1 2 3)) => T\""
|
||||
) {
|
||||
)
|
||||
{
|
||||
const char* symbol;
|
||||
if (!length || !params[0].string || params[0].string[0] == '\0') {
|
||||
symbol = "?";
|
||||
|
@ -316,7 +316,9 @@ fn(help,
|
|||
return nullTerminated("Help not found!");
|
||||
}
|
||||
|
||||
fnn(segfault, "seg", "Induces a segfault.")
|
||||
fn(segfault, "seg",
|
||||
"Induces a segfault."
|
||||
)
|
||||
{
|
||||
int* p = NULL;
|
||||
return numberObject(*p);
|
||||
|
@ -403,41 +405,40 @@ struct Environment defaultEnv()
|
|||
{"<", &lessThan},
|
||||
{"&", &and},
|
||||
{"|", &or},
|
||||
pf("cat", catObjects),
|
||||
pf("fil", filter),
|
||||
pf("len", len),
|
||||
pf("ap", append),
|
||||
pf("pre", prepend),
|
||||
pf("reduce", reduce),
|
||||
pf("at", at),
|
||||
pf("rest", rest),
|
||||
pf("chat", charAt),
|
||||
pf(catObjects),
|
||||
pf(filter),
|
||||
pf(len),
|
||||
pf(append),
|
||||
pf(prepend),
|
||||
pf(reduce),
|
||||
pf(at),
|
||||
pf(rest),
|
||||
pf(charAt),
|
||||
#ifndef LOW_MEM
|
||||
pf("rev", reverse),
|
||||
pf(reverse),
|
||||
#endif
|
||||
pf("isnum", isNum),
|
||||
pf("islist", isList),
|
||||
pf("isstr", isString),
|
||||
pf("iserr", isErr),
|
||||
pf("char", charVal),
|
||||
pf("eval", parseEvalO),
|
||||
pf("poss", possessive),
|
||||
pf(isNum),
|
||||
pf(isList),
|
||||
pf(isString),
|
||||
pf(isErr),
|
||||
pf(charVal),
|
||||
pf(parseEvalO),
|
||||
pf(possessive),
|
||||
#ifdef WEBSERVER
|
||||
pf("get", addGetRoute),
|
||||
pf("post", addPostRoute),
|
||||
pf("serve", startServer),
|
||||
pf(addGetRoute),
|
||||
pf(addPostRoute),
|
||||
pf(startServer),
|
||||
#endif
|
||||
#ifdef STANDALONE
|
||||
pf("seg", segfault),
|
||||
//pfn(segfault),
|
||||
pf("prn", print),
|
||||
pf("ch", numToChar),
|
||||
pf("penv", printEnvO),
|
||||
pf("sys", systemCall),
|
||||
pf("loadfile", loadFile),
|
||||
pf("inp", takeInput),
|
||||
pf("rf", readFileToObject),
|
||||
pf("?", help)
|
||||
pf(segfault),
|
||||
pf(print),
|
||||
pf(numToChar),
|
||||
pf(printEnvO),
|
||||
pf(systemCall),
|
||||
pf(loadFile),
|
||||
pf(takeInput),
|
||||
pf(readFileToObject),
|
||||
pf(help)
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -12,20 +12,21 @@
|
|||
|
||||
#define UNPACK(...) __VA_ARGS__
|
||||
|
||||
#define fn(_name, _docs, ...) \
|
||||
#define fnn(_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* params, int length, struct Environment* env)
|
||||
|
||||
// GCC warns without the attribute, even when typeChecks are used
|
||||
#define tfn(_name, _type, _docs, ...) \
|
||||
#define tfn(_name, _symbol, _type, _docs, ...) \
|
||||
__attribute__((unused)) static int (*_name ## TypeChecks[])(Object) = UNPACK _type; \
|
||||
fn(_name, _docs, __VA_ARGS__)
|
||||
|
||||
#define fnn(_name, _symbol, _docs, ...) \
|
||||
static const char * const _name ## Symbol = _symbol; \
|
||||
fn(_name, _docs, __VA_ARGS__)
|
||||
fnn(_name, _docs, __VA_ARGS__)
|
||||
|
||||
#define fn(_name, _symbol, _docs, ...) \
|
||||
static const char * const _name ## Symbol = _symbol; \
|
||||
fnn(_name, _docs, __VA_ARGS__)
|
||||
|
||||
struct Slice {
|
||||
const char* text;
|
||||
|
|
48
src/plfunc.h
48
src/plfunc.h
|
@ -28,32 +28,32 @@ BASIC_OP(or);
|
|||
|
||||
#undef BASIC_OP
|
||||
|
||||
tfn(catObjects,
|
||||
tfn(catObjects, "cat",
|
||||
({ NULL, isStringy }),
|
||||
"Concatenate string representations of the given objects.",
|
||||
"(cat \"Stuff: \" (1 2 3))", "Stuff: ( 1 2 3 )",
|
||||
);
|
||||
|
||||
tfn(filter,
|
||||
tfn(filter, "fil",
|
||||
({ isFuncy, isListy, isListy }),
|
||||
"Filter a list based on the given condition.",
|
||||
"(fil (fn (a) (< 50 a)) (25 60 100))", "( 60 100 )",
|
||||
"(fil (fn (a) (< 0 (len a))) ( () (1) (1 2) () ))", "( ( 1 ) ( 1 2 ) )",
|
||||
);
|
||||
|
||||
tfn(append,
|
||||
tfn(append, "ap",
|
||||
({ isListy, NULL, isListy }),
|
||||
"Append the given element. Creates a new list.",
|
||||
"(ap (1 2) 3)", "( 1 2 3 )",
|
||||
);
|
||||
|
||||
tfn(prepend,
|
||||
tfn(prepend, "pre",
|
||||
({ isListy, NULL, isListy }),
|
||||
"Prepend the given element. Creates a new list",
|
||||
"(pre (2 3) 1)", "( 1 2 3 )",
|
||||
);
|
||||
|
||||
tfn(len,
|
||||
tfn(len, "len",
|
||||
({ isListy, isNumber }),
|
||||
"Returns the length of the given list, or an error if the expression is not a list.",
|
||||
"(len (2 3))", "2",
|
||||
|
@ -61,7 +61,7 @@ tfn(len,
|
|||
"(len \"string\")", "BAD_PARAMS_ON: len",
|
||||
);
|
||||
|
||||
tfn(reduce,
|
||||
tfn(reduce, "reduce",
|
||||
({ NULL, isFuncy, NULL, NULL }),
|
||||
"Performs a simple reduction. Does not currently work with lambdas.\n"
|
||||
"Takes three arguments:\n"
|
||||
|
@ -72,7 +72,7 @@ tfn(reduce,
|
|||
"(reduce (1 2 3) + 0)", "6",
|
||||
);
|
||||
|
||||
tfn(at,
|
||||
tfn(at, "at",
|
||||
({ isNumber, isListy, NULL }),
|
||||
"Get item at the given index in the given list.",
|
||||
"(at 1 (1 2 3))", "2",
|
||||
|
@ -80,7 +80,7 @@ tfn(at,
|
|||
"(at 99 \"string\")", "BAD_PARAMS_ON: at",
|
||||
);
|
||||
|
||||
tfn(rest,
|
||||
tfn(rest, "rest",
|
||||
({ isListy, isListy }),
|
||||
"Get the tail of a list. All but the first element.",
|
||||
"(rest (1 2 3))", "( 2 3 )",
|
||||
|
@ -88,14 +88,14 @@ tfn(rest,
|
|||
"(rest \"string\")", "BAD_PARAMS_ON: rest",
|
||||
);
|
||||
|
||||
tfn(reverse,
|
||||
tfn(reverse, "rev",
|
||||
({ isListy, isListy }),
|
||||
"Reverse a list.",
|
||||
"(rev (1 2 3))", "( 3 2 1 )",
|
||||
"(rev \"string\")", "BAD_PARAMS_ON: reverse",
|
||||
);
|
||||
|
||||
tfn(isNum,
|
||||
tfn(isNum, "isnum",
|
||||
({ NULL, isBool }),
|
||||
"Returns `T` only if the argument evaluates to a number.",
|
||||
"(isnum 1)", "T",
|
||||
|
@ -104,7 +104,7 @@ tfn(isNum,
|
|||
"(isnum \"Hello\")", "F",
|
||||
);
|
||||
|
||||
tfn(isList,
|
||||
tfn(isList, "islist",
|
||||
({ NULL, isBool }),
|
||||
"Returns `T` only if the argument is a list.",
|
||||
"(islist (1 2 3))", "T",
|
||||
|
@ -112,7 +112,7 @@ tfn(isList,
|
|||
"(islist \"Stringy\")", "F",
|
||||
);
|
||||
|
||||
tfn(isString,
|
||||
tfn(isString, "isstr",
|
||||
({ NULL, isBool }),
|
||||
"Returns `T` only if the argument is a string.",
|
||||
"(isstr \"Heyo\")", "T",
|
||||
|
@ -121,21 +121,21 @@ tfn(isString,
|
|||
"(isstr 10)", "F",
|
||||
);
|
||||
|
||||
tfn(isErr,
|
||||
tfn(isErr, "iserr",
|
||||
({ NULL, isBool }),
|
||||
"Check if the argument is an error.",
|
||||
"(iserr (at 10 ()))", "T",
|
||||
"(iserr 5)", "F",
|
||||
);
|
||||
|
||||
tfn(charAt,
|
||||
tfn(charAt, "chat",
|
||||
({ isStringy, isNumber, isStringy }),
|
||||
"Get the char in the given string at the given index.",
|
||||
"(chat \"Hello\" 1)", "e",
|
||||
"(chat \"Hello\" 10)", "",
|
||||
);
|
||||
|
||||
tfn(charVal,
|
||||
tfn(charVal, "char",
|
||||
({ isStringy, isNumber }),
|
||||
"Get the ascii integer representaton of the given character.",
|
||||
"(char \"h\")", "104",
|
||||
|
@ -144,14 +144,14 @@ tfn(charVal,
|
|||
);
|
||||
|
||||
/// STRING/SLIST => ANY
|
||||
fn(parseEvalO,
|
||||
fn(parseEvalO, "eval",
|
||||
"Evaluate the given string or quoted list.",
|
||||
"(eval \"(1 2 3)\")", "( 1 2 3 )",
|
||||
"(eval '(+ 5 5))", "10",
|
||||
);
|
||||
|
||||
/// STRUCT, STRING => ANY
|
||||
fn(possessive,
|
||||
fn(possessive, "poss",
|
||||
"(struct Post (title body))\n"
|
||||
"(def p (Post \"TI\" \"BO\"))\n"
|
||||
"p's title => TI"
|
||||
|
@ -159,25 +159,25 @@ fn(possessive,
|
|||
|
||||
#ifdef STANDALONE
|
||||
|
||||
fn(print, "Prints the string representation of the given object to stdout.");
|
||||
fn(print, "prn", "Prints the string representation of the given object to stdout.");
|
||||
|
||||
tfn(numToChar,
|
||||
tfn(numToChar, "ch",
|
||||
({ isNumber, isStringy }),
|
||||
"Gets a string containing the ascii character for the given number value.",
|
||||
"(ch 107)", "k",
|
||||
"(ch 0x21)", "!",
|
||||
);
|
||||
|
||||
fn(printEnvO, "Prints out the current scoped environment.");
|
||||
fn(printEnvO, "penv", "Prints out the current scoped environment.");
|
||||
|
||||
tfn(systemCall,
|
||||
tfn(systemCall, "sys",
|
||||
({ isStringy, isNumber }),
|
||||
"Opens a shell and runs the given command, returning 0 if successful.\n"
|
||||
"If the argument is not a string, returns 255.\n",
|
||||
"(sys \"echo yee > /dev/null\")", "0",
|
||||
);
|
||||
|
||||
tfn(loadFile,
|
||||
tfn(loadFile, "loadfile",
|
||||
({ isStringy, NULL }),
|
||||
"Loads and parses the given file.\n"
|
||||
"Returns 0 if the file was loaded and parsed successfully. Otherwise 1.\n"
|
||||
|
@ -189,13 +189,13 @@ tfn(loadFile,
|
|||
/// @code
|
||||
/// () => STRING
|
||||
/// STRING => STRING
|
||||
fn(takeInput,
|
||||
fn(takeInput, "inp",
|
||||
"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"
|
||||
);
|
||||
|
||||
tfn(readFileToObject,
|
||||
tfn(readFileToObject, "rf",
|
||||
({ isStringy, isStringy }),
|
||||
"Read a file into a string object."
|
||||
);
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#include "pebblisp.h"
|
||||
|
||||
fn(startServer,
|
||||
fn(startServer, "serve",
|
||||
"(serve) => 0\n"
|
||||
"Starts a simple web server with routes that have been added using (get) and (post).\n"
|
||||
"Returns 0 if the server was successfully started, otherwise 1.\n"
|
||||
"Note: Not a blocking call! Calling (inp) is a simple way to keep the server open.\n"
|
||||
);
|
||||
|
||||
fn(addGetRoute,
|
||||
fn(addGetRoute, "get",
|
||||
"Adds a GET route at the given path with the given function.\n"
|
||||
" (get \"/\" (fn () (\"Hello, world!\")))\n"
|
||||
" (get \"/parampath\" (fn (req) (req's queryParams)))\n"
|
||||
|
@ -15,7 +15,7 @@ fn(addGetRoute,
|
|||
"Also see: (serve)\n"
|
||||
);
|
||||
|
||||
fn(addPostRoute,
|
||||
fn(addPostRoute, "post",
|
||||
"Adds a POST route at the given path with the given function.\n"
|
||||
"Note: Can't do anything with POSTed data yet.\n"
|
||||
" (post \"/\" (fn () (\"Hello, world!\")))\n"
|
||||
|
|
Loading…
Reference in New Issue