| @@ -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 | |||