Handle more testing stuff statically.

Remove fnt and just have centralized fn.
Longer printObj temp string.
`?` now shows tests.
Add more internal testing.
This commit is contained in:
Sage Vaillancourt 2022-03-22 11:44:22 -04:00 committed by Sage Vaillancourt
parent 097cbf6a5c
commit 5a622736d3
6 changed files with 73 additions and 47 deletions

View File

@ -242,21 +242,22 @@ struct helpText {
const char* symbol; const char* symbol;
const char* help; const char* help;
const char*const* tests; const char*const* tests;
size_t testCount;
}; };
int currentHelp = 0; int currentHelp = 0;
struct helpText helpTexts[100]; struct helpText helpTexts[100];
#define pf(_name, _func) buildFuncSym(_name, &(_func), _func ## Doc, NULL)
#define pfn(_func) buildFuncSym(_func ## Symbol, &(_func), _func ## Doc, NULL) #define pfn(_func) buildFuncSym(_func ## Symbol, &(_func), _func ## Doc, NULL)
#define pft(_name, _func) buildFuncSym(_name, &(_func), _func ## Doc, _func ## Tests) #define pf(_name, _func) buildFuncSym(_name, &(_func), _func ## Doc, _func ## Tests, array_length(_func ## Tests))
int helpInitialized = 0; int helpInitialized = 0;
struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object, Object, struct Environment*), const char* help, const char*const* tests) struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object, Object, struct Environment*), const char* help, const char*const* tests, size_t testLength)
{ {
if (!helpInitialized) { if (!helpInitialized) {
helpTexts[currentHelp].help = help; helpTexts[currentHelp].help = help;
helpTexts[currentHelp].symbol = symbol; helpTexts[currentHelp].symbol = symbol;
helpTexts[currentHelp].tests = tests; helpTexts[currentHelp].tests = tests;
helpTexts[currentHelp].testCount = testLength;
currentHelp += 1; currentHelp += 1;
} }
@ -266,12 +267,20 @@ struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object, Object,
}; };
} }
const char* getHelp(const char* symbol) char* getHelp(const char* symbol)
{ {
for (int i = 0; i < currentHelp; i++) { for (int i = 0; i < currentHelp; i++) {
struct helpText h = helpTexts[i]; struct helpText h = helpTexts[i];
if (strcmp(symbol, h.symbol) == 0) { if (strcmp(symbol, h.symbol) == 0) {
return h.help; char* text = calloc(sizeof(char), 1024);
char* textCursor = text;
textCursor += sprintf(textCursor, "%s", h.help);
for (int ti = 0; ti < h.testCount; ti += 2) {
const char* test = h.tests[ti];
const char* expected = h.tests[ti + 1];
textCursor += sprintf(textCursor, "\n %s => %s", test, expected);
}
return text;
} }
} }
return "Help not found!"; return "Help not found!";
@ -293,9 +302,8 @@ int runTests()
for (int hi = 0; hi < currentHelp; hi++) { for (int hi = 0; hi < currentHelp; hi++) {
struct helpText h = helpTexts[hi]; struct helpText h = helpTexts[hi];
if (h.tests) { if (h.tests) {
int ti = 0;
char result[1024]; char result[1024];
while (h.tests[ti]) { for (int ti = 0; ti < h.testCount; ti += 2) {
const char* test = h.tests[ti]; const char* test = h.tests[ti];
const char* expected = h.tests[ti + 1]; const char* expected = h.tests[ti + 1];
struct Environment env = defaultEnv(); struct Environment env = defaultEnv();
@ -310,7 +318,6 @@ int runTests()
} else { } else {
passCount++; passCount++;
} }
ti += 2;
} }
} }
} }
@ -354,8 +361,8 @@ struct Environment defaultEnv()
{"<", &lth}, {"<", &lth},
{"&", &and}, {"&", &and},
{"|", &or}, {"|", &or},
pft("cat", catObjects), pf("cat", catObjects),
pft("fil", filter), pf("fil", filter),
pf("len", len), pf("len", len),
pf("ap", append), pf("ap", append),
pf("pre", prepend), pf("pre", prepend),
@ -379,7 +386,8 @@ struct Environment defaultEnv()
pf("serve", startServer), pf("serve", startServer),
#endif #endif
#ifdef STANDALONE #ifdef STANDALONE
pfn(segfault), pf("seg", segfault),
//pfn(segfault),
pf("prn", print), pf("prn", print),
pf("pch", pChar), pf("pch", pChar),
pf("penv", printEnvO), pf("penv", printEnvO),

View File

@ -43,7 +43,8 @@ struct StructDef getStructDef(struct Environment* env, const char* name);
int getStructIndex(struct Environment* env, const char* name); int getStructIndex(struct Environment* env, const char* name);
const char* getHelp(const char* symbol); /// Needs to be freed!
char* getHelp(const char* symbol);
int runTests(); int runTests();

View File

@ -477,8 +477,8 @@ void _printObj(const Object* obj, int newline)
printObj(&obj->lambda->body); printObj(&obj->lambda->body);
return; return;
} }
char temp[200] = ""; char temp[1024] = "";
stringObj(temp, obj); stringNObj(temp, obj, 1024);
if (newline) { if (newline) {
printf("%s\n", temp); printf("%s\n", temp);
if (obj->type == TYPE_ERROR) { if (obj->type == TYPE_ERROR) {

View File

@ -6,16 +6,20 @@
#include "env.h" #include "env.h"
#include "object.h" #include "object.h"
#define static_assert _Static_assert
#define array_length(_array) (sizeof(_array) / sizeof((_array)[0]))
#define fn(_name, _docs, ...) static const char * const _name ## Doc = _docs; \ #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
#define fnn(_name, _symbol, _docs)\ #define fnn(_name, _symbol, _docs, ...)\
static const char * const _name ## Doc = _docs;\ static const char * const _name ## Doc = _docs;\
static const char * const _name ## Symbol = _symbol;\ static const char * const _name ## Symbol = _symbol;\
Object _name 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."); \
#define fnt(_name, _docs, ...) static const char * const _name ## Doc = _docs;\
static const char * const _name ## Tests[] = {__VA_ARGS__, NULL}; \
Object _name Object _name
struct Slice { struct Slice {

View File

@ -233,8 +233,7 @@ Object basicOp(const Object* obj1, const Object* obj2, const char op,
if (lists == 0) { if (lists == 0) {
return _basicOp(obj1, obj2, op, env); return _basicOp(obj1, obj2, op, env);
} else if (lists == } else if (lists == 1) { // Single operand is applied to each element in list
1) { // Single operand is applied to each element in list
const Object* listObj = (obj1->type == TYPE_LIST) ? obj1 : obj2; const Object* listObj = (obj1->type == TYPE_LIST) ? obj1 : obj2;
const Object* singleObj = (obj1->type == TYPE_LIST) ? obj2 : obj1; const Object* singleObj = (obj1->type == TYPE_LIST) ? obj2 : obj1;
@ -275,25 +274,25 @@ Object len(Object obj1, Object o_ignore, struct Environment* e_ignore)
return basicOp(&obj1, &obj2, _char, env); \ return basicOp(&obj1, &obj2, _char, env); \
} }
BASIC_OP(add, '+'); BASIC_OP(add, '+')
BASIC_OP(sub, '-'); BASIC_OP(sub, '-')
BASIC_OP(mul, '*'); BASIC_OP(mul, '*')
BASIC_OP(dvi, '/'); BASIC_OP(dvi, '/')
BASIC_OP(mod, '%'); BASIC_OP(mod, '%')
BASIC_OP(equ, '='); BASIC_OP(equ, '=')
BASIC_OP(gth, '>'); BASIC_OP(gth, '>')
BASIC_OP(lth, '<'); BASIC_OP(lth, '<')
BASIC_OP(and, '&'); BASIC_OP(and, '&')
BASIC_OP(or, '|'); BASIC_OP(or, '|')
#undef BASIC_OP #undef BASIC_OP
@ -343,8 +342,10 @@ Object systemCall(Object process, Object _, struct Environment* env)
Object help(Object symbol, Object ignore, struct Environment* ignore2) Object help(Object symbol, Object ignore, struct Environment* ignore2)
{ {
const char* help = getHelp(symbol.string); char* help = getHelp(symbol.string);
return objFromSlice(&help[-1], strlen(help) + 2); Object helpText = newObject(TYPE_STRING);
helpText.string = help;
return helpText;
} }
#endif // STANDALONE #endif // STANDALONE

View File

@ -28,29 +28,41 @@ BASIC_OP(or);
#undef BASIC_OP #undef BASIC_OP
fnt(catObjects, fn(catObjects,
"Concatenate string versions of the given objects.\n", "Concatenate string versions of the given objects.",
"(cat \"Stuff: \" (1 2 3))", "Stuff: ( 1 2 3 )" "(cat \"Stuff: \" (1 2 3))", "Stuff: ( 1 2 3 )"
)(Object obj1, Object obj2, struct Environment* env); )(Object obj1, Object obj2, struct Environment* env);
fnt(filter, fn(filter,
"Filter a list based on the given condition.\n", "Filter a list based on the given condition.",
"(fil (< 50) (25 60 100))", "( 60 100 )" "(fil (< 50) (25 60 100))", "( 60 100 )"
)(Object condition, Object list, struct Environment* env); )(Object condition, Object list, struct Environment* env);
fn(append, fn(append,
"Append the given element", "Append the given element. Creates a new list.",
"(ap (1 2) 3)", "( 1 2 3 )") "(ap (1 2) 3)", "( 1 2 3 )"
(Object list, Object newElement, struct Environment* env); )(Object list, Object newElement, struct Environment* env);
fn(prepend, "(pre (2 3) 1) => ( 1 2 3 )") fn(prepend,
(Object list, Object newElement, struct Environment* env); "Prepend the given element. Creates a new list",
"(pre (2 3) 1)", "( 1 2 3 )"
)(Object list, Object newElement, struct Environment* env);
fn(len, "(len (2 3)) => 2") fn(len,
(Object obj1, Object o_ignore, struct Environment* e_ignore); "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);
fn(reduce, "(reduce ((1 2 3) 0) +) => 6") fn(reduce,
(Object listInitial, Object func, struct Environment* env); "Performs a simple reduction. Does not currently work with lambdas.\n"
"Takes two arguments:\n"
" - A two-element list: (`values` `initial`)\n"
" - A function to apply to each value.",
"(reduce (5 6) +)", "11",
"(reduce ((1 2 3) 0) +)", "6"
)(Object listInitial, Object func, struct Environment* env);
fn(at, "(at 1 (1 2 3)) => 2") fn(at, "(at 1 (1 2 3)) => 2")
(Object index, Object list, struct Environment* env); (Object index, Object list, struct Environment* env);