Added some tests for the built-ins
This commit is contained in:
@@ -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_Nil,
|
||||||
|
|
||||||
Ast_Node_Type_Symbol,
|
Ast_Node_Type_Symbol,
|
||||||
|
Ast_Node_Type_Keyword,
|
||||||
Ast_Node_Type_Number,
|
Ast_Node_Type_Number,
|
||||||
Ast_Node_Type_String,
|
Ast_Node_Type_String,
|
||||||
|
|
||||||
Ast_Node_Type_Cons_Cell,
|
Ast_Node_Type_Cons_Cell,
|
||||||
Ast_Node_Type_Function,
|
Ast_Node_Type_Function,
|
||||||
Ast_Node_Type_Built_In_Function,
|
Ast_Node_Type_Built_In_Function,
|
||||||
} Ast_Type;
|
} Ast_Node_Type;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char* identifier;
|
char* identifier;
|
||||||
} Symbol;
|
} Symbol;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* identifier;
|
||||||
|
} Keyword;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
double value;
|
double value;
|
||||||
} Number;
|
} Number;
|
||||||
@@ -44,9 +49,10 @@ typedef struct {
|
|||||||
} Built_In_Function;
|
} Built_In_Function;
|
||||||
|
|
||||||
struct Ast_Node {
|
struct Ast_Node {
|
||||||
Ast_Type type;
|
Ast_Node_Type type;
|
||||||
union {
|
union {
|
||||||
Symbol* symbol;
|
Symbol* symbol;
|
||||||
|
Keyword* keyword;
|
||||||
Number* number;
|
Number* number;
|
||||||
String* string;
|
String* string;
|
||||||
Cons_Cell* cons_cell;
|
Cons_Cell* cons_cell;
|
||||||
@@ -73,6 +79,13 @@ Ast_Node* create_ast_node_number(double number) {
|
|||||||
return node;
|
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* create_ast_node_symbol(char* identifier) {
|
||||||
Ast_Node* node = new(Ast_Node);
|
Ast_Node* node = new(Ast_Node);
|
||||||
|
|||||||
+36
-5
@@ -1,7 +1,7 @@
|
|||||||
Ast_Node* built_in_add(Ast_Node* operands) {
|
Ast_Node* built_in_add(Ast_Node* operands) {
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
while (operands->type == Ast_Node_Type_Cons_Cell) {
|
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;
|
sum += operands->value.cons_cell->first->value.number->value;
|
||||||
operands = operands->value.cons_cell->rest;
|
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) {
|
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;
|
operands = operands->value.cons_cell->rest;
|
||||||
while (operands->type == Ast_Node_Type_Cons_Cell) {
|
while (operands->type == Ast_Node_Type_Cons_Cell) {
|
||||||
// TODO(Felix): assert type
|
assert_type(operands->value.cons_cell->first, Ast_Node_Type_Number);
|
||||||
diff -= operands->value.cons_cell->first->value.number->value;
|
|
||||||
|
difference -= operands->value.cons_cell->first->value.number->value;
|
||||||
operands = operands->value.cons_cell->rest;
|
operands = operands->value.cons_cell->rest;
|
||||||
}
|
}
|
||||||
return create_ast_node_number(diff);
|
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) {
|
||||||
|
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(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;
|
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);
|
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) {
|
void eval_operands(Ast_Node* operands, Environment* env) {
|
||||||
while (1) {
|
while (!error) {
|
||||||
if (operands->type == Ast_Node_Type_Cons_Cell) {
|
if (operands->type == Ast_Node_Type_Cons_Cell) {
|
||||||
operands->value.cons_cell->first = eval_expr(operands->value.cons_cell->first, env);
|
operands->value.cons_cell->first = eval_expr(operands->value.cons_cell->first, env);
|
||||||
operands = operands->value.cons_cell->rest;
|
operands = operands->value.cons_cell->rest;
|
||||||
} else {
|
} 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) {
|
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);
|
Ast_Node* ret = new(Ast_Node);
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case Ast_Node_Type_Nil:
|
case Ast_Node_Type_Nil:
|
||||||
@@ -44,8 +53,32 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
|
|||||||
} else if (string_equal("-", operator_name)) {
|
} else if (string_equal("-", operator_name)) {
|
||||||
eval_operands(operands, env);
|
eval_operands(operands, env);
|
||||||
return built_in_substract(operands);
|
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 {
|
} 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:
|
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) {
|
int string_equal(char* a, char* b) {
|
||||||
return !strcmp(a, 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 {
|
typedef enum {
|
||||||
|
Log_Level_None,
|
||||||
|
|
||||||
Log_Level_Critical,
|
Log_Level_Critical,
|
||||||
Log_Level_Warning,
|
Log_Level_Warning,
|
||||||
Log_Level_Info,
|
Log_Level_Info,
|
||||||
Log_Level_Debug,
|
Log_Level_Debug,
|
||||||
} Log_Level;
|
} Log_Level;
|
||||||
|
|
||||||
|
Log_Level log_level = Log_Level_Debug;
|
||||||
|
|
||||||
void log_message(Log_Level type, char* message) {
|
void log_message(Log_Level type, char* message) {
|
||||||
|
if (type > log_level)
|
||||||
|
return;
|
||||||
|
|
||||||
char* prefix;
|
char* prefix;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Log_Level_Critical: prefix = "CRIT"; break;
|
case Log_Level_Critical: prefix = "CRITICAL"; break;
|
||||||
case Log_Level_Warning: prefix = "WARN"; break;
|
case Log_Level_Warning: prefix = "WARNING"; break;
|
||||||
case Log_Level_Info: prefix = "INFO"; break;
|
case Log_Level_Info: prefix = "INFO"; break;
|
||||||
case Log_Level_Debug: prefix = "DBUG"; 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) {
|
void log_error(char* message) {
|
||||||
char *fileContent = NULL;
|
log_message(Log_Level_Critical, message);
|
||||||
FILE *fp = fopen(filename, "r");
|
log_message(Log_Level_Critical, error->message);
|
||||||
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. */
|
void panic(char* message) {
|
||||||
fileContent = malloc(sizeof(char) * (bufsize));
|
log_message(Log_Level_Critical, message);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
/* Read the entire file into memory. */
|
|
||||||
size_t newLen = fread(fileContent, sizeof(char), bufsize, fp);
|
char* Ast_Node_Type_to_string (Ast_Node_Type type) {
|
||||||
if ( ferror( fp ) != 0 ) {
|
switch (type) {
|
||||||
fputs("Error reading file", stderr);
|
case(Ast_Node_Type_Nil):
|
||||||
}
|
return "nil";
|
||||||
}
|
case(Ast_Node_Type_Number):
|
||||||
closeFile:
|
return "number";
|
||||||
fclose(fp);
|
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";
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileContent;
|
|
||||||
/* Don't forget to call free() later! */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void print(Ast_Node* node) {
|
void print(Ast_Node* node) {
|
||||||
@@ -66,6 +71,9 @@ void print(Ast_Node* node) {
|
|||||||
case (Ast_Node_Type_Symbol): {
|
case (Ast_Node_Type_Symbol): {
|
||||||
printf("%s", node->value.symbol->identifier);
|
printf("%s", node->value.symbol->identifier);
|
||||||
} break;
|
} break;
|
||||||
|
case (Ast_Node_Type_Keyword): {
|
||||||
|
printf(":%s", node->value.keyword->identifier);
|
||||||
|
} break;
|
||||||
case (Ast_Node_Type_Function): {
|
case (Ast_Node_Type_Function): {
|
||||||
printf("[lambda]");
|
printf("[lambda]");
|
||||||
} break;
|
} break;
|
||||||
@@ -73,18 +81,22 @@ void print(Ast_Node* node) {
|
|||||||
printf("[built-in-function %s]", node->value.built_in_function->identifier);
|
printf("[built-in-function %s]", node->value.built_in_function->identifier);
|
||||||
} break;
|
} break;
|
||||||
case (Ast_Node_Type_Cons_Cell): {
|
case (Ast_Node_Type_Cons_Cell): {
|
||||||
|
Ast_Node* head = node;
|
||||||
printf("(");
|
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(")");
|
printf(")");
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +1,19 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h> /* needed for va_list */
|
||||||
#define new(type) (type*)malloc(sizeof(type))
|
#include <math.h>
|
||||||
|
|
||||||
#include "./helpers.c"
|
#include "./helpers.c"
|
||||||
#include "./ast.c"
|
#include "./ast.c"
|
||||||
|
#include "./error.c"
|
||||||
#include "./io.c"
|
#include "./io.c"
|
||||||
|
#include "./assert.c"
|
||||||
#include "./parse.c"
|
#include "./parse.c"
|
||||||
#include "./built_ins.c"
|
#include "./built_ins.c"
|
||||||
#include "./env.c"
|
#include "./env.c"
|
||||||
#include "./eval.c"
|
#include "./eval.c"
|
||||||
|
#include "./testing.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");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main (int argc, char *argv[]) {
|
int main (int argc, char *argv[]) {
|
||||||
/* if (argc > 1) { */
|
/* if (argc > 1) { */
|
||||||
@@ -61,7 +25,7 @@ int main (int argc, char *argv[]) {
|
|||||||
/* printf("No source provided.\n"); */
|
/* printf("No source provided.\n"); */
|
||||||
/* } */
|
/* } */
|
||||||
|
|
||||||
/* test(); */
|
run_all_tests();
|
||||||
print(parse_expression("(+ 4 7)"));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ Ast_Node* parse_expression(char* expr) {
|
|||||||
// initial state is code
|
// initial state is code
|
||||||
Parser_State current_parser_state = Parser_State_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
|
* 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 =
|
||||||
|
|
||||||
- if
|
** DONE if
|
||||||
- and
|
CLOSED: [2018-09-18 Di 12:14]
|
||||||
- or
|
** TODO and
|
||||||
- not
|
** TODO or
|
||||||
|
** TODO not
|
||||||
|
|
||||||
- first (car)
|
** TODO first (car)
|
||||||
- rest (cdr)
|
** TODO rest (cdr)
|
||||||
- pair (cons)
|
** TODO pair (cons)
|
||||||
|
|
||||||
- load (import)
|
** TODO load (import)
|
||||||
- define
|
** TODO define
|
||||||
|
|
||||||
- lambda
|
** TODO lambda
|
||||||
- progn
|
** TODO progn
|
||||||
|
|
||||||
- eval
|
** TODO eval
|
||||||
- quote
|
** TODO quote
|
||||||
|
|
||||||
- print
|
** TODO print
|
||||||
- read
|
** TODO read
|
||||||
|
|
||||||
|
** TODO help
|
||||||
|
|
||||||
- help
|
|
||||||
* Types
|
* Types
|
||||||
- Symbol
|
- Symbol
|
||||||
- Number
|
- Number
|
||||||
|
|||||||
Reference in New Issue
Block a user