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