| @@ -0,0 +1,2 @@ | |||
| ((nil . ((eval . (flycheck-mode 0)) | |||
| (eval . (rainbow-mode 0))))) | |||
| @@ -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); | |||
| } | |||
| @@ -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); | |||
| @@ -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); | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -0,0 +1,6 @@ | |||
| typedef struct { | |||
| char* message; | |||
| Ast_Node* location; | |||
| } Error; | |||
| Error* error; | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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! */ | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -8,5 +8,5 @@ Ast_Node* parse_expression(char* expr) { | |||
| // initial state is code | |||
| Parser_State current_parser_state = Parser_State_Code; | |||
| return NULL; | |||
| } | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| - read | |||
| - help | |||
| * Types | |||
| - Symbol | |||
| - Number | |||