We can parse expressions
This commit is contained in:
@@ -1,26 +1,25 @@
|
|||||||
@echo off
|
@echo off
|
||||||
|
@setlocal
|
||||||
pushd %~dp0
|
pushd %~dp0
|
||||||
|
|
||||||
|
set start=%time%
|
||||||
|
|
||||||
set exeName=lisp.exe
|
set exeName=lisp.exe
|
||||||
set binDir=bin
|
set binDir=bin
|
||||||
|
|
||||||
mkdir quickbuild
|
mkdir quickbuild 2>nul
|
||||||
pushd quickbuild
|
pushd quickbuild
|
||||||
|
|
||||||
cl^
|
echo ---------- Compiling ----------
|
||||||
../src/main.c^
|
call timecmd cl ../src/main.c /Fe%exeName% /W3 /TC /nologo /EHsc /Z7 /link /incremental /debug:fastlink
|
||||||
/Fe%exeName% /W3 /TC^
|
|
||||||
/nologo /EHsc /Z7^
|
|
||||||
/link /incremental /debug:fastlink
|
|
||||||
|
|
||||||
if %errorlevel% == 0 (
|
if %errorlevel% == 0 (
|
||||||
echo.
|
echo.
|
||||||
if not exist ..\%binDir% mkdir ..\%binDir%
|
if not exist ..\%binDir% mkdir ..\%binDir%
|
||||||
move %exeName% ..\%binDir%\ > NUL
|
move %exeName% ..\%binDir%\ > NUL
|
||||||
pushd ..\%binDir%
|
pushd ..\%binDir%
|
||||||
echo ---------- Output start ----------
|
echo ---------- Running ----------
|
||||||
%exeName%
|
call timecmd %exeName%
|
||||||
echo ---------- Output end ----------
|
|
||||||
del %exeName% /S /Q > NUL
|
del %exeName% /S /Q > NUL
|
||||||
popd
|
popd
|
||||||
) else (
|
) else (
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ typedef struct {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char* value;
|
char* value;
|
||||||
|
int length;
|
||||||
} String;
|
} String;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -42,6 +43,7 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
Lambda_List* parameters;
|
Lambda_List* parameters;
|
||||||
Pair* form;
|
Pair* form;
|
||||||
|
char* docstring;
|
||||||
} Function;
|
} Function;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -79,11 +81,12 @@ Ast_Node* create_ast_node_number(double number) {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast_Node* create_ast_node_string(char* str) {
|
Ast_Node* create_ast_node_string(char* str, int length) {
|
||||||
Ast_Node* node = new(Ast_Node);
|
Ast_Node* node = new(Ast_Node);
|
||||||
node->type = Ast_Node_Type_String;
|
node->type = Ast_Node_Type_String;
|
||||||
node->value.string = new(String);
|
node->value.string = new(String);
|
||||||
node->value.string->value = str;
|
node->value.string->value = str;
|
||||||
|
node->value.string->length = length;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +98,13 @@ Ast_Node* create_ast_node_symbol(char* identifier) {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ast_Node* create_ast_node_keyword(char* keyword) {
|
||||||
|
Ast_Node* node = new(Ast_Node);
|
||||||
|
node->type = Ast_Node_Type_Keyword;
|
||||||
|
node->value.keyword = new(Keyword);
|
||||||
|
node->value.keyword->identifier = keyword;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
Ast_Node* create_ast_node_built_in_function(char* identifier) {
|
Ast_Node* create_ast_node_built_in_function(char* identifier) {
|
||||||
Ast_Node* node = new(Ast_Node);
|
Ast_Node* node = new(Ast_Node);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ typedef enum {
|
|||||||
Error_Type_Symbol_Not_Defined,
|
Error_Type_Symbol_Not_Defined,
|
||||||
Error_Type_Not_A_Function,
|
Error_Type_Not_A_Function,
|
||||||
Error_Type_Not_Yet_Implemented,
|
Error_Type_Not_Yet_Implemented,
|
||||||
|
Error_Type_Syntax_Error,
|
||||||
Error_Type_Unknown_Error,
|
Error_Type_Unknown_Error,
|
||||||
} Error_Type;
|
} Error_Type;
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ char* Error_Type_to_string(Error_Type type) {
|
|||||||
case Error_Type_Wrong_Number_Of_Arguments: return "Wrong number of arguments";
|
case Error_Type_Wrong_Number_Of_Arguments: return "Wrong number of arguments";
|
||||||
case Error_Type_Type_Missmatch: return "Type Missmatch";
|
case Error_Type_Type_Missmatch: return "Type Missmatch";
|
||||||
case Error_Type_Not_Yet_Implemented: return "Not yet implemented";
|
case Error_Type_Not_Yet_Implemented: return "Not yet implemented";
|
||||||
|
case Error_Type_Syntax_Error: return "Syntax Error";
|
||||||
default: return "Unknown Error";
|
default: return "Unknown Error";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-2
@@ -81,6 +81,7 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
|
|||||||
} else if (string_equal("if", operator_name)) {
|
} else if (string_equal("if", operator_name)) {
|
||||||
int operands_length = list_length(operands);
|
int operands_length = list_length(operands);
|
||||||
if (operands_length != 2 && operands_length != 3) {
|
if (operands_length != 2 && operands_length != 3) {
|
||||||
|
if (!error)
|
||||||
create_error(Error_Type_Wrong_Number_Of_Arguments, operands);
|
create_error(Error_Type_Wrong_Number_Of_Arguments, operands);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -124,10 +125,14 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
|
|||||||
return create_ast_node_nil();
|
return create_ast_node_nil();
|
||||||
} else if (string_equal("not", operator_name)) {
|
} else if (string_equal("not", operator_name)) {
|
||||||
if (list_length(operands) != 1) {
|
if (list_length(operands) != 1) {
|
||||||
|
if (!error)
|
||||||
create_error(Error_Type_Wrong_Number_Of_Arguments, operands);
|
create_error(Error_Type_Wrong_Number_Of_Arguments, operands);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
bool truthy = is_truthy(operands->value.pair->first, env);
|
||||||
|
if (truthy)
|
||||||
|
return create_ast_node_nil();
|
||||||
|
return create_ast_node_number(1);
|
||||||
} else {
|
} else {
|
||||||
report_error(Error_Type_Not_Yet_Implemented);
|
report_error(Error_Type_Not_Yet_Implemented);
|
||||||
}
|
}
|
||||||
@@ -148,7 +153,7 @@ bool is_truthy (Ast_Node* expression, Environment* env) {
|
|||||||
Ast_Node* result = eval_expr(expression, env);
|
Ast_Node* result = eval_expr(expression, env);
|
||||||
switch (result->type) {
|
switch (result->type) {
|
||||||
case Ast_Node_Type_Nil: return false;
|
case Ast_Node_Type_Nil: return false;
|
||||||
case Ast_Node_Type_Number: return result->value.number->value != 0;
|
/* case Ast_Node_Type_Number: return result->value.number->value != 0; */
|
||||||
default: return true;
|
default: return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+254
-9
@@ -1,12 +1,257 @@
|
|||||||
Ast_Node* parse_expression(char* expr) {
|
typedef struct {
|
||||||
typedef enum {
|
Ast_Node** data;
|
||||||
Parser_State_Code,
|
int length;
|
||||||
Parser_State_String,
|
int next_index;
|
||||||
Parser_State_Comment,
|
} Ast_Node_Array_List;
|
||||||
} Parser_State;
|
|
||||||
|
|
||||||
// initial state is code
|
Ast_Node_Array_List* create_Ast_Node_Array_List() {
|
||||||
Parser_State current_parser_state = Parser_State_Code;
|
Ast_Node_Array_List* ret = new (Ast_Node_Array_List);
|
||||||
|
|
||||||
return nullptr;
|
// create one with 16 entries first
|
||||||
|
ret->length = 16;
|
||||||
|
ret->data = (Ast_Node**)malloc(ret->length * sizeof(Ast_Node));
|
||||||
|
ret->next_index = 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append_to_Ast_Node_Array_List(Ast_Node_Array_List* list, Ast_Node* node) {
|
||||||
|
if (list->next_index == list->length) {
|
||||||
|
list->length *= 2;
|
||||||
|
list->data =
|
||||||
|
(Ast_Node**)realloc(list->data, list->length * sizeof(Ast_Node));
|
||||||
|
}
|
||||||
|
|
||||||
|
list->data[list->next_index++] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void eat_comment_line(char* text, int* index_in_text) {
|
||||||
|
// safety check if we are actually starting a comment here
|
||||||
|
if (text[*index_in_text] != ';')
|
||||||
|
return;
|
||||||
|
|
||||||
|
// eat the comment line
|
||||||
|
do {
|
||||||
|
++(*index_in_text);
|
||||||
|
} while (text[(*index_in_text)] != '\n' &&
|
||||||
|
text[(*index_in_text)] != '\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
void eat_whitespace(char* text, int* index_in_text) {
|
||||||
|
// skip whitespaces
|
||||||
|
while (text[(*index_in_text)] == ' ' ||
|
||||||
|
text[(*index_in_text)] == '\t' ||
|
||||||
|
text[(*index_in_text)] == '\n') {
|
||||||
|
++(*index_in_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void eat_until_code(char* text, int* index_in_text) {
|
||||||
|
int position_before;
|
||||||
|
do {
|
||||||
|
position_before = *index_in_text;
|
||||||
|
eat_comment_line(text, index_in_text);
|
||||||
|
eat_whitespace(text, index_in_text);
|
||||||
|
} while (position_before != *index_in_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* read_atom(char* text, int* index_in_text) {
|
||||||
|
int atom_length = 0;
|
||||||
|
while (text[*index_in_text+atom_length] != ' ' &&
|
||||||
|
text[*index_in_text+atom_length] != ')' &&
|
||||||
|
text[*index_in_text+atom_length] != '(' &&
|
||||||
|
text[*index_in_text+atom_length] != '\0' &&
|
||||||
|
text[*index_in_text+atom_length] != '\n' &&
|
||||||
|
text[*index_in_text+atom_length] != '\t')
|
||||||
|
{
|
||||||
|
++atom_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// let's mark the end of the atom there quickly, so the string can
|
||||||
|
// be copied from there easily and then put the char that was
|
||||||
|
// before there back
|
||||||
|
char before = text[*index_in_text+atom_length];
|
||||||
|
text[*index_in_text+atom_length] = '\0';
|
||||||
|
|
||||||
|
// get the atom
|
||||||
|
char* atom = (char*)malloc(atom_length*sizeof(char)+1); // plus null char
|
||||||
|
strcpy(atom, text+(*index_in_text));
|
||||||
|
|
||||||
|
// restore the original string
|
||||||
|
text[*index_in_text+atom_length] = before;
|
||||||
|
|
||||||
|
// update the index to point to the character after the atom
|
||||||
|
// ended
|
||||||
|
*index_in_text += atom_length;
|
||||||
|
|
||||||
|
return atom;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* parse_number(char* text, int* index_in_text) {
|
||||||
|
double number;
|
||||||
|
char* str_number = read_atom(text, index_in_text);
|
||||||
|
sscanf(str_number, "%lf", &number);
|
||||||
|
return create_ast_node_number(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Ast_Node* parse_keyword(char* text, int* index_in_text) {
|
||||||
|
// we are now on the colon
|
||||||
|
++(*index_in_text);
|
||||||
|
char* str_keyword = read_atom(text, index_in_text);
|
||||||
|
return create_ast_node_keyword(str_keyword);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* parse_symbol(char* text, int* index_in_text) {
|
||||||
|
// we are now at the first char of the symbol
|
||||||
|
char* str_symbol = read_atom(text, index_in_text);
|
||||||
|
return create_ast_node_symbol(str_symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* parse_string(char* text, int* index_in_text) {
|
||||||
|
// the first character is the '"'
|
||||||
|
++(*index_in_text);
|
||||||
|
|
||||||
|
// now we are at the first letter, if this is the closing '"' then
|
||||||
|
// it's easy
|
||||||
|
if (text[*index_in_text] == '"') {
|
||||||
|
char* str = new(char);
|
||||||
|
*str = '\0';
|
||||||
|
return create_ast_node_string(str, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// okay so the first letter was not actually closing the string...
|
||||||
|
int string_length = 0;
|
||||||
|
while (text[*index_in_text+string_length] != '"' ||
|
||||||
|
text[*index_in_text+string_length] == '\\')
|
||||||
|
{
|
||||||
|
++string_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we found the end of the string
|
||||||
|
text[*index_in_text+string_length] = '\0';
|
||||||
|
|
||||||
|
char* string = (char*)malloc(string_length*sizeof(char)+1); // plus null char
|
||||||
|
strcpy(string, text+(*index_in_text));
|
||||||
|
|
||||||
|
text[*index_in_text+string_length] = '"';
|
||||||
|
|
||||||
|
*index_in_text += string_length +1; // plus one because we want to
|
||||||
|
// go after the quotes
|
||||||
|
|
||||||
|
return create_ast_node_string(string, string_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* parse_atom(char* text, int* index_in_text) {
|
||||||
|
// numbers
|
||||||
|
if ((text[*index_in_text] <= 57 && // if number
|
||||||
|
text[*index_in_text] >= 48)
|
||||||
|
||
|
||||||
|
((text[*index_in_text] == '+' || // or if sign and then number
|
||||||
|
text[*index_in_text] == '-')
|
||||||
|
&&
|
||||||
|
(text[*index_in_text +1] <= 57 &&
|
||||||
|
text[*index_in_text +1] >= 48))
|
||||||
|
||
|
||||||
|
((text[*index_in_text] == '.') // or if . and then number
|
||||||
|
&&
|
||||||
|
(text[*index_in_text +1] <= 57 &&
|
||||||
|
text[*index_in_text +1] >= 48)))
|
||||||
|
return parse_number(text, index_in_text);
|
||||||
|
|
||||||
|
// keywords
|
||||||
|
if (text[*index_in_text] == ':')
|
||||||
|
return parse_keyword(text, index_in_text);
|
||||||
|
|
||||||
|
// strings
|
||||||
|
if (text[*index_in_text] == '"')
|
||||||
|
return parse_string(text, index_in_text);
|
||||||
|
|
||||||
|
return parse_symbol(text, index_in_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* parse_expression(char* text, int* index_in_text) {
|
||||||
|
// okay we know the text under the index is '('
|
||||||
|
++(*index_in_text);
|
||||||
|
|
||||||
|
eat_whitespace(text, index_in_text);
|
||||||
|
|
||||||
|
// if there was actually nothing in the list return nil
|
||||||
|
if (text[(*index_in_text)] == ')') {
|
||||||
|
++(*index_in_text);
|
||||||
|
return create_ast_node_nil();
|
||||||
|
}
|
||||||
|
|
||||||
|
// okay there is something
|
||||||
|
Ast_Node* head = new(Ast_Node);
|
||||||
|
head->type = Ast_Node_Type_Pair;
|
||||||
|
head->value.pair = new(Pair);
|
||||||
|
Ast_Node* expression = head;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (text[(*index_in_text)] == '(') {
|
||||||
|
head->value.pair->first = parse_expression(text, index_in_text);
|
||||||
|
} else {
|
||||||
|
head->value.pair->first = parse_atom(text, index_in_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
eat_until_code(text, index_in_text);
|
||||||
|
|
||||||
|
if (text[(*index_in_text)] == ')') {
|
||||||
|
head->value.pair->rest = create_ast_node_nil();
|
||||||
|
++(*index_in_text);
|
||||||
|
break;
|
||||||
|
} else if (text[(*index_in_text)] == '.') {
|
||||||
|
++(*index_in_text);
|
||||||
|
eat_until_code(text, index_in_text);
|
||||||
|
|
||||||
|
if (text[(*index_in_text)] == '(')
|
||||||
|
head->value.pair->rest = parse_expression(text, index_in_text);
|
||||||
|
else
|
||||||
|
head->value.pair->rest = parse_atom(text, index_in_text);
|
||||||
|
|
||||||
|
eat_until_code(text, index_in_text);
|
||||||
|
|
||||||
|
if (text[(*index_in_text)] != ')')
|
||||||
|
create_error(Error_Type_Syntax_Error, nullptr);
|
||||||
|
++(*index_in_text);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
head->value.pair->rest = create_ast_node_pair(nullptr, nullptr);
|
||||||
|
head = head->value.pair->rest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* parse_single_expression(char* text) {
|
||||||
|
int index_in_text = 0;
|
||||||
|
return parse_expression(text, &index_in_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node_Array_List* parse_program(char* text) {
|
||||||
|
Ast_Node_Array_List* program = create_Ast_Node_Array_List();
|
||||||
|
|
||||||
|
int index_in_text = 0;
|
||||||
|
|
||||||
|
while (text[index_in_text] != '\0') {
|
||||||
|
switch (text[index_in_text]) {
|
||||||
|
case '(': {
|
||||||
|
append_to_Ast_Node_Array_List(
|
||||||
|
program, parse_expression(text, &index_in_text));
|
||||||
|
} break;
|
||||||
|
case ';': {
|
||||||
|
eat_comment_line(text, &index_in_text);
|
||||||
|
} break;
|
||||||
|
case ' ':
|
||||||
|
case '\t':
|
||||||
|
case '\n': {
|
||||||
|
++index_in_text;
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
create_error(Error_Type_Syntax_Error, nullptr);
|
||||||
|
/* syntax error */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return program;
|
||||||
}
|
}
|
||||||
|
|||||||
+180
-120
@@ -5,15 +5,13 @@
|
|||||||
#define fail 0
|
#define fail 0
|
||||||
|
|
||||||
#define print_assert_equal_fail(variable, value, type, format) \
|
#define print_assert_equal_fail(variable, value, type, format) \
|
||||||
printf("\nAssertion failed for '" #variable "'" \
|
printf("\n%s:%d: Assertion failed\n\tfor '" #variable "'" \
|
||||||
"\n\t in %s:%d" \
|
|
||||||
"\n\texpected: " format \
|
"\n\texpected: " format \
|
||||||
"\n\tgot: " format "\n", \
|
"\n\tgot: " format "\n", \
|
||||||
__FILE__, __LINE__, (type)value, (type)variable)
|
__FILE__, __LINE__, (type)value, (type)variable)
|
||||||
|
|
||||||
#define print_assert_not_equal_fail(variable, value, type, format) \
|
#define print_assert_not_equal_fail(variable, value, type, format) \
|
||||||
printf("\nAssertion failed for '" #variable "'" \
|
printf("\n%s:%d: Assertion failed\n\tfor '" #variable "'" \
|
||||||
"\n\t in %s:%d" \
|
|
||||||
"\n\texpected not: " format \
|
"\n\texpected not: " format \
|
||||||
"\n\tgot anyways: " format "\n", \
|
"\n\tgot anyways: " format "\n", \
|
||||||
__FILE__, __LINE__, (type)value, (type)variable)
|
__FILE__, __LINE__, (type)value, (type)variable)
|
||||||
@@ -50,6 +48,13 @@
|
|||||||
return fail; \
|
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_null(variable) \
|
#define assert_null(variable) \
|
||||||
assert_equal_int(variable, nullptr)
|
assert_equal_int(variable, nullptr)
|
||||||
|
|
||||||
@@ -74,21 +79,121 @@
|
|||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
|
|
||||||
|
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_int(result->type, Ast_Node_Type_Number);
|
||||||
|
assert_equal_double(result->value.number->value, 123);
|
||||||
|
|
||||||
|
++index_in_text;
|
||||||
|
|
||||||
|
result = parse_atom(string, &index_in_text);
|
||||||
|
assert_equal_int(result->type, 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_int(result->type, 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_int(result->type, Ast_Node_Type_Keyword);
|
||||||
|
assert_equal_string(result->value.keyword->identifier, "key1");
|
||||||
|
|
||||||
|
++index_in_text;
|
||||||
|
|
||||||
|
result = parse_atom(string, &index_in_text);
|
||||||
|
assert_equal_int(result->type, 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_int(result->type, Ast_Node_Type_Symbol);
|
||||||
|
assert_equal_string(result->value.symbol->identifier, "sym");
|
||||||
|
|
||||||
|
++index_in_text;
|
||||||
|
|
||||||
|
result = parse_atom(string, &index_in_text);
|
||||||
|
assert_equal_int(result->type, 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_int(result->type, Ast_Node_Type_Pair);
|
||||||
|
assert_equal_int(result->value.pair->first->type, Ast_Node_Type_Symbol);
|
||||||
|
assert_equal_string(result->value.pair->first->value.symbol->identifier, "fun");
|
||||||
|
|
||||||
|
result = result->value.pair->rest;
|
||||||
|
|
||||||
|
assert_equal_int(result->type, Ast_Node_Type_Pair);
|
||||||
|
assert_equal_int(result->value.pair->first->type, Ast_Node_Type_Symbol);
|
||||||
|
assert_equal_string(result->value.pair->first->value.symbol->identifier, "+");
|
||||||
|
|
||||||
|
result = result->value.pair->rest;
|
||||||
|
|
||||||
|
assert_equal_int(result->type, Ast_Node_Type_Pair);
|
||||||
|
assert_equal_int(result->value.pair->first->type, Ast_Node_Type_Number);
|
||||||
|
assert_equal_double(result->value.pair->first->value.number->value, 12);
|
||||||
|
|
||||||
|
result = result->value.pair->rest;
|
||||||
|
|
||||||
|
assert_equal_int(result->type, 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_int(result->type, Ast_Node_Type_Pair);
|
||||||
|
assert_equal_int(result->value.pair->first->type, Ast_Node_Type_Symbol);
|
||||||
|
assert_equal_string(result->value.pair->first->value.symbol->identifier, "define");
|
||||||
|
|
||||||
|
result = result->value.pair->rest;
|
||||||
|
|
||||||
|
assert_equal_int(result->type, Ast_Node_Type_Pair);
|
||||||
|
assert_equal_int(result->value.pair->first->type, Ast_Node_Type_Symbol);
|
||||||
|
assert_equal_string(result->value.pair->first->value.symbol->identifier, "fun");
|
||||||
|
|
||||||
|
result = result->value.pair->rest;
|
||||||
|
|
||||||
|
assert_equal_int(result->type, Ast_Node_Type_Pair);
|
||||||
|
assert_equal_int(result->value.pair->first->type, Ast_Node_Type_Pair);
|
||||||
|
assert_equal_int(result->value.pair->first->value.pair->first->type, 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() {
|
testresult test_built_in_add() {
|
||||||
Ast_Node* plus = create_ast_node_symbol("+");
|
char exp_string[] = "(+ 10 4)";
|
||||||
Ast_Node* ten = create_ast_node_number(10);
|
Ast_Node* expression = parse_single_expression(exp_string);
|
||||||
Ast_Node* four = create_ast_node_number(4);
|
Ast_Node* result = eval_expr(expression, create_empty_environment());
|
||||||
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_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -99,19 +204,9 @@ testresult test_built_in_add() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
testresult test_built_in_substract() {
|
testresult test_built_in_substract() {
|
||||||
Ast_Node* minus = create_ast_node_symbol("-");
|
char exp_string[] = "(- 10 4)";
|
||||||
Ast_Node* ten = create_ast_node_number(10);
|
Ast_Node* expression = parse_single_expression(exp_string);
|
||||||
Ast_Node* four = create_ast_node_number(4);
|
Ast_Node* result = eval_expr(expression, create_empty_environment());
|
||||||
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_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -123,19 +218,9 @@ testresult test_built_in_substract() {
|
|||||||
|
|
||||||
|
|
||||||
testresult test_built_in_multiply() {
|
testresult test_built_in_multiply() {
|
||||||
Ast_Node* times = create_ast_node_symbol("*");
|
char exp_string[] = "(* 10 4)";
|
||||||
Ast_Node* ten = create_ast_node_number(10);
|
Ast_Node* expression = parse_single_expression(exp_string);
|
||||||
Ast_Node* four = create_ast_node_number(4);
|
Ast_Node* result = eval_expr(expression, create_empty_environment());
|
||||||
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_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -147,19 +232,9 @@ testresult test_built_in_multiply() {
|
|||||||
|
|
||||||
|
|
||||||
testresult test_built_in_divide() {
|
testresult test_built_in_divide() {
|
||||||
Ast_Node* over = create_ast_node_symbol("/");
|
char exp_string[] = "(/ 20 4)";
|
||||||
Ast_Node* ten = create_ast_node_number(20);
|
Ast_Node* expression = parse_single_expression(exp_string);
|
||||||
Ast_Node* four = create_ast_node_number(4);
|
Ast_Node* result = eval_expr(expression, create_empty_environment());
|
||||||
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_null(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -171,35 +246,20 @@ testresult test_built_in_divide() {
|
|||||||
|
|
||||||
|
|
||||||
testresult test_built_in_if() {
|
testresult test_built_in_if() {
|
||||||
Ast_Node* _if = create_ast_node_symbol("if");
|
char exp_string1[] = "(if 1 4 5)";
|
||||||
Ast_Node* cond = create_ast_node_number(1);
|
Ast_Node* expression = parse_single_expression(exp_string1);
|
||||||
Ast_Node* four = create_ast_node_number(4);
|
Ast_Node* result = eval_expr(expression, create_empty_environment());
|
||||||
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_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
assert_equal_int(result->type, Ast_Node_Type_Number);
|
assert_equal_int(result->type, Ast_Node_Type_Number);
|
||||||
assert_equal_double(result->value.number->value, 4);
|
assert_equal_double(result->value.number->value, 4);
|
||||||
|
|
||||||
// test *else* case
|
char exp_string2[] = "(if () 4 5)";
|
||||||
cond->value.number->value = 0;
|
expression = parse_single_expression(exp_string2);
|
||||||
result = eval_expr(form, create_empty_environment());
|
result = eval_expr(expression, create_empty_environment());
|
||||||
|
|
||||||
assert_null(error);
|
assert_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
assert_equal_int(result->type, Ast_Node_Type_Number);
|
assert_equal_int(result->type, Ast_Node_Type_Number);
|
||||||
assert_equal_double(result->value.number->value, 5);
|
assert_equal_double(result->value.number->value, 5);
|
||||||
@@ -208,24 +268,9 @@ testresult test_built_in_if() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
testresult test_built_in_and() {
|
testresult test_built_in_and() {
|
||||||
Ast_Node* _and = create_ast_node_symbol("and");
|
char exp_string1[] = "(and 1 \"asd\" 4)";
|
||||||
Ast_Node* cond1 = create_ast_node_number(1);
|
Ast_Node* expression = parse_single_expression(exp_string1);
|
||||||
Ast_Node* cond2 = create_ast_node_string("asd");
|
Ast_Node* result = eval_expr(expression, create_empty_environment());
|
||||||
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_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -233,8 +278,9 @@ testresult test_built_in_and() {
|
|||||||
assert_equal_double(result->value.number->value, 1);
|
assert_equal_double(result->value.number->value, 1);
|
||||||
|
|
||||||
// a false case
|
// a false case
|
||||||
cond1->value.number->value = 0;
|
char exp_string2[] = "(and () \"asd\" 4)";
|
||||||
result = eval_expr(form, create_empty_environment());
|
expression = parse_single_expression(exp_string2);
|
||||||
|
result = eval_expr(expression, create_empty_environment());
|
||||||
|
|
||||||
assert_no_error(error);
|
assert_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -244,20 +290,9 @@ testresult test_built_in_and() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
testresult test_built_in_or() {
|
testresult test_built_in_or() {
|
||||||
Ast_Node* _or = create_ast_node_symbol("or");
|
char exp_string1[] = "(or \"asd\" nil)";
|
||||||
Ast_Node* cond1 = create_ast_node_nil();
|
Ast_Node* expression = parse_single_expression(exp_string1);
|
||||||
Ast_Node* cond2 = create_ast_node_string("asd");
|
Ast_Node* result = eval_expr(expression, create_empty_environment());
|
||||||
Ast_Node* nil = create_ast_node_nil();
|
|
||||||
Ast_Node* form = create_ast_node_pair(
|
|
||||||
_or,
|
|
||||||
create_ast_node_pair(
|
|
||||||
cond1,
|
|
||||||
create_ast_node_pair(
|
|
||||||
cond2, nil)));
|
|
||||||
Ast_Node* result;
|
|
||||||
|
|
||||||
// a true case
|
|
||||||
result = eval_expr(form, create_empty_environment());
|
|
||||||
|
|
||||||
assert_no_error(error);
|
assert_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -265,14 +300,9 @@ testresult test_built_in_or() {
|
|||||||
assert_equal_double(result->value.number->value, 1);
|
assert_equal_double(result->value.number->value, 1);
|
||||||
|
|
||||||
// a false case
|
// a false case
|
||||||
cond2 = create_ast_node_nil();
|
char exp_string2[] = "(or () ())";
|
||||||
form = create_ast_node_pair(
|
expression = parse_single_expression(exp_string2);
|
||||||
_or,
|
result = eval_expr(expression, create_empty_environment());
|
||||||
create_ast_node_pair(
|
|
||||||
cond1,
|
|
||||||
create_ast_node_pair(
|
|
||||||
cond2, nil)));
|
|
||||||
result = eval_expr(form, create_empty_environment());
|
|
||||||
|
|
||||||
assert_no_error(error);
|
assert_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -282,9 +312,37 @@ testresult test_built_in_or() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
// a true case
|
||||||
|
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
|
||||||
|
char exp_string2[] = "(not \"asd xD\")";
|
||||||
|
expression = parse_single_expression(exp_string2);
|
||||||
|
result = eval_expr(expression, create_empty_environment());
|
||||||
|
|
||||||
|
assert_no_error(error);
|
||||||
|
assert_not_null(result);
|
||||||
|
assert_equal_int(result->type, Ast_Node_Type_Nil);
|
||||||
|
|
||||||
|
return pass;
|
||||||
|
}
|
||||||
|
|
||||||
void run_all_tests() {
|
void run_all_tests() {
|
||||||
log_level = Log_Level_None;
|
log_level = Log_Level_None;
|
||||||
|
|
||||||
|
printf("-- Parsing --\n");
|
||||||
|
invoke_test(test_parse_atom);
|
||||||
|
invoke_test(test_parse_expression);
|
||||||
|
|
||||||
|
printf("\n-- Built ins --\n");
|
||||||
invoke_test(test_built_in_add);
|
invoke_test(test_built_in_add);
|
||||||
invoke_test(test_built_in_substract);
|
invoke_test(test_built_in_substract);
|
||||||
invoke_test(test_built_in_multiply);
|
invoke_test(test_built_in_multiply);
|
||||||
@@ -292,5 +350,7 @@ void run_all_tests() {
|
|||||||
invoke_test(test_built_in_if);
|
invoke_test(test_built_in_if);
|
||||||
invoke_test(test_built_in_and);
|
invoke_test(test_built_in_and);
|
||||||
invoke_test(test_built_in_or);
|
invoke_test(test_built_in_or);
|
||||||
|
invoke_test(test_built_in_not);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user