commit e1c93c3c4a88d465bef0b1c77781f9a48fabe654 Author: Felix Brendel Date: Tue Sep 11 13:51:54 2018 +0900 git init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec5d2a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/lisp diff --git a/ast.c b/ast.c new file mode 100644 index 0000000..cc1a494 --- /dev/null +++ b/ast.c @@ -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; +} diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..3cb9ac6 --- /dev/null +++ b/build.sh @@ -0,0 +1,6 @@ +clang main.c -g -o lisp --std=c99 || exit 1 + +echo "" +echo "--- Output Start ---" +./lisp test.lsp +echo "--- Output End ---" diff --git a/built_ins.c b/built_ins.c new file mode 100644 index 0000000..8996c2d --- /dev/null +++ b/built_ins.c @@ -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); +} diff --git a/env.c b/env.c new file mode 100644 index 0000000..bdb9ab5 --- /dev/null +++ b/env.c @@ -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; +} diff --git a/eval.c b/eval.c new file mode 100644 index 0000000..a74f492 --- /dev/null +++ b/eval.c @@ -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; + } +} diff --git a/helpers.c b/helpers.c new file mode 100644 index 0000000..1bf6a28 --- /dev/null +++ b/helpers.c @@ -0,0 +1,3 @@ +int string_equal(char* a, char* b) { + return !strcmp(a, b); +} diff --git a/io.c b/io.c new file mode 100644 index 0000000..5dd8454 --- /dev/null +++ b/io.c @@ -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; + } +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..366950a --- /dev/null +++ b/main.c @@ -0,0 +1,67 @@ +#include +#include +#include + +#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; +} diff --git a/parse.c b/parse.c new file mode 100644 index 0000000..ebb9b5b --- /dev/null +++ b/parse.c @@ -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; + + +} diff --git a/test.lsp b/test.lsp new file mode 100644 index 0000000..be768b8 --- /dev/null +++ b/test.lsp @@ -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) diff --git a/todo.org b/todo.org new file mode 100644 index 0000000..f29294b --- /dev/null +++ b/todo.org @@ -0,0 +1,50 @@ +* Build-in forms + + - + + - - + - * + - / + - > + - < + - = + + - if + - and + - or + - not + + - first (car) + - rest (cdr) + - pair (cons) + + - load (import) + - define + + - lambda + - progn + + - eval + - quote + + - print + - 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