| @@ -0,0 +1 @@ | |||||
| /lisp | |||||
| @@ -0,0 +1,101 @@ | |||||
| struct Ast_Node; | |||||
| typedef enum { | |||||
| Ast_Node_Type_Nil, | |||||
| Ast_Node_Type_Symbol, | |||||
| Ast_Node_Type_Number, | |||||
| Ast_Node_Type_String, | |||||
| Ast_Node_Type_Cons_Cell, | |||||
| Ast_Node_Type_Function, | |||||
| Ast_Node_Type_Built_In_Function, | |||||
| } Ast_Type; | |||||
| typedef struct { | |||||
| char* identifier; | |||||
| } Symbol; | |||||
| typedef struct { | |||||
| double value; | |||||
| } Number; | |||||
| typedef struct { | |||||
| char* value; | |||||
| } String; | |||||
| typedef struct { | |||||
| struct Ast_Node* first; | |||||
| struct Ast_Node* rest; | |||||
| } Cons_Cell; | |||||
| typedef struct { | |||||
| Cons_Cell* regular_params; | |||||
| Cons_Cell* keyword_params; | |||||
| } Lambda_List; | |||||
| typedef struct { | |||||
| Lambda_List* parameters; | |||||
| Cons_Cell* form; | |||||
| } Function; | |||||
| typedef struct { | |||||
| char* identifier; | |||||
| } Built_In_Function; | |||||
| struct Ast_Node { | |||||
| Ast_Type type; | |||||
| union { | |||||
| Symbol* symbol; | |||||
| Number* number; | |||||
| String* string; | |||||
| Cons_Cell* cons_cell; | |||||
| Function* function; | |||||
| Built_In_Function* built_in_function; | |||||
| } value; | |||||
| }; | |||||
| // was forward declarated | |||||
| typedef struct Ast_Node Ast_Node; | |||||
| Ast_Node* create_ast_node_nil() { | |||||
| Ast_Node* node = new(Ast_Node); | |||||
| node->type = Ast_Node_Type_Nil; | |||||
| node->value.cons_cell = NULL; | |||||
| return node; | |||||
| } | |||||
| Ast_Node* create_ast_node_number(double number) { | |||||
| Ast_Node* node = new(Ast_Node); | |||||
| node->type = Ast_Node_Type_Number; | |||||
| node->value.number = new(Number); | |||||
| node->value.number->value = number; | |||||
| return node; | |||||
| } | |||||
| Ast_Node* create_ast_node_symbol(char* identifier) { | |||||
| Ast_Node* node = new(Ast_Node); | |||||
| node->type = Ast_Node_Type_Symbol; | |||||
| node->value.symbol = new(Symbol); | |||||
| node->value.symbol->identifier = identifier; | |||||
| return node; | |||||
| } | |||||
| Ast_Node* create_ast_node_built_in_function(char* identifier) { | |||||
| 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; | |||||
| return node; | |||||
| } | |||||
| Ast_Node* create_ast_node_pair(Ast_Node* first, Ast_Node* rest) { | |||||
| Ast_Node* node = new(Ast_Node); | |||||
| node->type = Ast_Node_Type_Cons_Cell; | |||||
| node->value.cons_cell = new(Cons_Cell); | |||||
| node->value.cons_cell->first = first; | |||||
| node->value.cons_cell->rest = rest; | |||||
| return node; | |||||
| } | |||||
| @@ -0,0 +1,6 @@ | |||||
| clang main.c -g -o lisp --std=c99 || exit 1 | |||||
| echo "" | |||||
| echo "--- Output Start ---" | |||||
| ./lisp test.lsp | |||||
| echo "--- Output End ---" | |||||
| @@ -0,0 +1,21 @@ | |||||
| Ast_Node* built_in_add(Ast_Node* operands) { | |||||
| double sum = 0; | |||||
| while (operands->type == Ast_Node_Type_Cons_Cell) { | |||||
| // TODO(Felix): assert type | |||||
| sum += operands->value.cons_cell->first->value.number->value; | |||||
| operands = operands->value.cons_cell->rest; | |||||
| } | |||||
| return create_ast_node_number(sum); | |||||
| } | |||||
| Ast_Node* built_in_substract(Ast_Node* operands) { | |||||
| double diff = operands->value.cons_cell->first->value.number->value; | |||||
| operands = operands->value.cons_cell->rest; | |||||
| while (operands->type == Ast_Node_Type_Cons_Cell) { | |||||
| // TODO(Felix): assert type | |||||
| diff -= operands->value.cons_cell->first->value.number->value; | |||||
| operands = operands->value.cons_cell->rest; | |||||
| } | |||||
| return create_ast_node_number(diff); | |||||
| } | |||||
| @@ -0,0 +1,58 @@ | |||||
| struct Environment { | |||||
| struct Environment* parent; | |||||
| int key_count; | |||||
| char** keys; | |||||
| Ast_Node* values; | |||||
| }; | |||||
| typedef struct Environment Environment; | |||||
| Environment* create_empty_environment() { | |||||
| Environment* env = new(Environment); | |||||
| env->parent = NULL; | |||||
| env->key_count = 0; | |||||
| env->keys = NULL; | |||||
| env->values = NULL; | |||||
| return env; | |||||
| } | |||||
| Ast_Node* lookup_symbol(Symbol* sym, Environment* env) { | |||||
| for (int i = 0; i < env->key_count; ++i) | |||||
| if (string_equal(env->keys[i], sym->identifier)) | |||||
| return env->values+i; | |||||
| if (env->parent) | |||||
| return lookup_symbol(sym, env->parent); | |||||
| char* built_in_names[] = { | |||||
| // Math stuff | |||||
| "+", "-", "*", "/", | |||||
| ">", "<", "=", | |||||
| // Conditional stuff | |||||
| "if", "and", "or", "not", | |||||
| // Cons stuff | |||||
| "first", "rest", "pair", | |||||
| // rest | |||||
| "load", "define", | |||||
| "lambda", "progn", | |||||
| "eval", "quote", | |||||
| "print", "read", | |||||
| "help" | |||||
| }; | |||||
| int built_in_count = 23; | |||||
| 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; | |||||
| } | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| @@ -0,0 +1,60 @@ | |||||
| /* Ast_Node* apply_to_lambda () {} */ | |||||
| /* Ast_Node* apply_to_built_in (Ast_Node* function, Ast_Node* arguments) { */ | |||||
| /* } */ | |||||
| Ast_Node* eval_expr(Ast_Node* node, Environment* env); | |||||
| void eval_operands(Ast_Node* operands, Environment* env) { | |||||
| while (1) { | |||||
| if (operands->type == Ast_Node_Type_Cons_Cell) { | |||||
| operands->value.cons_cell->first = eval_expr(operands->value.cons_cell->first, env); | |||||
| operands = operands->value.cons_cell->rest; | |||||
| } else { | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||||
| Ast_Node* ret = new(Ast_Node); | |||||
| switch (node->type) { | |||||
| case Ast_Node_Type_Nil: | |||||
| ret->type = Ast_Node_Type_Nil; | |||||
| return ret; | |||||
| case Ast_Node_Type_Symbol: | |||||
| return lookup_symbol(node->value.symbol, env); | |||||
| case Ast_Node_Type_Number: | |||||
| case Ast_Node_Type_String: | |||||
| return node; | |||||
| case Ast_Node_Type_Cons_Cell: { | |||||
| Ast_Node* operator = eval_expr(node->value.cons_cell->first, env); | |||||
| Ast_Node* operands = node->value.cons_cell->rest; | |||||
| // check for built ins functions | |||||
| if (operator->type == Ast_Node_Type_Built_In_Function) { | |||||
| char* operator_name = operator->value.built_in_function->identifier; | |||||
| if (string_equal("quote", operator_name)) { | |||||
| return node->value.cons_cell->rest; | |||||
| } else if (string_equal("+", operator_name)) { | |||||
| eval_operands(operands, env); | |||||
| return built_in_add(operands); | |||||
| } else if (string_equal("-", operator_name)) { | |||||
| eval_operands(operands, env); | |||||
| return built_in_substract(operands); | |||||
| } else { | |||||
| log_message(Log_Level_Critical, "The operator is not yet implemented\n"); | |||||
| } | |||||
| } | |||||
| // assume it's lambda function and evaluate the operands in | |||||
| // place | |||||
| eval_operands(operands, env); | |||||
| } | |||||
| default: | |||||
| return 0; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,3 @@ | |||||
| int string_equal(char* a, char* b) { | |||||
| return !strcmp(a, b); | |||||
| } | |||||
| @@ -0,0 +1,91 @@ | |||||
| typedef enum { | |||||
| Log_Level_Critical, | |||||
| Log_Level_Warning, | |||||
| Log_Level_Info, | |||||
| Log_Level_Debug, | |||||
| } Log_Level; | |||||
| void log_message(Log_Level type, char* message) { | |||||
| char* prefix; | |||||
| switch (type) { | |||||
| case Log_Level_Critical: prefix = "CRIT"; break; | |||||
| case Log_Level_Warning: prefix = "WARN"; break; | |||||
| case Log_Level_Info: prefix = "INFO"; break; | |||||
| case Log_Level_Debug: prefix = "DBUG"; break; | |||||
| } | |||||
| printf("%s: %s",prefix, message); | |||||
| } | |||||
| char* read_entire_file (char* filename) { | |||||
| char *fileContent = NULL; | |||||
| FILE *fp = fopen(filename, "r"); | |||||
| if (fp != NULL) { | |||||
| /* Go to the end of the file. */ | |||||
| if (fseek(fp, 0L, SEEK_END) == 0) { | |||||
| /* Get the size of the file. */ | |||||
| long bufsize = ftell(fp); | |||||
| if (bufsize == -1) { | |||||
| fputs("Empty file", stderr); | |||||
| goto closeFile; | |||||
| } | |||||
| /* Go back to the start of the file. */ | |||||
| if (fseek(fp, 0L, SEEK_SET) != 0) { | |||||
| fputs("Error reading file", stderr); | |||||
| goto closeFile; | |||||
| } | |||||
| /* Allocate our buffer to that size. */ | |||||
| fileContent = malloc(sizeof(char) * (bufsize)); | |||||
| /* Read the entire file into memory. */ | |||||
| size_t newLen = fread(fileContent, sizeof(char), bufsize, fp); | |||||
| if ( ferror( fp ) != 0 ) { | |||||
| fputs("Error reading file", stderr); | |||||
| } | |||||
| } | |||||
| closeFile: | |||||
| fclose(fp); | |||||
| } | |||||
| return fileContent; | |||||
| /* Don't forget to call free() later! */ | |||||
| } | |||||
| void print(Ast_Node* node) { | |||||
| switch (node->type) { | |||||
| case (Ast_Node_Type_Nil): { | |||||
| printf("nil"); | |||||
| } break; | |||||
| case (Ast_Node_Type_Number): { | |||||
| printf("%f", node->value.number->value); | |||||
| } break; | |||||
| case (Ast_Node_Type_String): { | |||||
| printf("\"%s\"", node->value.string->value); | |||||
| } break; | |||||
| case (Ast_Node_Type_Symbol): { | |||||
| printf("%s", node->value.symbol->identifier); | |||||
| } break; | |||||
| case (Ast_Node_Type_Function): { | |||||
| printf("[lambda]"); | |||||
| } break; | |||||
| case (Ast_Node_Type_Built_In_Function): { | |||||
| printf("[built-in-function %s]", node->value.built_in_function->identifier); | |||||
| } break; | |||||
| case (Ast_Node_Type_Cons_Cell): { | |||||
| printf("("); | |||||
| if (node->value.cons_cell->first) | |||||
| print(node->value.cons_cell->first); | |||||
| else | |||||
| printf("nil"); | |||||
| printf(" . "); | |||||
| if (node->value.cons_cell->rest) | |||||
| print(node->value.cons_cell->rest); | |||||
| else | |||||
| printf("nil"); | |||||
| printf(")"); | |||||
| } break; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,67 @@ | |||||
| #include <stdio.h> | |||||
| #include <string.h> | |||||
| #include <stdlib.h> | |||||
| #define new(type) (type*)malloc(sizeof(type)) | |||||
| #include "./helpers.c" | |||||
| #include "./ast.c" | |||||
| #include "./io.c" | |||||
| #include "./parse.c" | |||||
| #include "./built_ins.c" | |||||
| #include "./env.c" | |||||
| #include "./eval.c" | |||||
| void test() { | |||||
| // (+ 4 10) | |||||
| Ast_Node* nil = create_ast_node_nil(); | |||||
| Ast_Node* ten = create_ast_node_number(10); | |||||
| Ast_Node* four = create_ast_node_number(4); | |||||
| Ast_Node* plus = create_ast_node_symbol("+"); | |||||
| Ast_Node* minus = create_ast_node_symbol("-"); | |||||
| Ast_Node* form1 = create_ast_node_pair( | |||||
| plus, | |||||
| create_ast_node_pair( | |||||
| ten, | |||||
| create_ast_node_pair( | |||||
| four, | |||||
| nil))); | |||||
| Ast_Node* form2 = create_ast_node_pair( | |||||
| plus, | |||||
| create_ast_node_pair( | |||||
| four, | |||||
| create_ast_node_pair( | |||||
| four, | |||||
| nil))); | |||||
| Ast_Node* root = create_ast_node_pair( | |||||
| minus, | |||||
| create_ast_node_pair( | |||||
| form1, | |||||
| create_ast_node_pair( | |||||
| form2, | |||||
| nil))); | |||||
| print(root); | |||||
| printf("\n"); | |||||
| Ast_Node* result = eval_expr(root, new(Environment)); | |||||
| print(result); | |||||
| printf("\n"); | |||||
| } | |||||
| int main (int argc, char *argv[]) { | |||||
| /* if (argc > 1) { */ | |||||
| /* char* fileContent = read_entire_file(argv[1]); */ | |||||
| /* if (fileContent) { */ | |||||
| /* printf("File: %s\n", fileContent); */ | |||||
| /* } */ | |||||
| /* } else { */ | |||||
| /* printf("No source provided.\n"); */ | |||||
| /* } */ | |||||
| /* test(); */ | |||||
| print(parse_expression("(+ 4 7)")); | |||||
| return 0; | |||||
| } | |||||
| @@ -0,0 +1,12 @@ | |||||
| Ast_Node* parse_expression(char* expr) { | |||||
| typedef enum { | |||||
| Parser_State_Code, | |||||
| Parser_State_String, | |||||
| Parser_State_Comment, | |||||
| } Parser_State; | |||||
| // initial state is code | |||||
| Parser_State current_parser_state = Parser_State_Code; | |||||
| } | |||||
| @@ -0,0 +1,13 @@ | |||||
| (+ 1 (- 100 7) 3) | |||||
| (defun unpack (&rest args) | |||||
| (car args)) | |||||
| (unpack '(1 2)) | |||||
| (defun add (&rest args) | |||||
| (if args | |||||
| (+ (first args) | |||||
| (apply 'add 'args))) | |||||
| 0) | |||||
| (add 2 123 99) | |||||
| @@ -0,0 +1,50 @@ | |||||
| * Build-in forms | |||||
| - + | |||||
| - - | |||||
| - * | |||||
| - / | |||||
| - > | |||||
| - < | |||||
| - = | |||||
| - if | |||||
| - and | |||||
| - or | |||||
| - not | |||||
| - first (car) | |||||
| - rest (cdr) | |||||
| - pair (cons) | |||||
| - load (import) | |||||
| - define | |||||
| - lambda | |||||
| - progn | |||||
| - eval | |||||
| - quote | |||||
| - read | |||||
| - help | |||||
| * Types | |||||
| - Symbol | |||||
| - Number | |||||
| - String | |||||
| - Cons-cell (list) | |||||
| - lambda function lambda | |||||
| - built-in function | |||||
| * Arbeitsweise | |||||
| 1) Parse to AST | |||||
| 2) Start with namespace only containing the built-ins | |||||
| 3) every AST node has its own namespace, | |||||
| inherting the one from its parent (pointer / | |||||
| references) because when a function sets a | |||||
| variable within an upper namespace it should | |||||
| infact change the environment even if the | |||||
| function exited | |||||