Use malloc'd strings. Add filter function

This commit is contained in:
= 2020-05-22 06:16:45 +01:00
parent ba2cf83759
commit 44c9c7a8a1
6 changed files with 146 additions and 112 deletions

View File

@ -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("<", &lth, &e);
addFunc("len", &len, &e);
addFunc("cat", &catObjects, &e);
addFunc("fil", &filter, &e);
int i = 0;
while(codes[i]) {

View File

@ -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);
}

View File

@ -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 *);

View File

@ -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,8 +343,9 @@ 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);
}
}
@ -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
struct Slice *debug = tokens;
printd("start slice\n");
if(debug) {
while(debug->text) {
char tok[MAX_TOK_LEN];
@ -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);
}

View File

@ -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

View File

@ -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"