Tweak (eval) to accept SLIST arguments.

Tweak implicit-cat detection.
Explicit empty-string notation in test display.
A couple more tests.
This commit is contained in:
Sage Vaillancourt 2022-03-23 01:00:11 -04:00
parent bf2b9504a6
commit d743970b1a
7 changed files with 101 additions and 55 deletions

View File

@ -241,7 +241,7 @@ void setGlobal(struct Environment* env)
struct helpText {
const char* symbol;
const char* help;
const char*const* tests;
const char* const* tests;
size_t testCount;
};
int currentHelp = 0;
@ -251,7 +251,9 @@ struct helpText helpTexts[100];
#define pf(_name, _func) buildFuncSym(_name, &(_func), _func ## Doc, _func ## Tests, array_length(_func ## Tests))
int helpInitialized = 0;
struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object, Object, struct Environment*), const char* help, const char*const* tests, size_t testLength)
struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object, Object, struct Environment*), const char* help,
const char* const* tests, size_t testLength)
{
if (!helpInitialized) {
helpTexts[currentHelp].help = help;
@ -281,7 +283,7 @@ char* getHelp(const char* symbol)
textCursor += sprintf(textCursor, "\n ");
int c = 0;
int depth = 0;
while(test[c]) {
while (test[c]) {
if (test[c] == '(') {
depth += 1;
textCursor += sprintf(textCursor, "[3%dm", (depth % 6) + 2);
@ -295,7 +297,12 @@ char* getHelp(const char* symbol)
c++;
}
//textCursor += sprintf(textCursor, "\n %s => %s", test, expected);
textCursor += sprintf(textCursor, " => %s", expected);
textCursor += sprintf(textCursor, " => ");
if (expected[0] == '\0') {
textCursor += sprintf(textCursor, "<empty string>");
} else {
textCursor += sprintf(textCursor, "%s", expected);
}
}
return text;
}

View File

@ -907,6 +907,11 @@ inline int eitherIs(const enum Type type, const Object* o1, const Object* o2)
return (o1->type == type) || (o2->type == type);
}
inline int eitherIsNot(const enum Type type, const Object* o1, const Object* o2)
{
return (o1->type != type) || (o2->type != type);
}
inline int areSameType(const Object* obj1, const Object* obj2)
{
return obj1->type == obj2->type;

View File

@ -192,6 +192,8 @@ int isError(Object obj, enum errorCode err);
int bothAre(enum Type type, const Object* obj1, const Object* obj2);
int eitherIsNot(const enum Type type, const Object* o1, const Object* o2);
int eitherIs(enum Type type, const Object* obj1, const Object* obj2);
int areSameType(const Object* obj1, const Object* obj2);

View File

@ -1,7 +1,9 @@
#ifdef STANDALONE
#define _GNU_SOURCE
#include <signal.h>
#include <ucontext.h>
#endif
#include "pebblisp.h"
@ -399,17 +401,12 @@ Object eval(const Object* obj, struct Environment* env)
case TYPE_BOOL:
case TYPE_STRING:
case TYPE_STRUCT:
case TYPE_SLIST:
return cloneObject(*obj);
case TYPE_SYMBOL:
return fetchFromEnvironment(obj->string, env);
case TYPE_SLIST: {
Object o = cloneObject(*obj);
o.type = TYPE_LIST;
return o;
}
case TYPE_LIST:
return evalList(obj, env);
@ -480,7 +477,9 @@ Result parse(struct Slice* slices)
struct Slice* rest = &slices[1];
if (token->text[0] == '\'' && token->text[1] == '(') {
Result r = readSeq(&slices[2]);
r.obj.type = TYPE_SLIST;
if (r.obj.type == TYPE_LIST) {
r.obj.type = TYPE_SLIST;
}
return r;
} else if (token->text[0] == '(') {
// todo check for null rest
@ -597,6 +596,7 @@ Result parseAtom(struct Slice* s)
}
struct Slice* lastOpen = NULL;
Object parseEval(const char* input, struct Environment* env)
{
struct Error err = noError();
@ -743,6 +743,7 @@ void loadArgsIntoEnv(int argc, const char* argv[], struct Environment* env)
}
int nestedSegfault = 0;
void handler(int nSignum, siginfo_t* si, void* vcontext)
{
if (nestedSegfault) {
@ -757,7 +758,7 @@ void handler(int nSignum, siginfo_t* si, void* vcontext)
} else {
printf("Happened before token processing.\n");
}
struct Environment *e = global();
struct Environment* e = global();
*e = defaultEnv();
ucontext_t* context = vcontext;
context->uc_mcontext.gregs[REG_RIP]++;

View File

@ -43,6 +43,8 @@ Result parseAtom(struct Slice* slice);
Object parseEval(const char* input, struct Environment* env);
Object evalList(const Object* obj, struct Environment* env);
void evalForms(Object* destList, const Object* src, struct Environment* env);
void copySlice(char* dest, struct Slice* src);

View File

@ -86,6 +86,9 @@ Object rest(Object list, Object ignore, struct Environment* env)
Object reverse(Object _list, Object ignore, struct Environment* ignore2)
{
if (!isListy(_list)) {
return errorObject(NOT_A_LIST);
}
const Object* list = &_list;
Object rev = listObject();
@ -137,6 +140,8 @@ Object parseEvalO(Object text, Object ignore, struct Environment* env)
Object parsed = parseEval(string.string, env);
cleanObject(&string);
return parsed;
} else if (text.type == TYPE_SLIST) {
return evalList(&text, env);
} else if (text.type != TYPE_STRING) {
return errorObject(CAN_ONLY_EVAL_STRINGS);
}
@ -187,7 +192,7 @@ Object _basicOp(const Object* obj1, const Object* obj2, const char op,
switch (op) {
case '+':
if (eitherIs(TYPE_STRING, obj1, obj2)) {
if (eitherIsNot(TYPE_NUMBER, obj1, obj2)) {
return catObjects(*obj1, *obj2, env);
}
return numberObject(n1 + n2);

View File

@ -29,84 +29,108 @@ BASIC_OP(or);
#undef BASIC_OP
fn(catObjects,
"Concatenate string versions of the given objects.",
"(cat \"Stuff: \" (1 2 3))", "Stuff: ( 1 2 3 )"
"Concatenate string versions of the given objects.",
"(cat \"Stuff: \" (1 2 3))", "Stuff: ( 1 2 3 )",
)(Object obj1, Object obj2, struct Environment* env);
fn(filter,
"Filter a list based on the given condition.",
"(fil (< 50) (25 60 100))", "( 60 100 )"
"Filter a list based on the given condition.",
"(fil (< 50) (25 60 100))", "( 60 100 )",
)(Object condition, Object list, struct Environment* env);
fn(append,
"Append the given element. Creates a new list.",
"(ap (1 2) 3)", "( 1 2 3 )"
"Append the given element. Creates a new list.",
"(ap (1 2) 3)", "( 1 2 3 )",
)(Object list, Object newElement, struct Environment* env);
fn(prepend,
"Prepend the given element. Creates a new list",
"(pre (2 3) 1)", "( 1 2 3 )"
"Prepend the given element. Creates a new list",
"(pre (2 3) 1)", "( 1 2 3 )",
)(Object list, Object newElement, struct Environment* env);
fn(len,
"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"
"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,
"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"
"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")
(Object index, Object list, struct Environment* env);
fn(at,
"Get item at the given index in the given list.",
"(at 1 (1 2 3))", "2",
"(at 99 (1 2 3))", "INDEX_PAST_END",
"(at 99 \"string\")", "INDEX_PAST_END",
)(Object index, Object list, struct Environment* env);
fn(rest, "(rest (1 2 3)) => ( 2 3 )")
(Object list, Object ignore, struct Environment* env);
fn(rest,
"Get the tail of a list. All but the first element.",
"(rest (1 2 3))", "( 2 3 )",
"(rest ())", "( )",
"(rest \"string\")", "( )",
)(Object list, Object ignore, struct Environment* env);
fn(reverse, "(rev (1 2 3)) => ( 3 2 1 )")
(Object _list, Object ignore, struct Environment* ignore2);
fn(reverse,
"Reverse a list.",
"(rev (1 2 3))", "( 3 2 1 )",
"(rev \"string\")", "NOT_A_LIST",
)(Object _list, Object ignore, struct Environment* ignore2);
fn(isNum, "(isnum 1) => T\n(isnum \"Hello\") => F")
(Object test, Object ignore, struct Environment* ignore2);
fn(isNum,
"Returns `T` only if the argument evaluates to a number.",
"(isnum 1)", "T",
"(isnum (+ 5 5))", "T",
"(isnum '(+ 5 5))", "F",
"(isnum \"Hello\")", "F",
)(Object test, Object ignore, struct Environment* ignore2);
fn(isList,
"Returns `T` only if the argument is a list.",
"(islist (1 2 3))", "T",
"(islist ())", "T",
"(islist \"Stringy\")", "F",
"Returns `T` only if the argument is a list.",
"(islist (1 2 3))", "T",
"(islist ())", "T",
"(islist \"Stringy\")", "F",
)(Object test, Object ignore, struct Environment* ignore2);
fn(isString,
"(isstr \"Heyo\") => T\n"
"(isstr \"\") => T\n"
"(isstr 10) => F"
"Returns `T` only if the argument is a string.",
"(isstr \"Heyo\")", "T",
"(isstr \"\")", "T",
"(isstr (cat 5 5))", "T",
"(isstr 10)", "F",
)(Object test, Object ignore, struct Environment* ignore2);
fn(isErr,
"(iserr (eval \"(((\") => T\n"
"(iserr 5) => F"
"Check if the argument is an error.",
"(iserr (at 10 ()))", "T",
"(iserr 5)", "F",
)(Object test, Object ignore, struct Environment* ignore2);
fn(charAt,
"(chat \"Hello\" 1) => \"e\"\n"
"(chat \"Hello\" 10) => \"\""
"Get the char in the given string at the given index.",
"(chat \"Hello\" 1)", "e",
"(chat \"Hello\" 10)", "",
)(Object string, Object at, struct Environment* ignore);
fn(charVal,
"(char \"h\") => 104\n"
"(char \"hello\") => 104\n"
"(char \"\") => 0"
"Get the ascii integer representaton of the given character.",
"(char \"h\")", "104",
"(char \"hello\")", "104",
"(char \"\")", "0",
)(Object test, Object ignore, struct Environment* ignore2);
fn(parseEvalO, "(eval \"(1 2 3)\") => (1 2 3)")
(Object text, Object ignore, struct Environment* env);
fn(parseEvalO,
"Evaluate the given string or quoted list.",
"(eval \"(1 2 3)\")", "( 1 2 3 )",
"(eval '(+ 5 5))", "10",
)(Object text, Object ignore, struct Environment* env);
fn(possessive,
"(struct Post (title body))\n"