Break printColored() into own function.

Replace (pch) with (ch).
Fix error memory bug.
Add sugar for (? fil) => (? "fil").
Inline some of (fil).
Simplify (print) and allow for varargs.
Total up valgrind errors when testing.
This commit is contained in:
Sage Vaillancourt 2022-03-25 13:33:26 -04:00 committed by Sage Vaillancourt
parent 581dfed359
commit 59c6ff50b5
8 changed files with 127 additions and 87 deletions

View File

@ -127,7 +127,7 @@ void addToEnv(struct Environment* env, const char* name, const Object obj)
if (is_local) { if (is_local) {
temp_env->strings[i] = temp_env->strings[i] =
calloc(sizeof(char), strlen(name) + 1); 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); temp_env->objects[i] = cloneObject(obj);
return; return;
} }
@ -155,7 +155,7 @@ void addToEnv(struct Environment* env, const char* name, const Object obj)
} }
env->strings[old_size] = malloc(strlen(name) + 1); 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); env->objects[old_size] = cloneObject(obj);
} }
@ -278,10 +278,40 @@ const int purple = 35;
const int teal = 36; const int teal = 36;
const int white = 37; const int white = 37;
const int colors[] = { const int colors[] = {
green, cyan, yellow, purple, red, white white, green, cyan, yellow, purple, red
}; };
const int colorCount = array_length(colors); 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) char* getHelp(const char* symbol)
{ {
for (int i = 0; i < currentHelp; i++) { 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) { for (int ti = 0; ti < h.testCount; ti += 2) {
const char* test = h.tests[ti]; const char* test = h.tests[ti];
const char* expected = h.tests[ti + 1]; const char* expected = h.tests[ti + 1];
textCursor += sprintf(textCursor, "\n "); textCursor += sprintf(textCursor, "\n %s => ", test);
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, " => ");
if (expected[0] == '\0') { if (expected[0] == '\0') {
textCursor += sprintf(textCursor, "<empty string>"); textCursor += sprintf(textCursor, "<empty string>");
} else { } else {
@ -320,7 +333,10 @@ char* getHelp(const char* symbol)
return text; 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.") fnn(segfault, "seg", "Induces a segfault.")
@ -428,7 +444,7 @@ struct Environment defaultEnv()
pf("seg", segfault), pf("seg", segfault),
//pfn(segfault), //pfn(segfault),
pf("prn", print), pf("prn", print),
pf("pch", pChar), pf("ch", numToChar),
pf("penv", printEnvO), pf("penv", printEnvO),
pf("sys", systemCall), pf("sys", systemCall),
pf("loadfile", loadFile), pf("loadfile", loadFile),

View File

@ -46,6 +46,8 @@ int getStructIndex(struct Environment* env, const char* name);
/// Needs to be freed! /// Needs to be freed!
char* getHelp(const char* symbol); char* getHelp(const char* symbol);
void printColored(const char* code);
int runTests(); int runTests();
#endif #endif

View File

@ -590,6 +590,7 @@ void cleanObject(Object* target)
break; break;
case TYPE_ERROR: case TYPE_ERROR:
#ifndef SIMPLE_ERRORS #ifndef SIMPLE_ERRORS
free(target->error->plContext);
free(target->error->context); free(target->error->context);
free(target->error); free(target->error);
target->error = NULL; target->error = NULL;

View File

@ -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) Result readSeq(struct Slice* tokens)
{ {
Object res = listObject(); Object res = listObject();
int isHelp = 0;
for (;;) { for (;;) {
struct Slice* next = tokens; struct Slice* next = tokens;
struct Slice* rest = next->text ? &next[1] : NULL; struct Slice* rest = next->text ? &next[1] : NULL;
@ -489,12 +496,18 @@ Result readSeq(struct Slice* tokens)
return (Result) {res, rest}; return (Result) {res, rest};
} }
Result r = parse(tokens); 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) { if (r.obj.type == TYPE_ERROR) {
return r; return r;
} }
nf_addToList(&res, cloneObject(r.obj)); nf_addToList(&res, cloneObject(r.obj));
tokens = r.slices; tokens = r.slices;
cleanObject(&r.obj); cleanObject(&r.obj);
isHelp = next->text[0] == '?';
} }
} }
@ -626,7 +639,8 @@ Object parseEval(const char* input, struct Environment* env)
Object parsed = parse(tok).obj; Object parsed = parse(tok).obj;
if (parsed.type == TYPE_ERROR) { if (parsed.type == TYPE_ERROR) {
obj = parsed; // TODO Check necessity obj = parsed; // TODO Check necessity
obj.error->plContext = lastOpen; obj.error->plContext = malloc(sizeof(struct Slice));
*obj.error->plContext = *lastOpen;
break; break;
} }
if (tok[i].text[0] == ')') { if (tok[i].text[0] == ')') {
@ -668,7 +682,7 @@ int _readFile(FILE* input, struct Environment* env)
char line[LINE_MAX]; char line[LINE_MAX];
if (fgets(line, LINE_MAX, input)) { if (fgets(line, LINE_MAX, input)) {
if (line[0] != '#' || line[1] != '!') { if (line[0] != '#' || line[1] != '!') {
strncat(page, line, strlen(line) - 1); strcat(page, line);
} }
} }
int isQuote = 0; int isQuote = 0;
@ -713,7 +727,10 @@ void repl(struct Environment* env)
} }
add_history(buf); add_history(buf);
Object o = parseEval(buf, env); Object o = parseEval(buf, env);
printObj(&o); char output[1024];
stringNObj(output, &o, 1024);
printColored(output);
printf("\n");
cleanObject(&o); cleanObject(&o);
free(buf); free(buf);
} }

View File

@ -39,14 +39,15 @@ Object filter(Object* params, int length, struct Environment* env)
Object filtered = listObject(); Object filtered = listObject();
FOR_POINTER_IN_LIST(&list) { FOR_POINTER_IN_LIST(&list) {
Object l = startList(cloneObject(condition)); Object cloned = cloneObject(condition);
Object first_eval = eval(&cloned, env);
Object testee = cloneObject(*POINTER); Object testee = cloneObject(*POINTER);
nf_addToList(&l, testee); Object e = listEvalLambda(&first_eval, &testee, env);
Object e = eval(&l, env);
if (e.number) { if (e.number) {
nf_addToList(&filtered, testee); // May need to re-clone testee? nf_addToList(&filtered, testee); // May need to re-clone testee?
} }
cleanObject(&l); cleanObject(&e);
cleanObject(&cloned);
} }
return filtered; return filtered;
} }
@ -144,14 +145,11 @@ Object isString(Object* params, int length, struct Environment* env)
return test.type == TYPE_STRING ? boolObject(1) : boolObject(0); 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 charVal(Object* params, int length, struct Environment* env)
{ {
Object test = params[0]; Object test = params[0];
return numberObject(test.string[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) 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]; Object text = params[0];
if (text.type == TYPE_SYMBOL) { switch (text.type) {
case TYPE_SYMBOL: {
Object string = eval(&text, env); Object string = eval(&text, 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) { }
case TYPE_SLIST:
return evalList(&text, env); return evalList(&text, env);
} else if (text.type != TYPE_STRING) { case TYPE_STRING:
return parseEval(text.string, env);
default:
return errorObject(CAN_ONLY_EVAL_STRINGS); return errorObject(CAN_ONLY_EVAL_STRINGS);
} }
return parseEval(text.string, env);
} }
Object listEquality(const Object* list1, const Object* list2) 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; const int n2 = obj2->number;
switch (op) { 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 '&': case '&':
return boolObject(n1 != 0 && n2 != 0); return boolObject(n1 != 0 && n2 != 0);
case '|': case '|':
@ -363,24 +349,21 @@ BASIC_OP(or, '|')
Object print(Object* params, int length, struct Environment* env) Object print(Object* params, int length, struct Environment* env)
{ {
Object p = params[0]; for (int i = 0; i < length; i++) {
Object c = cloneObject(p); _printObj(&params[i], 0);
Object e = eval(&c, env); }
_printObj(&e, 0);
cleanObject(&c);
cleanObject(&e);
return numberObject(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]; Object c = params[0];
if (c.type != TYPE_NUMBER) { if (c.type != TYPE_NUMBER) {
return errorObject(BAD_NUMBER); return errorObject(BAD_NUMBER);
} }
printf("%c", c.number % 256); char ch[1] = {c.number % 256};
return numberObject(0); return stringFromSlice(ch, 1);
} }
Object printEnvO(Object* params, int length, struct Environment* env) Object printEnvO(Object* params, int length, struct Environment* env)
@ -392,13 +375,15 @@ Object printEnvO(Object* params, int length, struct Environment* env)
Object takeInput(Object* params, int length, struct Environment* env) Object takeInput(Object* params, int length, struct Environment* env)
{ {
Object prompt = params[0]; Object prompt = params[0];
if (prompt.type == TYPE_STRING) { if (length > 0 && prompt.type == TYPE_STRING) {
printf("%s", prompt.string); printf("%s", prompt.string);
} }
char input[256] = ""; char input[256] = "";
fgets(input, 256, stdin); if (fgets(input, 256, stdin)) {
return stringFromSlice(input, strlen(input) - 1); return stringFromSlice(input, strlen(input) - 1);
} }
return errorWithContext(NULL_PARSE, "fgets() error");
}
Object loadFile(Object* params, int length, struct Environment* env) Object loadFile(Object* params, int length, struct Environment* env)
{ {

View File

@ -38,6 +38,7 @@ fn(catObjects,
fn(filter, fn(filter,
"Filter a list based on the given condition.", "Filter a list based on the given condition.",
"(fil (fn (a) (< 50 a)) (25 60 100))", "( 60 100 )", "(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 /// LIST, ANY => LIST
@ -160,15 +161,19 @@ fn(possessive,
fn(print, "Prints the string representation of the given object to stdout."); 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(printEnvO, "Prints out the current scoped environment.");
fn(systemCall, fn(systemCall,
"Opens a shell and runs the given command, returning the command's exit code.\n" "Opens a shell and runs the given command, returning 0 if successful.\n"
"(sys \"echo yee\")\n" "If the argument is not a string, returns 255.\n",
"yee\n" "(sys \"echo yee > /dev/null\")", "0",
"=> 0" "(sys 5)", "255",
); );
fn(loadFile, fn(loadFile,
@ -179,8 +184,6 @@ fn(loadFile,
"=> 0" "=> 0"
); );
/// @code /// @code
/// () => STRING /// () => STRING
/// STRING => STRING /// STRING => STRING

View File

@ -1,9 +1,10 @@
#!/bin/bash #!/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_PASSES=0
TOTAL_FAILS=0 TOTAL_FAILS=0
TOTAL_VALGRIND_ERRORS=0
FAILS=0 FAILS=0
FAIL_OUTPUT="" FAIL_OUTPUT=""
VALGRIND=false VALGRIND=false
@ -37,7 +38,12 @@ title() {
} }
pass() { pass() {
echo -n "✓" local color=32
if [[ "$2" != "0" ]]; then
((TOTAL_VALGRIND_ERRORS++))
color=33
fi
echo -n "[1;${color}m✓"
((TOTAL_PASSES++)) ((TOTAL_PASSES++))
} }
@ -56,20 +62,26 @@ check() {
fi fi
local output local output
local exit_code=0
if $VALGRIND; then if $VALGRIND; then
echo -ne "\n $1\r " echo -ne "\n $1"
output="$($VALCOM ./pl "(loadfile \"examples/lib.pbl\") $2" | grep -v PLT)" output="$($VALCOM ./pl "(loadfile \"examples/lib.pbl\") $2")"
else exit_code=$?
output="$(./pl "(loadfile \"examples/lib.pbl\") $2" | grep -v PLT)" if [[ "$exit_code" == "0" ]]; then
echo -ne "\r "
fi fi
else
output="$(./pl "(loadfile \"examples/lib.pbl\") $2")"
fi
if [ "$3" == "$regex" ]; then if [ "$3" == "$regex" ]; then
if [[ "$output" =~ ^$4$ ]]; then if [[ "$output" =~ ^$4$ ]]; then
pass "$1" pass "$1" $exit_code
return return
fi fi
elif [ "$output" == "$3" ]; then elif [ "$output" == "$3" ]; then
pass "$1" pass "$1" $exit_code
return return
fi fi
@ -208,6 +220,7 @@ check "NoMapList" "(map sq)" "( )"
check "BadNumber" "(5df)" regex "BAD_NUMBER.*" check "BadNumber" "(5df)" regex "BAD_NUMBER.*"
check "BadHex" "(0x0zf)" regex "BAD_NUMBER.*" check "BadHex" "(0x0zf)" regex "BAD_NUMBER.*"
check "BadBinary" "(0b01120)" regex "BAD_NUMBER.*" check "BadBinary" "(0b01120)" regex "BAD_NUMBER.*"
check "UnsupportedNumber" "(00000)" regex "UNSUPPORTED_NUMBER.*"
check "BadParens1" "(hey()" regex "'MISMATCHED_PARENS.*" check "BadParens1" "(hey()" regex "'MISMATCHED_PARENS.*"
check "BadParens2" "(hey)(" regex "'MISMATCHED_PARENS.*" check "BadParens2" "(hey)(" regex "'MISMATCHED_PARENS.*"
check "BadParens3" "((hey(" regex "'MISMATCHED_PARENS.*" check "BadParens3" "((hey(" regex "'MISMATCHED_PARENS.*"
@ -244,7 +257,11 @@ echo ""
if [ "$TOTAL_PASSES" -ne "0" ]; then if [ "$TOTAL_PASSES" -ne "0" ]; then
echo -n "" echo -n ""
fi 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 if [ "$TOTAL_FAILS" -ne "0" ]; then
echo -n "" echo -n ""

View File

@ -14,7 +14,7 @@
*/ */
// Is the char a standalone token? // Is the char a standalone token?
static const char singleTokens[] = "()+-*/='"; static const char singleTokens[] = "()+-*/='?";
int isSingle(const char c) int isSingle(const char c)
{ {
@ -48,7 +48,6 @@ int notWhitespace(const char c)
return !isWhitespace(c); return !isWhitespace(c);
} }
// Return needs to be freed, if not null
struct Slice* nf_tokenize(const char* input, struct Error* err) struct Slice* nf_tokenize(const char* input, struct Error* err)
{ {
if (!input) { if (!input) {