diff --git a/src/env.c b/src/env.c index 7ccb3b8..104fd07 100644 --- a/src/env.c +++ b/src/env.c @@ -19,12 +19,15 @@ Object fetchFromEnvironment(const char *name, struct Environment *env) return errorObject(EMPTY_ENV); printd("Fetching '%s' from env\n", name); - // printEnv(env); for(int i = 0; i < env->size; i++) { - printd("Try %d\n", i); - if(env->strings[i] == NULL) + if(env->strings[i] == NULL) { + printd("Try %d (NULL)\n", i); break; + } + printd("Try %d (%s)\n", i, env->strings[i]); + debugObj(&env->objects[i]); if(strcmp(name, env->strings[i]) == 0) { + printd("Returning!\n"); return env->objects[i]; } } @@ -58,7 +61,7 @@ struct Environment envForLambda(const Object *params, const Object *arg_forms, const Object *march = arg_forms; for(int i = 0; i < paramCount; i++) { - const char *newObjName = itemAt(params, i)->name; + const char *newObjName = itemAt(params, i)->string; const Object newEnvObj = eval(march, outer); addToEnv(&env, newObjName, newEnvObj); // Could use eval_forms? march = march->forward; @@ -76,16 +79,12 @@ void addToEnv(struct Environment *env, const char *name, const Object obj) if(env->strings[i] == NULL) { env->strings[i] = calloc(sizeof(char), strlen(name) + 1); strncpy(env->strings[i], name, strlen(name)); - env->objects[i] = obj; - // if(obj.type == TYPE_LIST) - // copyList(&env->objects[i], &obj); + env->objects[i] = cloneObject(obj); return; } if(strcmp(env->strings[i], name) == 0) { cleanObject(&env->objects[i]); - env->objects[i] = obj; - // if(obj.type == TYPE_LIST) - // copyList(&env->objects[i], &obj); + env->objects[i] = cloneObject(obj); return; } } @@ -102,7 +101,7 @@ void addToEnv(struct Environment *env, const char *name, const Object obj) env->strings[i] = malloc(strlen(name) + 1); strncpy(env->strings[i], name, strlen(name) + 1); - env->objects[i] = obj; + env->objects[i] = cloneObject(obj); } void printEnv(struct Environment *env) @@ -152,7 +151,7 @@ const char *codes[] = { "))", // Square a - "(def sq (fn (a) (exp a 2)))", + "(def sq (fn (a) (* a a)))", // Cube a "(def cube (fn (a) (exp a 3)))", @@ -168,7 +167,7 @@ const char *codes[] = { // A demo tip calculator "(def spent (fn (a) \ - (cat \"Tip: $\" \"\" \"\" (/ a 5) \".\" \ + (cat \"Tip: $\" (/ a 5) \".\" \ (/ (* 100 (% a 5)) 5) \ ) \ ))", @@ -194,6 +193,7 @@ struct Environment defaultEnv() addFunc("<", <h, &e); addFunc("len", &len, &e); addFunc("cat", &catObjects, &e); + addFunc("fil", &filter, &e); int i = 0; while(codes[i]) { diff --git a/src/object.c b/src/object.c index f737dd3..6be4b04 100644 --- a/src/object.c +++ b/src/object.c @@ -95,7 +95,7 @@ inline int isEmpty(const Object *obj) case TYPE_LAMBDA: return obj->lambda == NULL; case TYPE_SYMBOL: - return obj->name[0] == '\0'; + return obj->string == NULL; case TYPE_FUNC: return obj->func == NULL; case TYPE_ERROR: @@ -251,11 +251,9 @@ char* stringObj(char *dest, const Object *obj) if(t == TYPE_NUMBER) { snprintf(dest, RESULT_LENGTH, "%d", obj->number); - } else if(t == TYPE_SYMBOL) { - snprintf(dest, RESULT_LENGTH, "%s", obj->name); } else if(t == TYPE_BOOL) { snprintf(dest, RESULT_LENGTH, "%s", obj->number ? "T" : "F"); - } else if(t == TYPE_STRING) { + } else if(t == TYPE_STRING || t == TYPE_SYMBOL) { snprintf(dest, RESULT_LENGTH, "%s", obj->string); } else if(t == TYPE_LIST) { stringList(dest, obj); @@ -358,11 +356,12 @@ void printList(const Object *list) */ void cleanObject(Object *target) { + //printf("CLEANING:"); if(target == NULL) return; const Type t = target->type; - if(t == TYPE_STRING) { + if(t == TYPE_STRING || t == TYPE_SYMBOL) { free(target->string); target->string = NULL; } else if(t == TYPE_LIST) { @@ -430,9 +429,8 @@ void _copyList(Object *dest, const Object *src, int delete) nf_addToList(dest, *POINTER); tail(dest)->list = NULL; _copyList(tail(dest), POINTER, 0); - } else if (POINTER->type == TYPE_STRING) { - Object t = copyString(*POINTER); - nf_addToList(dest, t); + } else if (isStringy(*POINTER)) { + nf_addToList(dest, cloneString(*POINTER)); } else { nf_addToList(dest, *POINTER); } @@ -495,6 +493,28 @@ inline Object startList(const Object start) return list; } +inline int isStringy(const Object src) +{ + return src.type == TYPE_STRING || src.type == TYPE_SYMBOL; +} + +/** + * Clones the given lambda with new allocations + * Will behave unexpectedly if given something other than a lambda object! + */ +inline Object cloneLambda(const Object old) +{ + return constructLambda(&old.lambda->params, &old.lambda->body); +} + +Object cloneString(Object obj) +{ + const char* string = obj.string; + obj.string = malloc(sizeof(char) * (strlen(string) + 1)); + strcpy(obj.string, string); + return obj; +} + // Returns an Object with a deep copy of the given Object inline Object cloneList(const Object src) { @@ -505,7 +525,10 @@ inline Object cloneList(const Object src) inline Object cloneObject(const Object src) { - return src.type == TYPE_LIST? cloneList(src) : src; + return src.type == TYPE_LIST? cloneList(src) : + src.type == TYPE_LAMBDA? cloneLambda(src) : + isStringy(src)? cloneString(src) : + src; } inline Object numberObject(int num) @@ -518,44 +541,25 @@ inline Object numberObject(int num) inline Object boolObject(int b) { Object o = newObject(TYPE_BOOL); - o.number = b; + o.number = !!b; return o; } -inline Object symbolObject() -{ - return newObject(TYPE_SYMBOL); -} - -inline Object lambdaObject() -{ - Object o = newObject(TYPE_LAMBDA); - o.lambda = malloc(sizeof(struct Lambda)); - return o; -} - -// Skips first char! Assumed to be '"' +// Skips first and last chars! Assumed to be '"' inline Object objFromSlice(const char *string, int len) { - Object o = newObject(TYPE_STRING); - o.string = calloc(sizeof(char), len); - for(int i = 0; i < len - 1; i++) { - o.string[i] = string[i + 1]; - } - o.string[len - 1] = '\0'; + Object o = symFromSlice(&string[1], len - 1); + o.type = TYPE_STRING; return o; } -// inline Object objFromString(const char *string) -// { -// Object o = newObject(TYPE_STRING); -// o.string = calloc(strlen(string) + 1); -// for(int i = 0; i < len - 1; i++) { -// o.string[i] = string[i]; -// } -// o.string[len - 1] = '\0'; -// return o; -// } +inline Object symFromSlice(const char *string, int len) +{ + Object o = newObject(TYPE_SYMBOL); + o.string = calloc(sizeof(char), len + 1); + strncpy(o.string, string, len); + return o; +} inline Object constructLambda(const Object *params, const Object *body) { @@ -565,7 +569,8 @@ inline Object constructLambda(const Object *params, const Object *body) if(params->type != TYPE_LIST || body->type != TYPE_LIST) return errorObject(LAMBDA_ARGS_NOT_LIST); - Object o = lambdaObject(); + Object o = newObject(TYPE_LAMBDA); + o.lambda = malloc(sizeof(struct Lambda)); o.lambda->params = listObject(); o.lambda->body = listObject(); copyList(&o.lambda->params, params); @@ -580,16 +585,9 @@ inline Object errorObject(enum errorCode err) return o; } -Object copyString(Object obj) -{ - const char* string = obj.string; - obj.string = malloc(sizeof(char) * (strlen(string) + 1)); - strcpy(obj.string, string); - return obj; -} - inline Object toBool(const Object test) { if(test.number == 0) return boolObject(0); + return boolObject(1); } diff --git a/src/object.h b/src/object.h index dcb9737..90740da 100644 --- a/src/object.h +++ b/src/object.h @@ -1,9 +1,8 @@ #ifndef OBJECT_H #define OBJECT_H -#define MAX_TOK_LEN 10 // 11 #define MAX_TOK_CNT 128 // 128 -#define MAX_ENV_ELM 25 // 50 +#define MAX_ENV_ELM 50 // 50 #define FOR_POINTER_IN_LIST(_list) \ for(Object *_element = _list->list; \ @@ -36,7 +35,8 @@ enum errorCode { UNEXPECTED_FORM, LISTS_NOT_SAME_SIZE, SYMBOLS_CANT_START_WITH_DIGITS, - UNSUPPORTED_NUMBER_TYPE + UNSUPPORTED_NUMBER_TYPE, + NOT_A_SYMBOL }; //#ifdef STANDALONE @@ -56,7 +56,8 @@ static const char *errorText[] = { "UNEXPECTED_FORM", "LISTS_NOT_SAME_SIZE", "SYMBOLS_CANT_START_WITH_DIGITS", - "UNSUPPORTED_NUMBER_TYPE" + "UNSUPPORTED_NUMBER_TYPE", + "NOT_A_SYMBOL" }; //#endif @@ -84,7 +85,6 @@ struct Object { union { int number; Object *list; - char name[MAX_TOK_LEN]; char *string; Object (*func)(Object, Object, struct Environment *); struct Lambda *lambda; // Maybe better as not a pointer? @@ -121,20 +121,25 @@ void printAndClean(Object *target); void allocObject(Object **spot, const Object src); void appendList(Object *dest, const Object *src); +int isStringy(const Object src); + +Object cloneList(const Object src); +Object cloneString(Object obj); +Object cloneLambda(const Object old); +Object cloneObject(const Object src); + Object newObject(Type type); Object listObject(); Object startList(const Object start); -Object cloneList(const Object src); -Object cloneObject(const Object src); Object lambdaObject(); Object symbolObject(); Object objFromSlice(const char *string, int len); +Object symFromSlice(const char *string, int len); Object boolObject(int b); Object numberObject(int num); Object errorObject(enum errorCode err); Object constructLambda(const Object *params, const Object *body); -Object copyString(Object obj); // Object version of listLength() Object len(Object obj1, Object, struct Environment *); diff --git a/src/pebblisp.c b/src/pebblisp.c index b9a6870..fd4128d 100644 --- a/src/pebblisp.c +++ b/src/pebblisp.c @@ -12,13 +12,14 @@ Object evalDefArgs(const Object *argForms, struct Environment *env) { const Object *newSymbol = argForms; - const char *name = newSymbol->name; + const char *name = newSymbol->string; - const Object newValue = eval(newSymbol->forward, env); + Object newValue = eval(newSymbol->forward, env); addToEnv(env, name, newValue); + cleanObject(&newValue); - return *newSymbol; + return cloneObject(*newSymbol); } Object evalIfArgs(const Object *argForms, struct Environment *env) @@ -67,13 +68,16 @@ Object evalMapArgs(const Object *argForms, struct Environment *env) Object evalBuiltIns(const Object *first, const Object *rest, struct Environment *env) { - if(strcmp(first->name, "def") == 0) { + if(first->type != TYPE_SYMBOL) { + return errorObject(NOT_A_SYMBOL); + } + if(strcmp(first->string, "def") == 0) { return evalDefArgs(rest, env); - } else if(strcmp(first->name, "if") == 0) { + } else if(strcmp(first->string, "if") == 0) { return evalIfArgs(rest, env); - } else if(strcmp(first->name, "fn") == 0) { + } else if(strcmp(first->string, "fn") == 0) { return evalLambdaArgs(rest); - } else if(strcmp(first->name, "map") == 0) { + } else if(strcmp(first->string, "map") == 0) { return evalMapArgs(rest, env); } @@ -91,18 +95,14 @@ void eval_forms(Object *destList, const Object *src, struct Environment *env) Object eval(const Object *obj, struct Environment *env) { - // printf("eval():\n"); - // printObj(obj); switch(obj->type) { case TYPE_NUMBER: case TYPE_BOOL: - return *obj; // Return as is - case TYPE_STRING: - return copyString(*obj); + return cloneObject(*obj); case TYPE_SYMBOL: - return fetchFromEnvironment(obj->name, env); + return fetchFromEnvironment(obj->string, env); case TYPE_LIST: { @@ -138,6 +138,10 @@ Object eval(const Object *obj, struct Environment *env) cleanObject(&rest[i]); } + // Consider the object a partial function + if(length == 1) + return cloneObject(*obj); + return func_eval; } else if (first_eval.type == TYPE_LAMBDA) { @@ -155,6 +159,7 @@ Object eval(const Object *obj, struct Environment *env) return list; } } + case TYPE_LAMBDA: return errorObject(UNEXPECTED_FORM); @@ -213,6 +218,8 @@ Object _basicOp(const Object *obj1, const Object *obj2, const char op, return numberObject(n1 % n2); case '=': + if(obj1->type == TYPE_STRING || obj2->type == TYPE_STRING) + return boolObject(!strcmp(obj1->string, obj2->string)); return boolObject(n1 == n2); case '>': return boolObject(n1 > n2); @@ -271,6 +278,21 @@ BASIC_OP(lth, '<'); #undef BASIC_OP +Object filter(Object obj1, Object obj2, struct Environment *env) +{ + Object filteredList = listObject(); + Object *filteringList = &obj2; + FOR_POINTER_IN_LIST(filteringList) { + Object conditional = cloneObject(obj1); + nf_addToList(&conditional, *POINTER); // cloneObject()? + conditional = eval(&conditional, env); + if(conditional.number == 1) { + nf_addToList(&filteredList, *POINTER); + } + } + return filteredList; +} + void copySlice(char * dest, struct Slice *src) { if(!dest || !src) @@ -321,22 +343,23 @@ Result readSeq(struct Slice *tokens) return result(res, rest); } Result r = parse(tokens); - nf_addToList(&res, r.obj); + nf_addToList(&res, cloneObject(r.obj)); tokens = r.slices; + cleanObject(&r.obj); } } Object parseDecimal(struct Slice *s) { - int num = 0; - for(int i = 0; i < s->length; i++) { - if(!isDigit(s->text[i])) { - return errorObject(SYMBOLS_CANT_START_WITH_DIGITS); - } - num *= 10; - num += s->text[i] - '0'; + int num = 0; + for(int i = 0; i < s->length; i++) { + if(!isDigit(s->text[i])) { + return errorObject(SYMBOLS_CANT_START_WITH_DIGITS); } - return numberObject(num); + num *= 10; + num += s->text[i] - '0'; + } + return numberObject(num); } Object parseAtom(struct Slice *s) @@ -357,9 +380,7 @@ Object parseAtom(struct Slice *s) } else if (s->text[0] == '"') { return objFromSlice(s->text, s->length); } else { - Object o = symbolObject(); - copySlice(o.name, s); - return o; + return symFromSlice(s->text, s->length); } } @@ -368,9 +389,12 @@ Object parseEval(const char *input, struct Environment *env) struct Slice *tokens = nf_tokenize(input); if(!tokens) return errorObject(MISMATCHED_PARENS); + if(!tokens->text) + return symFromSlice(" ", 1); -#ifdef DEBUG + #ifdef DEBUG struct Slice *debug = tokens; + printd("start slice\n"); if(debug) { while(debug->text) { char tok[MAX_TOK_LEN]; @@ -379,7 +403,7 @@ Object parseEval(const char *input, struct Environment *env) debug++; } } -#endif + #endif int i = 0; int parens = 0; @@ -400,6 +424,7 @@ Object parseEval(const char *input, struct Environment *env) Object parsed = parse(tok).obj; tok = &tok[i + 1]; i = -1; + //printObj(&parsed); obj = eval(&parsed, env); cleanObject(&parsed); } diff --git a/src/pebblisp.h b/src/pebblisp.h index 4bfa11e..294c06b 100644 --- a/src/pebblisp.h +++ b/src/pebblisp.h @@ -35,14 +35,6 @@ void eval_forms(Object *destList, const Object *src, struct Environment *env); Object evalLambdaArgs(const Object *arg_forms); -struct Environment defaultEnv(); -void deleteEnv(struct Environment *e); -void addToEnv(struct Environment *env, const char *name, const Object obj); -Object fetchFromEnvironment(const char *name, struct Environment *env); -void printEnv(struct Environment *env); -struct Environment envForLambda(const Object *params, const Object *arg_forms, - struct Environment *outer); - Result result(Object obj, struct Slice *slices); // Slices @@ -57,5 +49,6 @@ BASIC_OP(mod); BASIC_OP(equ); BASIC_OP(gth); BASIC_OP(lth); #undef BASIC_OP Object catObjects(const Object obj1, const Object obj2, struct Environment *env); +Object filter(Object obj1, Object obj2, struct Environment *env); #endif diff --git a/src/tests.sh b/src/tests.sh index 7871e87..2b9d127 100755 --- a/src/tests.sh +++ b/src/tests.sh @@ -13,7 +13,7 @@ pass() { } fail() { - echo "$1 test FAILED" + echo "$1 test FAILED $2" ((FAILS++)) } @@ -28,7 +28,7 @@ check() { if [ "$output" == "$3" ]; then pass $1 else - fail $1 + fail $1 "$2" echo " Expected '$3' but received '$output'" fi } @@ -51,11 +51,13 @@ echo "" check "GratrThn" "(> 23847123 19375933)" "T" check "LessThan" "(< 23847123 19375933)" "F" check "Equality" "(= 987654321 987654321 )" "T" +check "StringEquality" "(= \"Bean\" \"Bean\" )" "T" +check "Stringinequality" "(= \"Beans\" \"Bean\" )" "F" echo "" check "IfReturn" "(if (T) 123456789 987654321)" "123456789" check "IfRetTwo" "(if F 123456789 987654321)" "987654321" check "EmptyCnd" "(if () T F)" "F" -# check "EtyLstLt" "(if (()) T F)" "T" +check "EtyLstLt" "(if (()) T F)" "T" echo "" check "RegLists" "(1 2 3 4 5)" "( 1 2 3 4 5 )" check "MultiTypeList" "(10 20 \"rascals\")" "( 10 20 rascals )" @@ -68,28 +70,39 @@ check "VWidwSpc" " ( + 1093 102852 ) " "103945" echo "" check "DemoFunc" "(spent 68)" "Tip: \$13.60" echo "" -check "MultStmt" "(def yee (fn (a) (* 10 a))) (yee 5)" "50" -check "DefinMap" "(def yee (fn (a) (* 10 a))) (map yee (5 10 2 (+ 12 0)))" "( 50 100 20 120 )" +check "MultStmt" "(def yee (fn (a) (* 10 a))) ; (yee 5)" "50" +check "DefinMap" "(def yee (fn (a) (* 10 a))) ; (map yee (5 10 2 (+ 12 0)))" "( 50 100 20 120 )" check "FbnciSeq" "(def fib (fn (a) \ (if (< a 2) \ a \ (+ (fib (- a 1)) (fib (- a 2))) \ - ))) \ + )));\ (fib 20)" "6765" +check "Factorial" "(def fac (fn (a) \ + (if (= a 1) \ + 1 \ + (* a (fac (- a 1)) ) \ + )));\ + (fac 11)" "39916800" echo "" check "ExplicitCat" "(cat \"Big\" \" Kitty\")" "Big Kitty" check "CatNums" "(cat \"There are \" (+ 2 3) \" kitties\")" "There are 5 kitties" check "ImplicitCat" "(+ \"There are \" (* 5 4) \" bonks\")" "There are 20 bonks" -# Not recommended +# Mixing of `+` and implicit cat not recommended: check "CatAssocLeft" "(+ 10 20 \" rascals\")" "30 rascals" echo "" +check "LambdaClone" "(def y (fn (a) (* 10 a))) (def b y) (def y 12345) ((b 5) y)" "( 50 12345 )" +echo "" check "EvalElems" "((* 10 10) 7)" "( 100 7 )" -check "Duplicate" "(def dupe (fn (a) (a a a))) (dupe (*10 10))" "( 100 100 100 )" +check "Duplicate" "(def dupe (fn (a) (a a a)));(dupe (*10 10))" "( 100 100 100 )" echo "" check "Squaring" "(sq 9876)" "97535376" check "Cubing" "(cube 81)" "531441" check "Exponent" "(exp 9 9)" "387420489" echo "" +check "Filtering" "(fil (< 321) (30 300 90 1200 135 801))" "( 1200 801 )" +check "MapFilter" "(fil (< 50) (map sq (1 2 3 4 5 6 7 8 9 10 11 12)))" "( 64 81 100 121 144 )" +echo "" if [ "$FAILS" -ne "0" ]; then echo "$FAILS Tests Failed"