Start some internal testing frameworking.
This commit is contained in:
parent
3bf65577c0
commit
097cbf6a5c
|
@ -12,7 +12,7 @@ mkfile_dir := $(dir $(mkfile_path))
|
||||||
GCC_COM ?= gcc -g -O0 -Wall -o $(exe) -D WEBSERVER -D STANDALONE -DSCRIPTDIR=\"$(SCRIPTDIR)\"
|
GCC_COM ?= gcc -g -O0 -Wall -o $(exe) -D WEBSERVER -D STANDALONE -DSCRIPTDIR=\"$(SCRIPTDIR)\"
|
||||||
|
|
||||||
all:
|
all:
|
||||||
$(GCC_COM) $(files) $(libs) && echo && ./tests.sh
|
$(GCC_COM) $(files) $(libs) && echo && ./tests.sh && ./pl --run-tests
|
||||||
|
|
||||||
notest:
|
notest:
|
||||||
$(GCC_COM) $(file_libs)
|
$(GCC_COM) $(file_libs)
|
||||||
|
|
85
src/env.c
85
src/env.c
|
@ -52,6 +52,7 @@ struct Environment envForLambda(const Object* params, const Object* arg_forms,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Environment env = {
|
struct Environment env = {
|
||||||
|
.name = "lambdaEnv",
|
||||||
.outer = outer,
|
.outer = outer,
|
||||||
.strings = NULL,
|
.strings = NULL,
|
||||||
.objects = NULL,
|
.objects = NULL,
|
||||||
|
@ -240,17 +241,24 @@ void setGlobal(struct Environment* env)
|
||||||
struct helpText {
|
struct helpText {
|
||||||
const char* symbol;
|
const char* symbol;
|
||||||
const char* help;
|
const char* help;
|
||||||
|
const char*const* tests;
|
||||||
};
|
};
|
||||||
int currentHelp = 0;
|
int currentHelp = 0;
|
||||||
struct helpText helpTexts[100];
|
struct helpText helpTexts[100];
|
||||||
|
|
||||||
#define pf(_name, _func) buildHelp(_name, &(_func), _func ## Doc)
|
#define pf(_name, _func) buildFuncSym(_name, &(_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)
|
||||||
|
|
||||||
struct symFunc buildHelp(const char* symbol, Object (* func)(Object, Object, struct Environment*), const char* help)
|
int helpInitialized = 0;
|
||||||
|
struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object, Object, struct Environment*), const char* help, const char*const* tests)
|
||||||
{
|
{
|
||||||
|
if (!helpInitialized) {
|
||||||
helpTexts[currentHelp].help = help;
|
helpTexts[currentHelp].help = help;
|
||||||
helpTexts[currentHelp].symbol = symbol;
|
helpTexts[currentHelp].symbol = symbol;
|
||||||
|
helpTexts[currentHelp].tests = tests;
|
||||||
currentHelp += 1;
|
currentHelp += 1;
|
||||||
|
}
|
||||||
|
|
||||||
return (struct symFunc) {
|
return (struct symFunc) {
|
||||||
.func = func,
|
.func = func,
|
||||||
|
@ -269,6 +277,54 @@ const char* getHelp(const char* symbol)
|
||||||
return "Help not found!";
|
return "Help not found!";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fnn(segfault, "seg", "Induces a segfault.")
|
||||||
|
(Object ignore1, Object ignore2, struct Environment* env)
|
||||||
|
{
|
||||||
|
int* p = NULL;
|
||||||
|
return numberObject(*p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns number of failures
|
||||||
|
int runTests()
|
||||||
|
{
|
||||||
|
printf("Running tests...\n");
|
||||||
|
int failureCount = 0;
|
||||||
|
int passCount = 0;
|
||||||
|
for (int hi = 0; hi < currentHelp; hi++) {
|
||||||
|
struct helpText h = helpTexts[hi];
|
||||||
|
if (h.tests) {
|
||||||
|
int ti = 0;
|
||||||
|
char result[1024];
|
||||||
|
while (h.tests[ti]) {
|
||||||
|
const char* test = h.tests[ti];
|
||||||
|
const char* expected = h.tests[ti + 1];
|
||||||
|
struct Environment env = defaultEnv();
|
||||||
|
Object o = parseEval(test, &env);
|
||||||
|
stringObj(result, &o);
|
||||||
|
cleanObject(&o);
|
||||||
|
if (strcmp(result, expected) != 0) {
|
||||||
|
failureCount++;
|
||||||
|
printf("Test failed!\n");
|
||||||
|
printf("%s\n", test);
|
||||||
|
printf("Expected '%s' but received '%s'\n", expected, result);
|
||||||
|
} else {
|
||||||
|
passCount++;
|
||||||
|
}
|
||||||
|
ti += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (passCount > 0) {
|
||||||
|
printf("[32;1m");
|
||||||
|
}
|
||||||
|
printf("%d tests passed![0m\n", passCount);
|
||||||
|
if (failureCount > 0) {
|
||||||
|
printf("[31m");
|
||||||
|
}
|
||||||
|
printf("%d tests failed![0m\n", failureCount);
|
||||||
|
return failureCount;
|
||||||
|
}
|
||||||
|
|
||||||
struct Environment defaultEnv()
|
struct Environment defaultEnv()
|
||||||
{
|
{
|
||||||
char** strings = calloc(sizeof(char*), MAX_ENV_ELM);
|
char** strings = calloc(sizeof(char*), MAX_ENV_ELM);
|
||||||
|
@ -276,6 +332,7 @@ struct Environment defaultEnv()
|
||||||
char size = MAX_ENV_ELM;
|
char size = MAX_ENV_ELM;
|
||||||
|
|
||||||
struct Environment e = {
|
struct Environment e = {
|
||||||
|
.name = "defaultEnv",
|
||||||
.outer = NULL,
|
.outer = NULL,
|
||||||
.strings = strings,
|
.strings = strings,
|
||||||
.objects = objects,
|
.objects = objects,
|
||||||
|
@ -297,8 +354,8 @@ struct Environment defaultEnv()
|
||||||
{"<", <h},
|
{"<", <h},
|
||||||
{"&", &and},
|
{"&", &and},
|
||||||
{"|", &or},
|
{"|", &or},
|
||||||
pf("cat", catObjects),
|
pft("cat", catObjects),
|
||||||
pf("fil", filter),
|
pft("fil", filter),
|
||||||
pf("len", len),
|
pf("len", len),
|
||||||
pf("ap", append),
|
pf("ap", append),
|
||||||
pf("pre", prepend),
|
pf("pre", prepend),
|
||||||
|
@ -312,22 +369,23 @@ struct Environment defaultEnv()
|
||||||
pf("isnum", isNum),
|
pf("isnum", isNum),
|
||||||
pf("islist", isList),
|
pf("islist", isList),
|
||||||
pf("isstr", isString),
|
pf("isstr", isString),
|
||||||
{"iserr", &isErr},
|
pf("iserr", isErr),
|
||||||
pf("char", charVal),
|
pf("char", charVal),
|
||||||
{"eval", &parseEvalO},
|
pf("eval", parseEvalO),
|
||||||
{"poss", &possessive},
|
pf("poss", possessive),
|
||||||
#ifdef WEBSERVER
|
#ifdef WEBSERVER
|
||||||
pf("get", addGetRoute),
|
pf("get", addGetRoute),
|
||||||
pf("post", addPostRoute),
|
pf("post", addPostRoute),
|
||||||
pf("serve", startServer),
|
pf("serve", startServer),
|
||||||
#endif
|
#endif
|
||||||
#ifdef STANDALONE
|
#ifdef STANDALONE
|
||||||
{"prn", &print},
|
pfn(segfault),
|
||||||
{"pch", &pChar},
|
pf("prn", print),
|
||||||
{"penv", &printEnvO},
|
pf("pch", pChar),
|
||||||
//{"sys", &systemCall},
|
pf("penv", printEnvO),
|
||||||
{"loadfile", &loadFile},
|
pf("sys", systemCall),
|
||||||
{"inp", &takeInput},
|
pf("loadfile", loadFile),
|
||||||
|
pf("inp", takeInput),
|
||||||
pf("?", help)
|
pf("?", help)
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
@ -336,6 +394,7 @@ struct Environment defaultEnv()
|
||||||
addFunc(symFuncs[i].sym, symFuncs[i].func, &e);
|
addFunc(symFuncs[i].sym, symFuncs[i].func, &e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
helpInitialized = 1;
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ struct Environment {
|
||||||
struct StructDef* structDefs;
|
struct StructDef* structDefs;
|
||||||
|
|
||||||
int refs;
|
int refs;
|
||||||
|
const char* name;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Environment* global();
|
struct Environment* global();
|
||||||
|
@ -44,4 +45,6 @@ int getStructIndex(struct Environment* env, const char* name);
|
||||||
|
|
||||||
const char* getHelp(const char* symbol);
|
const char* getHelp(const char* symbol);
|
||||||
|
|
||||||
|
int runTests();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -36,12 +36,13 @@ and the interpreter won't even instantly crash over it! It's truly astounding
|
||||||
stuff, when you think about it."
|
stuff, when you think about it."
|
||||||
))
|
))
|
||||||
|
|
||||||
(def homepage (fn () (html (
|
(def homepage (fn (req) (html (
|
||||||
(head (
|
(head (
|
||||||
(link ((rel "stylesheet") (href "styles.css")))
|
(link ((rel "stylesheet") (href "styles.css")))
|
||||||
))
|
))
|
||||||
(body (
|
(body (
|
||||||
(h1 "This is a sweet PebbLisp blog")
|
(h1 "This is a sweet PebbLisp site")
|
||||||
|
(p (cat "" req))
|
||||||
(htmlize p1)
|
(htmlize p1)
|
||||||
(htmlize p2)))
|
(htmlize p2)))
|
||||||
))))
|
))))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "object.h"
|
#include "object.h"
|
||||||
|
#include "pebblisp.h"
|
||||||
#include "env.h"
|
#include "env.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -476,11 +477,16 @@ void _printObj(const Object* obj, int newline)
|
||||||
printObj(&obj->lambda->body);
|
printObj(&obj->lambda->body);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char temp[200] = "";
|
char temp[200] = "";
|
||||||
stringObj(temp, obj);
|
stringObj(temp, obj);
|
||||||
if (newline) {
|
if (newline) {
|
||||||
printf("%s\n", temp);
|
printf("%s\n", temp);
|
||||||
|
if (obj->type == TYPE_ERROR) {
|
||||||
|
if (obj->error && obj->error->plContext) {
|
||||||
|
printf("%s\n", obj->error->plContext->text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
printf("%s", temp);
|
printf("%s", temp);
|
||||||
}
|
}
|
||||||
|
@ -922,6 +928,7 @@ inline Object errorObject(enum errorCode err)
|
||||||
o.error = malloc(sizeof(struct Error));
|
o.error = malloc(sizeof(struct Error));
|
||||||
o.error->code = err;
|
o.error->code = err;
|
||||||
o.error->context = NULL;
|
o.error->context = NULL;
|
||||||
|
o.error->plContext = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return o;
|
return o;
|
||||||
|
|
|
@ -85,11 +85,11 @@ typedef struct Object Object;
|
||||||
|
|
||||||
struct Lambda;
|
struct Lambda;
|
||||||
struct Environment;
|
struct Environment;
|
||||||
struct Slice;
|
|
||||||
struct Other;
|
struct Other;
|
||||||
struct Error {
|
struct Error {
|
||||||
enum errorCode code;
|
enum errorCode code;
|
||||||
char* context;
|
char* context;
|
||||||
|
struct Slice* plContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Object {
|
struct Object {
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
#ifdef STANDALONE
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <signal.h>
|
||||||
|
#include <ucontext.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "pebblisp.h"
|
#include "pebblisp.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -590,6 +596,7 @@ Result parseAtom(struct Slice* s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Slice* lastOpen = NULL;
|
||||||
Object parseEval(const char* input, struct Environment* env)
|
Object parseEval(const char* input, struct Environment* env)
|
||||||
{
|
{
|
||||||
struct Error err = noError();
|
struct Error err = noError();
|
||||||
|
@ -623,6 +630,7 @@ Object parseEval(const char* input, struct Environment* env)
|
||||||
struct Slice* tok = tokens;
|
struct Slice* tok = tokens;
|
||||||
while (tok[i].text != NULL) {
|
while (tok[i].text != NULL) {
|
||||||
if (tok[i].text[0] == '(') {
|
if (tok[i].text[0] == '(') {
|
||||||
|
lastOpen = &tok[i];
|
||||||
parens++;
|
parens++;
|
||||||
} else if (tok[i].text[0] == ')') {
|
} else if (tok[i].text[0] == ')') {
|
||||||
parens--;
|
parens--;
|
||||||
|
@ -633,6 +641,7 @@ Object parseEval(const char* input, struct Environment* env)
|
||||||
Object parsed = parse(tok).obj;
|
Object parsed = parse(tok).obj;
|
||||||
if (parsed.type == TYPE_ERROR) {
|
if (parsed.type == TYPE_ERROR) {
|
||||||
obj = parsed; // TODO Check necessity
|
obj = parsed; // TODO Check necessity
|
||||||
|
obj.error->plContext = lastOpen;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (tok[i].text[0] == ')') {
|
if (tok[i].text[0] == ')') {
|
||||||
|
@ -733,10 +742,48 @@ void loadArgsIntoEnv(int argc, const char* argv[], struct Environment* env)
|
||||||
addToEnv(env, "args", args);
|
addToEnv(env, "args", args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int nestedSegfault = 0;
|
||||||
|
void handler(int nSignum, siginfo_t* si, void* vcontext)
|
||||||
|
{
|
||||||
|
if (nestedSegfault) {
|
||||||
|
printf("Nested segfault!!!\n");
|
||||||
|
exit(139);
|
||||||
|
}
|
||||||
|
nestedSegfault = 1;
|
||||||
|
|
||||||
|
printf("Segfaulted!\n");
|
||||||
|
if (lastOpen) {
|
||||||
|
printf("line: %d\n%s\n", lastOpen->lineNumber, lastOpen->text);
|
||||||
|
} else {
|
||||||
|
printf("Happened before token processing.\n");
|
||||||
|
}
|
||||||
|
struct Environment *e = global();
|
||||||
|
*e = defaultEnv();
|
||||||
|
ucontext_t* context = vcontext;
|
||||||
|
context->uc_mcontext.gregs[REG_RIP]++;
|
||||||
|
exit(139);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, const char* argv[])
|
int main(int argc, const char* argv[])
|
||||||
{
|
{
|
||||||
struct Environment env = defaultEnv();
|
struct Environment env = defaultEnv();
|
||||||
setGlobal(&env);
|
setGlobal(&env);
|
||||||
|
|
||||||
|
if (argc == 2 && strcmp(argv[1], "--run-tests") == 0) {
|
||||||
|
runTests();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sigaction action;
|
||||||
|
memset(&action, 0, sizeof(struct sigaction));
|
||||||
|
action.sa_flags = SA_SIGINFO;
|
||||||
|
action.sa_sigaction = handler;
|
||||||
|
sigaction(SIGSEGV, &action, NULL);
|
||||||
|
|
||||||
|
// struct Environment* e = &env;
|
||||||
|
// e += 10000;
|
||||||
|
// printEnv(e);
|
||||||
|
|
||||||
readFile(SCRIPTDIR "/lib.pbl", &env);
|
readFile(SCRIPTDIR "/lib.pbl", &env);
|
||||||
if (argc >= 2) {
|
if (argc >= 2) {
|
||||||
FILE* file = fopen(argv[1], "r");
|
FILE* file = fopen(argv[1], "r");
|
||||||
|
|
|
@ -6,11 +6,22 @@
|
||||||
#include "env.h"
|
#include "env.h"
|
||||||
#include "object.h"
|
#include "object.h"
|
||||||
|
|
||||||
#define F(_name, _docs) static const char * const _name ## Doc = _docs; Object _name
|
#define fn(_name, _docs, ...) static const char * const _name ## Doc = _docs; \
|
||||||
|
Object _name
|
||||||
|
|
||||||
|
#define fnn(_name, _symbol, _docs)\
|
||||||
|
static const char * const _name ## Doc = _docs;\
|
||||||
|
static const char * const _name ## Symbol = _symbol;\
|
||||||
|
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;
|
||||||
|
int lineNumber;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct Result {
|
typedef struct Result {
|
||||||
|
@ -43,12 +54,6 @@ Object simpleFuncEval(Object func, Object arg1, Object arg2, struct Environment*
|
||||||
|
|
||||||
#ifdef STANDALONE
|
#ifdef STANDALONE
|
||||||
|
|
||||||
Object takeInput(Object i1, Object i2, struct Environment* i3);
|
|
||||||
|
|
||||||
Object systemCall(Object call, Object _, struct Environment* i3);
|
|
||||||
|
|
||||||
Object loadFile(Object filename, Object _, struct Environment* env);
|
|
||||||
|
|
||||||
int _readFile(FILE* input, struct Environment* env);
|
int _readFile(FILE* input, struct Environment* env);
|
||||||
|
|
||||||
int readFile(const char* filename, struct Environment* env);
|
int readFile(const char* filename, struct Environment* env);
|
||||||
|
|
91
src/plfunc.h
91
src/plfunc.h
|
@ -4,7 +4,7 @@
|
||||||
#include "pebblisp.h"
|
#include "pebblisp.h"
|
||||||
|
|
||||||
#define BASIC_OP(_name) \
|
#define BASIC_OP(_name) \
|
||||||
Object _name(Object obj1, Object obj2, struct Environment *env);
|
Object _name(Object obj1, Object obj2, struct Environment *env)
|
||||||
|
|
||||||
BASIC_OP(add);
|
BASIC_OP(add);
|
||||||
|
|
||||||
|
@ -28,85 +28,118 @@ BASIC_OP(or);
|
||||||
|
|
||||||
#undef BASIC_OP
|
#undef BASIC_OP
|
||||||
|
|
||||||
F(catObjects,
|
fnt(catObjects,
|
||||||
"Concatenate string versions of the given objects.\n"
|
"Concatenate string versions of the given objects.\n",
|
||||||
"(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);
|
||||||
|
|
||||||
F(filter, "(filter (< 50) (25 60 100)) => ( 60 100 )")
|
fnt(filter,
|
||||||
(Object condition, Object list, struct Environment* env);
|
"Filter a list based on the given condition.\n",
|
||||||
|
"(fil (< 50) (25 60 100))", "( 60 100 )"
|
||||||
|
)(Object condition, Object list, struct Environment* env);
|
||||||
|
|
||||||
F(append, "(ap (1 2) 3) => ( 1 2 3 )")
|
fn(append,
|
||||||
|
"Append the given element",
|
||||||
|
"(ap (1 2) 3)", "( 1 2 3 )")
|
||||||
(Object list, Object newElement, struct Environment* env);
|
(Object list, Object newElement, struct Environment* env);
|
||||||
|
|
||||||
F(prepend, "(pre (2 3) 1) => ( 1 2 3 )")
|
fn(prepend, "(pre (2 3) 1) => ( 1 2 3 )")
|
||||||
(Object list, Object newElement, struct Environment* env);
|
(Object list, Object newElement, struct Environment* env);
|
||||||
|
|
||||||
F(len, "(len (2 3)) => 2")
|
fn(len, "(len (2 3)) => 2")
|
||||||
(Object obj1, Object o_ignore, struct Environment* e_ignore);
|
(Object obj1, Object o_ignore, struct Environment* e_ignore);
|
||||||
|
|
||||||
F(reduce, "(reduce ((1 2 3) 0) +) => 6")
|
fn(reduce, "(reduce ((1 2 3) 0) +) => 6")
|
||||||
(Object listInitial, Object func, struct Environment* env);
|
(Object listInitial, Object func, struct Environment* env);
|
||||||
|
|
||||||
F(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);
|
||||||
|
|
||||||
F(rest, "(rest (1 2 3)) => ( 2 3 )")
|
fn(rest, "(rest (1 2 3)) => ( 2 3 )")
|
||||||
(Object list, Object ignore, struct Environment* env);
|
(Object list, Object ignore, struct Environment* env);
|
||||||
|
|
||||||
F(reverse, "(rev (1 2 3)) => ( 3 2 1 )")
|
fn(reverse, "(rev (1 2 3)) => ( 3 2 1 )")
|
||||||
(Object _list, Object ignore, struct Environment* ignore2);
|
(Object _list, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
F(isNum, "(isnum 1) => T\n(isnum \"Hello\") => F")
|
fn(isNum, "(isnum 1) => T\n(isnum \"Hello\") => F")
|
||||||
(Object test, Object ignore, struct Environment* ignore2);
|
(Object test, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
F(isList,
|
fn(isList,
|
||||||
"(islist (1 2 3)) => T\n"
|
"(islist (1 2 3)) => T\n"
|
||||||
"(islist ()) => T\n"
|
"(islist ()) => T\n"
|
||||||
"(islist \"Stringy\") => F"
|
"(islist \"Stringy\") => F"
|
||||||
)(Object test, Object ignore, struct Environment* ignore2);
|
)(Object test, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
F(isString,
|
fn(isString,
|
||||||
"(isstr \"Heyo\") => T\n"
|
"(isstr \"Heyo\") => T\n"
|
||||||
"(isstr \"\") => T\n"
|
"(isstr \"\") => T\n"
|
||||||
"(isstr 10) => F"
|
"(isstr 10) => F"
|
||||||
)(Object test, Object ignore, struct Environment* ignore2);
|
)(Object test, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
F(isErr, "")
|
fn(isErr,
|
||||||
(Object test, Object ignore, struct Environment* ignore2);
|
"(iserr (eval \"(((\") => T\n"
|
||||||
|
"(iserr 5) => F"
|
||||||
|
)(Object test, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
F(charAt,
|
fn(charAt,
|
||||||
"(chat \"Hello\" 1) => \"e\"\n"
|
"(chat \"Hello\" 1) => \"e\"\n"
|
||||||
"(chat \"Hello\" 10) => \"\""
|
"(chat \"Hello\" 10) => \"\""
|
||||||
)(Object string, Object at, struct Environment* ignore);
|
)(Object string, Object at, struct Environment* ignore);
|
||||||
|
|
||||||
F(charVal,
|
fn(charVal,
|
||||||
"(char \"h\") => 104\n"
|
"(char \"h\") => 104\n"
|
||||||
"(char \"hello\") => 104\n"
|
"(char \"hello\") => 104\n"
|
||||||
"(char \"\") => 0"
|
"(char \"\") => 0"
|
||||||
)(Object test, Object ignore, struct Environment* ignore2);
|
)(Object test, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
F(parseEvalO, "(eval \"(1 2 3)\") => (1 2 3)")
|
fn(parseEvalO, "(eval \"(1 2 3)\") => (1 2 3)")
|
||||||
(Object text, Object ignore, struct Environment* env);
|
(Object text, Object ignore, struct Environment* env);
|
||||||
|
|
||||||
F(possessive,
|
fn(possessive,
|
||||||
"(struct Post (title body)) (def p (Post \"TI\" \"BO\")) p's title => TI"
|
"(struct Post (title body))\n"
|
||||||
|
"(def p (Post \"TI\" \"BO\"))\n"
|
||||||
|
"p's title => TI"
|
||||||
)(Object structo, Object field, struct Environment* env);
|
)(Object structo, Object field, struct Environment* env);
|
||||||
|
|
||||||
#ifdef STANDALONE
|
#ifdef STANDALONE
|
||||||
|
|
||||||
F(print, "Prints the string representation of the given object to stdout.")
|
fn(print, "Prints the string representation of the given object to stdout.")
|
||||||
(Object p, Object ignore, struct Environment* ignore2);
|
(Object p, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
F(pChar, "Prints the ascii character for the given number value.")
|
fn(pChar, "Prints the ascii character for the given number value.")
|
||||||
(Object c, Object i1, struct Environment* i2);
|
(Object c, Object i1, struct Environment* i2);
|
||||||
|
|
||||||
F(printEnvO, "Prints out the current scoped environment.")
|
fn(printEnvO, "Prints out the current scoped environment.")
|
||||||
(Object i1, Object i2, struct Environment* env);
|
(Object i1, Object i2, struct Environment* env);
|
||||||
|
|
||||||
F(help, "(? +) => \"(+ 1 2) => 3\"")
|
fn(systemCall,
|
||||||
(Object symbol, Object ignore, struct Environment* ignore2);
|
"Opens a shell and runs the given command, returning the command's exit code.\n"
|
||||||
|
"(sys \"echo yee\")\n"
|
||||||
|
"yee\n"
|
||||||
|
"=> 0"
|
||||||
|
)(Object process, Object _, struct Environment* env);
|
||||||
|
|
||||||
#endif
|
fn(loadFile,
|
||||||
|
"Loads and parses the given file.\n"
|
||||||
|
"Returns 0 if the file was loaded and parsed successfully. Otherwise 1.\n"
|
||||||
|
"(loadfile \"printdate.pl\")\n"
|
||||||
|
"Mon 21 Mar 2022 10:35:03 AM EDT\n"
|
||||||
|
"=> 0"
|
||||||
|
)(Object filename, Object _, struct Environment* env);
|
||||||
|
|
||||||
|
fn(takeInput,
|
||||||
|
"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"
|
||||||
|
)(Object i1, Object i2, struct Environment* i3);
|
||||||
|
|
||||||
|
fn(help,
|
||||||
|
"Displays help text for the given function.\n"
|
||||||
|
"Currently requires the function name as a string, but future syntactic sugar may\n"
|
||||||
|
"loosen this requirement.\n"
|
||||||
|
"(? \"+\") => \"(+ 1 2) => 3\""
|
||||||
|
)(Object symbol, Object ignore, struct Environment* ignore2);
|
||||||
|
|
||||||
|
#endif // STANDALONE
|
||||||
|
|
||||||
#endif // PEBBLISP_PLFUNC_H
|
#endif // PEBBLISP_PLFUNC_H
|
||||||
|
|
23
src/tests.sh
23
src/tests.sh
|
@ -55,21 +55,26 @@ check() {
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
local output
|
||||||
if $VALGRIND; then
|
if $VALGRIND; then
|
||||||
echo -ne "\n $1\r "
|
echo -ne "\n $1\r "
|
||||||
local output="$($VALCOM ./pl "(loadfile \"examples/lib.pbl\") $2" | grep -v PLT)"
|
output="$($VALCOM ./pl "(loadfile \"examples/lib.pbl\") $2" | grep -v PLT)"
|
||||||
else
|
else
|
||||||
local output="$(./pl "(loadfile \"examples/lib.pbl\") $2" | grep -v PLT)"
|
output="$(./pl "(loadfile \"examples/lib.pbl\") $2" | grep -v PLT)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$output" == "$3" ]; then
|
if [ "$3" == "$regex" ]; then
|
||||||
|
if [[ "$output" =~ ^$4$ ]]; then
|
||||||
pass "$1"
|
pass "$1"
|
||||||
elif [ "$3" == "$regex" ] && [[ "$output" =~ $4 ]]; then
|
return
|
||||||
|
fi
|
||||||
|
elif [ "$output" == "$3" ]; then
|
||||||
pass "$1"
|
pass "$1"
|
||||||
else
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
fail "$1" "$2"
|
fail "$1" "$2"
|
||||||
FAIL_OUTPUT="${FAIL_OUTPUT}\n [31m expected '$3' but received '$output'\n"
|
FAIL_OUTPUT="${FAIL_OUTPUT}\n [31m expected '$3' but received '$output'\n"
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "[1;33mSTARTING TESTS[0;m"
|
echo "[1;33mSTARTING TESTS[0;m"
|
||||||
|
@ -206,9 +211,9 @@ title "ShouldError"
|
||||||
check "LenOfNotList" "(len 5)" "NOT_A_LIST"
|
check "LenOfNotList" "(len 5)" "NOT_A_LIST"
|
||||||
check "NoMapList" "(map sq)" "( )"
|
check "NoMapList" "(map sq)" "( )"
|
||||||
check "UnevenLists" "(+ (1 2) (1 2 3))" "LISTS_NOT_SAME_SIZE"
|
check "UnevenLists" "(+ (1 2) (1 2 3))" "LISTS_NOT_SAME_SIZE"
|
||||||
check "BadNumber" "(5df)" "BAD_NUMBER"
|
check "BadNumber" "(5df)" regex "BAD_NUMBER.*"
|
||||||
check "BadHex" "(0x0zf)" "BAD_NUMBER"
|
check "BadHex" "(0x0zf)" regex "BAD_NUMBER.*"
|
||||||
check "BadBinary" "(0b01120)" "BAD_NUMBER"
|
check "BadBinary" "(0b01120)" regex "BAD_NUMBER.*"
|
||||||
check "BadParens1" "(hey()" regex "'MISMATCHED_PARENS.*"
|
check "BadParens1" "(hey()" regex "'MISMATCHED_PARENS.*"
|
||||||
check "BadParens2" "(hey)(" regex "'MISMATCHED_PARENS.*"
|
check "BadParens2" "(hey)(" regex "'MISMATCHED_PARENS.*"
|
||||||
check "BadParens3" "((hey(" regex "'MISMATCHED_PARENS.*"
|
check "BadParens3" "((hey(" regex "'MISMATCHED_PARENS.*"
|
||||||
|
|
11
src/tokens.c
11
src/tokens.c
|
@ -66,6 +66,7 @@ struct Slice* nf_tokenize(const char* input, struct Error* err)
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int slice = 0;
|
int slice = 0;
|
||||||
|
int lineNumber = 1;
|
||||||
|
|
||||||
int parens = 0;
|
int parens = 0;
|
||||||
while (input[i] != '\0') {
|
while (input[i] != '\0') {
|
||||||
|
@ -73,6 +74,9 @@ struct Slice* nf_tokenize(const char* input, struct Error* err)
|
||||||
// printd("input: '%c'\n", input[i]);
|
// printd("input: '%c'\n", input[i]);
|
||||||
|
|
||||||
if (isWhitespace(input[i]) || input[i] == ';') {
|
if (isWhitespace(input[i]) || input[i] == ';') {
|
||||||
|
if (input[i] == '\n') {
|
||||||
|
lineNumber++;
|
||||||
|
}
|
||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -92,6 +96,7 @@ struct Slice* nf_tokenize(const char* input, struct Error* err)
|
||||||
}
|
}
|
||||||
|
|
||||||
slices[slice].text = &input[i];
|
slices[slice].text = &input[i];
|
||||||
|
slices[slice].lineNumber = lineNumber;
|
||||||
|
|
||||||
if (isSingle(input[i])) {
|
if (isSingle(input[i])) {
|
||||||
i++;
|
i++;
|
||||||
|
@ -105,6 +110,9 @@ struct Slice* nf_tokenize(const char* input, struct Error* err)
|
||||||
if (input[i] == '"' && input[i + 1] == '"' && input[i + 2] == '"') {
|
if (input[i] == '"' && input[i + 1] == '"' && input[i + 2] == '"') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (input[i] == '\n') {
|
||||||
|
lineNumber++;
|
||||||
|
}
|
||||||
l++;
|
l++;
|
||||||
if (input[i] == '\0' || input[i + 1] == '\0' || input[i + 2] == '\0') {
|
if (input[i] == '\0' || input[i + 1] == '\0' || input[i + 2] == '\0') {
|
||||||
err->context = malloc(sizeof(char) * ERR_LEN + 1);
|
err->context = malloc(sizeof(char) * ERR_LEN + 1);
|
||||||
|
@ -118,6 +126,9 @@ struct Slice* nf_tokenize(const char* input, struct Error* err)
|
||||||
} else {
|
} else {
|
||||||
// Simple string
|
// Simple string
|
||||||
while (input[++i] != '"' && input[i] != '\0') {
|
while (input[++i] != '"' && input[i] != '\0') {
|
||||||
|
if (input[i] == '\n') {
|
||||||
|
lineNumber++;
|
||||||
|
}
|
||||||
l++;
|
l++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,8 +80,8 @@ answer_to_connection(void* cls, struct MHD_Connection* connection,
|
||||||
Object queryParams = listObject();
|
Object queryParams = listObject();
|
||||||
MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, add_query_param, &queryParams);
|
MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, add_query_param, &queryParams);
|
||||||
|
|
||||||
Object res = structObject(requestDefinition);
|
Object res = numberObject(1010);//structObject(requestDefinition);
|
||||||
res.structObject->fields[0] = queryParams;
|
//res.structObject->fields[0] = queryParams;
|
||||||
Object route = cloneObject(routes[i].routeAction);
|
Object route = cloneObject(routes[i].routeAction);
|
||||||
|
|
||||||
Object result = listEvalLambda(&route, &res, routes[i].env);
|
Object result = listEvalLambda(&route, &res, routes[i].env);
|
||||||
|
|
|
@ -16,21 +16,21 @@ int addRoute(struct Route route);
|
||||||
|
|
||||||
int start(int port);
|
int start(int port);
|
||||||
|
|
||||||
F(startServer,
|
fn(startServer,
|
||||||
"(serve) => 0\n"
|
"(serve) => 0\n"
|
||||||
"Starts a simple web server with routes that have been added with (get) and (post).\n"
|
"Starts a simple web server with routes that have been added with (get) and (post).\n"
|
||||||
"Note: This is a non-blocking call. It is recommended to wait for user input before exiting.\n"
|
"Note: This is a non-blocking call. It is recommended to wait for user input before exiting.\n"
|
||||||
" A simple way would be to use (inp) immediately after the (serve) call."
|
" A simple way would be to use (inp) immediately after the (serve) call."
|
||||||
)(Object path, Object textFunc, struct Environment* env);
|
)(Object path, Object textFunc, struct Environment* env);
|
||||||
|
|
||||||
F(addGetRoute,
|
fn(addGetRoute,
|
||||||
"Note: Implementation bugs currently prevent using an inline lambda.\n"
|
"Note: Implementation bugs currently prevent using an inline lambda.\n"
|
||||||
" (def homepage (fn () (\"Hello, world!\")));(get \"/\" homepage)\n"
|
" (def homepage (fn () (\"Hello, world!\")));(get \"/\" homepage)\n"
|
||||||
" (def queryPage (fn (req) (req's queryParams)));(get \"/x\" queryPage)\n"
|
" (def queryPage (fn (req) (req's queryParams)));(get \"/x\" queryPage)\n"
|
||||||
" (serve)\n"
|
" (serve)\n"
|
||||||
)(Object path, Object textFunc, struct Environment* env);
|
)(Object path, Object textFunc, struct Environment* env);
|
||||||
|
|
||||||
F(addPostRoute,
|
fn(addPostRoute,
|
||||||
"Note: Implementation bugs currently prevent using an inline lambda.\n"
|
"Note: Implementation bugs currently prevent using an inline lambda.\n"
|
||||||
" (def homepage (fn () (\"Hello, world!\")));(post \"/\" homepage)\n"
|
" (def homepage (fn () (\"Hello, world!\")));(post \"/\" homepage)\n"
|
||||||
" (serve)\n"
|
" (serve)\n"
|
||||||
|
|
Loading…
Reference in New Issue