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:
parent
bf2b9504a6
commit
d743970b1a
15
src/env.c
15
src/env.c
|
@ -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 [1m=>[0m %s", test, expected);
|
||||
textCursor += sprintf(textCursor, " [0m[1m=>[0m %s", expected);
|
||||
textCursor += sprintf(textCursor, " [0m[1m=>[0m ");
|
||||
if (expected[0] == '\0') {
|
||||
textCursor += sprintf(textCursor, "<empty string>");
|
||||
} else {
|
||||
textCursor += sprintf(textCursor, "%s", expected);
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]++;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
108
src/plfunc.h
108
src/plfunc.h
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue