Reorganize folders and added windows build with MSVC for debugging
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
((c-mode . ((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);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
struct Ast_Node;
|
||||
|
||||
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_Pair,
|
||||
Ast_Node_Type_Function,
|
||||
Ast_Node_Type_Built_In_Function,
|
||||
} Ast_Node_Type;
|
||||
|
||||
typedef struct {
|
||||
char* identifier;
|
||||
} Symbol;
|
||||
|
||||
typedef struct {
|
||||
char* identifier;
|
||||
} Keyword;
|
||||
|
||||
typedef struct {
|
||||
double value;
|
||||
} Number;
|
||||
|
||||
typedef struct {
|
||||
char* value;
|
||||
} String;
|
||||
|
||||
typedef struct {
|
||||
struct Ast_Node* first;
|
||||
struct Ast_Node* rest;
|
||||
} Pair;
|
||||
|
||||
typedef struct {
|
||||
Pair* regular_params;
|
||||
Pair* keyword_params;
|
||||
} Lambda_List;
|
||||
|
||||
typedef struct {
|
||||
Lambda_List* parameters;
|
||||
Pair* form;
|
||||
} Function;
|
||||
|
||||
typedef struct {
|
||||
char* identifier;
|
||||
} Built_In_Function;
|
||||
|
||||
struct Ast_Node {
|
||||
Ast_Node_Type type;
|
||||
union {
|
||||
Symbol* symbol;
|
||||
Keyword* keyword;
|
||||
Number* number;
|
||||
String* string;
|
||||
Pair* pair;
|
||||
Function* function;
|
||||
Built_In_Function* built_in_function;
|
||||
} value;
|
||||
};
|
||||
// was forward declarated
|
||||
typedef struct Ast_Node Ast_Node;
|
||||
|
||||
|
||||
Ast_Node* create_ast_node_nil() {
|
||||
Ast_Node* node = new(Ast_Node);
|
||||
node->type = Ast_Node_Type_Nil;
|
||||
node->value.pair = NULL;
|
||||
return node;
|
||||
}
|
||||
|
||||
Ast_Node* create_ast_node_number(double number) {
|
||||
Ast_Node* node = new(Ast_Node);
|
||||
node->type = Ast_Node_Type_Number;
|
||||
node->value.number = new(Number);
|
||||
node->value.number->value = 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);
|
||||
node->type = Ast_Node_Type_Symbol;
|
||||
node->value.symbol = new(Symbol);
|
||||
node->value.symbol->identifier = identifier;
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
Ast_Node* create_ast_node_built_in_function(char* identifier) {
|
||||
Ast_Node* node = new(Ast_Node);
|
||||
node->type = Ast_Node_Type_Built_In_Function;
|
||||
node->value.built_in_function = new(Built_In_Function);
|
||||
node->value.built_in_function->identifier = identifier;
|
||||
return node;
|
||||
}
|
||||
|
||||
Ast_Node* create_ast_node_pair(Ast_Node* first, Ast_Node* rest) {
|
||||
Ast_Node* node = new(Ast_Node);
|
||||
node->type = Ast_Node_Type_Pair;
|
||||
node->value.pair = new(Pair);
|
||||
node->value.pair->first = first;
|
||||
node->value.pair->rest = rest;
|
||||
return node;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
Ast_Node* built_in_add(Ast_Node* operands) {
|
||||
double sum = 0;
|
||||
while (operands->type == Ast_Node_Type_Pair) {
|
||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
||||
sum += operands->value.pair->first->value.number->value;
|
||||
operands = operands->value.pair->rest;
|
||||
}
|
||||
|
||||
return create_ast_node_number(sum);
|
||||
}
|
||||
|
||||
Ast_Node* built_in_substract(Ast_Node* operands) {
|
||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
||||
double difference = operands->value.pair->first->value.number->value;
|
||||
|
||||
operands = operands->value.pair->rest;
|
||||
while (operands->type == Ast_Node_Type_Pair) {
|
||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
||||
|
||||
difference -= operands->value.pair->first->value.number->value;
|
||||
operands = operands->value.pair->rest;
|
||||
}
|
||||
return create_ast_node_number(difference);
|
||||
}
|
||||
|
||||
Ast_Node* built_in_multiply(Ast_Node* operands) {
|
||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
||||
double product = operands->value.pair->first->value.number->value;
|
||||
|
||||
operands = operands->value.pair->rest;
|
||||
while (operands->type == Ast_Node_Type_Pair) {
|
||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
||||
|
||||
product *= operands->value.pair->first->value.number->value;
|
||||
operands = operands->value.pair->rest;
|
||||
}
|
||||
return create_ast_node_number(product);
|
||||
}
|
||||
|
||||
Ast_Node* built_in_divide(Ast_Node* operands) {
|
||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
||||
double quotient = operands->value.pair->first->value.number->value;
|
||||
|
||||
operands = operands->value.pair->rest;
|
||||
while (operands->type == Ast_Node_Type_Pair) {
|
||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
||||
|
||||
quotient /= operands->value.pair->first->value.number->value;
|
||||
operands = operands->value.pair->rest;
|
||||
}
|
||||
return create_ast_node_number(quotient);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
|
||||
struct Environment {
|
||||
struct Environment* parent;
|
||||
int key_count;
|
||||
char** keys;
|
||||
Ast_Node* values;
|
||||
};
|
||||
|
||||
typedef struct Environment Environment;
|
||||
|
||||
Environment* create_empty_environment() {
|
||||
Environment* env = new(Environment);
|
||||
|
||||
env->parent = NULL;
|
||||
env->key_count = 0;
|
||||
env->keys = NULL;
|
||||
env->values = NULL;
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
Ast_Node* lookup_symbol(Symbol* sym, Environment* env) {
|
||||
for (int i = 0; i < env->key_count; ++i)
|
||||
if (string_equal(env->keys[i], sym->identifier))
|
||||
return env->values+i;
|
||||
|
||||
if (env->parent)
|
||||
return lookup_symbol(sym, env->parent);
|
||||
|
||||
char* built_in_names[] = {
|
||||
// Math stuff
|
||||
"+", "-", "*", "/",
|
||||
">", "<", "=",
|
||||
// Conditional stuff
|
||||
"if", "and", "or", "not",
|
||||
// Cons stuff
|
||||
"first", "rest", "pair",
|
||||
// rest
|
||||
"load", "define",
|
||||
"lambda", "progn",
|
||||
"eval", "quote",
|
||||
"print", "read",
|
||||
"help"
|
||||
};
|
||||
int built_in_count = 23;
|
||||
|
||||
for (int i = 0; i < built_in_count; ++i) {
|
||||
if (string_equal(built_in_names[i], sym->identifier)) {
|
||||
Ast_Node* ret = new(Ast_Node);
|
||||
ret->type = Ast_Node_Type_Built_In_Function;
|
||||
ret->value.built_in_function = new(Built_In_Function);
|
||||
ret->value.built_in_function->identifier = built_in_names[i];
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
char* message;
|
||||
asprintf(&message, "Symbol not defined: %s\n", sym->identifier);
|
||||
panic(message);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
typedef enum {
|
||||
Error_Type_Ill_Formed_List,
|
||||
Error_Type_Wrong_Number_Of_Arguments,
|
||||
Error_Type_Type_Missmatch,
|
||||
Error_Type_Symbol_Not_Defined,
|
||||
Error_Type_Not_A_Function,
|
||||
Error_Type_Not_Yet_Implemented,
|
||||
Error_Type_Unknown_Error,
|
||||
} Error_Type;
|
||||
|
||||
typedef struct {
|
||||
Error_Type type;
|
||||
Ast_Node* location;
|
||||
} Error;
|
||||
|
||||
Error* error;
|
||||
|
||||
void delete_error() {
|
||||
if (error) {
|
||||
free(error);
|
||||
error = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void create_error(Error_Type type, Ast_Node* location) {
|
||||
delete_error();
|
||||
|
||||
error = new(Error);
|
||||
error->type = type;
|
||||
error->location = location;
|
||||
}
|
||||
|
||||
char* Error_Type_to_string(Error_Type type) {
|
||||
switch (type) {
|
||||
case Error_Type_Ill_Formed_List: return "Ill formed list";
|
||||
case Error_Type_Not_A_Function: return "Not a function";
|
||||
case Error_Type_Symbol_Not_Defined: return "Symbol not defined";
|
||||
case Error_Type_Wrong_Number_Of_Arguments: return "Wrong number of arguments";
|
||||
case Error_Type_Type_Missmatch: return "Type Missmatch";
|
||||
case Error_Type_Not_Yet_Implemented: return "Not yet implemented";
|
||||
default: return "Unknown Error";
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/* Ast_Node* apply_to_lambda () {} */
|
||||
|
||||
/* Ast_Node* apply_to_built_in (Ast_Node* function, Ast_Node* arguments) { */
|
||||
|
||||
/* } */
|
||||
|
||||
Ast_Node* eval_expr(Ast_Node* node, Environment* env);
|
||||
int is_truthy (Ast_Node* expression, Environment* env);
|
||||
|
||||
int list_length(Ast_Node* node) {
|
||||
if (node->type == Ast_Node_Type_Nil)
|
||||
return 0;
|
||||
|
||||
if (node->type != Ast_Node_Type_Pair) {
|
||||
create_error(Error_Type_Type_Missmatch, node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int len = 0;
|
||||
while (node->type == Ast_Node_Type_Pair) {
|
||||
++len;
|
||||
node = node->value.pair->rest;
|
||||
if (node->type == Ast_Node_Type_Nil)
|
||||
return len;
|
||||
}
|
||||
|
||||
create_error(Error_Type_Ill_Formed_List, node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void eval_operands(Ast_Node* operands, Environment* env) {
|
||||
while (!error) {
|
||||
if (operands->type == Ast_Node_Type_Pair) {
|
||||
operands->value.pair->first = eval_expr(operands->value.pair->first, env);
|
||||
operands = operands->value.pair->rest;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
|
||||
#define report_error(_type) \
|
||||
create_error(_type, node); \
|
||||
return NULL
|
||||
|
||||
Ast_Node* ret = new(Ast_Node);
|
||||
switch (node->type) {
|
||||
case Ast_Node_Type_Nil:
|
||||
ret->type = Ast_Node_Type_Nil;
|
||||
return ret;
|
||||
case Ast_Node_Type_Symbol:
|
||||
return lookup_symbol(node->value.symbol, env);
|
||||
case Ast_Node_Type_Number:
|
||||
case Ast_Node_Type_String:
|
||||
return node;
|
||||
|
||||
case Ast_Node_Type_Pair: {
|
||||
Ast_Node* operator = eval_expr(node->value.pair->first, env);
|
||||
Ast_Node* operands = node->value.pair->rest;
|
||||
|
||||
// check for built ins functions
|
||||
if (operator->type == Ast_Node_Type_Built_In_Function) {
|
||||
char* operator_name = operator->value.built_in_function->identifier;
|
||||
if (string_equal("quote", operator_name)) {
|
||||
return node->value.pair->rest;
|
||||
} else if (string_equal("+", operator_name)) {
|
||||
eval_operands(operands, env);
|
||||
return built_in_add(operands);
|
||||
} 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)) {
|
||||
int operands_length = list_length(operands);
|
||||
if (operands_length != 2 && operands_length != 3) {
|
||||
create_error(Error_Type_Wrong_Number_Of_Arguments, operands);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Ast_Node* condition = operands->value.pair->first;
|
||||
Ast_Node* then_part = operands->value.pair->rest;
|
||||
Ast_Node* else_part = then_part->value.pair->rest;
|
||||
|
||||
if (is_truthy(condition, env))
|
||||
return eval_expr(then_part->value.pair->first, env);
|
||||
else if (operands_length == 3)
|
||||
return eval_expr(else_part->value.pair->first, env);
|
||||
else return create_ast_node_nil();
|
||||
/* } else if (string_equal("not", operator_name)) { */
|
||||
|
||||
/* } else if (string_equal("and", operator_name)) { */
|
||||
/* } else if (string_equal("or", operator_name)) { */
|
||||
} else {
|
||||
report_error(Error_Type_Not_Yet_Implemented);
|
||||
}
|
||||
}
|
||||
|
||||
// assume it's lambda function and evaluate the operands in
|
||||
// place
|
||||
eval_operands(operands, env);
|
||||
}
|
||||
|
||||
default:
|
||||
report_error(Error_Type_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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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! */
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
#define console_normal "\x1B[0m"
|
||||
#define console_red "\x1B[31m"
|
||||
#define console_green "\x1B[32m"
|
||||
#define console_cyan "\x1B[36m"
|
||||
|
||||
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 = "CRITICAL"; break;
|
||||
case Log_Level_Warning: prefix = "WARNING"; break;
|
||||
case Log_Level_Info: prefix = "INFO"; break;
|
||||
case Log_Level_Debug: prefix = "DEBUG"; break;
|
||||
default: return;
|
||||
}
|
||||
printf("%s: %s\n",prefix, message);
|
||||
}
|
||||
|
||||
|
||||
void panic(char* message) {
|
||||
log_message(Log_Level_Critical, message);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
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_Pair): return "pair";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
void print(Ast_Node* node) {
|
||||
switch (node->type) {
|
||||
case (Ast_Node_Type_Nil): {
|
||||
printf("nil");
|
||||
} break;
|
||||
case (Ast_Node_Type_Number): {
|
||||
printf("%f", node->value.number->value);
|
||||
} break;
|
||||
case (Ast_Node_Type_String): {
|
||||
printf("\"%s\"", node->value.string->value);
|
||||
} break;
|
||||
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;
|
||||
case (Ast_Node_Type_Built_In_Function): {
|
||||
printf("[built-in-function %s]", node->value.built_in_function->identifier);
|
||||
} break;
|
||||
case (Ast_Node_Type_Pair): {
|
||||
Ast_Node* head = node;
|
||||
printf("(");
|
||||
|
||||
while (1) {
|
||||
print(head->value.pair->first);
|
||||
head = head->value.pair->rest;
|
||||
if (head->type != Ast_Node_Type_Pair)
|
||||
break;
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
if (head->type != Ast_Node_Type_Nil) {
|
||||
printf(" . ");
|
||||
print(head);
|
||||
}
|
||||
|
||||
printf(")");
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void log_error() {
|
||||
printf("%s%s%s\n", console_red,
|
||||
Error_Type_to_string(error->type),
|
||||
console_normal);
|
||||
printf(" in: %s", console_cyan);
|
||||
print(error->location);
|
||||
printf("%s\n", console_normal);
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#define _CRT_SECURE_NO_DEPRECATE
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#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 "./env.c"
|
||||
#include "./built_ins.c"
|
||||
#include "./eval.c"
|
||||
#include "./testing.c"
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
/* if (argc > 1) { */
|
||||
/* char* fileContent = read_entire_file(argv[1]); */
|
||||
/* if (fileContent) { */
|
||||
/* printf("File: %s\n", fileContent); */
|
||||
/* } */
|
||||
/* } else { */
|
||||
/* printf("No source provided.\n"); */
|
||||
/* } */
|
||||
|
||||
run_all_tests();
|
||||
|
||||
return 0;
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
Ast_Node* parse_expression(char* expr) {
|
||||
typedef enum {
|
||||
Parser_State_Code,
|
||||
Parser_State_String,
|
||||
Parser_State_Comment,
|
||||
} Parser_State;
|
||||
|
||||
// initial state is code
|
||||
Parser_State current_parser_state = Parser_State_Code;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
(+ 1 (- 100 7) 3)
|
||||
|
||||
(defun unpack (&rest args)
|
||||
(car args))
|
||||
(unpack '(1 2))
|
||||
|
||||
(defun add (&rest args)
|
||||
(if args
|
||||
(+ (first args)
|
||||
(apply 'add 'args)))
|
||||
0)
|
||||
|
||||
(add 2 123 99)
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
#define epsilon 2.2204460492503131E-16
|
||||
|
||||
#define testresult int
|
||||
#define pass 1
|
||||
#define fail 0
|
||||
|
||||
#define print_assert_equal_fail(variable, value, type, format) \
|
||||
printf("\nAssertion 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("\nAssertion 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) { \
|
||||
printf("\nExpected no error to occur," \
|
||||
" but an error occured anyways:\n"); \
|
||||
log_error(); \
|
||||
return fail; \
|
||||
} \
|
||||
|
||||
#define assert_equal_int(variable, value) \
|
||||
if (variable != value) { \
|
||||
print_assert_equal_fail(variable, value, size_t, "%zd"); \
|
||||
return fail; \
|
||||
}
|
||||
|
||||
#define assert_not_equal_int(variable, value) \
|
||||
if (variable == value) { \
|
||||
print_assert_not_equal_fail(variable, value, size_t, "%zd"); \
|
||||
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 ":"); \
|
||||
if (name() == pass) { \
|
||||
for(size_t i = strlen(#name); i < 70; ++i) \
|
||||
printf((i%3==1)? "." : " "); \
|
||||
printf("%spassed%s\n", console_green, console_normal); \
|
||||
} \
|
||||
else { \
|
||||
for(int i = -1; i < 70; ++i) \
|
||||
printf((i%3==1)? "." : " "); \
|
||||
printf("%sfailed%s\n", console_red, console_normal); \
|
||||
if(error) { \
|
||||
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, create_empty_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, create_empty_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, create_empty_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, create_empty_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, create_empty_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, create_empty_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, create_empty_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, create_empty_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);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user