built-ins are identified by an enum now
This commit is contained in:
+2
-2
@@ -32,5 +32,5 @@
|
||||
("d" save-and-find-debug-script-and-compile "debug" :color blue)
|
||||
("t" save-and-find-test-script-and-compile "test" :color blue)
|
||||
("q" nil "quit" :color blue))
|
||||
(define-key context-mode-map (kbd "<f2>") 'hydra-context/body)
|
||||
)))))
|
||||
|
||||
(define-key context-mode-map (kbd "<f2>") 'hydra-context/body))))))
|
||||
|
||||
@@ -46,8 +46,77 @@ typedef struct {
|
||||
char* docstring;
|
||||
} Function;
|
||||
|
||||
typedef enum {
|
||||
Built_In_Addition,
|
||||
Built_In_Subtraction,
|
||||
Built_In_Multiplication,
|
||||
Built_In_Division,
|
||||
Built_In_Greater,
|
||||
Built_In_Less,
|
||||
Built_In_Equal,
|
||||
Built_In_Greater_Equal,
|
||||
Built_In_Less_Equal,
|
||||
Built_In_If,
|
||||
Built_In_And,
|
||||
Built_In_Or,
|
||||
Built_In_Not,
|
||||
Built_In_Pair,
|
||||
Built_In_First,
|
||||
Built_In_Rest,
|
||||
Built_In_Load,
|
||||
Built_In_Define,
|
||||
Built_In_Lambda,
|
||||
Built_In_Eval,
|
||||
Built_In_Quote,
|
||||
Built_In_Prog,
|
||||
Built_In_List,
|
||||
Built_In_Print,
|
||||
Built_In_Read,
|
||||
Built_In_Info,
|
||||
Built_In_Type,
|
||||
Built_In_Exit,
|
||||
} Built_In_Name;
|
||||
|
||||
/**
|
||||
This is used only for printing.
|
||||
**/
|
||||
char* Built_In_Name_to_string(Built_In_Name name) {
|
||||
switch (name) {
|
||||
case Built_In_Addition: return "+";
|
||||
case Built_In_And: return "and";
|
||||
case Built_In_Define: return "define";
|
||||
case Built_In_Division: return "/";
|
||||
case Built_In_Equal: return "=";
|
||||
case Built_In_Eval: return "eval";
|
||||
case Built_In_Exit: return "exit";
|
||||
case Built_In_First: return "first";
|
||||
case Built_In_Greater: return ">";
|
||||
case Built_In_Greater_Equal: return ">=";
|
||||
case Built_In_If: return "if";
|
||||
case Built_In_Info: return "info";
|
||||
case Built_In_Lambda: return "lambda";
|
||||
case Built_In_Less: return "<";
|
||||
case Built_In_Less_Equal: return "<=";
|
||||
case Built_In_List: return "list";
|
||||
case Built_In_Load: return "load";
|
||||
case Built_In_Multiplication: return "*";
|
||||
case Built_In_Not: return "not";
|
||||
case Built_In_Or: return "or";
|
||||
case Built_In_Pair: return "pair";
|
||||
case Built_In_Print: return "print";
|
||||
case Built_In_Prog: return "prog";
|
||||
case Built_In_Quote: return "quote";
|
||||
case Built_In_Read: return "read";
|
||||
case Built_In_Rest: return "rest";
|
||||
case Built_In_Subtraction: return "-";
|
||||
case Built_In_Type: return "type";
|
||||
}
|
||||
|
||||
return "Built in string missing in Built_In_Name_to_string";
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char* identifier;
|
||||
Built_In_Name type;
|
||||
} Built_In_Function;
|
||||
|
||||
struct Ast_Node {
|
||||
@@ -106,11 +175,42 @@ Ast_Node* create_ast_node_keyword(char* keyword) {
|
||||
return node;
|
||||
}
|
||||
|
||||
Ast_Node* create_ast_node_built_in_function(char* identifier) {
|
||||
Ast_Node* create_ast_node_built_in_function(char* name) {
|
||||
Built_In_Name type;
|
||||
if (string_equal(name, "+")) type = Built_In_Addition;
|
||||
else if (string_equal(name, "-")) type = Built_In_Subtraction;
|
||||
else if (string_equal(name, "*")) type = Built_In_Multiplication;
|
||||
else if (string_equal(name, "/")) type = Built_In_Division;
|
||||
else if (string_equal(name, "=")) type = Built_In_Equal;
|
||||
else if (string_equal(name, ">")) type = Built_In_Greater;
|
||||
else if (string_equal(name, ">=")) type = Built_In_Greater_Equal;
|
||||
else if (string_equal(name, "<")) type = Built_In_Less;
|
||||
else if (string_equal(name, "<=")) type = Built_In_Less_Equal;
|
||||
else if (string_equal(name, "if")) type = Built_In_If;
|
||||
else if (string_equal(name, "and")) type = Built_In_And;
|
||||
else if (string_equal(name, "or")) type = Built_In_Or;
|
||||
else if (string_equal(name, "not")) type = Built_In_Not;
|
||||
else if (string_equal(name, "pair")) type = Built_In_Pair;
|
||||
else if (string_equal(name, "first")) type = Built_In_First;
|
||||
else if (string_equal(name, "rest")) type = Built_In_Rest;
|
||||
else if (string_equal(name, "load")) type = Built_In_Load;
|
||||
else if (string_equal(name, "define")) type = Built_In_Define;
|
||||
else if (string_equal(name, "lambda")) type = Built_In_Lambda;
|
||||
else if (string_equal(name, "eval")) type = Built_In_Eval;
|
||||
else if (string_equal(name, "quote")) type = Built_In_Quote;
|
||||
else if (string_equal(name, "prog")) type = Built_In_Prog;
|
||||
else if (string_equal(name, "list")) type = Built_In_List;
|
||||
else if (string_equal(name, "print")) type = Built_In_Print;
|
||||
else if (string_equal(name, "read")) type = Built_In_Read;
|
||||
else if (string_equal(name, "info")) type = Built_In_Info;
|
||||
else if (string_equal(name, "type")) type = Built_In_Type;
|
||||
else if (string_equal(name, "exit")) type = Built_In_Exit;
|
||||
else return nullptr;
|
||||
|
||||
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;
|
||||
node->value.built_in_function->type = type;
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -6,9 +6,8 @@ bool ast_node_equal(Ast_Node* n1, Ast_Node* n2) {
|
||||
|
||||
switch (n1->type) {
|
||||
case Ast_Node_Type_Built_In_Function:
|
||||
return string_equal(
|
||||
n1->value.built_in_function->identifier,
|
||||
n2->value.built_in_function->identifier);
|
||||
return n1->value.built_in_function->type
|
||||
== n2->value.built_in_function->type;
|
||||
case Ast_Node_Type_Function:
|
||||
// if they have the same pointer, true is
|
||||
// returned a few lines above
|
||||
|
||||
@@ -3,6 +3,7 @@ struct Environment {
|
||||
struct Environment* parent;
|
||||
int capacity;
|
||||
int next_index;
|
||||
// TODO(Felix): Use a hashmap here.
|
||||
char** keys;
|
||||
Ast_Node* values;
|
||||
};
|
||||
@@ -49,37 +50,10 @@ Ast_Node* lookup_symbol(Symbol* sym, Environment* env) {
|
||||
if (env->parent)
|
||||
return lookup_symbol(sym, env->parent);
|
||||
|
||||
char* built_in_names[] = {
|
||||
// Math stuff
|
||||
"+", "-", "*", "/",
|
||||
">", "<", "=", "<=", ">=",
|
||||
// Conditional stuff
|
||||
"if", "and", "or", "not",
|
||||
// Cons stuff
|
||||
"pair", "first", "rest",
|
||||
// rest
|
||||
"load", "define",
|
||||
"lambda", "progn",
|
||||
"eval", "quote", "prog", "list",
|
||||
"print", "read",
|
||||
"info", "type", "exit"
|
||||
};
|
||||
int built_in_count = sizeof(built_in_names) / sizeof(char*);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
Ast_Node* built_in = create_ast_node_built_in_function(sym->identifier);
|
||||
if (built_in)
|
||||
return built_in;
|
||||
|
||||
create_error(Error_Type_Symbol_Not_Defined, create_ast_node_nil());
|
||||
/* char* message; */
|
||||
/* asprintf(&message, "Symbol not defined: %s\n", sym->identifier); */
|
||||
/* panic(message); */
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
typedef enum {
|
||||
Error_Type_Ill_Formed_List,
|
||||
Error_Type_Ill_Formed_Arguments,
|
||||
Error_Type_Wrong_Number_Of_Arguments,
|
||||
Error_Type_Type_Missmatch,
|
||||
Error_Type_Symbol_Not_Defined,
|
||||
@@ -37,6 +38,7 @@ void create_error(Error_Type type, Ast_Node* location) {
|
||||
char* Error_Type_to_string(Error_Type type) {
|
||||
switch (type) {
|
||||
case Error_Type_Ill_Formed_List: return "Evaluation-error: Ill formed list";
|
||||
case Error_Type_Ill_Formed_Arguments: return "Evaluation-error: Ill formed arguments";
|
||||
case Error_Type_Not_A_Function: return "Evaluation-error: Not a function";
|
||||
case Error_Type_Symbol_Not_Defined: return "Evaluation-error: Symbol not defined";
|
||||
case Error_Type_Wrong_Number_Of_Arguments: return "Evaluation-error: Wrong number of arguments";
|
||||
|
||||
+278
-266
@@ -27,7 +27,7 @@ int list_length(Ast_Node* node) {
|
||||
|
||||
/**
|
||||
Copies a list, in that it creates a new list, however the items are
|
||||
the same as in the original (same pointers). This is neede to copy
|
||||
the same as in the original (same pointers). This is needed to copy
|
||||
a list when evaluating a parameters list, but not wanting to change
|
||||
the parameters list. This happens if you have someting like:
|
||||
|
||||
@@ -56,27 +56,83 @@ Ast_Node* copy_list(Ast_Node* node) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Ast_Node* eval_operands(Ast_Node* operands, Environment* env) {
|
||||
if (operands->type == Ast_Node_Type_Nil) {
|
||||
return operands;
|
||||
typedef struct {
|
||||
Ast_Node_Array_List* positional_arguments;
|
||||
// TODO(Felix): Really use hashmap (keyword[sting] ->
|
||||
// value[Ast_Node*]) here
|
||||
Ast_Node_Array_List* keyword_keys;
|
||||
Ast_Node_Array_List* keyword_values;
|
||||
} Parsed_Arguments;
|
||||
|
||||
Parsed_Arguments* eval_and_parse_arguments(Ast_Node* arguments, Environment* env) {
|
||||
// TODO(Felix): if arguments is nil then maybe return a constant
|
||||
// Parsed_Arguments_nil?
|
||||
|
||||
|
||||
typedef enum {
|
||||
Expecting_Anything,
|
||||
Expecting_Keyword_Key,
|
||||
Expecting_Keyword_Value
|
||||
} Parse_State;
|
||||
|
||||
Parse_State parse_state = Expecting_Anything;
|
||||
|
||||
Parsed_Arguments* result = new(Parsed_Arguments);
|
||||
result->positional_arguments = create_Ast_Node_Array_List();
|
||||
result->keyword_keys = create_Ast_Node_Array_List();
|
||||
result->keyword_values = create_Ast_Node_Array_List();
|
||||
|
||||
Ast_Node* current_head = copy_list(arguments);
|
||||
Ast_Node* current_evaluated_head;
|
||||
while (current_head->type == Ast_Node_Type_Pair) {
|
||||
try {
|
||||
current_evaluated_head = eval_expr(current_head->value.pair->first, env);
|
||||
}
|
||||
if (current_evaluated_head->type == Ast_Node_Type_Keyword) {
|
||||
if (parse_state == Expecting_Anything || Expecting_Keyword_Key) {
|
||||
append_to_Ast_Node_Array_List(result->keyword_keys, current_evaluated_head);
|
||||
parse_state = Expecting_Keyword_Value;
|
||||
} else {
|
||||
create_error(Error_Type_Ill_Formed_Arguments, arguments);
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
if (parse_state == Expecting_Anything)
|
||||
append_to_Ast_Node_Array_List(result->positional_arguments, current_evaluated_head);
|
||||
else if (parse_state == Expecting_Keyword_Value) {
|
||||
append_to_Ast_Node_Array_List(result->keyword_values, current_evaluated_head);
|
||||
parse_state = Expecting_Keyword_Key;
|
||||
}
|
||||
else {
|
||||
create_error(Error_Type_Ill_Formed_Arguments, arguments);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
current_head = current_head->value.pair->rest;
|
||||
}
|
||||
|
||||
// NOTE(Feilx): Instead of copying and then mutating it, we could
|
||||
return result;
|
||||
}
|
||||
|
||||
Ast_Node* eval_arguments(Ast_Node* arguments, Environment* env, int *out_arguments_length) {
|
||||
*out_arguments_length = 0;
|
||||
if (arguments->type == Ast_Node_Type_Nil) {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
// NOTE(Felix): Instead of copying and then mutating it, we could
|
||||
// just build the resulting list on the fly
|
||||
Ast_Node* evaluated_operands = copy_list(operands);
|
||||
Ast_Node* current_head = evaluated_operands;
|
||||
/* print(operands); */
|
||||
/* printf("\n"); */
|
||||
/* print(evaluated_operands); */
|
||||
/* printf("\n"); */
|
||||
Ast_Node* evaluated_arguments = copy_list(arguments);
|
||||
Ast_Node* current_head = evaluated_arguments;
|
||||
while (current_head->type == Ast_Node_Type_Pair) {
|
||||
try {
|
||||
current_head->value.pair->first =
|
||||
eval_expr(current_head->value.pair->first, env);
|
||||
}
|
||||
current_head = current_head->value.pair->rest;
|
||||
++(*out_arguments_length);
|
||||
}
|
||||
return evaluated_operands;
|
||||
return evaluated_arguments;
|
||||
}
|
||||
|
||||
Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
|
||||
@@ -108,232 +164,69 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
|
||||
operator = eval_expr(node->value.pair->first, env);
|
||||
}
|
||||
|
||||
Ast_Node* operands = node->value.pair->rest;
|
||||
int operands_length;
|
||||
Ast_Node* arguments = node->value.pair->rest;
|
||||
int arguments_length;
|
||||
|
||||
// check for built ins functions
|
||||
// check for special form
|
||||
if (operator->type == Ast_Node_Type_Built_In_Function) {
|
||||
char* operator_name = operator->value.built_in_function->identifier;
|
||||
if (string_equal("quote", operator_name)) {
|
||||
operands_length = list_length(operands);
|
||||
if (operands_length != 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
return operands->value.pair->first;
|
||||
} else if (string_equal("eval", operator_name)) {
|
||||
try {
|
||||
operands_length = list_length(operands);
|
||||
}
|
||||
if (operands_length != 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
try {
|
||||
operands = eval_operands(operands, env);
|
||||
}
|
||||
/* print(operands); */
|
||||
Ast_Node* result;
|
||||
try {
|
||||
result = eval_expr(operands->value.pair->first, env);
|
||||
}
|
||||
return result;
|
||||
} else if (string_equal("prog", operator_name)) {
|
||||
if (operands->type == Ast_Node_Type_Nil)
|
||||
return operands;
|
||||
|
||||
operands = copy_list(operands);
|
||||
Ast_Node* evaluated_operands;
|
||||
try {
|
||||
evaluated_operands = eval_operands(operands, env);
|
||||
}
|
||||
|
||||
// skip to the last evaluated operand and return it,
|
||||
// we use eval_operands here instead of doing it
|
||||
// manually, becuase we want to increase code reuse,
|
||||
// but at the cost that we have to find the end of the
|
||||
// list again
|
||||
while (evaluated_operands->value.pair->rest->type == Ast_Node_Type_Pair) {
|
||||
evaluated_operands = evaluated_operands->value.pair->rest;
|
||||
}
|
||||
return evaluated_operands->value.pair->first;
|
||||
} else if (string_equal("list", operator_name)) {
|
||||
try {
|
||||
operands = eval_operands(operands, env);
|
||||
}
|
||||
return operands;
|
||||
} else if (string_equal("=", operator_name)) {
|
||||
try {
|
||||
operands = eval_operands(operands, env);
|
||||
}
|
||||
return built_in_equals(operands);
|
||||
} else if (string_equal("+", operator_name)) {
|
||||
try {
|
||||
operands = eval_operands(operands, env);
|
||||
}
|
||||
return built_in_add(operands);
|
||||
} else if (string_equal("-", operator_name)) {
|
||||
try {
|
||||
operands = eval_operands(operands, env);
|
||||
}
|
||||
return built_in_substract(operands);
|
||||
} else if (string_equal("*", operator_name)) {
|
||||
try {
|
||||
operands = eval_operands(operands, env);
|
||||
}
|
||||
return built_in_multiply(operands);
|
||||
} else if (string_equal("/", operator_name)) {
|
||||
try {
|
||||
operands = eval_operands(operands, env);
|
||||
}
|
||||
return built_in_divide(operands);
|
||||
} else if (string_equal("define", operator_name)) {
|
||||
try {
|
||||
operands_length = list_length(operands);
|
||||
}
|
||||
if (operands_length != 2) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
|
||||
Ast_Node* symbol = operands->value.pair->first;
|
||||
if (symbol->type != Ast_Node_Type_Symbol)
|
||||
report_error(Error_Type_Type_Missmatch);
|
||||
|
||||
Ast_Node* value = operands->value.pair->rest->value.pair->first;
|
||||
try {
|
||||
value = eval_expr(value, env);
|
||||
}
|
||||
|
||||
define_symbol(symbol, value, env);
|
||||
|
||||
return value;
|
||||
} else if (string_equal("type", operator_name)) {
|
||||
try {
|
||||
operands_length = list_length(operands);
|
||||
}
|
||||
if (operands_length != 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
try {
|
||||
operands = eval_operands(operands, env);
|
||||
}
|
||||
Ast_Node_Type type = operands->value.pair->first->type;
|
||||
switch (type) {
|
||||
case Ast_Node_Type_Built_In_Function: return create_ast_node_keyword("built-in-function");
|
||||
case Ast_Node_Type_Function: return create_ast_node_keyword("dynamic-function");
|
||||
case Ast_Node_Type_Keyword: return create_ast_node_keyword("keyword");
|
||||
case Ast_Node_Type_Nil: return create_ast_node_keyword("nil");
|
||||
case Ast_Node_Type_Number: return create_ast_node_keyword("number");
|
||||
case Ast_Node_Type_Pair: return create_ast_node_keyword("pair");
|
||||
case Ast_Node_Type_String: return create_ast_node_keyword("string");
|
||||
case Ast_Node_Type_Symbol: return create_ast_node_keyword("symbol");
|
||||
}
|
||||
|
||||
} else if (string_equal("exit", operator_name)) {
|
||||
try {
|
||||
operands_length = list_length(operands);
|
||||
}
|
||||
if (operands_length > 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
|
||||
if (operands_length == 1) {
|
||||
try {
|
||||
operands = eval_operands(operands, env);
|
||||
switch (operator->value.built_in_function->type) {
|
||||
case Built_In_And: {
|
||||
bool result = true;
|
||||
while (arguments->type != Ast_Node_Type_Nil) {
|
||||
if (arguments->type != Ast_Node_Type_Pair) {
|
||||
report_error(Error_Type_Ill_Formed_List);
|
||||
}
|
||||
Ast_Node* error_code = operands->value.pair->first;
|
||||
if (error_code->type != Ast_Node_Type_Number)
|
||||
report_error(Error_Type_Type_Missmatch);
|
||||
|
||||
exit((int)error_code->value.number->value);
|
||||
}
|
||||
exit(0);
|
||||
|
||||
} else if (string_equal("print", operator_name)) {
|
||||
try {
|
||||
operands_length = list_length(operands);
|
||||
}
|
||||
if (operands_length != 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
try {
|
||||
operands = eval_operands(operands, env);
|
||||
}
|
||||
print(operands->value.pair->first);
|
||||
printf("\n");
|
||||
return operands->value.pair->first;
|
||||
} else if (string_equal("read", operator_name)) {
|
||||
try {
|
||||
operands_length = list_length(operands);
|
||||
}
|
||||
if (operands_length > 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
|
||||
if (operands_length == 1) {
|
||||
try {
|
||||
operands = eval_operands(operands, env);
|
||||
result &= is_truthy(arguments->value.pair->first, env);
|
||||
}
|
||||
Ast_Node* prompt = operands->value.pair->first;
|
||||
if (prompt->type == Ast_Node_Type_String)
|
||||
printf("%s", prompt->value.string->value);
|
||||
else
|
||||
print(operands->value.pair->first);
|
||||
}
|
||||
char* line = read_line();
|
||||
return create_ast_node_string(line, (int)strlen(line));
|
||||
} else if (string_equal("pair", operator_name)) {
|
||||
try {
|
||||
operands_length = list_length(operands);
|
||||
}
|
||||
if (operands_length != 2) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
try {
|
||||
operands = eval_operands(operands, env);
|
||||
arguments = arguments->value.pair->rest;
|
||||
|
||||
if (!result) return create_ast_node_nil();
|
||||
}
|
||||
|
||||
return create_ast_node_pair(operands->value.pair->first, operands->value.pair->rest->value.pair->first);
|
||||
} else if (string_equal("first", operator_name)) {
|
||||
try {
|
||||
operands_length = list_length(operands);
|
||||
return create_ast_node_number(1);
|
||||
}
|
||||
case Built_In_Or: {
|
||||
bool result = false;
|
||||
while (arguments->type != Ast_Node_Type_Nil) {
|
||||
if (arguments->type != Ast_Node_Type_Pair) {
|
||||
report_error(Error_Type_Ill_Formed_List);
|
||||
}
|
||||
try {
|
||||
result |= is_truthy(arguments->value.pair->first, env);
|
||||
}
|
||||
arguments = arguments->value.pair->rest;
|
||||
|
||||
if (result) return create_ast_node_number(1);;
|
||||
}
|
||||
if (operands_length != 1) {
|
||||
|
||||
return create_ast_node_nil();
|
||||
}
|
||||
case Built_In_Not: {
|
||||
try {
|
||||
arguments_length = list_length(arguments);
|
||||
}
|
||||
if (arguments_length != 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
bool truthy;
|
||||
try {
|
||||
operands = eval_operands(operands, env);
|
||||
truthy = is_truthy(arguments->value.pair->first, env);
|
||||
}
|
||||
if (operands->value.pair->first->type == Ast_Node_Type_Nil)
|
||||
if (truthy)
|
||||
return create_ast_node_nil();
|
||||
if (operands->value.pair->first->type != Ast_Node_Type_Pair)
|
||||
report_error(Error_Type_Type_Missmatch);
|
||||
|
||||
return operands->value.pair->first->value.pair->first;
|
||||
} else if (string_equal("rest", operator_name)) {
|
||||
return create_ast_node_number(1);
|
||||
}
|
||||
case Built_In_If: {
|
||||
try {
|
||||
operands_length = list_length(operands);
|
||||
arguments_length = list_length(arguments);
|
||||
}
|
||||
if (operands_length != 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
try {
|
||||
operands = eval_operands(operands, env);
|
||||
}
|
||||
if (operands->value.pair->first->type == Ast_Node_Type_Nil)
|
||||
return create_ast_node_nil();
|
||||
if (operands->value.pair->first->type != Ast_Node_Type_Pair)
|
||||
report_error(Error_Type_Type_Missmatch);
|
||||
|
||||
return operands->value.pair->first->value.pair->rest;
|
||||
} else if (string_equal("if", operator_name)) {
|
||||
try {
|
||||
operands_length = list_length(operands);
|
||||
}
|
||||
if (operands_length != 2 && operands_length != 3) {
|
||||
if (arguments_length != 2 && arguments_length != 3) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
|
||||
Ast_Node* condition = operands->value.pair->first;
|
||||
Ast_Node* then_part = operands->value.pair->rest;
|
||||
Ast_Node* condition = arguments->value.pair->first;
|
||||
Ast_Node* then_part = arguments->value.pair->rest;
|
||||
Ast_Node* else_part = then_part->value.pair->rest;
|
||||
|
||||
bool truthy;
|
||||
@@ -345,66 +238,185 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
|
||||
try{
|
||||
result = eval_expr(then_part->value.pair->first, env);
|
||||
}
|
||||
else if (operands_length == 3)
|
||||
else if (arguments_length == 3)
|
||||
try {
|
||||
result = eval_expr(else_part->value.pair->first, env);
|
||||
}
|
||||
else return create_ast_node_nil();
|
||||
return result;
|
||||
|
||||
} else if (string_equal("and", operator_name)) {
|
||||
bool result = true;
|
||||
while (operands->type != Ast_Node_Type_Nil) {
|
||||
if (operands->type != Ast_Node_Type_Pair) {
|
||||
report_error(Error_Type_Ill_Formed_List);
|
||||
}
|
||||
try {
|
||||
result &= is_truthy(operands->value.pair->first, env);
|
||||
}
|
||||
operands = operands->value.pair->rest;
|
||||
|
||||
if (!result) return create_ast_node_nil();
|
||||
}
|
||||
|
||||
return create_ast_node_number(1);
|
||||
|
||||
} else if (string_equal("or", operator_name)) {
|
||||
bool result = false;
|
||||
while (operands->type != Ast_Node_Type_Nil) {
|
||||
if (operands->type != Ast_Node_Type_Pair) {
|
||||
report_error(Error_Type_Ill_Formed_List);
|
||||
}
|
||||
try {
|
||||
result |= is_truthy(operands->value.pair->first, env);
|
||||
}
|
||||
operands = operands->value.pair->rest;
|
||||
|
||||
if (result) return create_ast_node_number(1);;
|
||||
}
|
||||
|
||||
return create_ast_node_nil();
|
||||
} else if (string_equal("not", operator_name)) {
|
||||
try {
|
||||
operands_length = list_length(operands);
|
||||
}
|
||||
if (operands_length != 1) {
|
||||
}
|
||||
case Built_In_Quote: {
|
||||
arguments_length = list_length(arguments);
|
||||
if (arguments_length != 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
bool truthy;
|
||||
return arguments->value.pair->first;
|
||||
}
|
||||
case Built_In_Define: {
|
||||
try {
|
||||
truthy = is_truthy(operands->value.pair->first, env);
|
||||
arguments_length = list_length(arguments);
|
||||
}
|
||||
if (truthy)
|
||||
if (arguments_length != 2) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
|
||||
Ast_Node* symbol = arguments->value.pair->first;
|
||||
if (symbol->type != Ast_Node_Type_Symbol)
|
||||
report_error(Error_Type_Type_Missmatch);
|
||||
|
||||
Ast_Node* value = arguments->value.pair->rest->value.pair->first;
|
||||
try {
|
||||
value = eval_expr(value, env);
|
||||
}
|
||||
|
||||
define_symbol(symbol, value, env);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// okay it is not a special form, so in any case we want
|
||||
// to evaluate the arguments; eval_arguments will also tell
|
||||
// us the arguments_length.
|
||||
Ast_Node* evaluated_arguments;
|
||||
try {
|
||||
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||
}
|
||||
|
||||
switch (operator->value.built_in_function->type) {
|
||||
case Built_In_Addition: {
|
||||
return built_in_add(evaluated_arguments);
|
||||
}
|
||||
case Built_In_Subtraction: {
|
||||
return built_in_substract(evaluated_arguments);
|
||||
}
|
||||
case Built_In_Multiplication: {
|
||||
return built_in_multiply(evaluated_arguments);
|
||||
}
|
||||
case Built_In_Division: {
|
||||
return built_in_divide(evaluated_arguments);
|
||||
}
|
||||
case Built_In_Equal: {
|
||||
return built_in_equals(evaluated_arguments);
|
||||
}
|
||||
case Built_In_Pair: {
|
||||
if (arguments_length != 2) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
return create_ast_node_pair(evaluated_arguments->value.pair->first, evaluated_arguments->value.pair->rest->value.pair->first);
|
||||
}
|
||||
case Built_In_First: {
|
||||
if (arguments_length != 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
if (evaluated_arguments->value.pair->first->type == Ast_Node_Type_Nil)
|
||||
return create_ast_node_nil();
|
||||
return create_ast_node_number(1);
|
||||
} else {
|
||||
if (evaluated_arguments->value.pair->first->type != Ast_Node_Type_Pair)
|
||||
report_error(Error_Type_Type_Missmatch);
|
||||
|
||||
return evaluated_arguments->value.pair->first->value.pair->first;
|
||||
}
|
||||
case Built_In_Rest: {
|
||||
if (arguments_length != 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
if (evaluated_arguments->value.pair->first->type == Ast_Node_Type_Nil)
|
||||
return create_ast_node_nil();
|
||||
if (evaluated_arguments->value.pair->first->type != Ast_Node_Type_Pair)
|
||||
report_error(Error_Type_Type_Missmatch);
|
||||
|
||||
return evaluated_arguments->value.pair->first->value.pair->rest;
|
||||
}
|
||||
case Built_In_Eval: {
|
||||
if (arguments_length != 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
Ast_Node* result;
|
||||
try {
|
||||
result = eval_expr(evaluated_arguments->value.pair->first, env);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case Built_In_Prog: {
|
||||
if (evaluated_arguments->type == Ast_Node_Type_Nil)
|
||||
return evaluated_arguments;
|
||||
|
||||
// skip to the last evaluated operand and return it,
|
||||
// we use eval_arguments here instead of doing it
|
||||
// manually, because we want to increase code reuse,
|
||||
// but at the cost that we have to find the end of the
|
||||
// list again
|
||||
while (evaluated_arguments->value.pair->rest->type == Ast_Node_Type_Pair) {
|
||||
evaluated_arguments = evaluated_arguments->value.pair->rest;
|
||||
}
|
||||
return evaluated_arguments->value.pair->first;
|
||||
}
|
||||
case Built_In_List: {
|
||||
return evaluated_arguments;
|
||||
}
|
||||
case Built_In_Print: {
|
||||
if (arguments_length != 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
print(evaluated_arguments->value.pair->first);
|
||||
printf("\n");
|
||||
return arguments->value.pair->first;
|
||||
}
|
||||
case Built_In_Read: {
|
||||
if (arguments_length > 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
|
||||
if (arguments_length == 1) {
|
||||
Ast_Node* prompt = evaluated_arguments->value.pair->first;
|
||||
if (prompt->type == Ast_Node_Type_String)
|
||||
printf("%s", prompt->value.string->value);
|
||||
else
|
||||
print(evaluated_arguments->value.pair->first);
|
||||
}
|
||||
char* line = read_line();
|
||||
return create_ast_node_string(line, (int)strlen(line));
|
||||
}
|
||||
case Built_In_Type: {
|
||||
if (arguments_length != 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
|
||||
Ast_Node_Type type = evaluated_arguments->value.pair->first->type;
|
||||
switch (type) {
|
||||
case Ast_Node_Type_Built_In_Function: return create_ast_node_keyword("built-in-function");
|
||||
case Ast_Node_Type_Function: return create_ast_node_keyword("dynamic-function");
|
||||
case Ast_Node_Type_Keyword: return create_ast_node_keyword("keyword");
|
||||
case Ast_Node_Type_Nil: return create_ast_node_keyword("nil");
|
||||
case Ast_Node_Type_Number: return create_ast_node_keyword("number");
|
||||
case Ast_Node_Type_Pair: return create_ast_node_keyword("pair");
|
||||
case Ast_Node_Type_String: return create_ast_node_keyword("string");
|
||||
case Ast_Node_Type_Symbol: return create_ast_node_keyword("symbol");
|
||||
}
|
||||
}
|
||||
case Built_In_Exit: {
|
||||
if (arguments_length > 1) {
|
||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||
}
|
||||
|
||||
if (arguments_length == 1) {
|
||||
Ast_Node* error_code = evaluated_arguments->value.pair->first;
|
||||
if (error_code->type != Ast_Node_Type_Number)
|
||||
report_error(Error_Type_Type_Missmatch);
|
||||
|
||||
exit((int)error_code->value.number->value);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
default:
|
||||
report_error(Error_Type_Not_Yet_Implemented);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// assume it's lambda function and evaluate the operands in
|
||||
// place
|
||||
operands = eval_operands(operands, env);
|
||||
// assume it's lambda function and evaluate the arguments
|
||||
arguments = eval_arguments(arguments, env, &arguments_length);
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -71,7 +71,7 @@ void print(Ast_Node* node) {
|
||||
printf("[lambda]");
|
||||
} break;
|
||||
case (Ast_Node_Type_Built_In_Function): {
|
||||
printf("[built-in-function %s]", node->value.built_in_function->identifier);
|
||||
printf("[built-in-function %s]", Built_In_Name_to_string(node->value.built_in_function->type));
|
||||
} break;
|
||||
case (Ast_Node_Type_Pair): {
|
||||
Ast_Node* head = node;
|
||||
|
||||
+2
-1
@@ -82,7 +82,8 @@
|
||||
testresult test_eval_operands() {
|
||||
char operands_string[] = "((eval 1) (+ 1 2) \"okay\" (eval :haha))";
|
||||
Ast_Node* operands = parse_single_expression(operands_string);
|
||||
operands = eval_operands(operands, create_empty_environment());
|
||||
int operands_length;
|
||||
operands = eval_arguments(operands, create_empty_environment(), &operands_length);
|
||||
|
||||
assert_no_error(error);
|
||||
assert_equal_int(list_length(operands), 4);
|
||||
|
||||
Reference in New Issue
Block a user