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 {
|
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;
|
size_t testCount;
|
||||||
};
|
};
|
||||||
int currentHelp = 0;
|
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))
|
#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;
|
||||||
|
@ -281,7 +283,7 @@ char* getHelp(const char* symbol)
|
||||||
textCursor += sprintf(textCursor, "\n ");
|
textCursor += sprintf(textCursor, "\n ");
|
||||||
int c = 0;
|
int c = 0;
|
||||||
int depth = 0;
|
int depth = 0;
|
||||||
while(test[c]) {
|
while (test[c]) {
|
||||||
if (test[c] == '(') {
|
if (test[c] == '(') {
|
||||||
depth += 1;
|
depth += 1;
|
||||||
textCursor += sprintf(textCursor, "[3%dm", (depth % 6) + 2);
|
textCursor += sprintf(textCursor, "[3%dm", (depth % 6) + 2);
|
||||||
|
@ -295,7 +297,12 @@ char* getHelp(const char* symbol)
|
||||||
c++;
|
c++;
|
||||||
}
|
}
|
||||||
//textCursor += sprintf(textCursor, "\n %s [1m=>[0m %s", test, expected);
|
//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;
|
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);
|
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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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]);
|
||||||
r.obj.type = TYPE_SLIST;
|
if (r.obj.type == TYPE_LIST) {
|
||||||
|
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) {
|
||||||
|
@ -757,7 +758,7 @@ void handler(int nSignum, siginfo_t* si, void* vcontext)
|
||||||
} else {
|
} else {
|
||||||
printf("Happened before token processing.\n");
|
printf("Happened before token processing.\n");
|
||||||
}
|
}
|
||||||
struct Environment *e = global();
|
struct Environment* e = global();
|
||||||
*e = defaultEnv();
|
*e = defaultEnv();
|
||||||
ucontext_t* context = vcontext;
|
ucontext_t* context = vcontext;
|
||||||
context->uc_mcontext.gregs[REG_RIP]++;
|
context->uc_mcontext.gregs[REG_RIP]++;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
108
src/plfunc.h
108
src/plfunc.h
|
@ -29,84 +29,108 @@ BASIC_OP(or);
|
||||||
#undef BASIC_OP
|
#undef BASIC_OP
|
||||||
|
|
||||||
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,
|
||||||
"Performs a simple reduction. Does not currently work with lambdas.\n"
|
"Performs a simple reduction. Does not currently work with lambdas.\n"
|
||||||
"Takes two arguments:\n"
|
"Takes two arguments:\n"
|
||||||
" - 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.",
|
||||||
"(islist (1 2 3))", "T",
|
"(islist (1 2 3))", "T",
|
||||||
"(islist ())", "T",
|
"(islist ())", "T",
|
||||||
"(islist \"Stringy\")", "F",
|
"(islist \"Stringy\")", "F",
|
||||||
)(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"
|
||||||
|
|
Loading…
Reference in New Issue