Pārlūkot izejas kodu

Added some tests for the built-ins

master
Felix Brendel pirms 7 gadiem
vecāks
revīzija
9d0427f790
13 mainītis faili ar 547 papildinājumiem un 128 dzēšanām
  1. +2
    -0
      .dir-locals.el
  2. +11
    -0
      assert.c
  3. +15
    -2
      ast.c
  4. +36
    -5
      built_ins.c
  5. +4
    -0
      env.c
  6. +6
    -0
      error.c
  7. +47
    -4
      eval.c
  8. +68
    -0
      helpers.c
  9. +56
    -44
      io.c
  10. +7
    -43
      main.c
  11. +1
    -1
      parse.c
  12. +259
    -0
      testing.c
  13. +35
    -29
      todo.org

+ 2
- 0
.dir-locals.el Parādīt failu

@@ -0,0 +1,2 @@
((nil . ((eval . (flycheck-mode 0))
(eval . (rainbow-mode 0)))))

+ 11
- 0
assert.c Parādīt failu

@@ -0,0 +1,11 @@
void assert_type (Ast_Node* node, Ast_Node_Type type) {
if (node->type == type) return;

char *wanted, *got, *message;
wanted = Ast_Node_Type_to_string(type);
got = Ast_Node_Type_to_string(node->type);
asprintf(&message, "Type assertion failed:\n\t"
"Wanted: %s\n\t"
"Got : %s\n", wanted, got);
panic(message);
}

+ 15
- 2
ast.c Parādīt failu

@@ -4,18 +4,23 @@ typedef enum {
Ast_Node_Type_Nil,

Ast_Node_Type_Symbol,
Ast_Node_Type_Keyword,
Ast_Node_Type_Number,
Ast_Node_Type_String,

Ast_Node_Type_Cons_Cell,
Ast_Node_Type_Function,
Ast_Node_Type_Built_In_Function,
} Ast_Type;
} Ast_Node_Type;

typedef struct {
char* identifier;
} Symbol;

typedef struct {
char* identifier;
} Keyword;

typedef struct {
double value;
} Number;
@@ -44,9 +49,10 @@ typedef struct {
} Built_In_Function;

struct Ast_Node {
Ast_Type type;
Ast_Node_Type type;
union {
Symbol* symbol;
Keyword* keyword;
Number* number;
String* string;
Cons_Cell* cons_cell;
@@ -73,6 +79,13 @@ Ast_Node* create_ast_node_number(double number) {
return node;
}

Ast_Node* create_ast_node_string(char* str) {
Ast_Node* node = new(Ast_Node);
node->type = Ast_Node_Type_String;
node->value.string = new(String);
node->value.string->value = str;
return node;
}

Ast_Node* create_ast_node_symbol(char* identifier) {
Ast_Node* node = new(Ast_Node);


+ 36
- 5
built_ins.c Parādīt failu

@@ -1,7 +1,7 @@
Ast_Node* built_in_add(Ast_Node* operands) {
double sum = 0;
while (operands->type == Ast_Node_Type_Cons_Cell) {
// TODO(Felix): assert type
assert_type(operands->value.cons_cell->first, Ast_Node_Type_Number);
sum += operands->value.cons_cell->first->value.number->value;
operands = operands->value.cons_cell->rest;
}
@@ -10,12 +10,43 @@ Ast_Node* built_in_add(Ast_Node* operands) {
}

Ast_Node* built_in_substract(Ast_Node* operands) {
double diff = operands->value.cons_cell->first->value.number->value;
assert_type(operands->value.cons_cell->first, Ast_Node_Type_Number);
double difference = operands->value.cons_cell->first->value.number->value;

operands = operands->value.cons_cell->rest;
while (operands->type == Ast_Node_Type_Cons_Cell) {
assert_type(operands->value.cons_cell->first, Ast_Node_Type_Number);

difference -= operands->value.cons_cell->first->value.number->value;
operands = operands->value.cons_cell->rest;
}
return create_ast_node_number(difference);
}

Ast_Node* built_in_multiply(Ast_Node* operands) {
assert_type(operands->value.cons_cell->first, Ast_Node_Type_Number);
double product = operands->value.cons_cell->first->value.number->value;

operands = operands->value.cons_cell->rest;
while (operands->type == Ast_Node_Type_Cons_Cell) {
// TODO(Felix): assert type
diff -= operands->value.cons_cell->first->value.number->value;
assert_type(operands->value.cons_cell->first, Ast_Node_Type_Number);

product *= operands->value.cons_cell->first->value.number->value;
operands = operands->value.cons_cell->rest;
}
return create_ast_node_number(product);
}

Ast_Node* built_in_divide(Ast_Node* operands) {
assert_type(operands->value.cons_cell->first, Ast_Node_Type_Number);
double quotient = operands->value.cons_cell->first->value.number->value;

operands = operands->value.cons_cell->rest;
while (operands->type == Ast_Node_Type_Cons_Cell) {
assert_type(operands->value.cons_cell->first, Ast_Node_Type_Number);

quotient /= operands->value.cons_cell->first->value.number->value;
operands = operands->value.cons_cell->rest;
}
return create_ast_node_number(diff);
return create_ast_node_number(quotient);
}

+ 4
- 0
env.c Parādīt failu

@@ -54,5 +54,9 @@ Ast_Node* lookup_symbol(Symbol* sym, Environment* env) {
}
}

char* message;
asprintf(&message, "Symbol not defined: %s\n", sym->identifier);
panic(message);

return NULL;
}

+ 6
- 0
error.c Parādīt failu

@@ -0,0 +1,6 @@
typedef struct {
char* message;
Ast_Node* location;
} Error;

Error* error;

+ 47
- 4
eval.c Parādīt failu

@@ -5,19 +5,28 @@
/* } */

Ast_Node* eval_expr(Ast_Node* node, Environment* env);
int is_truthy (Ast_Node* expression, Environment* env);

void eval_operands(Ast_Node* operands, Environment* env) {
while (1) {
while (!error) {
if (operands->type == Ast_Node_Type_Cons_Cell) {
operands->value.cons_cell->first = eval_expr(operands->value.cons_cell->first, env);
operands = operands->value.cons_cell->rest;
} else {
break;
return;
}
}
// if we reach here we got an error
log_error("An error occurred while evaluating operands to a function:");
}

Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
#define report_error(_message) \
error = new(Error); \
error->message = _message; \
error->location = node; \
return NULL

Ast_Node* ret = new(Ast_Node);
switch (node->type) {
case Ast_Node_Type_Nil:
@@ -44,8 +53,32 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
} else if (string_equal("-", operator_name)) {
eval_operands(operands, env);
return built_in_substract(operands);
} else if (string_equal("*", operator_name)) {
eval_operands(operands, env);
return built_in_multiply(operands);
} else if (string_equal("/", operator_name)) {
eval_operands(operands, env);
return built_in_divide(operands);
} else if (string_equal("if", operator_name)) {
if (Ast_Node_Type_Cons_Cell != operands->value.cons_cell->rest->type ||
Ast_Node_Type_Cons_Cell != operands->value.cons_cell->rest->value.cons_cell->rest->type ||
Ast_Node_Type_Nil != operands->value.cons_cell->rest->value.cons_cell->rest->value.cons_cell->rest->type)
{
report_error("Ill formed if statement");
}

Ast_Node* then_part = operands->value.cons_cell->rest;
Ast_Node* else_part = then_part->value.cons_cell->rest;

if (is_truthy(operands->value.cons_cell->first, env))
return eval_expr(then_part->value.cons_cell->first, env);
else
return eval_expr(else_part->value.cons_cell->first, env);
} else if (string_equal("not", operator_name)) {
} else if (string_equal("and", operator_name)) {
} else if (string_equal("or", operator_name)) {
} else {
log_message(Log_Level_Critical, "The operator is not yet implemented\n");
report_error("The operator is not yet implemented");
}
}

@@ -55,6 +88,16 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
}

default:
return 0;
report_error("Unknown error");
}
#undef report_error
}

int is_truthy (Ast_Node* expression, Environment* env) {
Ast_Node* result = eval_expr(expression, env);
switch (result->type) {
case Ast_Node_Type_Nil: return 0;
case Ast_Node_Type_Number: return result->value.number->value != 0;
default: return 1;
}
}

+ 68
- 0
helpers.c Parādīt failu

@@ -1,3 +1,71 @@
#define new(type) (type*)malloc(sizeof(type))

int string_equal(char* a, char* b) {
return !strcmp(a, b);
}

// asprintf implementation
int _vscprintf_so(const char * format, va_list pargs) {
int retval;
va_list argcopy;
va_copy(argcopy, pargs);
retval = vsnprintf(NULL, 0, format, argcopy);
va_end(argcopy);
return retval;
}

int vasprintf(char **strp, const char *fmt, va_list ap) {
int len = _vscprintf_so(fmt, ap);
if (len == -1) return -1;
char *str = malloc((size_t) len + 1);
if (!str) return -1;
int r = vsnprintf(str, len + 1, fmt, ap); /* "secure" version of vsprintf */
if (r == -1) return free(str), -1;
*strp = str;
return r;
}

int asprintf(char *strp[], const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
int r = vasprintf(strp, fmt, ap);
va_end(ap);
return r;
}
// asprintf implementation end

char* read_entire_file (char* filename) {
char *fileContent = NULL;
FILE *fp = fopen(filename, "r");
if (fp != NULL) {
/* Go to the end of the file. */
if (fseek(fp, 0L, SEEK_END) == 0) {
/* Get the size of the file. */
long bufsize = ftell(fp);
if (bufsize == -1) {
fputs("Empty file", stderr);
goto closeFile;
}

/* Go back to the start of the file. */
if (fseek(fp, 0L, SEEK_SET) != 0) {
fputs("Error reading file", stderr);
goto closeFile;
}

/* Allocate our buffer to that size. */
fileContent = malloc(sizeof(char) * (bufsize));

/* Read the entire file into memory. */
size_t newLen = fread(fileContent, sizeof(char), bufsize, fp);
if ( ferror( fp ) != 0 ) {
fputs("Error reading file", stderr);
}
}
closeFile:
fclose(fp);
}

return fileContent;
/* Don't forget to call free() later! */
}

+ 56
- 44
io.c Parādīt failu

@@ -1,55 +1,60 @@
typedef enum {
Log_Level_None,

Log_Level_Critical,
Log_Level_Warning,
Log_Level_Info,
Log_Level_Debug,
} Log_Level;

Log_Level log_level = Log_Level_Debug;

void log_message(Log_Level type, char* message) {
if (type > log_level)
return;

char* prefix;
switch (type) {
case Log_Level_Critical: prefix = "CRIT"; break;
case Log_Level_Warning: prefix = "WARN"; break;
case Log_Level_Info: prefix = "INFO"; break;
case Log_Level_Debug: prefix = "DBUG"; break;
case Log_Level_Critical: prefix = "CRITICAL"; break;
case Log_Level_Warning: prefix = "WARNING"; break;
case Log_Level_Info: prefix = "INFO"; break;
case Log_Level_Debug: prefix = "DEBUG"; break;
case Log_Level_None: return;
}
printf("%s: %s",prefix, message);
printf("%s: %s\n",prefix, message);
}

char* read_entire_file (char* filename) {
char *fileContent = NULL;
FILE *fp = fopen(filename, "r");
if (fp != NULL) {
/* Go to the end of the file. */
if (fseek(fp, 0L, SEEK_END) == 0) {
/* Get the size of the file. */
long bufsize = ftell(fp);
if (bufsize == -1) {
fputs("Empty file", stderr);
goto closeFile;
}
void log_error(char* message) {
log_message(Log_Level_Critical, message);
log_message(Log_Level_Critical, error->message);
}

/* Go back to the start of the file. */
if (fseek(fp, 0L, SEEK_SET) != 0) {
fputs("Error reading file", stderr);
goto closeFile;
}

/* Allocate our buffer to that size. */
fileContent = malloc(sizeof(char) * (bufsize));
void panic(char* message) {
log_message(Log_Level_Critical, message);
exit(1);
}

/* Read the entire file into memory. */
size_t newLen = fread(fileContent, sizeof(char), bufsize, fp);
if ( ferror( fp ) != 0 ) {
fputs("Error reading file", stderr);
}
}
closeFile:
fclose(fp);
}

return fileContent;
/* Don't forget to call free() later! */
char* Ast_Node_Type_to_string (Ast_Node_Type type) {
switch (type) {
case(Ast_Node_Type_Nil):
return "nil";
case(Ast_Node_Type_Number):
return "number";
case(Ast_Node_Type_String):
return "string";
case(Ast_Node_Type_Symbol):
return "symbol";
case(Ast_Node_Type_Keyword):
return "keyword";
case(Ast_Node_Type_Function):
return "function";
case(Ast_Node_Type_Built_In_Function):
return "built-in function";
case(Ast_Node_Type_Cons_Cell):
return "pair";
}
}

void print(Ast_Node* node) {
@@ -66,6 +71,9 @@ void print(Ast_Node* node) {
case (Ast_Node_Type_Symbol): {
printf("%s", node->value.symbol->identifier);
} break;
case (Ast_Node_Type_Keyword): {
printf(":%s", node->value.keyword->identifier);
} break;
case (Ast_Node_Type_Function): {
printf("[lambda]");
} break;
@@ -73,18 +81,22 @@ void print(Ast_Node* node) {
printf("[built-in-function %s]", node->value.built_in_function->identifier);
} break;
case (Ast_Node_Type_Cons_Cell): {
Ast_Node* head = node;
printf("(");
if (node->value.cons_cell->first)
print(node->value.cons_cell->first);
else
printf("nil");

printf(" . ");
while (1) {
print(head->value.cons_cell->first);
head = head->value.cons_cell->rest;
if (head->type != Ast_Node_Type_Cons_Cell)
break;
printf(" ");
}

if (head->type != Ast_Node_Type_Nil) {
printf(" . ");
print(head);
}

if (node->value.cons_cell->rest)
print(node->value.cons_cell->rest);
else
printf("nil");
printf(")");
} break;
}


+ 7
- 43
main.c Parādīt failu

@@ -1,55 +1,19 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define new(type) (type*)malloc(sizeof(type))
#include <stdarg.h> /* needed for va_list */
#include <math.h>

#include "./helpers.c"
#include "./ast.c"
#include "./error.c"
#include "./io.c"
#include "./assert.c"
#include "./parse.c"
#include "./built_ins.c"
#include "./env.c"
#include "./eval.c"

void test() {
// (+ 4 10)
Ast_Node* nil = create_ast_node_nil();
Ast_Node* ten = create_ast_node_number(10);
Ast_Node* four = create_ast_node_number(4);
Ast_Node* plus = create_ast_node_symbol("+");
Ast_Node* minus = create_ast_node_symbol("-");

Ast_Node* form1 = create_ast_node_pair(
plus,
create_ast_node_pair(
ten,
create_ast_node_pair(
four,
nil)));

Ast_Node* form2 = create_ast_node_pair(
plus,
create_ast_node_pair(
four,
create_ast_node_pair(
four,
nil)));

Ast_Node* root = create_ast_node_pair(
minus,
create_ast_node_pair(
form1,
create_ast_node_pair(
form2,
nil)));

print(root);
printf("\n");
Ast_Node* result = eval_expr(root, new(Environment));
print(result);
printf("\n");
}
#include "./testing.c"

int main (int argc, char *argv[]) {
/* if (argc > 1) { */
@@ -61,7 +25,7 @@ int main (int argc, char *argv[]) {
/* printf("No source provided.\n"); */
/* } */

/* test(); */
print(parse_expression("(+ 4 7)"));
run_all_tests();
return 0;
}

+ 1
- 1
parse.c Parādīt failu

@@ -8,5 +8,5 @@ Ast_Node* parse_expression(char* expr) {
// initial state is code
Parser_State current_parser_state = Parser_State_Code;

return NULL;
}

+ 259
- 0
testing.c Parādīt failu

@@ -0,0 +1,259 @@
#define console_normal "\x1B[0m"
#define console_red "\x1B[31m"
#define console_green "\x1B[32m"

#define epsilon 2.2204460492503131E-16

#define testresult int
#define pass 1
#define fail 0

#define print_assert_equal_fail(variable, value, type, format) \
printf("\n\tAssertion failed for '" #variable "'" \
"\n\t in %s:%d" \
"\n\texpected: " format \
"\n\tgot: " format "\n", \
__FILE__, __LINE__, (type)value, (type)variable)

#define print_assert_not_equal_fail(variable, value, type, format) \
printf("\n\tAssertion failed for '" #variable "'" \
"\n\t in %s:%d" \
"\n\texpected not: " format \
"\n\tgot anyways: " format "\n", \
__FILE__, __LINE__, (type)value, (type)variable)

#define assert_no_error(error) \
if (error) { \
print_assert_not_equal_fail(error, 0, int, "%d"); \
printf("\t%s%s%s\n", console_red, error->message,console_normal); \
return fail; \
} \

#define assert_equal_int(variable, value) \
if (variable != value) { \
print_assert_equal_fail(variable, value, int, "%d"); \
return fail; \
}

#define assert_not_equal_int(variable, value) \
if (variable == value) { \
print_assert_not_equal_fail(variable, value, int, "%d"); \
return fail; \
}

#define assert_equal_double(variable, value) \
if (fabs((double)variable - (double)value) > epsilon) { \
print_assert_equal_fail(variable, value, double, "%f"); \
return fail; \
}

#define assert_not_equal_double(variable, value) \
if (fabs((double)variable - (double)value) <= epsilon) { \
print_assert_not_equal_fail(variable, value, double, "%f"); \
return fail; \
}

#define assert_null(variable) \
assert_equal_int(variable, NULL)

#define assert_not_null(variable) \
assert_not_equal_int(variable, NULL)


#define invoke_test(name) \
printf("" #name ":"); \
for(int i = 0; i < 45 - strlen(#name); ++i) \
printf(" "); \
if (name() == pass) \
printf("%spassed%s\n", console_green, console_normal); \
else { \
printf("%sfailed%s\n", console_red, console_normal); \
if(error) { \
log_error("Error while running test" #name); \
free(error); \
error = NULL; \
} \
} \


testresult test_built_in_add() {
Ast_Node* plus = create_ast_node_symbol("+");
Ast_Node* ten = create_ast_node_number(10);
Ast_Node* four = create_ast_node_number(4);
Ast_Node* nil = create_ast_node_nil();
Ast_Node* form = create_ast_node_pair(
plus,
create_ast_node_pair(
ten,
create_ast_node_pair(
four,
nil)));

Ast_Node* result = eval_expr(form, new(Environment));

assert_no_error(error);
assert_not_null(result);
assert_equal_int(result->type, Ast_Node_Type_Number);
assert_equal_double(result->value.number->value, 14);

return pass;
}

testresult test_built_in_substract() {
Ast_Node* minus = create_ast_node_symbol("-");
Ast_Node* ten = create_ast_node_number(10);
Ast_Node* four = create_ast_node_number(4);
Ast_Node* nil = create_ast_node_nil();
Ast_Node* form = create_ast_node_pair(
minus,
create_ast_node_pair(
ten,
create_ast_node_pair(
four,
nil)));

Ast_Node* result = eval_expr(form, new(Environment));

assert_no_error(error);
assert_not_null(result);
assert_equal_int(result->type, Ast_Node_Type_Number);
assert_equal_double(result->value.number->value, 6);

return pass;
}


testresult test_built_in_multiply() {
Ast_Node* times = create_ast_node_symbol("*");
Ast_Node* ten = create_ast_node_number(10);
Ast_Node* four = create_ast_node_number(4);
Ast_Node* nil = create_ast_node_nil();
Ast_Node* form = create_ast_node_pair(
times,
create_ast_node_pair(
ten,
create_ast_node_pair(
four,
nil)));

Ast_Node* result = eval_expr(form, new(Environment));

assert_no_error(error);
assert_not_null(result);
assert_equal_int(result->type, Ast_Node_Type_Number);
assert_equal_double(result->value.number->value, 40);

return pass;
}


testresult test_built_in_divide() {
Ast_Node* over = create_ast_node_symbol("/");
Ast_Node* ten = create_ast_node_number(20);
Ast_Node* four = create_ast_node_number(4);
Ast_Node* nil = create_ast_node_nil();
Ast_Node* form = create_ast_node_pair(
over,
create_ast_node_pair(
ten,
create_ast_node_pair(
four,
nil)));

Ast_Node* result = eval_expr(form, new(Environment));

assert_null(error);
assert_not_null(result);
assert_equal_int(result->type, Ast_Node_Type_Number);
assert_equal_double(result->value.number->value, 5);

return pass;
}


testresult test_built_in_if() {
Ast_Node* _if = create_ast_node_symbol("if");
Ast_Node* cond = create_ast_node_number(1);
Ast_Node* four = create_ast_node_number(4);
Ast_Node* five = create_ast_node_number(5);
Ast_Node* nil = create_ast_node_nil();
Ast_Node* form = create_ast_node_pair(
_if,
create_ast_node_pair(
cond,
create_ast_node_pair(
four,
create_ast_node_pair(
five,
nil))));
Ast_Node* result;

// test *then* case
result = eval_expr(form, new(Environment));

assert_no_error(error);
assert_not_null(result);
assert_equal_int(result->type, Ast_Node_Type_Number);
assert_equal_double(result->value.number->value, 4);

// test *else* case
cond->value.number->value = 0;
result = eval_expr(form, new(Environment));

assert_null(error);
assert_not_null(result);
assert_equal_int(result->type, Ast_Node_Type_Number);
assert_equal_double(result->value.number->value, 5);

return pass;
}

testresult test_built_in_and() {
Ast_Node* _and = create_ast_node_symbol("and");
Ast_Node* cond1 = create_ast_node_number(1);
Ast_Node* cond2 = create_ast_node_string("asd");
Ast_Node* cond3 = create_ast_node_number(4);
Ast_Node* nil = create_ast_node_nil();
Ast_Node* form = create_ast_node_pair(
_and,
create_ast_node_pair(
cond1,
create_ast_node_pair(
cond2,
create_ast_node_pair(
cond3,
nil))));
Ast_Node* result;

// a true case
result = eval_expr(form, new(Environment));

assert_no_error(error);
assert_not_null(result);
assert_equal_int(result->type, Ast_Node_Type_Number);
assert_equal_double(result->value.number->value, 1);

// a false case
cond1->value.number->value = 0;
result = eval_expr(form, new(Environment));

assert_no_error(error);
assert_not_null(result);
assert_equal_int(result->type, Ast_Node_Type_Number);
assert_equal_double(result->value.number->value, 0);

return pass;
}


void run_all_tests() {
log_level = Log_Level_None;

invoke_test(test_built_in_add);
invoke_test(test_built_in_substract);
invoke_test(test_built_in_multiply);
invoke_test(test_built_in_divide);
invoke_test(test_built_in_if);
invoke_test(test_built_in_and);

}

+ 35
- 29
todo.org Parādīt failu

@@ -1,35 +1,41 @@
* Build-in forms

- +
- -
- *
- /
- >
- <
- =
** DONE +
CLOSED: [2018-09-18 Di 12:14]
** DONE -
CLOSED: [2018-09-18 Di 12:14]
** DONE *
CLOSED: [2018-09-18 Di 12:14]
** DONE /
CLOSED: [2018-09-18 Di 12:14]
** TODO >
** TODO <
** TODO =

** DONE if
CLOSED: [2018-09-18 Di 12:14]
** TODO and
** TODO or
** TODO not

** TODO first (car)
** TODO rest (cdr)
** TODO pair (cons)

** TODO load (import)
** TODO define

** TODO lambda
** TODO progn

** TODO eval
** TODO quote

** TODO print
** TODO read

** TODO help

- if
- and
- or
- not

- first (car)
- rest (cdr)
- pair (cons)

- load (import)
- define

- lambda
- progn

- eval
- quote

- print
- read

- help
* Types
- Symbol
- Number


Notiek ielāde…
Atcelt
Saglabāt