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) {
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, "<empty string>");
} 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),

View File

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

View File

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

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

View File

@ -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) {
switch (text.type) {
case TYPE_SYMBOL: {
Object string = eval(&text, env);
Object parsed = parseEval(string.string, env);
cleanObject(&string);
return parsed;
} else if (text.type == TYPE_SLIST) {
}
case TYPE_SLIST:
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 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(&params[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,13 +375,15 @@ 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);
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)
{

View File

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

View File

@ -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)"
else
output="$(./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")"
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 ""

View File

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