#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%s:%d: Assertion failed\n\tfor '" #variable "'" \ "\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%s:%d: Assertion failed\n\tfor '" #variable "'" \ "\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_equal_string(variable, value) \ if (!string_equal(variable, value)) { \ print_assert_equal_fail(variable, value, char*, "%s"); \ return fail; \ } #define assert_equal_type(node, _type) \ if (node->type != _type) { \ print_assert_equal_fail( \ Ast_Node_Type_to_string(node->type), \ Ast_Node_Type_to_string(_type), char*, "%s"); \ return fail; \ } \ #define assert_null(variable) \ assert_equal_int(variable, nullptr) #define assert_not_null(variable) \ assert_not_equal_int(variable, nullptr) #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 = nullptr; \ } \ } \ testresult test_eval_operands() { char operands_string[] = "((eval 1) (+ 1 2) \"okay\" (eval :haha))"; Ast_Node* operands = parse_single_expression(operands_string); int operands_length; operands = eval_arguments(operands, create_empty_environment(Environment_Type_Let), &operands_length); assert_no_error(error); assert_equal_int(list_length(operands), 4); assert_equal_type(operands, Ast_Node_Type_Pair); assert_equal_type(operands->value.pair->first, Ast_Node_Type_Number); assert_equal_double(operands->value.pair->first->value.number->value, 1); operands = operands->value.pair->rest; assert_equal_type(operands, Ast_Node_Type_Pair); assert_equal_type(operands->value.pair->first, Ast_Node_Type_Number); assert_equal_double(operands->value.pair->first->value.number->value, 3); operands = operands->value.pair->rest; assert_equal_type(operands, Ast_Node_Type_Pair); assert_equal_type(operands->value.pair->first, Ast_Node_Type_String); assert_equal_string(operands->value.pair->first->value.string->value, "okay"); operands = operands->value.pair->rest; assert_equal_type(operands, Ast_Node_Type_Pair); assert_equal_type(operands->value.pair->first, Ast_Node_Type_Keyword); assert_equal_string(operands->value.pair->first->value.keyword->identifier, "haha"); return pass; } testresult test_parse_atom() { int index_in_text = 0; char string[] = "123 -1.23e-2 " // numbers "\"asd\" " // strings ":key1 :key:2 " // keywords "sym +"; // symbols // test numbers Ast_Node* result = parse_atom(string, &index_in_text); assert_equal_type(result, Ast_Node_Type_Number); assert_equal_double(result->value.number->value, 123); ++index_in_text; result = parse_atom(string, &index_in_text); assert_equal_type(result, Ast_Node_Type_Number); assert_equal_double(result->value.number->value, -1.23e-2); // test strings ++index_in_text; result = parse_atom(string, &index_in_text); assert_equal_type(result, Ast_Node_Type_String); assert_equal_string(result->value.string->value, "asd"); // test keywords ++index_in_text; result = parse_atom(string, &index_in_text); assert_equal_type(result, Ast_Node_Type_Keyword); assert_equal_string(result->value.keyword->identifier, "key1"); ++index_in_text; result = parse_atom(string, &index_in_text); assert_equal_type(result, Ast_Node_Type_Keyword); assert_equal_string(result->value.keyword->identifier, "key:2"); // test symbols ++index_in_text; result = parse_atom(string, &index_in_text); assert_equal_type(result, Ast_Node_Type_Symbol); assert_equal_string(result->value.symbol->identifier, "sym"); ++index_in_text; result = parse_atom(string, &index_in_text); assert_equal_type(result, Ast_Node_Type_Symbol); assert_equal_string(result->value.symbol->identifier, "+"); return pass; } testresult test_parse_expression() { int index_in_text = 0; char string[] = "(fun + 12)"; Ast_Node* result = parse_expression(string, &index_in_text); assert_no_error(error); assert_equal_type(result, Ast_Node_Type_Pair); assert_equal_type(result->value.pair->first, Ast_Node_Type_Symbol); assert_equal_string(result->value.pair->first->value.symbol->identifier, "fun"); result = result->value.pair->rest; assert_equal_type(result, Ast_Node_Type_Pair); assert_equal_type(result->value.pair->first, Ast_Node_Type_Symbol); assert_equal_string(result->value.pair->first->value.symbol->identifier, "+"); result = result->value.pair->rest; assert_equal_type(result, Ast_Node_Type_Pair); assert_equal_type(result->value.pair->first, Ast_Node_Type_Number); assert_equal_double(result->value.pair->first->value.number->value, 12); result = result->value.pair->rest; assert_equal_type(result, Ast_Node_Type_Nil); char string2[] = "(define fun (lambda (x) (+ 5 (* x x ))))"; index_in_text = 0; result = parse_expression(string2, &index_in_text); assert_no_error(error); assert_equal_type(result, Ast_Node_Type_Pair); assert_equal_type(result->value.pair->first, Ast_Node_Type_Symbol); assert_equal_string(result->value.pair->first->value.symbol->identifier, "define"); result = result->value.pair->rest; assert_equal_type(result, Ast_Node_Type_Pair); assert_equal_type(result->value.pair->first, Ast_Node_Type_Symbol); assert_equal_string(result->value.pair->first->value.symbol->identifier, "fun"); result = result->value.pair->rest; assert_equal_type(result, Ast_Node_Type_Pair); assert_equal_type(result->value.pair->first, Ast_Node_Type_Pair); assert_equal_type(result->value.pair->first->value.pair->first, Ast_Node_Type_Symbol); assert_equal_string(result->value.pair->first->value.pair->first->value.symbol->identifier, "lambda"); result = result->value.pair->rest; return pass; } testresult test_built_in_add() { char exp_string[] = "(+ 10 4)"; Ast_Node* expression = parse_single_expression(exp_string); Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); assert_no_error(error); assert_not_null(result); assert_equal_type(result, Ast_Node_Type_Number); assert_equal_double(result->value.number->value, 14); return pass; } testresult test_built_in_substract() { char exp_string[] = "(- 10 4)"; Ast_Node* expression = parse_single_expression(exp_string); Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); assert_no_error(error); assert_not_null(result); assert_equal_type(result, Ast_Node_Type_Number); assert_equal_double(result->value.number->value, 6); return pass; } testresult test_built_in_multiply() { char exp_string[] = "(* 10 4)"; Ast_Node* expression = parse_single_expression(exp_string); Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); assert_no_error(error); assert_not_null(result); assert_equal_type(result, Ast_Node_Type_Number); assert_equal_double(result->value.number->value, 40); return pass; } testresult test_built_in_divide() { char exp_string[] = "(/ 20 4)"; Ast_Node* expression = parse_single_expression(exp_string); Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); assert_null(error); assert_not_null(result); assert_equal_type(result, Ast_Node_Type_Number); assert_equal_double(result->value.number->value, 5); return pass; } testresult test_built_in_if() { char exp_string1[] = "(if 1 4 5)"; Ast_Node* expression = parse_single_expression(exp_string1); Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); assert_no_error(error); assert_not_null(result); assert_equal_type(result, Ast_Node_Type_Number); assert_equal_double(result->value.number->value, 4); char exp_string2[] = "(if () 4 5)"; expression = parse_single_expression(exp_string2); result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); assert_no_error(error); assert_not_null(result); assert_equal_type(result, Ast_Node_Type_Number); assert_equal_double(result->value.number->value, 5); return pass; } testresult test_built_in_and() { char exp_string1[] = "(and 1 \"asd\" 4)"; Ast_Node* expression = parse_single_expression(exp_string1); Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); assert_no_error(error); assert_not_null(result); assert_equal_type(result, Ast_Node_Type_Number); assert_equal_double(result->value.number->value, 1); // a false case char exp_string2[] = "(and () \"asd\" 4)"; expression = parse_single_expression(exp_string2); result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); assert_no_error(error); assert_not_null(result); assert_equal_type(result, Ast_Node_Type_Nil); return pass; } testresult test_built_in_or() { char exp_string1[] = "(or \"asd\" nil)"; Ast_Node* expression = parse_single_expression(exp_string1); Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); assert_no_error(error); assert_not_null(result); assert_equal_type(result, Ast_Node_Type_Number); assert_equal_double(result->value.number->value, 1); // a false case char exp_string2[] = "(or () ())"; expression = parse_single_expression(exp_string2); result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); assert_no_error(error); assert_not_null(result); assert_equal_type(result, Ast_Node_Type_Nil); return pass; } testresult test_built_in_not() { char exp_string1[] = "(not ())"; Ast_Node* expression = parse_single_expression(exp_string1); Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); // a true case assert_no_error(error); assert_not_null(result); assert_equal_type(result, Ast_Node_Type_Number); assert_equal_double(result->value.number->value, 1); // a false case char exp_string2[] = "(not \"asd xD\")"; expression = parse_single_expression(exp_string2); result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); assert_no_error(error); assert_not_null(result); assert_equal_type(result, Ast_Node_Type_Nil); return pass; } void run_all_tests() { log_level = Log_Level_None; printf("-- Parsing --\n"); invoke_test(test_parse_atom); invoke_test(test_parse_expression); printf("\n-- Basic evaluating --\n"); invoke_test(test_eval_operands); printf("\n-- Built ins --\n"); 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); invoke_test(test_built_in_or); invoke_test(test_built_in_not); }