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:
parent
097cbf6a5c
commit
5a622736d3
30
src/env.c
30
src/env.c
|
@ -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()
|
||||||
{"<", <h},
|
{"<", <h},
|
||||||
{"&", &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),
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -6,18 +6,22 @@
|
||||||
#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;\
|
||||||
|
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 fnt(_name, _docs, ...) static const char * const _name ## Doc = _docs;\
|
|
||||||
static const char * const _name ## Tests[] = {__VA_ARGS__, NULL}; \
|
|
||||||
Object _name
|
|
||||||
|
|
||||||
struct Slice {
|
struct Slice {
|
||||||
const char* text;
|
const char* text;
|
||||||
unsigned char length;
|
unsigned char length;
|
||||||
|
|
29
src/plfunc.c
29
src/plfunc.c
|
@ -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
|
38
src/plfunc.h
38
src/plfunc.h
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue