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)
|
("d" save-and-find-debug-script-and-compile "debug" :color blue)
|
||||||
("t" save-and-find-test-script-and-compile "test" :color blue)
|
("t" save-and-find-test-script-and-compile "test" :color blue)
|
||||||
("q" nil "quit" :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;
|
char* docstring;
|
||||||
} Function;
|
} 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 {
|
typedef struct {
|
||||||
char* identifier;
|
Built_In_Name type;
|
||||||
} Built_In_Function;
|
} Built_In_Function;
|
||||||
|
|
||||||
struct Ast_Node {
|
struct Ast_Node {
|
||||||
@@ -106,11 +175,42 @@ Ast_Node* create_ast_node_keyword(char* keyword) {
|
|||||||
return node;
|
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);
|
Ast_Node* node = new(Ast_Node);
|
||||||
node->type = Ast_Node_Type_Built_In_Function;
|
node->type = Ast_Node_Type_Built_In_Function;
|
||||||
node->value.built_in_function = new(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;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -6,9 +6,8 @@ bool ast_node_equal(Ast_Node* n1, Ast_Node* n2) {
|
|||||||
|
|
||||||
switch (n1->type) {
|
switch (n1->type) {
|
||||||
case Ast_Node_Type_Built_In_Function:
|
case Ast_Node_Type_Built_In_Function:
|
||||||
return string_equal(
|
return n1->value.built_in_function->type
|
||||||
n1->value.built_in_function->identifier,
|
== n2->value.built_in_function->type;
|
||||||
n2->value.built_in_function->identifier);
|
|
||||||
case Ast_Node_Type_Function:
|
case Ast_Node_Type_Function:
|
||||||
// if they have the same pointer, true is
|
// if they have the same pointer, true is
|
||||||
// returned a few lines above
|
// returned a few lines above
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ struct Environment {
|
|||||||
struct Environment* parent;
|
struct Environment* parent;
|
||||||
int capacity;
|
int capacity;
|
||||||
int next_index;
|
int next_index;
|
||||||
|
// TODO(Felix): Use a hashmap here.
|
||||||
char** keys;
|
char** keys;
|
||||||
Ast_Node* values;
|
Ast_Node* values;
|
||||||
};
|
};
|
||||||
@@ -49,37 +50,10 @@ Ast_Node* lookup_symbol(Symbol* sym, Environment* env) {
|
|||||||
if (env->parent)
|
if (env->parent)
|
||||||
return lookup_symbol(sym, env->parent);
|
return lookup_symbol(sym, env->parent);
|
||||||
|
|
||||||
char* built_in_names[] = {
|
Ast_Node* built_in = create_ast_node_built_in_function(sym->identifier);
|
||||||
// Math stuff
|
if (built_in)
|
||||||
"+", "-", "*", "/",
|
return built_in;
|
||||||
">", "<", "=", "<=", ">=",
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
create_error(Error_Type_Symbol_Not_Defined, create_ast_node_nil());
|
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
Error_Type_Ill_Formed_List,
|
Error_Type_Ill_Formed_List,
|
||||||
|
Error_Type_Ill_Formed_Arguments,
|
||||||
Error_Type_Wrong_Number_Of_Arguments,
|
Error_Type_Wrong_Number_Of_Arguments,
|
||||||
Error_Type_Type_Missmatch,
|
Error_Type_Type_Missmatch,
|
||||||
Error_Type_Symbol_Not_Defined,
|
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) {
|
char* Error_Type_to_string(Error_Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Error_Type_Ill_Formed_List: return "Evaluation-error: Ill formed list";
|
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_Not_A_Function: return "Evaluation-error: Not a function";
|
||||||
case Error_Type_Symbol_Not_Defined: return "Evaluation-error: Symbol not defined";
|
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";
|
case Error_Type_Wrong_Number_Of_Arguments: return "Evaluation-error: Wrong number of arguments";
|
||||||
|
|||||||
+274
-262
@@ -27,7 +27,7 @@ int list_length(Ast_Node* node) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
Copies a list, in that it creates a new list, however the items are
|
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
|
a list when evaluating a parameters list, but not wanting to change
|
||||||
the parameters list. This happens if you have someting like:
|
the parameters list. This happens if you have someting like:
|
||||||
|
|
||||||
@@ -56,27 +56,83 @@ Ast_Node* copy_list(Ast_Node* node) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast_Node* eval_operands(Ast_Node* operands, Environment* env) {
|
typedef struct {
|
||||||
if (operands->type == Ast_Node_Type_Nil) {
|
Ast_Node_Array_List* positional_arguments;
|
||||||
return operands;
|
// 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
|
// just build the resulting list on the fly
|
||||||
Ast_Node* evaluated_operands = copy_list(operands);
|
Ast_Node* evaluated_arguments = copy_list(arguments);
|
||||||
Ast_Node* current_head = evaluated_operands;
|
Ast_Node* current_head = evaluated_arguments;
|
||||||
/* print(operands); */
|
|
||||||
/* printf("\n"); */
|
|
||||||
/* print(evaluated_operands); */
|
|
||||||
/* printf("\n"); */
|
|
||||||
while (current_head->type == Ast_Node_Type_Pair) {
|
while (current_head->type == Ast_Node_Type_Pair) {
|
||||||
try {
|
try {
|
||||||
current_head->value.pair->first =
|
current_head->value.pair->first =
|
||||||
eval_expr(current_head->value.pair->first, env);
|
eval_expr(current_head->value.pair->first, env);
|
||||||
}
|
}
|
||||||
current_head = current_head->value.pair->rest;
|
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) {
|
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);
|
operator = eval_expr(node->value.pair->first, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast_Node* operands = node->value.pair->rest;
|
Ast_Node* arguments = node->value.pair->rest;
|
||||||
int operands_length;
|
int arguments_length;
|
||||||
|
|
||||||
// check for built ins functions
|
// check for special form
|
||||||
if (operator->type == Ast_Node_Type_Built_In_Function) {
|
if (operator->type == Ast_Node_Type_Built_In_Function) {
|
||||||
char* operator_name = operator->value.built_in_function->identifier;
|
switch (operator->value.built_in_function->type) {
|
||||||
if (string_equal("quote", operator_name)) {
|
case Built_In_And: {
|
||||||
operands_length = list_length(operands);
|
bool result = true;
|
||||||
if (operands_length != 1) {
|
while (arguments->type != Ast_Node_Type_Nil) {
|
||||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
if (arguments->type != Ast_Node_Type_Pair) {
|
||||||
}
|
report_error(Error_Type_Ill_Formed_List);
|
||||||
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 {
|
try {
|
||||||
operands = eval_operands(operands, env);
|
result &= is_truthy(arguments->value.pair->first, env);
|
||||||
}
|
}
|
||||||
/* print(operands); */
|
arguments = arguments->value.pair->rest;
|
||||||
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);
|
if (!result) return create_ast_node_nil();
|
||||||
Ast_Node* evaluated_operands;
|
|
||||||
try {
|
|
||||||
evaluated_operands = eval_operands(operands, env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip to the last evaluated operand and return it,
|
return create_ast_node_number(1);
|
||||||
// we use eval_operands here instead of doing it
|
}
|
||||||
// manually, becuase we want to increase code reuse,
|
case Built_In_Or: {
|
||||||
// but at the cost that we have to find the end of the
|
bool result = false;
|
||||||
// list again
|
while (arguments->type != Ast_Node_Type_Nil) {
|
||||||
while (evaluated_operands->value.pair->rest->type == Ast_Node_Type_Pair) {
|
if (arguments->type != Ast_Node_Type_Pair) {
|
||||||
evaluated_operands = evaluated_operands->value.pair->rest;
|
report_error(Error_Type_Ill_Formed_List);
|
||||||
}
|
}
|
||||||
return evaluated_operands->value.pair->first;
|
|
||||||
} else if (string_equal("list", operator_name)) {
|
|
||||||
try {
|
try {
|
||||||
operands = eval_operands(operands, env);
|
result |= is_truthy(arguments->value.pair->first, env);
|
||||||
}
|
}
|
||||||
return operands;
|
arguments = arguments->value.pair->rest;
|
||||||
} else if (string_equal("=", operator_name)) {
|
|
||||||
try {
|
if (result) return create_ast_node_number(1);;
|
||||||
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);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
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();
|
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)) {
|
|
||||||
try {
|
|
||||||
operands_length = list_length(operands);
|
|
||||||
}
|
}
|
||||||
if (operands_length != 1) {
|
case Built_In_Not: {
|
||||||
|
try {
|
||||||
|
arguments_length = list_length(arguments);
|
||||||
|
}
|
||||||
|
if (arguments_length != 1) {
|
||||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
}
|
}
|
||||||
|
bool truthy;
|
||||||
try {
|
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();
|
return create_ast_node_nil();
|
||||||
if (operands->value.pair->first->type != Ast_Node_Type_Pair)
|
return create_ast_node_number(1);
|
||||||
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) {
|
case Built_In_If: {
|
||||||
|
try {
|
||||||
|
arguments_length = list_length(arguments);
|
||||||
|
}
|
||||||
|
if (arguments_length != 2 && arguments_length != 3) {
|
||||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast_Node* condition = operands->value.pair->first;
|
Ast_Node* condition = arguments->value.pair->first;
|
||||||
Ast_Node* then_part = operands->value.pair->rest;
|
Ast_Node* then_part = arguments->value.pair->rest;
|
||||||
Ast_Node* else_part = then_part->value.pair->rest;
|
Ast_Node* else_part = then_part->value.pair->rest;
|
||||||
|
|
||||||
bool truthy;
|
bool truthy;
|
||||||
@@ -345,66 +238,185 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
|
|||||||
try{
|
try{
|
||||||
result = eval_expr(then_part->value.pair->first, env);
|
result = eval_expr(then_part->value.pair->first, env);
|
||||||
}
|
}
|
||||||
else if (operands_length == 3)
|
else if (arguments_length == 3)
|
||||||
try {
|
try {
|
||||||
result = eval_expr(else_part->value.pair->first, env);
|
result = eval_expr(else_part->value.pair->first, env);
|
||||||
}
|
}
|
||||||
else return create_ast_node_nil();
|
else return create_ast_node_nil();
|
||||||
return result;
|
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 {
|
case Built_In_Quote: {
|
||||||
result &= is_truthy(operands->value.pair->first, env);
|
arguments_length = list_length(arguments);
|
||||||
}
|
if (arguments_length != 1) {
|
||||||
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) {
|
|
||||||
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
}
|
}
|
||||||
bool truthy;
|
return arguments->value.pair->first;
|
||||||
try {
|
|
||||||
truthy = is_truthy(operands->value.pair->first, env);
|
|
||||||
}
|
}
|
||||||
if (truthy)
|
case Built_In_Define: {
|
||||||
return create_ast_node_nil();
|
try {
|
||||||
return create_ast_node_number(1);
|
arguments_length = list_length(arguments);
|
||||||
} else {
|
}
|
||||||
report_error(Error_Type_Not_Yet_Implemented);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// assume it's lambda function and evaluate the operands in
|
// okay it is not a special form, so in any case we want
|
||||||
// place
|
// to evaluate the arguments; eval_arguments will also tell
|
||||||
operands = eval_operands(operands, env);
|
// 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();
|
||||||
|
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 arguments
|
||||||
|
arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ void print(Ast_Node* node) {
|
|||||||
printf("[lambda]");
|
printf("[lambda]");
|
||||||
} break;
|
} break;
|
||||||
case (Ast_Node_Type_Built_In_Function): {
|
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;
|
} break;
|
||||||
case (Ast_Node_Type_Pair): {
|
case (Ast_Node_Type_Pair): {
|
||||||
Ast_Node* head = node;
|
Ast_Node* head = node;
|
||||||
|
|||||||
+2
-1
@@ -82,7 +82,8 @@
|
|||||||
testresult test_eval_operands() {
|
testresult test_eval_operands() {
|
||||||
char operands_string[] = "((eval 1) (+ 1 2) \"okay\" (eval :haha))";
|
char operands_string[] = "((eval 1) (+ 1 2) \"okay\" (eval :haha))";
|
||||||
Ast_Node* operands = parse_single_expression(operands_string);
|
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_no_error(error);
|
||||||
assert_equal_int(list_length(operands), 4);
|
assert_equal_int(list_length(operands), 4);
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
* TODO =assert_equal_type= macro in testing
|
* TODO =assert_equal_type= macro in testing
|
||||||
|
* DONE use an enum for builtin identifiers
|
||||||
|
CLOSED: [2018-10-11 Do 17:15]
|
||||||
|
* TODO dont create new nils or builtins, but store one of each globally
|
||||||
|
* TODO source code locations for errors
|
||||||
* Build-in forms
|
* Build-in forms
|
||||||
|
|
||||||
** DONE +
|
** DONE +
|
||||||
|
|||||||
Reference in New Issue
Block a user