git init
This commit is contained in:
@@ -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 ---"
|
||||||
+21
@@ -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,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
|
||||||
|
|
||||||
|
- 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
|
||||||
Reference in New Issue
Block a user