From e1c93c3c4a88d465bef0b1c77781f9a48fabe654 Mon Sep 17 00:00:00 2001 From: Felix Brendel Date: Tue, 11 Sep 2018 13:51:54 +0900 Subject: [PATCH] git init --- .gitignore | 1 + ast.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++ build.sh | 6 ++++ built_ins.c | 21 +++++++++++ env.c | 58 ++++++++++++++++++++++++++++++ eval.c | 60 +++++++++++++++++++++++++++++++ helpers.c | 3 ++ io.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++ main.c | 67 ++++++++++++++++++++++++++++++++++ parse.c | 12 +++++++ test.lsp | 13 +++++++ todo.org | 50 ++++++++++++++++++++++++++ 12 files changed, 483 insertions(+) create mode 100644 .gitignore create mode 100644 ast.c create mode 100755 build.sh create mode 100644 built_ins.c create mode 100644 env.c create mode 100644 eval.c create mode 100644 helpers.c create mode 100644 io.c create mode 100644 main.c create mode 100644 parse.c create mode 100644 test.lsp create mode 100644 todo.org 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