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

@ -251,7 +251,9 @@ struct helpText helpTexts[100];
#define pf(_name, _func) buildFuncSym(_name, &(_func), _func ## Doc, _func ## Tests, array_length(_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, 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) { if (!helpInitialized) {
helpTexts[currentHelp].help = help; helpTexts[currentHelp].help = help;
@ -295,7 +297,12 @@ char* getHelp(const char* symbol)
c++; c++;
} }
//textCursor += sprintf(textCursor, "\n %s => %s", test, expected); //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; 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); 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) inline int areSameType(const Object* obj1, const Object* obj2)
{ {
return obj1->type == obj2->type; 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 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 eitherIs(enum Type type, const Object* obj1, const Object* obj2);
int areSameType(const Object* obj1, const Object* obj2); int areSameType(const Object* obj1, const Object* obj2);

View File

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

View File

@ -43,6 +43,8 @@ Result parseAtom(struct Slice* slice);
Object parseEval(const char* input, struct Environment* env); 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 evalForms(Object* destList, const Object* src, struct Environment* env);
void copySlice(char* dest, struct Slice* src); 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) Object reverse(Object _list, Object ignore, struct Environment* ignore2)
{ {
if (!isListy(_list)) {
return errorObject(NOT_A_LIST);
}
const Object* list = &_list; const Object* list = &_list;
Object rev = listObject(); Object rev = listObject();
@ -137,6 +140,8 @@ Object parseEvalO(Object text, Object ignore, struct Environment* env)
Object parsed = parseEval(string.string, env); Object parsed = parseEval(string.string, env);
cleanObject(&string); cleanObject(&string);
return parsed; return parsed;
} else if (text.type == TYPE_SLIST) {
return evalList(&text, env);
} else if (text.type != TYPE_STRING) { } else if (text.type != TYPE_STRING) {
return errorObject(CAN_ONLY_EVAL_STRINGS); return errorObject(CAN_ONLY_EVAL_STRINGS);
} }
@ -187,7 +192,7 @@ Object _basicOp(const Object* obj1, const Object* obj2, const char op,
switch (op) { switch (op) {
case '+': case '+':
if (eitherIs(TYPE_STRING, obj1, obj2)) { if (eitherIsNot(TYPE_NUMBER, obj1, obj2)) {
return catObjects(*obj1, *obj2, env); return catObjects(*obj1, *obj2, env);
} }
return numberObject(n1 + n2); return numberObject(n1 + n2);

View File

@ -30,29 +30,29 @@ BASIC_OP(or);
fn(catObjects, fn(catObjects,
"Concatenate string versions of the given objects.", "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);
fn(filter, fn(filter,
"Filter a list based on the given condition.", "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. 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 )",
)(Object list, Object newElement, struct Environment* env); )(Object list, Object newElement, struct Environment* env);
fn(prepend, fn(prepend,
"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 )",
)(Object list, Object newElement, struct Environment* env); )(Object list, Object newElement, struct Environment* env);
fn(len, fn(len,
"Returns the length of the given list, or a NOT_A_LIST error if the expression is 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 (2 3))", "2",
"(len ())", "0", "(len ())", "0",
"(len \"string\")", "NOT_A_LIST" "(len \"string\")", "NOT_A_LIST",
)(Object obj1, Object o_ignore, struct Environment* e_ignore); )(Object obj1, Object o_ignore, struct Environment* e_ignore);
fn(reduce, fn(reduce,
@ -61,20 +61,36 @@ fn(reduce,
" - A two-element list: (`values` `initial`)\n" " - A two-element list: (`values` `initial`)\n"
" - A function to apply to each value.", " - A function to apply to each value.",
"(reduce (5 6) +)", "11", "(reduce (5 6) +)", "11",
"(reduce ((1 2 3) 0) +)", "6" "(reduce ((1 2 3) 0) +)", "6",
)(Object listInitial, Object func, struct Environment* env); )(Object listInitial, Object func, struct Environment* env);
fn(at, "(at 1 (1 2 3)) => 2") fn(at,
(Object index, Object list, struct Environment* env); "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 )") fn(rest,
(Object list, Object ignore, struct Environment* env); "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 )") fn(reverse,
(Object _list, Object ignore, struct Environment* ignore2); "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") fn(isNum,
(Object test, Object ignore, struct Environment* ignore2); "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, fn(isList,
"Returns `T` only if the argument is a list.", "Returns `T` only if the argument is a list.",
@ -84,29 +100,37 @@ fn(isList,
)(Object test, Object ignore, struct Environment* ignore2); )(Object test, Object ignore, struct Environment* ignore2);
fn(isString, fn(isString,
"(isstr \"Heyo\") => T\n" "Returns `T` only if the argument is a string.",
"(isstr \"\") => T\n" "(isstr \"Heyo\")", "T",
"(isstr 10) => F" "(isstr \"\")", "T",
"(isstr (cat 5 5))", "T",
"(isstr 10)", "F",
)(Object test, Object ignore, struct Environment* ignore2); )(Object test, Object ignore, struct Environment* ignore2);
fn(isErr, fn(isErr,
"(iserr (eval \"(((\") => T\n" "Check if the argument is an error.",
"(iserr 5) => F" "(iserr (at 10 ()))", "T",
"(iserr 5)", "F",
)(Object test, Object ignore, struct Environment* ignore2); )(Object test, Object ignore, struct Environment* ignore2);
fn(charAt, fn(charAt,
"(chat \"Hello\" 1) => \"e\"\n" "Get the char in the given string at the given index.",
"(chat \"Hello\" 10) => \"\"" "(chat \"Hello\" 1)", "e",
"(chat \"Hello\" 10)", "",
)(Object string, Object at, struct Environment* ignore); )(Object string, Object at, struct Environment* ignore);
fn(charVal, fn(charVal,
"(char \"h\") => 104\n" "Get the ascii integer representaton of the given character.",
"(char \"hello\") => 104\n" "(char \"h\")", "104",
"(char \"\") => 0" "(char \"hello\")", "104",
"(char \"\")", "0",
)(Object test, Object ignore, struct Environment* ignore2); )(Object test, Object ignore, struct Environment* ignore2);
fn(parseEvalO, "(eval \"(1 2 3)\") => (1 2 3)") fn(parseEvalO,
(Object text, Object ignore, struct Environment* env); "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, fn(possessive,
"(struct Post (title body))\n" "(struct Post (title body))\n"