diff --git a/src/env.c b/src/env.c index 35d1a3b..86c3f39 100644 --- a/src/env.c +++ b/src/env.c @@ -127,7 +127,7 @@ void addToEnv(struct Environment* env, const char* name, const Object obj) if (is_local) { temp_env->strings[i] = calloc(sizeof(char), strlen(name) + 1); - strncpy(temp_env->strings[i], name, strlen(name)); + strcpy(temp_env->strings[i], name); temp_env->objects[i] = cloneObject(obj); return; } @@ -155,7 +155,7 @@ void addToEnv(struct Environment* env, const char* name, const Object obj) } env->strings[old_size] = malloc(strlen(name) + 1); - strncpy(env->strings[old_size], name, strlen(name) + 1); + strcpy(env->strings[old_size], name); env->objects[old_size] = cloneObject(obj); } @@ -278,10 +278,40 @@ const int purple = 35; const int teal = 36; const int white = 37; const int colors[] = { - green, cyan, yellow, purple, red, white + white, green, cyan, yellow, purple, red }; const int colorCount = array_length(colors); +int getColor(int depth) +{ + depth = depth >= 0 ? depth : depth * -1; + return colors[depth % colorCount]; +} + +void printColored(const char* code) +{ + int c = 0; + int depth = 0; + printf("[%dm", getColor(depth)); + while (code[c]) { + if (code[c] == '(') { + depth += 1; + printf("[%dm", getColor(depth)); + } else if (code[c] == ')') { + depth -= 1; + printf(")[%dm", getColor(depth)); + c++; + continue; + } else if (code[c] == '=' && code[c + 1] == '>') { + printf("=>[0;%dm", getColor(depth)); + c += 2; + continue; + } + printf("%c", code[c]); + c++; + } +} + char* getHelp(const char* symbol) { for (int i = 0; i < currentHelp; i++) { @@ -293,24 +323,7 @@ char* getHelp(const char* symbol) for (int ti = 0; ti < h.testCount; ti += 2) { const char* test = h.tests[ti]; const char* expected = h.tests[ti + 1]; - textCursor += sprintf(textCursor, "\n "); - int c = 0; - int depth = -1; - while (test[c]) { - if (test[c] == '(') { - depth += 1; - textCursor += sprintf(textCursor, "[%dm", colors[depth % colorCount]); - } else if (test[c] == ')') { - depth -= 1; - textCursor += sprintf(textCursor, ")[%dm", colors[depth % colorCount]); - c++; - continue; - } - textCursor += sprintf(textCursor, "%c", test[c]); - c++; - } - //textCursor += sprintf(textCursor, "\n %s => %s", test, expected); - textCursor += sprintf(textCursor, " => "); + textCursor += sprintf(textCursor, "\n %s => ", test); if (expected[0] == '\0') { textCursor += sprintf(textCursor, ""); } else { @@ -320,7 +333,10 @@ char* getHelp(const char* symbol) return text; } } - return "Help not found!"; + const char* notFound = "Help not found!"; + char* help = malloc(sizeof(char) * (strlen(notFound) + 1)); + sprintf(help, "%s", notFound); + return help; } fnn(segfault, "seg", "Induces a segfault.") @@ -428,7 +444,7 @@ struct Environment defaultEnv() pf("seg", segfault), //pfn(segfault), pf("prn", print), - pf("pch", pChar), + pf("ch", numToChar), pf("penv", printEnvO), pf("sys", systemCall), pf("loadfile", loadFile), diff --git a/src/env.h b/src/env.h index 36574ec..eaf4147 100644 --- a/src/env.h +++ b/src/env.h @@ -46,6 +46,8 @@ int getStructIndex(struct Environment* env, const char* name); /// Needs to be freed! char* getHelp(const char* symbol); +void printColored(const char* code); + int runTests(); #endif diff --git a/src/object.c b/src/object.c index e9251bc..0b61491 100644 --- a/src/object.c +++ b/src/object.c @@ -590,6 +590,7 @@ void cleanObject(Object* target) break; case TYPE_ERROR: #ifndef SIMPLE_ERRORS + free(target->error->plContext); free(target->error->context); free(target->error); target->error = NULL; diff --git a/src/pebblisp.c b/src/pebblisp.c index 7f92053..c5f0293 100644 --- a/src/pebblisp.c +++ b/src/pebblisp.c @@ -479,9 +479,16 @@ Result parse(struct Slice* slices) } } +#ifdef SUGAR +#define sugar(_desc, _code) ; +#else +#define sugar(_desc, _code) _code +#endif + Result readSeq(struct Slice* tokens) { Object res = listObject(); + int isHelp = 0; for (;;) { struct Slice* next = tokens; struct Slice* rest = next->text ? &next[1] : NULL; @@ -489,12 +496,18 @@ Result readSeq(struct Slice* tokens) return (Result) {res, rest}; } Result r = parse(tokens); + sugar("(? fil) => (? 'fil')", + if (isHelp && r.obj.type == TYPE_SYMBOL) { + r.obj.type = TYPE_STRING; + } + ) if (r.obj.type == TYPE_ERROR) { return r; } nf_addToList(&res, cloneObject(r.obj)); tokens = r.slices; cleanObject(&r.obj); + isHelp = next->text[0] == '?'; } } @@ -626,7 +639,8 @@ Object parseEval(const char* input, struct Environment* env) Object parsed = parse(tok).obj; if (parsed.type == TYPE_ERROR) { obj = parsed; // TODO Check necessity - obj.error->plContext = lastOpen; + obj.error->plContext = malloc(sizeof(struct Slice)); + *obj.error->plContext = *lastOpen; break; } if (tok[i].text[0] == ')') { @@ -668,7 +682,7 @@ int _readFile(FILE* input, struct Environment* env) char line[LINE_MAX]; if (fgets(line, LINE_MAX, input)) { if (line[0] != '#' || line[1] != '!') { - strncat(page, line, strlen(line) - 1); + strcat(page, line); } } int isQuote = 0; @@ -713,7 +727,10 @@ void repl(struct Environment* env) } add_history(buf); Object o = parseEval(buf, env); - printObj(&o); + char output[1024]; + stringNObj(output, &o, 1024); + printColored(output); + printf("\n"); cleanObject(&o); free(buf); } diff --git a/src/plfunc.c b/src/plfunc.c index 1577e48..c2cf18a 100644 --- a/src/plfunc.c +++ b/src/plfunc.c @@ -39,14 +39,15 @@ Object filter(Object* params, int length, struct Environment* env) Object filtered = listObject(); FOR_POINTER_IN_LIST(&list) { - Object l = startList(cloneObject(condition)); + Object cloned = cloneObject(condition); + Object first_eval = eval(&cloned, env); Object testee = cloneObject(*POINTER); - nf_addToList(&l, testee); - Object e = eval(&l, env); + Object e = listEvalLambda(&first_eval, &testee, env); if (e.number) { nf_addToList(&filtered, testee); // May need to re-clone testee? } - cleanObject(&l); + cleanObject(&e); + cleanObject(&cloned); } return filtered; } @@ -144,14 +145,11 @@ Object isString(Object* params, int length, struct Environment* env) return test.type == TYPE_STRING ? boolObject(1) : boolObject(0); } -// Get the int value of a string's first character Object charVal(Object* params, int length, struct Environment* env) { Object test = params[0]; return numberObject(test.string[0]); - // return test.type == TYPE_STRING && test.string[0] == '\0' ? - // boolObject(1) : boolObject(0); } Object isErr(Object* params, int length, struct Environment* env) @@ -165,17 +163,20 @@ Object parseEvalO(Object* params, int length, struct Environment* env) { Object text = params[0]; - if (text.type == TYPE_SYMBOL) { - Object string = eval(&text, 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); + switch (text.type) { + case TYPE_SYMBOL: { + Object string = eval(&text, env); + Object parsed = parseEval(string.string, env); + cleanObject(&string); + return parsed; + } + case TYPE_SLIST: + return evalList(&text, env); + case TYPE_STRING: + return parseEval(text.string, env); + default: + return errorObject(CAN_ONLY_EVAL_STRINGS); } - return parseEval(text.string, env); } Object listEquality(const Object* list1, const Object* list2) @@ -231,21 +232,6 @@ Object _basicOp(const Object* obj1, const Object* obj2, const char op, const int n2 = obj2->number; switch (op) { - case '+': - if (eitherIsNot(TYPE_NUMBER, obj1, obj2)) { - Object objects[2] = {*obj1, *obj2}; - return catObjects(objects, 2, env); - } - return numberObject(n1 + n2); - case '-': - return numberObject(n1 - n2); - case '*': - return numberObject(n1 * n2); - case '/': - return numberObject(n1 / n2); - case '%': - return numberObject(n1 % n2); - case '&': return boolObject(n1 != 0 && n2 != 0); case '|': @@ -363,24 +349,21 @@ BASIC_OP(or, '|') Object print(Object* params, int length, struct Environment* env) { - Object p = params[0]; - Object c = cloneObject(p); - Object e = eval(&c, env); - _printObj(&e, 0); - cleanObject(&c); - cleanObject(&e); + for (int i = 0; i < length; i++) { + _printObj(¶ms[i], 0); + } return numberObject(0); } -Object pChar(Object* params, int length, struct Environment* env) +Object numToChar(Object* params, int length, struct Environment* env) { Object c = params[0]; if (c.type != TYPE_NUMBER) { return errorObject(BAD_NUMBER); } - printf("%c", c.number % 256); - return numberObject(0); + char ch[1] = {c.number % 256}; + return stringFromSlice(ch, 1); } Object printEnvO(Object* params, int length, struct Environment* env) @@ -392,12 +375,14 @@ Object printEnvO(Object* params, int length, struct Environment* env) Object takeInput(Object* params, int length, struct Environment* env) { Object prompt = params[0]; - if (prompt.type == TYPE_STRING) { + if (length > 0 && prompt.type == TYPE_STRING) { printf("%s", prompt.string); } char input[256] = ""; - fgets(input, 256, stdin); - return stringFromSlice(input, strlen(input) - 1); + if (fgets(input, 256, stdin)) { + return stringFromSlice(input, strlen(input) - 1); + } + return errorWithContext(NULL_PARSE, "fgets() error"); } Object loadFile(Object* params, int length, struct Environment* env) diff --git a/src/plfunc.h b/src/plfunc.h index a9f6b4e..08f5a53 100644 --- a/src/plfunc.h +++ b/src/plfunc.h @@ -38,6 +38,7 @@ fn(catObjects, fn(filter, "Filter a list based on the given condition.", "(fil (fn (a) (< 50 a)) (25 60 100))", "( 60 100 )", + "(fil (fn (a) (< 0 (len a))) ( () (1) (1 2) () ))", "( ( 1 ) ( 1 2 ) )", ); /// LIST, ANY => LIST @@ -160,15 +161,19 @@ fn(possessive, fn(print, "Prints the string representation of the given object to stdout."); -fn(pChar, "Prints the ascii character for the given number value."); +fn(numToChar, + "Gets a string containing the ascii character for the given number value.", + "(ch 107)", "k", + "(ch 0x21)", "!", +); fn(printEnvO, "Prints out the current scoped environment."); fn(systemCall, - "Opens a shell and runs the given command, returning the command's exit code.\n" - "(sys \"echo yee\")\n" - "yee\n" - "=> 0" + "Opens a shell and runs the given command, returning 0 if successful.\n" + "If the argument is not a string, returns 255.\n", + "(sys \"echo yee > /dev/null\")", "0", + "(sys 5)", "255", ); fn(loadFile, @@ -179,8 +184,6 @@ fn(loadFile, "=> 0" ); - - /// @code /// () => STRING /// STRING => STRING diff --git a/src/tests.sh b/src/tests.sh index a22d0c3..185178e 100755 --- a/src/tests.sh +++ b/src/tests.sh @@ -1,9 +1,10 @@ #!/bin/bash -VALCOM="valgrind -q --leak-check=full --track-origins=yes" +VALCOM="valgrind -q --leak-check=full --error-exitcode=1 --track-origins=yes" TOTAL_PASSES=0 TOTAL_FAILS=0 +TOTAL_VALGRIND_ERRORS=0 FAILS=0 FAIL_OUTPUT="" VALGRIND=false @@ -37,7 +38,12 @@ title() { } pass() { - echo -n "✓" + local color=32 + if [[ "$2" != "0" ]]; then + ((TOTAL_VALGRIND_ERRORS++)) + color=33 + fi + echo -n "[1;${color}m✓" ((TOTAL_PASSES++)) } @@ -56,20 +62,26 @@ check() { fi local output + local exit_code=0 if $VALGRIND; then - echo -ne "\n $1\r " - output="$($VALCOM ./pl "(loadfile \"examples/lib.pbl\") $2" | grep -v PLT)" + echo -ne "\n $1" + output="$($VALCOM ./pl "(loadfile \"examples/lib.pbl\") $2")" + exit_code=$? + if [[ "$exit_code" == "0" ]]; then + echo -ne "\r " + fi else - output="$(./pl "(loadfile \"examples/lib.pbl\") $2" | grep -v PLT)" + output="$(./pl "(loadfile \"examples/lib.pbl\") $2")" fi + if [ "$3" == "$regex" ]; then if [[ "$output" =~ ^$4$ ]]; then - pass "$1" + pass "$1" $exit_code return fi elif [ "$output" == "$3" ]; then - pass "$1" + pass "$1" $exit_code return fi @@ -208,6 +220,7 @@ check "NoMapList" "(map sq)" "( )" check "BadNumber" "(5df)" regex "BAD_NUMBER.*" check "BadHex" "(0x0zf)" regex "BAD_NUMBER.*" check "BadBinary" "(0b01120)" regex "BAD_NUMBER.*" +check "UnsupportedNumber" "(00000)" regex "UNSUPPORTED_NUMBER.*" check "BadParens1" "(hey()" regex "'MISMATCHED_PARENS.*" check "BadParens2" "(hey)(" regex "'MISMATCHED_PARENS.*" check "BadParens3" "((hey(" regex "'MISMATCHED_PARENS.*" @@ -244,7 +257,11 @@ echo "" if [ "$TOTAL_PASSES" -ne "0" ]; then echo -n "" fi -echo "$TOTAL_PASSES Tests Passed" +echo -n "$TOTAL_PASSES Tests Passed" +if [ "$TOTAL_VALGRIND_ERRORS" -ne "0" ]; then + echo -n " ($TOTAL_VALGRIND_ERRORS with valgrind errors)" +fi +echo if [ "$TOTAL_FAILS" -ne "0" ]; then echo -n "" diff --git a/src/tokens.c b/src/tokens.c index 7f5216a..5dd0951 100644 --- a/src/tokens.c +++ b/src/tokens.c @@ -14,7 +14,7 @@ */ // Is the char a standalone token? -static const char singleTokens[] = "()+-*/='"; +static const char singleTokens[] = "()+-*/='?"; int isSingle(const char c) { @@ -48,7 +48,6 @@ int notWhitespace(const char c) return !isWhitespace(c); } -// Return needs to be freed, if not null struct Slice* nf_tokenize(const char* input, struct Error* err) { if (!input) {