some things
This commit is contained in:
+11
-34
@@ -1,31 +1,5 @@
|
||||
struct Ast_Node;
|
||||
|
||||
// #define define_array_list(type, name) \
|
||||
// struct name##_Array_List { \
|
||||
// type* data; \
|
||||
// int length; \
|
||||
// int next_index; \
|
||||
// }; \
|
||||
// \
|
||||
// \
|
||||
// void append_to_##name##_array_list(name##_Array_List* arraylist, type element) { \
|
||||
// if (arraylist->next_index == arraylist->length) { \
|
||||
// arraylist->length *= 2; \
|
||||
// arraylist->data = \
|
||||
// (type*)realloc(arraylist->data, arraylist->length * sizeof(type)); \
|
||||
// } \
|
||||
// arraylist->data[arraylist->next_index++] = element; \
|
||||
// } \
|
||||
// \
|
||||
// \
|
||||
// name##_Array_List* create_##name##_array_list(int initial_capacity) { \
|
||||
// name##_Array_List* ret = new(name##_Array_List); \
|
||||
// ret->data = (type*)malloc(initial_capacity * sizeof(type)); \
|
||||
// ret->next_index = 0; \
|
||||
// ret->length = initial_capacity; \
|
||||
// return ret; \
|
||||
// }
|
||||
|
||||
define_array_list(Ast_Node*, Ast_Node);
|
||||
|
||||
enum struct Ast_Node_Type {
|
||||
@@ -73,8 +47,8 @@ struct String {
|
||||
};
|
||||
|
||||
struct Pair {
|
||||
struct Ast_Node* first;
|
||||
struct Ast_Node* rest;
|
||||
Ast_Node* first;
|
||||
Ast_Node* rest;
|
||||
};
|
||||
|
||||
struct Positional_Arguments {
|
||||
@@ -87,17 +61,14 @@ struct Positional_Arguments {
|
||||
|
||||
struct Keyword_Arguments {
|
||||
char** identifiers;
|
||||
// values[i] will be nullptr if no defalut value was declared for
|
||||
// key identifiers[i]
|
||||
// NOTE(Felix): values[i] will be nullptr if no defalut value was
|
||||
// declared for key identifiers[i]
|
||||
Ast_Node_Array_List* values;
|
||||
int next_index;
|
||||
int length;
|
||||
};
|
||||
|
||||
|
||||
/* Ast_Node_Array_List* create_Ast_Node_Array_List(int initial_length); */
|
||||
/* void append_to_Ast_Node_Array_List(Ast_Node_Array_List* list, struct Ast_Node* node); */
|
||||
|
||||
Positional_Arguments* create_positional_argument_list(int initial_capacity) {
|
||||
Positional_Arguments* ret = new(Positional_Arguments);
|
||||
ret->identifiers = (char**)malloc(initial_capacity * sizeof(char*));
|
||||
@@ -138,8 +109,14 @@ void append_to_keyword_argument_list(Keyword_Arguments* args,
|
||||
|
||||
struct Environment;
|
||||
|
||||
enum struct Function_Type {
|
||||
Lambda,
|
||||
Special_Lambda,
|
||||
Macro
|
||||
};
|
||||
|
||||
struct Function {
|
||||
bool is_special_form;
|
||||
Function_Type type;
|
||||
char* docstring;
|
||||
Positional_Arguments* positional_arguments;
|
||||
Keyword_Arguments* keyword_arguments;
|
||||
|
||||
+161
-19
@@ -450,6 +450,67 @@ void load_built_ins_into_environment(Environment* env) {
|
||||
|
||||
return arguments->value.pair->first;
|
||||
});
|
||||
defun("quasiquote", cLambda {
|
||||
try {
|
||||
arguments_length = list_length(arguments);
|
||||
}
|
||||
|
||||
if (arguments_length != 1) {
|
||||
report_error(Error_Type::Wrong_Number_Of_Arguments);
|
||||
}
|
||||
// print(arguments);
|
||||
// printf("\n");
|
||||
|
||||
// recursive lambdas in lambdas yay!!
|
||||
std::function<Ast_Node*(Ast_Node*)> unquoteSomeExpressions;
|
||||
unquoteSomeExpressions = [&unquoteSomeExpressions, &env] (Ast_Node* expr) -> Ast_Node* {
|
||||
// if it is an atom, return it
|
||||
if (expr->type != Ast_Node_Type::Pair)
|
||||
return copy_ast_node(expr);
|
||||
|
||||
// it is a pair!
|
||||
Ast_Node* originalPair = expr->value.pair->first;
|
||||
if (originalPair->type == Ast_Node_Type::Symbol &&
|
||||
string_equal(originalPair->value.symbol->identifier, "unquote"))
|
||||
{
|
||||
// eval replace the stuff
|
||||
return eval_expr(expr->value.pair->rest->value.pair->first, env);
|
||||
}
|
||||
|
||||
// it is a list but not starting with the symbol
|
||||
// unquote, so search in there for stuff to unquote.
|
||||
// While copying the list
|
||||
|
||||
//NOTE(Felix): Of fucking course we have to copy the
|
||||
// list. The quasiquote will be part of the body of a
|
||||
// funciton, we can't jsut modify it because otherwise
|
||||
// we modify the body of the function and would bake
|
||||
// in the result...
|
||||
Ast_Node* newPair = create_ast_node_pair(nullptr, nullptr);
|
||||
Ast_Node* newPairHead = newPair;
|
||||
Ast_Node* head = expr;
|
||||
while (head->type == Ast_Node_Type::Pair) {
|
||||
newPairHead->value.pair->first = unquoteSomeExpressions(head->value.pair->first);
|
||||
|
||||
if (head->value.pair->rest->type != Ast_Node_Type::Pair)
|
||||
break;
|
||||
|
||||
newPairHead->value.pair->rest = create_ast_node_pair(nullptr, nullptr);
|
||||
|
||||
newPairHead = newPairHead->value.pair->rest;
|
||||
head = head->value.pair->rest;
|
||||
}
|
||||
newPairHead->value.pair->rest = create_ast_node_nil();
|
||||
|
||||
return newPair;
|
||||
};
|
||||
|
||||
Ast_Node* ret = arguments->value.pair->first;
|
||||
Ast_Node* head = ret;
|
||||
|
||||
ret = unquoteSomeExpressions(ret);
|
||||
return ret;
|
||||
});
|
||||
defun("and", cLambda {
|
||||
bool result = true;
|
||||
while (arguments->type != Ast_Node_Type::Nil) {
|
||||
@@ -596,7 +657,7 @@ void load_built_ins_into_environment(Environment* env) {
|
||||
|
||||
Function* function = new(Function);
|
||||
function->parent_environment = env;
|
||||
function->is_special_form = false;
|
||||
function->type = Function_Type::Lambda;
|
||||
|
||||
// if parameters were specified
|
||||
if (arguments->value.pair->first->type != Ast_Node_Type::Nil) {
|
||||
@@ -646,7 +707,7 @@ void load_built_ins_into_environment(Environment* env) {
|
||||
|
||||
Function* function = new(Function);
|
||||
function->parent_environment = env;
|
||||
function->is_special_form = true;
|
||||
function->type = Function_Type::Special_Lambda;
|
||||
|
||||
// if parameters were specified
|
||||
if (arguments->value.pair->first->type != Ast_Node_Type::Nil) {
|
||||
@@ -726,11 +787,6 @@ void load_built_ins_into_environment(Environment* env) {
|
||||
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||
}
|
||||
|
||||
// BUG(Felix): Why is arguments_length for '(1 (2)) == 3 and not 2??
|
||||
// BUG(Felix): Why is arguments_length for '(1 (2)) == 3 and not 2??
|
||||
// BUG(Felix): Why is arguments_length for '(1 (2)) == 3 and not 2??
|
||||
|
||||
// if (list_length(evaluated_arguments) != 2) {
|
||||
if (arguments_length != 2) {
|
||||
report_error(Error_Type::Wrong_Number_Of_Arguments);
|
||||
}
|
||||
@@ -776,9 +832,14 @@ void load_built_ins_into_environment(Environment* env) {
|
||||
switch (type) {
|
||||
case Ast_Node_Type::CFunction: return create_ast_node_keyword("cfunction");
|
||||
case Ast_Node_Type::Function: {
|
||||
if (evaluated_arguments->value.pair->first->value.function->is_special_form)
|
||||
return create_ast_node_keyword("dynamic-macro");
|
||||
return create_ast_node_keyword("dynamic-function");
|
||||
Function* fun = evaluated_arguments->value.pair->first->value.function;
|
||||
if (fun->type == Function_Type::Lambda)
|
||||
return create_ast_node_keyword("lambda");
|
||||
else if (fun->type == Function_Type::Special_Lambda)
|
||||
return create_ast_node_keyword("special-lambda");
|
||||
else if (fun->type == Function_Type::Macro)
|
||||
return create_ast_node_keyword("macro");
|
||||
else return create_ast_node_keyword("unknown");
|
||||
}
|
||||
case Ast_Node_Type::Keyword: return create_ast_node_keyword("keyword");
|
||||
case Ast_Node_Type::Nil: return create_ast_node_keyword("nil");
|
||||
@@ -810,15 +871,16 @@ void load_built_ins_into_environment(Environment* env) {
|
||||
if (type) {
|
||||
printf(" is of type ");
|
||||
print(type);
|
||||
printf("\n");
|
||||
// just make sure type was not redefined and
|
||||
// returns something that is not a keyword
|
||||
printf("\n\n");
|
||||
|
||||
// TODO(Felix): Maybe don't compare strings here?? Wtf
|
||||
if (type->type == Ast_Node_Type::Keyword &&
|
||||
(string_equal(type->value.keyword->identifier, "dynamic-function") ||
|
||||
string_equal(type->value.keyword->identifier, "dynamic-macro")))
|
||||
(string_equal(type->value.keyword->identifier, "lambda") ||
|
||||
string_equal(type->value.keyword->identifier, "special-lambda") ||
|
||||
string_equal(type->value.keyword->identifier, "macro")))
|
||||
{
|
||||
Ast_Node* fun = eval_expr(arguments->value.pair->first, env);
|
||||
printf("\nspecial-lambda? %s\n", (fun->value.function->is_special_form) ? "yes" : "no");
|
||||
|
||||
if (fun->value.function->docstring)
|
||||
printf("Docstring:\n==========\n%s\n\n", fun->value.function->docstring);
|
||||
else
|
||||
@@ -914,9 +976,9 @@ void load_built_ins_into_environment(Environment* env) {
|
||||
});
|
||||
defun("break", cLambda {
|
||||
print_environment(env);
|
||||
#ifdef _DEBUG
|
||||
__debugbreak();
|
||||
#endif
|
||||
if_debug {
|
||||
__debugbreak();
|
||||
}
|
||||
return create_ast_node_nil();
|
||||
});
|
||||
defun("try", cLambda {
|
||||
@@ -961,6 +1023,9 @@ void load_built_ins_into_environment(Environment* env) {
|
||||
|
||||
});
|
||||
defun("copy", cLambda {
|
||||
|
||||
// TODO(Felix): if we are copying string nodes, then
|
||||
// shouldn't the string itself also get copied??
|
||||
try {
|
||||
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||
}
|
||||
@@ -990,6 +1055,83 @@ void load_built_ins_into_environment(Environment* env) {
|
||||
report_error(Error_Type::Unknown_Error);
|
||||
});
|
||||
|
||||
defun("string->symbol", cLambda {
|
||||
|
||||
// TODO(Felix): do some sanity checks on the string. For
|
||||
// example, numbers are not valid symbols.
|
||||
|
||||
try {
|
||||
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||
}
|
||||
|
||||
if (arguments_length != 1) {
|
||||
report_error(Error_Type::Wrong_Number_Of_Arguments);
|
||||
}
|
||||
|
||||
Ast_Node* source = evaluated_arguments->value.pair->first;
|
||||
|
||||
if (source->type != Ast_Node_Type::String) {
|
||||
report_error(Error_Type::Type_Missmatch);
|
||||
}
|
||||
|
||||
return create_ast_node_symbol(_strdup(source->value.string->value));
|
||||
});
|
||||
defun("symbol->string", cLambda {
|
||||
try {
|
||||
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||
}
|
||||
|
||||
if (arguments_length != 1) {
|
||||
report_error(Error_Type::Wrong_Number_Of_Arguments);
|
||||
}
|
||||
|
||||
Ast_Node* source = evaluated_arguments->value.pair->first;
|
||||
|
||||
if (source->type != Ast_Node_Type::Symbol) {
|
||||
report_error(Error_Type::Type_Missmatch);
|
||||
}
|
||||
|
||||
// TODO(Felix): this is not really fast what we are doing here:
|
||||
return create_ast_node_string(_strdup(source->value.symbol->identifier), (int)strlen(source->value.symbol->identifier));
|
||||
});
|
||||
defun("concat-strings", cLambda {
|
||||
try {
|
||||
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||
}
|
||||
|
||||
if (arguments_length < 1) {
|
||||
report_error(Error_Type::Wrong_Number_Of_Arguments);
|
||||
}
|
||||
|
||||
int resulting_string_len = 0;
|
||||
|
||||
Ast_Node* head = evaluated_arguments;
|
||||
|
||||
while (head->type == Ast_Node_Type::Pair) {
|
||||
try {
|
||||
assert_type(head->value.pair->first, Ast_Node_Type::String);
|
||||
}
|
||||
resulting_string_len += head->value.pair->first->value.string->length;
|
||||
|
||||
head = head->value.pair->rest;
|
||||
}
|
||||
|
||||
head = evaluated_arguments;
|
||||
|
||||
char* resulting_string = (char*)malloc(resulting_string_len * sizeof(char)) + 1;
|
||||
int index_in_string = 0;
|
||||
|
||||
while (head->type == Ast_Node_Type::Pair) {
|
||||
strcpy(resulting_string+index_in_string, head->value.pair->first->value.string->value);
|
||||
index_in_string += head->value.pair->first->value.string->length;
|
||||
head = head->value.pair->rest;
|
||||
}
|
||||
|
||||
resulting_string[index_in_string] = '\0';
|
||||
|
||||
return create_ast_node_string(resulting_string, resulting_string_len);
|
||||
});
|
||||
|
||||
#undef report_error
|
||||
#undef cLambda
|
||||
}
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ Ast_Node* lookup_symbol(Ast_Node* node, Environment* env) {
|
||||
}
|
||||
|
||||
create_error(Error_Type::Symbol_Not_Defined, node->sourceCodeLocation);
|
||||
/* printf("%s\n", sym->identifier); */
|
||||
printf("%s\n", sym->identifier);
|
||||
return nullptr;
|
||||
}
|
||||
void print_indent(int indent) {
|
||||
|
||||
+1
-1
@@ -52,6 +52,6 @@ char* Error_Type_to_string(Error_Type type) {
|
||||
case Error_Type::Unexpected_Eof: return "Parsing-error: Unexpected EOF";
|
||||
case Error_Type::Unknown_Keyword_Argument: return "Evaluation-error: Unknown keyword argument";
|
||||
case Error_Type::Wrong_Number_Of_Arguments: return "Evaluation-error: Wrong number of arguments";
|
||||
default: return "Unknown Error";
|
||||
default: return "Unknown Error";
|
||||
}
|
||||
}
|
||||
|
||||
+15
-16
@@ -13,17 +13,17 @@ Ast_Node* apply_arguments_to_function(Ast_Node* arguments, Function* function) {
|
||||
create_ast_node_symbol(function->positional_arguments->identifiers[i]),
|
||||
arguments->value.pair->first, new_env);
|
||||
} else {
|
||||
// not enough arguments given
|
||||
|
||||
create_error(Error_Type::Ill_Formed_Arguments, arguments->sourceCodeLocation);
|
||||
return nullptr;
|
||||
}
|
||||
arguments = arguments->value.pair->rest;
|
||||
}
|
||||
|
||||
if (arguments->type == Ast_Node_Type::Nil)
|
||||
goto eval_time;
|
||||
|
||||
String_Array_List* read_in_keywords = create_String_array_list(16);
|
||||
|
||||
if (arguments->type == Ast_Node_Type::Nil)
|
||||
goto checks;
|
||||
// keyword arguments: use all given ones and keep track of the
|
||||
// added ones (array list), if end of parameters in encountered or
|
||||
// something that is not a keyword is encountered or a keyword
|
||||
@@ -85,7 +85,7 @@ Ast_Node* apply_arguments_to_function(Ast_Node* arguments, Function* function) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
checks:
|
||||
// check if all necessary keywords have been read in
|
||||
for (int i = 0; i < function->keyword_arguments->next_index; ++i) {
|
||||
char* defined_keyword = function->keyword_arguments->identifiers[i];
|
||||
@@ -135,14 +135,14 @@ Ast_Node* apply_arguments_to_function(Ast_Node* arguments, Function* function) {
|
||||
}
|
||||
}
|
||||
|
||||
eval_time: {
|
||||
Ast_Node* result;
|
||||
try {
|
||||
result = eval_expr(function->body, new_env);
|
||||
}
|
||||
|
||||
return result;
|
||||
Ast_Node* result;
|
||||
try {
|
||||
result = eval_expr(function->body, new_env);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -397,9 +397,11 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// check for list function
|
||||
// check for lisp function
|
||||
if (lispOperator->type == Ast_Node_Type::Function) {
|
||||
if (!lispOperator->value.function->is_special_form) {
|
||||
// only for lambdas we evaluate the arguments before
|
||||
// apllying
|
||||
if (lispOperator->value.function->type == Function_Type::Lambda) {
|
||||
try {
|
||||
arguments = eval_arguments(arguments, env, &arguments_length);
|
||||
}
|
||||
@@ -413,9 +415,6 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
|
||||
}
|
||||
}
|
||||
default: {
|
||||
#ifdef _DEBUG
|
||||
__debugbreak();
|
||||
#endif
|
||||
report_error(Error_Type::Not_A_Function);
|
||||
}
|
||||
}
|
||||
|
||||
+13
-6
@@ -1,14 +1,22 @@
|
||||
#define new(type) new type
|
||||
#define nullptr NULL
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define assert(cond) \
|
||||
if (!cond) \
|
||||
__debugbreak();
|
||||
constexpr bool is_debug_build = true;
|
||||
#else
|
||||
#define assert(cond)
|
||||
constexpr bool is_debug_build = false;
|
||||
#endif
|
||||
|
||||
#define if_debug if constexpr (is_debug_build)
|
||||
|
||||
#define assert(cond) \
|
||||
if_debug { \
|
||||
if (!cond) { \
|
||||
printf("Assertion failed: %s %d", __FILE__, __LINE__); \
|
||||
__debugbreak(); \
|
||||
} \
|
||||
} else {} \
|
||||
|
||||
|
||||
#define concat_( a, b) a##b
|
||||
#define label(prefix, lnum) concat_(prefix,lnum)
|
||||
#define try \
|
||||
@@ -62,7 +70,6 @@
|
||||
|
||||
define_array_list(char*, String);
|
||||
|
||||
|
||||
// int string_equal(char* a, char* b) {
|
||||
// return !strcmp(a, b);
|
||||
// }
|
||||
|
||||
+69
-4
@@ -50,7 +50,7 @@ void print(Ast_Node* node) {
|
||||
printf("%f", node->value.number->value);
|
||||
} break;
|
||||
case (Ast_Node_Type::String): {
|
||||
printf("%s", node->value.string->value);
|
||||
printf("\"%s\"", node->value.string->value);
|
||||
} break;
|
||||
case (Ast_Node_Type::Symbol): {
|
||||
printf("%s", node->value.symbol->identifier);
|
||||
@@ -59,10 +59,14 @@ void print(Ast_Node* node) {
|
||||
printf(":%s", node->value.keyword->identifier);
|
||||
} break;
|
||||
case (Ast_Node_Type::Function): {
|
||||
if (node->value.function->is_special_form)
|
||||
printf("[special-lambda]");
|
||||
else
|
||||
if (node->value.function->type == Function_Type::Lambda)
|
||||
printf("[lambda]");
|
||||
else if (node->value.function->type == Function_Type::Special_Lambda)
|
||||
printf("[special-lambda]");
|
||||
else if (node->value.function->type == Function_Type::Macro)
|
||||
printf("[macro]");
|
||||
else
|
||||
assert(false);
|
||||
} break;
|
||||
case (Ast_Node_Type::CFunction): {
|
||||
printf("[C-function]");
|
||||
@@ -94,6 +98,67 @@ void print(Ast_Node* node) {
|
||||
}
|
||||
}
|
||||
|
||||
// XXX(Felix): obv code dublicate
|
||||
void fprint(FILE* f, Ast_Node* node) {
|
||||
switch (node->type) {
|
||||
case (Ast_Node_Type::Nil): {
|
||||
fprintf(f, "nil");
|
||||
} break;
|
||||
case (Ast_Node_Type::T): {
|
||||
fprintf(f, "t");
|
||||
} break;
|
||||
case (Ast_Node_Type::Number): {
|
||||
fprintf(f, "%f", node->value.number->value);
|
||||
} break;
|
||||
case (Ast_Node_Type::String): {
|
||||
fprintf(f, "\"%s\"", node->value.string->value);
|
||||
} break;
|
||||
case (Ast_Node_Type::Symbol): {
|
||||
fprintf(f, "%s", node->value.symbol->identifier);
|
||||
} break;
|
||||
case (Ast_Node_Type::Keyword): {
|
||||
fprintf(f, ":%s", node->value.keyword->identifier);
|
||||
} break;
|
||||
case (Ast_Node_Type::Function): {
|
||||
if (node->value.function->type == Function_Type::Lambda)
|
||||
fprintf(f, "[lambda]");
|
||||
else if (node->value.function->type == Function_Type::Special_Lambda)
|
||||
fprintf(f, "[special-lambda]");
|
||||
else if (node->value.function->type == Function_Type::Macro)
|
||||
fprintf(f, "[macro]");
|
||||
else
|
||||
assert(false);
|
||||
} break;
|
||||
case (Ast_Node_Type::CFunction): {
|
||||
fprintf(f, "[C-function]");
|
||||
} break;
|
||||
case (Ast_Node_Type::Pair): {
|
||||
Ast_Node* head = node;
|
||||
fprintf(f, "(");
|
||||
|
||||
// NOTE(Felix): We cold do a while true here, however in case
|
||||
// we want to print a broken list (for logging the error) we
|
||||
// should do mo checks.
|
||||
while (head) {
|
||||
fprint(f, head->value.pair->first);
|
||||
head = head->value.pair->rest;
|
||||
if (!head)
|
||||
return;
|
||||
if (head->type != Ast_Node_Type::Pair)
|
||||
break;
|
||||
fprintf(f, " ");
|
||||
}
|
||||
|
||||
if (head->type != Ast_Node_Type::Nil) {
|
||||
fprintf(f, " . ");
|
||||
print(head);
|
||||
}
|
||||
|
||||
fprintf(f, ")");
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void print_error_location() {
|
||||
if (error->location) {
|
||||
printf("%s (line %d, position %d)",
|
||||
|
||||
+3
-2
@@ -52,12 +52,13 @@ Ast_Node* interprete_file (char* file_name) {
|
||||
int interprete_stdin () {
|
||||
printf("Welcome to the lispy interpreter.\n");
|
||||
char* line;
|
||||
Environment* env = create_empty_environment();
|
||||
load_built_ins_into_environment(env);
|
||||
Environment* env = create_built_ins_environment();
|
||||
|
||||
Parser::init(env);
|
||||
|
||||
built_in_load("pre.slime", env);
|
||||
built_in_load("test.slime", env);
|
||||
|
||||
if (error) {
|
||||
log_error();
|
||||
delete_error();
|
||||
|
||||
+82
-18
@@ -1,8 +1,8 @@
|
||||
// forward decls -- start
|
||||
void load_built_ins_into_environment(Environment*);
|
||||
int list_length(Ast_Node*);
|
||||
void parse_argument_list(Ast_Node*, Function*);
|
||||
Ast_Node* eval_expr(Ast_Node*, Environment*);
|
||||
void parse_argument_list(Ast_Node*, Function*);
|
||||
int list_length(Ast_Node*);
|
||||
// forward decls -- end
|
||||
|
||||
|
||||
@@ -18,16 +18,16 @@ namespace Parser {
|
||||
int parser_line;
|
||||
int parser_col;
|
||||
|
||||
// NOTE(Felix): In this environment, the build in vunctions will
|
||||
// NOTE(Felix): In this environment, the build in functions will
|
||||
// be loaded, and the macros will be stroed in form of
|
||||
// special-lambdas, that get executed in this environment at
|
||||
// read-time
|
||||
// read-time. This should always be the global environment.
|
||||
Environment* environment_for_macros;
|
||||
|
||||
void init(Environment* env) {
|
||||
// if we already initialized it, then skip
|
||||
if (environment_for_macros)
|
||||
return;
|
||||
// NOTE(Felix): it is important to keep the parser environment
|
||||
// up to date with the global environment. When donig tests,
|
||||
// or running a programm we have to reaload it.
|
||||
|
||||
// NOTE(Felix): For now we just allow executing built-ins at
|
||||
// read-time (while creating macros). If later we want to
|
||||
@@ -148,6 +148,10 @@ namespace Parser {
|
||||
*str = '\0';
|
||||
Ast_Node* ret = create_ast_node_string(str, 0);
|
||||
inject_scl(ret);
|
||||
|
||||
// plus one because we want to go after the quotes
|
||||
*index_in_text += 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -219,11 +223,19 @@ namespace Parser {
|
||||
Ast_Node* parse_expression(char* text, int* index_in_text) {
|
||||
|
||||
// if it is quoted
|
||||
if (text[*index_in_text] == '\'') {
|
||||
if (text[*index_in_text] == '\'' ||
|
||||
text[*index_in_text] == '`' ||
|
||||
text[*index_in_text] == ',')
|
||||
{
|
||||
char quoteType = text[*index_in_text];
|
||||
++(*index_in_text);
|
||||
++parser_col;
|
||||
Ast_Node* result;
|
||||
if (text[*index_in_text] == '(' || text[*index_in_text] == '\'' ) {
|
||||
if (text[*index_in_text] == '(' ||
|
||||
text[*index_in_text] == '\'' ||
|
||||
text[*index_in_text] == '`' ||
|
||||
text[*index_in_text] == ',')
|
||||
{
|
||||
try {
|
||||
result = parse_expression(text, index_in_text);
|
||||
}
|
||||
@@ -232,11 +244,22 @@ namespace Parser {
|
||||
result = parse_atom(text, index_in_text);
|
||||
}
|
||||
}
|
||||
|
||||
if (quoteType == '\'')
|
||||
return create_ast_node_pair(
|
||||
create_ast_node_symbol("quote"),
|
||||
create_ast_node_pair(result, create_ast_node_nil()));
|
||||
else if (quoteType == '`')
|
||||
return create_ast_node_pair(
|
||||
create_ast_node_symbol("quasiquote"),
|
||||
create_ast_node_pair(result, create_ast_node_nil()));
|
||||
// it has to be an unquote
|
||||
return create_ast_node_pair(
|
||||
create_ast_node_symbol("quote"),
|
||||
create_ast_node_symbol("unquote"),
|
||||
create_ast_node_pair(result, create_ast_node_nil()));
|
||||
}
|
||||
|
||||
|
||||
// if it is not quoted
|
||||
++(*index_in_text);
|
||||
++parser_col;
|
||||
@@ -258,7 +281,11 @@ namespace Parser {
|
||||
Ast_Node* expression = head;
|
||||
|
||||
while (true) {
|
||||
if (text[(*index_in_text)] == '(' || text[(*index_in_text)] == '\'' ) {
|
||||
if (text[*index_in_text] == '(' ||
|
||||
text[*index_in_text] == '\''||
|
||||
text[*index_in_text] == '`' ||
|
||||
text[*index_in_text] == ',')
|
||||
{
|
||||
try {
|
||||
head->value.pair->first = parse_expression(text, index_in_text);
|
||||
}
|
||||
@@ -333,7 +360,7 @@ namespace Parser {
|
||||
|
||||
Function* function = new(Function);
|
||||
function->parent_environment = environment_for_macros;
|
||||
function->is_special_form = true;
|
||||
function->type = Function_Type::Macro;
|
||||
|
||||
// if parameters were specified
|
||||
if (arguments->value.pair->first->type != Ast_Node_Type::Nil) {
|
||||
@@ -393,14 +420,20 @@ namespace Parser {
|
||||
|
||||
for (int i = 0; i < environment_for_macros->next_index; ++i) {
|
||||
if (string_equal(expression->value.pair->first->value.symbol->identifier, environment_for_macros->keys[i]) &&
|
||||
environment_for_macros->values[i]->type == Ast_Node_Type::Function)
|
||||
environment_for_macros->values[i]->type == Ast_Node_Type::Function &&
|
||||
environment_for_macros->values[i]->value.function->type == Function_Type::Macro)
|
||||
{
|
||||
// every `Function` that is defined in this
|
||||
// environment _has_ to be a macro because
|
||||
// only built_ins are in there otherwise,
|
||||
// which are `CFunction`s.
|
||||
try {
|
||||
// if (string_equal(environment_for_macros->keys[i], "when")) {
|
||||
// printf("invoking macro for %s in %s:%d to:\n\t", environment_for_macros->keys[i], parser_file, parser_line);
|
||||
// print(environment_for_macros->values[i]->value.function->body);
|
||||
// }
|
||||
expression = eval_expr(expression, environment_for_macros);
|
||||
// if (string_equal(environment_for_macros->keys[i], "when")) {
|
||||
// printf("\nresult: \n\t");
|
||||
// print(expression);
|
||||
// printf("\n\n");
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -421,10 +454,15 @@ namespace Parser {
|
||||
eat_until_code(text, &index_in_text);
|
||||
if (text[(index_in_text)] == '\0')
|
||||
return create_ast_node_nil();
|
||||
if (text[(index_in_text)] == '(' || text[(index_in_text)] == '\'' )
|
||||
if (text[index_in_text] == '(' ||
|
||||
text[index_in_text] == '\'' ||
|
||||
text[index_in_text] == '`' ||
|
||||
text[index_in_text] == ',')
|
||||
{
|
||||
try {
|
||||
result = parse_expression(text, &index_in_text);
|
||||
}
|
||||
}
|
||||
else
|
||||
try {
|
||||
result = parse_atom(text, &index_in_text);
|
||||
@@ -436,6 +474,29 @@ namespace Parser {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void write_expanded_file(char* file_name, Ast_Node_Array_List* program) {
|
||||
char* ext = ".expanded";
|
||||
char* newName = (char*)calloc(4 + strlen(file_name), sizeof(char));
|
||||
strcpy(newName, file_name);
|
||||
strcat(newName, ext);
|
||||
|
||||
FILE *f = fopen(newName, "w");
|
||||
if (f == NULL) {
|
||||
printf("Error opening file!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < program->next_index; ++i) {
|
||||
// a macro will parse as nil for now, so we skip those
|
||||
if (program->data[i]->type == Ast_Node_Type::Nil)
|
||||
continue;
|
||||
fprint(f, program->data[i]);
|
||||
fprintf(f, "\n\n");
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
Ast_Node_Array_List* parse_program(char* file_name, char* text) {
|
||||
parser_file = (char*)malloc(strlen(file_name) * sizeof(char) + 1);
|
||||
strcpy(parser_file, file_name);
|
||||
@@ -468,6 +529,9 @@ namespace Parser {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
write_expanded_file(file_name, program);
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user