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:
parent
581dfed359
commit
59c6ff50b5
62
src/env.c
62
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("[1m=>[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 [1m=>[0m %s", test, expected);
|
||||
textCursor += sprintf(textCursor, " [0m[1m=>[0m ");
|
||||
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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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("[0m\n");
|
||||
cleanObject(&o);
|
||||
free(buf);
|
||||
}
|
||||
|
|
73
src/plfunc.c
73
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)
|
||||
|
|
17
src/plfunc.h
17
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
|
||||
|
|
33
src/tests.sh
33
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 "[1;32m✓[0m"
|
||||
local color=32
|
||||
if [[ "$2" != "0" ]]; then
|
||||
((TOTAL_VALGRIND_ERRORS++))
|
||||
color=33
|
||||
fi
|
||||
echo -n "[1;${color}m✓[0m"
|
||||
((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 "[1;32m"
|
||||
fi
|
||||
echo "$TOTAL_PASSES Tests Passed[0m"
|
||||
echo -n "$TOTAL_PASSES Tests Passed[0m"
|
||||
if [ "$TOTAL_VALGRIND_ERRORS" -ne "0" ]; then
|
||||
echo -n " [33m($TOTAL_VALGRIND_ERRORS with valgrind errors)[0m"
|
||||
fi
|
||||
echo
|
||||
|
||||
if [ "$TOTAL_FAILS" -ne "0" ]; then
|
||||
echo -n "[1;31m"
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue