Define function symbols at their declaration.

This commit is contained in:
Sage Vaillancourt 2022-03-29 19:00:01 -04:00
parent 52c928846d
commit 5779ad5427
4 changed files with 71 additions and 69 deletions

View File

@ -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
};

View File

@ -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;

View File

@ -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,
({isStringy, isNumber, isStringy}),
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."
);

View File

@ -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"