step 1 in transitioning to c++
This commit is contained in:
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
((c-mode . ((eval . (company-clang-set-prefix "main.c"))
|
((c++-mode . ((eval . (company-clang-set-prefix "main.cpp"))
|
||||||
(eval . (flycheck-mode 0))
|
(eval . (flycheck-mode 0))
|
||||||
(eval . (rainbow-mode 0))))
|
(eval . (rainbow-mode 0))))
|
||||||
(nil . ((eval . (progn
|
(nil . ((eval . (progn
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
ACME_MODULE(
|
||||||
|
#==========================================================================
|
||||||
|
# general module information
|
||||||
|
#==========================================================================
|
||||||
|
NAME TSE_LispIntegration
|
||||||
|
TYPE LIBRARY
|
||||||
|
|
||||||
|
#==========================================================================
|
||||||
|
# files of this module
|
||||||
|
#==========================================================================
|
||||||
|
INCLUDE_BASE src
|
||||||
|
bin
|
||||||
|
FILES_PRIVATE_HEADER src/*.h
|
||||||
|
FILES_SOURCE src/main.cpp
|
||||||
|
#DEPENDENCIES TSE_Engine
|
||||||
|
)
|
||||||
|
|
||||||
|
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/bin/pre.slime
|
||||||
|
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/bin/test.slime
|
||||||
|
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
+12
-4
@@ -1,4 +1,12 @@
|
|||||||
(defun ! (n)
|
|
||||||
(ifs (< n 2)
|
(defun make-vector (x y z)
|
||||||
1
|
(let ((local-x x)
|
||||||
(* n (! (- n 1)))))
|
(local-y y)
|
||||||
|
(local-z z))
|
||||||
|
(lambda ()
|
||||||
|
local-x)))
|
||||||
|
|
||||||
|
(define v (make-vector 1 2 3))
|
||||||
|
(print (v))
|
||||||
|
|
||||||
|
(read " ")
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ pushd build
|
|||||||
taskkill /F /IM %exeName% > NUL 2> NUL
|
taskkill /F /IM %exeName% > NUL 2> NUL
|
||||||
|
|
||||||
echo ---------- Compiling ----------
|
echo ---------- Compiling ----------
|
||||||
call timecmd cl ../src/main.c /Fe%exeName% /D_DEBUG /W3 /TC /Zi /nologo /EHsc /link
|
call timecmd cl ../src/main.cpp /Fe%exeName% /D_DEBUG /W3 /Zi /nologo /EHsc /link /NODEFAULTLIB:libucrt libucrtd.lib
|
||||||
|
|
||||||
if %errorlevel% == 0 (
|
if %errorlevel% == 0 (
|
||||||
echo.
|
echo.
|
||||||
|
|||||||
Binary file not shown.
+10
-137
@@ -12,7 +12,7 @@ typedef enum {
|
|||||||
|
|
||||||
Ast_Node_Type_Pair,
|
Ast_Node_Type_Pair,
|
||||||
Ast_Node_Type_Function,
|
Ast_Node_Type_Function,
|
||||||
Ast_Node_Type_Built_In_Function,
|
Ast_Node_Type_CFunction,
|
||||||
} Ast_Node_Type;
|
} Ast_Node_Type;
|
||||||
|
|
||||||
char* Ast_Node_Type_to_string(Ast_Node_Type type) {
|
char* Ast_Node_Type_to_string(Ast_Node_Type type) {
|
||||||
@@ -24,7 +24,7 @@ char* Ast_Node_Type_to_string(Ast_Node_Type type) {
|
|||||||
case(Ast_Node_Type_Symbol): return "symbol";
|
case(Ast_Node_Type_Symbol): return "symbol";
|
||||||
case(Ast_Node_Type_Keyword): return "keyword";
|
case(Ast_Node_Type_Keyword): return "keyword";
|
||||||
case(Ast_Node_Type_Function): return "function";
|
case(Ast_Node_Type_Function): return "function";
|
||||||
case(Ast_Node_Type_Built_In_Function): return "built-in function";
|
case(Ast_Node_Type_CFunction): return "C-function";
|
||||||
case(Ast_Node_Type_Pair): return "pair";
|
case(Ast_Node_Type_Pair): return "pair";
|
||||||
}
|
}
|
||||||
return "unknown";
|
return "unknown";
|
||||||
@@ -122,96 +122,10 @@ typedef struct {
|
|||||||
struct Ast_Node* body; // implicit prog
|
struct Ast_Node* body; // implicit prog
|
||||||
} Function;
|
} Function;
|
||||||
|
|
||||||
typedef enum {
|
struct Environment;
|
||||||
Built_In_Addition,
|
|
||||||
Built_In_And,
|
|
||||||
Built_In_Breakpoint,
|
|
||||||
Built_In_Copy,
|
|
||||||
Built_In_Define,
|
|
||||||
Built_In_Division,
|
|
||||||
Built_In_Equal,
|
|
||||||
Built_In_Eval,
|
|
||||||
Built_In_Error,
|
|
||||||
Built_In_Exit,
|
|
||||||
Built_In_First,
|
|
||||||
Built_In_Greater,
|
|
||||||
Built_In_Greater_Equal,
|
|
||||||
Built_In_If,
|
|
||||||
Built_In_Info,
|
|
||||||
Built_In_Lambda,
|
|
||||||
Built_In_Less,
|
|
||||||
Built_In_Less_Equal,
|
|
||||||
Built_In_Let,
|
|
||||||
Built_In_List,
|
|
||||||
Built_In_Load,
|
|
||||||
Built_In_Macro,
|
|
||||||
Built_In_Macro_Define,
|
|
||||||
Built_In_Multiplication,
|
|
||||||
Built_In_Mutate,
|
|
||||||
Built_In_Not,
|
|
||||||
Built_In_Or,
|
|
||||||
Built_In_Pair,
|
|
||||||
Built_In_Print,
|
|
||||||
Built_In_Prog,
|
|
||||||
Built_In_Quote,
|
|
||||||
Built_In_Read,
|
|
||||||
Built_In_Rest,
|
|
||||||
Built_In_Subtraction,
|
|
||||||
Built_In_Try,
|
|
||||||
Built_In_Type,
|
|
||||||
Built_In_While,
|
|
||||||
} Built_In_Name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
This is used only for printing.
|
|
||||||
**/
|
|
||||||
char* Built_In_Name_to_string(Built_In_Name name) {
|
|
||||||
switch (name) {
|
|
||||||
case Built_In_Addition: return "+";
|
|
||||||
case Built_In_And: return "and";
|
|
||||||
case Built_In_Breakpoint: return "breakpoint";
|
|
||||||
case Built_In_Copy: return "copy";
|
|
||||||
case Built_In_Define: return "define";
|
|
||||||
case Built_In_Division: return "/";
|
|
||||||
case Built_In_Equal: return "=";
|
|
||||||
case Built_In_Error: return "error";
|
|
||||||
case Built_In_Eval: return "eval";
|
|
||||||
case Built_In_Exit: return "exit";
|
|
||||||
case Built_In_First: return "first";
|
|
||||||
case Built_In_Greater: return ">";
|
|
||||||
case Built_In_Greater_Equal: return ">=";
|
|
||||||
case Built_In_If: return "if";
|
|
||||||
case Built_In_Info: return "info";
|
|
||||||
case Built_In_Lambda: return "lambda";
|
|
||||||
case Built_In_Less: return "<";
|
|
||||||
case Built_In_Less_Equal: return "<=";
|
|
||||||
case Built_In_Let: return "let";
|
|
||||||
case Built_In_List: return "list";
|
|
||||||
case Built_In_Load: return "load";
|
|
||||||
case Built_In_Macro: return "macro";
|
|
||||||
case Built_In_Macro_Define: return "macro-define";
|
|
||||||
case Built_In_Multiplication: return "*";
|
|
||||||
case Built_In_Mutate: return "mutate";
|
|
||||||
case Built_In_Not: return "not";
|
|
||||||
case Built_In_Or: return "or";
|
|
||||||
case Built_In_Pair: return "pair";
|
|
||||||
case Built_In_Print: return "print";
|
|
||||||
case Built_In_Prog: return "prog";
|
|
||||||
case Built_In_Quote: return "quote";
|
|
||||||
case Built_In_Read: return "read";
|
|
||||||
case Built_In_Rest: return "rest";
|
|
||||||
case Built_In_Subtraction: return "-";
|
|
||||||
case Built_In_Try: return "try";
|
|
||||||
case Built_In_Type: return "type";
|
|
||||||
case Built_In_While: return "while";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Built in string missing in Built_In_Name_to_string";
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Built_In_Name type;
|
std::function<Ast_Node*(Ast_Node*, Environment*)> function;
|
||||||
} Built_In_Function;
|
} CFunction;
|
||||||
|
|
||||||
struct Ast_Node {
|
struct Ast_Node {
|
||||||
Source_Code_Location* sourceCodeLocation;
|
Source_Code_Location* sourceCodeLocation;
|
||||||
@@ -223,7 +137,7 @@ struct Ast_Node {
|
|||||||
String* string;
|
String* string;
|
||||||
Pair* pair;
|
Pair* pair;
|
||||||
Function* function;
|
Function* function;
|
||||||
Built_In_Function* built_in_function;
|
CFunction* cfunction;
|
||||||
} value;
|
} value;
|
||||||
};
|
};
|
||||||
// was forward declarated
|
// was forward declarated
|
||||||
@@ -290,52 +204,11 @@ Ast_Node* create_ast_node_keyword(char* keyword) {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast_Node* create_ast_node_built_in_function(char* name) {
|
Ast_Node* create_ast_node_cfunction(std::function<Ast_Node*(Ast_Node*, Environment*)> function) {
|
||||||
Built_In_Name type;
|
|
||||||
if (string_equal(name, "+")) type = Built_In_Addition;
|
|
||||||
else if (string_equal(name, "*")) type = Built_In_Multiplication;
|
|
||||||
else if (string_equal(name, "-")) type = Built_In_Subtraction;
|
|
||||||
else if (string_equal(name, "/")) type = Built_In_Division;
|
|
||||||
else if (string_equal(name, "<")) type = Built_In_Less;
|
|
||||||
else if (string_equal(name, "<=")) type = Built_In_Less_Equal;
|
|
||||||
else if (string_equal(name, "=")) type = Built_In_Equal;
|
|
||||||
else if (string_equal(name, ">")) type = Built_In_Greater;
|
|
||||||
else if (string_equal(name, ">=")) type = Built_In_Greater_Equal;
|
|
||||||
else if (string_equal(name, "and")) type = Built_In_And;
|
|
||||||
else if (string_equal(name, "breakpoint")) type = Built_In_Breakpoint;
|
|
||||||
else if (string_equal(name, "copy")) type = Built_In_Copy;
|
|
||||||
else if (string_equal(name, "define")) type = Built_In_Define;
|
|
||||||
else if (string_equal(name, "error")) type = Built_In_Error;
|
|
||||||
else if (string_equal(name, "eval")) type = Built_In_Eval;
|
|
||||||
else if (string_equal(name, "exit")) type = Built_In_Exit;
|
|
||||||
else if (string_equal(name, "first")) type = Built_In_First;
|
|
||||||
else if (string_equal(name, "if")) type = Built_In_If;
|
|
||||||
else if (string_equal(name, "info")) type = Built_In_Info;
|
|
||||||
else if (string_equal(name, "info")) type = Built_In_Info;
|
|
||||||
else if (string_equal(name, "lambda")) type = Built_In_Lambda;
|
|
||||||
else if (string_equal(name, "let")) type = Built_In_Let;
|
|
||||||
else if (string_equal(name, "list")) type = Built_In_List;
|
|
||||||
else if (string_equal(name, "load")) type = Built_In_Load;
|
|
||||||
else if (string_equal(name, "macro")) type = Built_In_Macro;
|
|
||||||
else if (string_equal(name, "macro-define")) type = Built_In_Macro_Define;
|
|
||||||
else if (string_equal(name, "mutate")) type = Built_In_Mutate;
|
|
||||||
else if (string_equal(name, "not")) type = Built_In_Not;
|
|
||||||
else if (string_equal(name, "or")) type = Built_In_Or;
|
|
||||||
else if (string_equal(name, "pair")) type = Built_In_Pair;
|
|
||||||
else if (string_equal(name, "print")) type = Built_In_Print;
|
|
||||||
else if (string_equal(name, "prog")) type = Built_In_Prog;
|
|
||||||
else if (string_equal(name, "quote")) type = Built_In_Quote;
|
|
||||||
else if (string_equal(name, "read")) type = Built_In_Read;
|
|
||||||
else if (string_equal(name, "rest")) type = Built_In_Rest;
|
|
||||||
else if (string_equal(name, "try")) type = Built_In_Try;
|
|
||||||
else if (string_equal(name, "type")) type = Built_In_Type;
|
|
||||||
else if (string_equal(name, "while")) type = Built_In_While;
|
|
||||||
else return nullptr;
|
|
||||||
|
|
||||||
Ast_Node* node = create_ast_node();
|
Ast_Node* node = create_ast_node();
|
||||||
node->type = Ast_Node_Type_Built_In_Function;
|
node->type = Ast_Node_Type_CFunction;
|
||||||
node->value.built_in_function = new(Built_In_Function);
|
node->value.cfunction = new(CFunction);
|
||||||
node->value.built_in_function->type = type;
|
node->value.cfunction->function = function;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
-218
@@ -1,218 +0,0 @@
|
|||||||
Ast_Node* eval_expr(Ast_Node* node, Environment* env);
|
|
||||||
|
|
||||||
bool ast_node_equal(Ast_Node* n1, Ast_Node* n2) {
|
|
||||||
if (n1 == n2)
|
|
||||||
return true;
|
|
||||||
if (n1->type != n2->type)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
switch (n1->type) {
|
|
||||||
case Ast_Node_Type_Built_In_Function:
|
|
||||||
return n1->value.built_in_function->type
|
|
||||||
== n2->value.built_in_function->type;
|
|
||||||
case Ast_Node_Type_Function:
|
|
||||||
// if they have the same pointer, true is
|
|
||||||
// returned a few lines above
|
|
||||||
return false;
|
|
||||||
case Ast_Node_Type_Keyword:
|
|
||||||
return string_equal(
|
|
||||||
n1->value.keyword->identifier,
|
|
||||||
n2->value.keyword->identifier);
|
|
||||||
case Ast_Node_Type_T:
|
|
||||||
case Ast_Node_Type_Nil:
|
|
||||||
return true;
|
|
||||||
case Ast_Node_Type_Number:
|
|
||||||
return
|
|
||||||
n1->value.number->value ==
|
|
||||||
n2->value.number->value;
|
|
||||||
case Ast_Node_Type_Pair:
|
|
||||||
create_error(Error_Type_Not_Yet_Implemented, n1->sourceCodeLocation);
|
|
||||||
return false;
|
|
||||||
case Ast_Node_Type_String:
|
|
||||||
return string_equal(
|
|
||||||
n1->value.string->value,
|
|
||||||
n2->value.string->value);
|
|
||||||
case Ast_Node_Type_Symbol:
|
|
||||||
return string_equal(
|
|
||||||
n1->value.symbol->identifier,
|
|
||||||
n2->value.symbol->identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we should never reach here
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ast_Node* built_in_equals(Ast_Node* operands) {
|
|
||||||
if (operands->type == Ast_Node_Type_Nil)
|
|
||||||
return create_ast_node_t();
|
|
||||||
|
|
||||||
Ast_Node* first = operands->value.pair->first;
|
|
||||||
|
|
||||||
while (operands->type == Ast_Node_Type_Pair) {
|
|
||||||
if (!ast_node_equal(operands->value.pair->first, first))
|
|
||||||
return create_ast_node_nil();
|
|
||||||
operands = operands->value.pair->rest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return create_ast_node_t();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ast_Node* built_in_greater(Ast_Node* operands) {
|
|
||||||
double last_number = strtod("Inf", NULL);
|
|
||||||
|
|
||||||
while (operands->type == Ast_Node_Type_Pair) {
|
|
||||||
try {
|
|
||||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (operands->value.pair->first->value.number->value >= last_number)
|
|
||||||
return create_ast_node_nil();
|
|
||||||
|
|
||||||
last_number = operands->value.pair->first->value.number->value;
|
|
||||||
operands = operands->value.pair->rest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return create_ast_node_t();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ast_Node* built_in_greater_equal(Ast_Node* operands) {
|
|
||||||
double last_number = strtod("Inf", NULL);
|
|
||||||
|
|
||||||
while (operands->type == Ast_Node_Type_Pair) {
|
|
||||||
try {
|
|
||||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (operands->value.pair->first->value.number->value > last_number)
|
|
||||||
return create_ast_node_nil();
|
|
||||||
|
|
||||||
last_number = operands->value.pair->first->value.number->value;
|
|
||||||
operands = operands->value.pair->rest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return create_ast_node_t();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ast_Node* built_in_less(Ast_Node* operands) {
|
|
||||||
double last_number = strtod("-Inf", NULL);
|
|
||||||
|
|
||||||
while (operands->type == Ast_Node_Type_Pair) {
|
|
||||||
try {
|
|
||||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (operands->value.pair->first->value.number->value <= last_number)
|
|
||||||
return create_ast_node_nil();
|
|
||||||
|
|
||||||
last_number = operands->value.pair->first->value.number->value;
|
|
||||||
operands = operands->value.pair->rest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return create_ast_node_t();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ast_Node* built_in_less_equal(Ast_Node* operands) {
|
|
||||||
double last_number = strtod("-Inf", NULL);
|
|
||||||
|
|
||||||
while (operands->type == Ast_Node_Type_Pair) {
|
|
||||||
try {
|
|
||||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (operands->value.pair->first->value.number->value < last_number)
|
|
||||||
return create_ast_node_nil();
|
|
||||||
|
|
||||||
last_number = operands->value.pair->first->value.number->value;
|
|
||||||
operands = operands->value.pair->rest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return create_ast_node_t();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ast_Node* built_in_add(Ast_Node* operands) {
|
|
||||||
double sum = 0;
|
|
||||||
while (operands->type == Ast_Node_Type_Pair) {
|
|
||||||
try {
|
|
||||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
|
||||||
}
|
|
||||||
sum += operands->value.pair->first->value.number->value;
|
|
||||||
operands = operands->value.pair->rest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return create_ast_node_number(sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ast_Node* built_in_substract(Ast_Node* operands) {
|
|
||||||
try {
|
|
||||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
|
||||||
}
|
|
||||||
double difference = operands->value.pair->first->value.number->value;
|
|
||||||
|
|
||||||
operands = operands->value.pair->rest;
|
|
||||||
while (operands->type == Ast_Node_Type_Pair) {
|
|
||||||
try {
|
|
||||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
|
||||||
}
|
|
||||||
|
|
||||||
difference -= operands->value.pair->first->value.number->value;
|
|
||||||
operands = operands->value.pair->rest;
|
|
||||||
}
|
|
||||||
return create_ast_node_number(difference);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ast_Node* built_in_multiply(Ast_Node* operands) {
|
|
||||||
try {
|
|
||||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
|
||||||
}
|
|
||||||
double product = operands->value.pair->first->value.number->value;
|
|
||||||
|
|
||||||
operands = operands->value.pair->rest;
|
|
||||||
while (operands->type == Ast_Node_Type_Pair) {
|
|
||||||
try {
|
|
||||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
|
||||||
}
|
|
||||||
|
|
||||||
product *= operands->value.pair->first->value.number->value;
|
|
||||||
operands = operands->value.pair->rest;
|
|
||||||
}
|
|
||||||
return create_ast_node_number(product);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ast_Node* built_in_divide(Ast_Node* operands) {
|
|
||||||
try {
|
|
||||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
|
||||||
}
|
|
||||||
double quotient = operands->value.pair->first->value.number->value;
|
|
||||||
|
|
||||||
operands = operands->value.pair->rest;
|
|
||||||
while (operands->type == Ast_Node_Type_Pair) {
|
|
||||||
try {
|
|
||||||
assert_type(operands->value.pair->first, Ast_Node_Type_Number);
|
|
||||||
}
|
|
||||||
|
|
||||||
quotient /= operands->value.pair->first->value.number->value;
|
|
||||||
operands = operands->value.pair->rest;
|
|
||||||
}
|
|
||||||
return create_ast_node_number(quotient);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Ast_Node* built_in_load(char* file_name, Environment* env) {
|
|
||||||
char* file_content = read_entire_file(file_name);
|
|
||||||
if (file_content) {
|
|
||||||
Ast_Node* result = create_ast_node_nil();
|
|
||||||
Ast_Node_Array_List* program;
|
|
||||||
try {
|
|
||||||
program = parse_program(file_name, file_content);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < program->next_index; ++i) {
|
|
||||||
try {
|
|
||||||
result = eval_expr(program->data[i], env);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
create_error(Error_Type_Unknown_Error, nullptr);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,968 @@
|
|||||||
|
Ast_Node* eval_arguments(Ast_Node* arguments, Environment* env, int *out_arguments_length);
|
||||||
|
int list_length(Ast_Node* node);
|
||||||
|
Ast_Node* eval_expr(Ast_Node* node, Environment* env);
|
||||||
|
bool is_truthy (Ast_Node* expression, Environment* env);
|
||||||
|
void parse_argument_list(Ast_Node* arguments, Function* function);
|
||||||
|
|
||||||
|
bool ast_node_equal(Ast_Node* n1, Ast_Node* n2) {
|
||||||
|
if (n1 == n2)
|
||||||
|
return true;
|
||||||
|
if (n1->type != n2->type)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (n1->type) {
|
||||||
|
case Ast_Node_Type_CFunction:
|
||||||
|
// TODO(Felix): make comparing work again.
|
||||||
|
return false;
|
||||||
|
/* return n1->value.built_in_function->type */
|
||||||
|
/* == n2->value.built_in_function->type; */
|
||||||
|
case Ast_Node_Type_Function:
|
||||||
|
// if they have the same pointer, true is
|
||||||
|
// returned a few lines above
|
||||||
|
return false;
|
||||||
|
case Ast_Node_Type_Keyword:
|
||||||
|
return string_equal(
|
||||||
|
n1->value.keyword->identifier,
|
||||||
|
n2->value.keyword->identifier);
|
||||||
|
case Ast_Node_Type_T:
|
||||||
|
case Ast_Node_Type_Nil:
|
||||||
|
return true;
|
||||||
|
case Ast_Node_Type_Number:
|
||||||
|
return
|
||||||
|
n1->value.number->value ==
|
||||||
|
n2->value.number->value;
|
||||||
|
case Ast_Node_Type_Pair:
|
||||||
|
create_error(Error_Type_Not_Yet_Implemented, n1->sourceCodeLocation);
|
||||||
|
return false;
|
||||||
|
case Ast_Node_Type_String:
|
||||||
|
return string_equal(
|
||||||
|
n1->value.string->value,
|
||||||
|
n2->value.string->value);
|
||||||
|
case Ast_Node_Type_Symbol:
|
||||||
|
return string_equal(
|
||||||
|
n1->value.symbol->identifier,
|
||||||
|
n2->value.symbol->identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we should never reach here
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* built_in_equals(Ast_Node* arguments, Environment* env) {
|
||||||
|
int arguments_length;
|
||||||
|
try {
|
||||||
|
arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments->type == Ast_Node_Type_Nil)
|
||||||
|
return create_ast_node_t();
|
||||||
|
|
||||||
|
Ast_Node* first = arguments->value.pair->first;
|
||||||
|
|
||||||
|
while (arguments->type == Ast_Node_Type_Pair) {
|
||||||
|
if (!ast_node_equal(arguments->value.pair->first, first))
|
||||||
|
return create_ast_node_nil();
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return create_ast_node_t();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* built_in_greater(Ast_Node* arguments, Environment* env) {
|
||||||
|
int arguments_length;
|
||||||
|
try {
|
||||||
|
arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
double last_number = strtod("Inf", NULL);
|
||||||
|
|
||||||
|
while (arguments->type == Ast_Node_Type_Pair) {
|
||||||
|
try {
|
||||||
|
assert_type(arguments->value.pair->first, Ast_Node_Type_Number);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments->value.pair->first->value.number->value >= last_number)
|
||||||
|
return create_ast_node_nil();
|
||||||
|
|
||||||
|
last_number = arguments->value.pair->first->value.number->value;
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return create_ast_node_t();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* built_in_greater_equal(Ast_Node* arguments, Environment* env) {
|
||||||
|
int arguments_length;
|
||||||
|
try {
|
||||||
|
arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
double last_number = strtod("Inf", NULL);
|
||||||
|
|
||||||
|
while (arguments->type == Ast_Node_Type_Pair) {
|
||||||
|
try {
|
||||||
|
assert_type(arguments->value.pair->first, Ast_Node_Type_Number);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments->value.pair->first->value.number->value > last_number)
|
||||||
|
return create_ast_node_nil();
|
||||||
|
|
||||||
|
last_number = arguments->value.pair->first->value.number->value;
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return create_ast_node_t();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* built_in_less(Ast_Node* arguments, Environment* env) {
|
||||||
|
int arguments_length;
|
||||||
|
try {
|
||||||
|
arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
double last_number = strtod("-Inf", NULL);
|
||||||
|
|
||||||
|
while (arguments->type == Ast_Node_Type_Pair) {
|
||||||
|
try {
|
||||||
|
assert_type(arguments->value.pair->first, Ast_Node_Type_Number);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments->value.pair->first->value.number->value <= last_number)
|
||||||
|
return create_ast_node_nil();
|
||||||
|
|
||||||
|
last_number = arguments->value.pair->first->value.number->value;
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return create_ast_node_t();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* built_in_less_equal(Ast_Node* arguments, Environment* env) {
|
||||||
|
int arguments_length;
|
||||||
|
try {
|
||||||
|
arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
double last_number = strtod("-Inf", NULL);
|
||||||
|
|
||||||
|
while (arguments->type == Ast_Node_Type_Pair) {
|
||||||
|
try {
|
||||||
|
assert_type(arguments->value.pair->first, Ast_Node_Type_Number);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments->value.pair->first->value.number->value < last_number)
|
||||||
|
return create_ast_node_nil();
|
||||||
|
|
||||||
|
last_number = arguments->value.pair->first->value.number->value;
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return create_ast_node_t();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* built_in_add(Ast_Node* arguments, Environment* env) {
|
||||||
|
int arguments_length;
|
||||||
|
try {
|
||||||
|
arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
double sum = 0;
|
||||||
|
while (arguments->type == Ast_Node_Type_Pair) {
|
||||||
|
try {
|
||||||
|
assert_type(arguments->value.pair->first, Ast_Node_Type_Number);
|
||||||
|
}
|
||||||
|
sum += arguments->value.pair->first->value.number->value;
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return create_ast_node_number(sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* built_in_substract(Ast_Node* arguments, Environment* env) {
|
||||||
|
int arguments_length;
|
||||||
|
try {
|
||||||
|
arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert_type(arguments->value.pair->first, Ast_Node_Type_Number);
|
||||||
|
}
|
||||||
|
double difference = arguments->value.pair->first->value.number->value;
|
||||||
|
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
while (arguments->type == Ast_Node_Type_Pair) {
|
||||||
|
try {
|
||||||
|
assert_type(arguments->value.pair->first, Ast_Node_Type_Number);
|
||||||
|
}
|
||||||
|
|
||||||
|
difference -= arguments->value.pair->first->value.number->value;
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
}
|
||||||
|
return create_ast_node_number(difference);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* built_in_multiply(Ast_Node* arguments, Environment* env) {
|
||||||
|
int arguments_length;
|
||||||
|
try {
|
||||||
|
arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert_type(arguments->value.pair->first, Ast_Node_Type_Number);
|
||||||
|
}
|
||||||
|
double product = arguments->value.pair->first->value.number->value;
|
||||||
|
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
while (arguments->type == Ast_Node_Type_Pair) {
|
||||||
|
try {
|
||||||
|
assert_type(arguments->value.pair->first, Ast_Node_Type_Number);
|
||||||
|
}
|
||||||
|
|
||||||
|
product *= arguments->value.pair->first->value.number->value;
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
}
|
||||||
|
return create_ast_node_number(product);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* built_in_divide(Ast_Node* arguments, Environment* env) {
|
||||||
|
int arguments_length;
|
||||||
|
try {
|
||||||
|
arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert_type(arguments->value.pair->first, Ast_Node_Type_Number);
|
||||||
|
}
|
||||||
|
double quotient = arguments->value.pair->first->value.number->value;
|
||||||
|
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
while (arguments->type == Ast_Node_Type_Pair) {
|
||||||
|
try {
|
||||||
|
assert_type(arguments->value.pair->first, Ast_Node_Type_Number);
|
||||||
|
}
|
||||||
|
|
||||||
|
quotient /= arguments->value.pair->first->value.number->value;
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
}
|
||||||
|
return create_ast_node_number(quotient);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Ast_Node* built_in_load(char* file_name, Environment* env) {
|
||||||
|
char* file_content = read_entire_file(file_name);
|
||||||
|
if (file_content) {
|
||||||
|
Ast_Node* result = create_ast_node_nil();
|
||||||
|
Ast_Node_Array_List* program;
|
||||||
|
try {
|
||||||
|
program = parse_program(file_name, file_content);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < program->next_index; ++i) {
|
||||||
|
try {
|
||||||
|
result = eval_expr(program->data[i], env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
create_error(Error_Type_Unknown_Error, nullptr);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_built_ins_into_environment(Environment* env) {
|
||||||
|
int arguments_length;
|
||||||
|
Ast_Node* evaluated_arguments;
|
||||||
|
|
||||||
|
#define cLambda [=](Ast_Node* arguments, Environment* env) mutable -> Ast_Node*
|
||||||
|
#define report_error(_type) { \
|
||||||
|
create_error(_type, arguments->sourceCodeLocation); \
|
||||||
|
return nullptr; \
|
||||||
|
}
|
||||||
|
|
||||||
|
auto defun = [&](char* name, std::function<Ast_Node*(Ast_Node*, Environment*)> fun) {
|
||||||
|
define_symbol(
|
||||||
|
create_ast_node_symbol(name),
|
||||||
|
create_ast_node_cfunction(fun),
|
||||||
|
env);
|
||||||
|
};
|
||||||
|
|
||||||
|
defun("=", built_in_equals);
|
||||||
|
defun(">", built_in_greater);
|
||||||
|
defun(">=", built_in_greater_equal);
|
||||||
|
defun("<", built_in_less);
|
||||||
|
defun("<=", built_in_less_equal);
|
||||||
|
defun("+", built_in_add);
|
||||||
|
defun("-", built_in_substract);
|
||||||
|
defun("*", built_in_multiply);
|
||||||
|
defun("/", built_in_divide);
|
||||||
|
defun("define", cLambda {
|
||||||
|
try {
|
||||||
|
arguments_length = list_length(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length != 2) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* symbol = arguments->value.pair->first;
|
||||||
|
|
||||||
|
if (symbol->type == Ast_Node_Type_Pair) {
|
||||||
|
try {
|
||||||
|
symbol = eval_expr(symbol, env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symbol->type != Ast_Node_Type_Symbol) {
|
||||||
|
report_error(Error_Type_Type_Missmatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* value = arguments->value.pair->rest->value.pair->first;
|
||||||
|
try {
|
||||||
|
value = eval_expr(value, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
define_symbol(symbol, value, env);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
defun("macro-define", cLambda {
|
||||||
|
try {
|
||||||
|
arguments_length = list_length(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length != 2) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* symbol = arguments->value.pair->first;
|
||||||
|
|
||||||
|
if (symbol->type == Ast_Node_Type_Pair) {
|
||||||
|
try {
|
||||||
|
symbol = eval_expr(symbol, env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symbol->type != Ast_Node_Type_Symbol) {
|
||||||
|
report_error(Error_Type_Type_Missmatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* value = arguments->value.pair->rest->value.pair->first;
|
||||||
|
try {
|
||||||
|
value = eval_expr(value, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
define_macro_symbol(symbol, value, env);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
defun("mutate", cLambda {
|
||||||
|
try {
|
||||||
|
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length != 2)
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
|
||||||
|
if (evaluated_arguments->value.pair->first->type == Ast_Node_Type_Nil ||
|
||||||
|
evaluated_arguments->value.pair->first->type == Ast_Node_Type_Keyword)
|
||||||
|
{
|
||||||
|
report_error(Error_Type_Type_Missmatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* target = evaluated_arguments->value.pair->first;
|
||||||
|
Ast_Node* source = evaluated_arguments->value.pair->rest->value.pair->first;
|
||||||
|
|
||||||
|
*target = *source;
|
||||||
|
return target;
|
||||||
|
});
|
||||||
|
defun("if", cLambda {
|
||||||
|
try {
|
||||||
|
arguments_length = list_length(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length != 2 && arguments_length != 3) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* condition = arguments->value.pair->first;
|
||||||
|
Ast_Node* then_part = arguments->value.pair->rest;
|
||||||
|
Ast_Node* else_part = then_part->value.pair->rest;
|
||||||
|
|
||||||
|
bool truthy;
|
||||||
|
try {
|
||||||
|
truthy = is_truthy(condition, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* result;
|
||||||
|
|
||||||
|
if (truthy)
|
||||||
|
try{
|
||||||
|
result = eval_expr(then_part->value.pair->first, env);
|
||||||
|
}
|
||||||
|
else if (arguments_length == 3)
|
||||||
|
try {
|
||||||
|
result = eval_expr(else_part->value.pair->first, env);
|
||||||
|
}
|
||||||
|
else return create_ast_node_nil();
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
defun("quote", cLambda {
|
||||||
|
try {
|
||||||
|
arguments_length = list_length(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length != 1) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arguments->value.pair->first;
|
||||||
|
});
|
||||||
|
defun("and", cLambda {
|
||||||
|
bool result = true;
|
||||||
|
while (arguments->type != Ast_Node_Type_Nil) {
|
||||||
|
if (arguments->type != Ast_Node_Type_Pair) {
|
||||||
|
report_error(Error_Type_Ill_Formed_List);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
result &= is_truthy(arguments->value.pair->first, env);
|
||||||
|
}
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
|
||||||
|
if (!result) return create_ast_node_nil();
|
||||||
|
}
|
||||||
|
|
||||||
|
return create_ast_node_t();
|
||||||
|
});
|
||||||
|
defun("or", cLambda {
|
||||||
|
bool result = false;
|
||||||
|
while (arguments->type != Ast_Node_Type_Nil) {
|
||||||
|
if (arguments->type != Ast_Node_Type_Pair) {
|
||||||
|
report_error(Error_Type_Ill_Formed_List);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
result |= is_truthy(arguments->value.pair->first, env);
|
||||||
|
}
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
|
||||||
|
if (result) return create_ast_node_t();
|
||||||
|
}
|
||||||
|
|
||||||
|
return create_ast_node_nil();
|
||||||
|
});
|
||||||
|
defun("not", cLambda {
|
||||||
|
try {
|
||||||
|
arguments_length = list_length(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length != 1) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
bool truthy;
|
||||||
|
try {
|
||||||
|
truthy = is_truthy(arguments->value.pair->first, env);
|
||||||
|
}
|
||||||
|
if (truthy)
|
||||||
|
return create_ast_node_nil();
|
||||||
|
return create_ast_node_t();
|
||||||
|
});
|
||||||
|
defun("while", cLambda {
|
||||||
|
try {
|
||||||
|
arguments_length = list_length(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length < 2) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
Ast_Node* condition_part = arguments->value.pair->first;
|
||||||
|
Ast_Node* condition;
|
||||||
|
Ast_Node* then_part = arguments->value.pair->rest;
|
||||||
|
Ast_Node* result = create_ast_node_nil();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
condition = eval_expr(condition_part, env);
|
||||||
|
}
|
||||||
|
if (condition->type == Ast_Node_Type_Nil) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
result = eval_expr(then_part->value.pair->first, env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
|
||||||
|
});
|
||||||
|
defun("let", cLambda {
|
||||||
|
// (let ((a 10)(b 20)) (body1) (body2))
|
||||||
|
try {
|
||||||
|
arguments_length = list_length(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length < 1)
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
|
||||||
|
Environment* let_env = create_child_environment(env, Environment_Type_Let);
|
||||||
|
Ast_Node* bindings = arguments->value.pair->first;
|
||||||
|
while (true) {
|
||||||
|
if (bindings->type == Ast_Node_Type_Nil) {
|
||||||
|
break;
|
||||||
|
} else if (bindings->type != Ast_Node_Type_Pair) {
|
||||||
|
report_error(Error_Type_Ill_Formed_Arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* sym = bindings->value.pair->first->value.pair->first;
|
||||||
|
if(sym->type != Ast_Node_Type_Symbol) {
|
||||||
|
report_error(Error_Type_Ill_Formed_Arguments);
|
||||||
|
}
|
||||||
|
Ast_Node* rest_sym = bindings->value.pair->first->value.pair->rest;
|
||||||
|
if (rest_sym->type != Ast_Node_Type_Pair) {
|
||||||
|
report_error(Error_Type_Ill_Formed_Arguments);
|
||||||
|
}
|
||||||
|
if (rest_sym->value.pair->rest->type != Ast_Node_Type_Nil) {
|
||||||
|
report_error(Error_Type_Ill_Formed_Arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* value = eval_expr(rest_sym->value.pair->first, env);
|
||||||
|
|
||||||
|
define_symbol(sym, value, let_env);
|
||||||
|
|
||||||
|
bindings = bindings->value.pair->rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
|
||||||
|
Ast_Node* evaluated_arguments;
|
||||||
|
try {
|
||||||
|
evaluated_arguments = eval_arguments(arguments, let_env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evaluated_arguments->type == Ast_Node_Type_Nil)
|
||||||
|
return evaluated_arguments;
|
||||||
|
|
||||||
|
// skip to the last evaluated operand and return it,
|
||||||
|
// we use eval_arguments here instead of doing it
|
||||||
|
// manually, because we want to increase code reuse,
|
||||||
|
// but at the cost that we have to find the end of the
|
||||||
|
// list again
|
||||||
|
while (evaluated_arguments->value.pair->rest->type == Ast_Node_Type_Pair) {
|
||||||
|
evaluated_arguments = evaluated_arguments->value.pair->rest;
|
||||||
|
}
|
||||||
|
return evaluated_arguments->value.pair->first;
|
||||||
|
});
|
||||||
|
defun("lambda", cLambda {
|
||||||
|
/*
|
||||||
|
* (lambda ())
|
||||||
|
* (lambda (x d) (+ 1 2) (- 1 2) (* 1 2))
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
arguments_length = list_length(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length == 0)
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
|
||||||
|
Function* function = new(Function);
|
||||||
|
/* if (lispOperator->value.built_in_function->type == Built_In_Macro) { */
|
||||||
|
/* function->is_macro = true; */
|
||||||
|
/* } else { */
|
||||||
|
function->is_macro = false;
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
// if parameters were specified
|
||||||
|
if (arguments->value.pair->first->type != Ast_Node_Type_Nil) {
|
||||||
|
try {
|
||||||
|
assert_type(arguments->value.pair->first, Ast_Node_Type_Pair);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
parse_argument_list(arguments->value.pair->first, function);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
function->positional_arguments = create_positional_argument_list(1);
|
||||||
|
function->keyword_arguments = create_keyword_argument_list(1);
|
||||||
|
function->rest_argument = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
// if there is a docstring, use it
|
||||||
|
if (arguments->value.pair->first->type == Ast_Node_Type_String) {
|
||||||
|
function->docstring = arguments->value.pair->first->value.string->value;
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
} else {
|
||||||
|
function->docstring = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we are now in the function body, just wrap it in an
|
||||||
|
// implicit prog
|
||||||
|
function->body = create_ast_node_pair(
|
||||||
|
create_ast_node_symbol("prog"),
|
||||||
|
arguments);
|
||||||
|
|
||||||
|
Ast_Node* ret = new(Ast_Node);
|
||||||
|
ret->type = Ast_Node_Type_Function;
|
||||||
|
ret->value.function = function;
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
defun("macro", cLambda {
|
||||||
|
/*
|
||||||
|
* (macro ())
|
||||||
|
* (macro (x d) (+ 1 2) (- 1 2) (* 1 2))
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
arguments_length = list_length(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length == 0)
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
|
||||||
|
Function* function = new(Function);
|
||||||
|
function->is_macro = true;
|
||||||
|
|
||||||
|
// if parameters were specified
|
||||||
|
if (arguments->value.pair->first->type != Ast_Node_Type_Nil) {
|
||||||
|
try {
|
||||||
|
assert_type(arguments->value.pair->first, Ast_Node_Type_Pair);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
parse_argument_list(arguments->value.pair->first, function);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
function->positional_arguments = create_positional_argument_list(1);
|
||||||
|
function->keyword_arguments = create_keyword_argument_list(1);
|
||||||
|
function->rest_argument = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
// if there is a docstring, use it
|
||||||
|
if (arguments->value.pair->first->type == Ast_Node_Type_String) {
|
||||||
|
function->docstring = arguments->value.pair->first->value.string->value;
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
} else {
|
||||||
|
function->docstring = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we are now in the function body, just wrap it in an
|
||||||
|
// implicit prog
|
||||||
|
function->body = create_ast_node_pair(
|
||||||
|
create_ast_node_symbol("prog"),
|
||||||
|
arguments);
|
||||||
|
|
||||||
|
Ast_Node* ret = new(Ast_Node);
|
||||||
|
ret->type = Ast_Node_Type_Function;
|
||||||
|
ret->value.function = function;
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
defun("eval", cLambda {
|
||||||
|
try {
|
||||||
|
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
if (arguments_length != 1) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
Ast_Node* result;
|
||||||
|
try {
|
||||||
|
result = eval_expr(evaluated_arguments->value.pair->first, env);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
defun("prog", cLambda {
|
||||||
|
try {
|
||||||
|
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evaluated_arguments->type == Ast_Node_Type_Nil)
|
||||||
|
return evaluated_arguments;
|
||||||
|
|
||||||
|
// skip to the last evaluated operand and return it,
|
||||||
|
// we use eval_arguments here instead of doing it
|
||||||
|
// manually, because we want to increase code reuse,
|
||||||
|
// but at the cost that we have to find the end of the
|
||||||
|
// list again
|
||||||
|
while (evaluated_arguments->value.pair->rest->type == Ast_Node_Type_Pair) {
|
||||||
|
evaluated_arguments = evaluated_arguments->value.pair->rest;
|
||||||
|
}
|
||||||
|
return evaluated_arguments->value.pair->first;
|
||||||
|
});
|
||||||
|
defun("list", cLambda {
|
||||||
|
try {
|
||||||
|
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
return evaluated_arguments;
|
||||||
|
});
|
||||||
|
defun("pair", cLambda {
|
||||||
|
try {
|
||||||
|
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BUG(Felix): Why is arguments_length for '(1 (2)) == 3 and not 2??
|
||||||
|
// BUG(Felix): Why is arguments_length for '(1 (2)) == 3 and not 2??
|
||||||
|
// BUG(Felix): Why is arguments_length for '(1 (2)) == 3 and not 2??
|
||||||
|
/* if (arguments_length != 2) { */
|
||||||
|
if (list_length(evaluated_arguments) != 2) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
return create_ast_node_pair(evaluated_arguments->value.pair->first, evaluated_arguments->value.pair->rest->value.pair->first);
|
||||||
|
});
|
||||||
|
defun("first", cLambda {
|
||||||
|
try {
|
||||||
|
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
if (arguments_length != 1) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
if (evaluated_arguments->value.pair->first->type == Ast_Node_Type_Nil)
|
||||||
|
return create_ast_node_nil();
|
||||||
|
if (evaluated_arguments->value.pair->first->type != Ast_Node_Type_Pair)
|
||||||
|
report_error(Error_Type_Type_Missmatch);
|
||||||
|
|
||||||
|
return evaluated_arguments->value.pair->first->value.pair->first;
|
||||||
|
});
|
||||||
|
defun("rest", cLambda {
|
||||||
|
try {
|
||||||
|
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
if (arguments_length != 1) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
if (evaluated_arguments->value.pair->first->type == Ast_Node_Type_Nil)
|
||||||
|
return create_ast_node_nil();
|
||||||
|
if (evaluated_arguments->value.pair->first->type != Ast_Node_Type_Pair)
|
||||||
|
report_error(Error_Type_Type_Missmatch);
|
||||||
|
|
||||||
|
return evaluated_arguments->value.pair->first->value.pair->rest;
|
||||||
|
});
|
||||||
|
defun("type", cLambda {
|
||||||
|
try {
|
||||||
|
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
if (arguments_length != 1) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node_Type type = evaluated_arguments->value.pair->first->type;
|
||||||
|
switch (type) {
|
||||||
|
case Ast_Node_Type_CFunction: return create_ast_node_keyword("cfunction");
|
||||||
|
case Ast_Node_Type_Function: {
|
||||||
|
if (evaluated_arguments->value.pair->first->value.function->is_macro)
|
||||||
|
return create_ast_node_keyword("dynamic-macro");
|
||||||
|
return create_ast_node_keyword("dynamic-function");
|
||||||
|
}
|
||||||
|
case Ast_Node_Type_Keyword: return create_ast_node_keyword("keyword");
|
||||||
|
case Ast_Node_Type_Nil: return create_ast_node_keyword("nil");
|
||||||
|
case Ast_Node_Type_T: return create_ast_node_keyword("t");
|
||||||
|
case Ast_Node_Type_Number: return create_ast_node_keyword("number");
|
||||||
|
case Ast_Node_Type_Pair: return create_ast_node_keyword("pair");
|
||||||
|
case Ast_Node_Type_String: return create_ast_node_keyword("string");
|
||||||
|
case Ast_Node_Type_Symbol: return create_ast_node_keyword("symbol");
|
||||||
|
}
|
||||||
|
return create_ast_node_keyword("unknown");
|
||||||
|
});
|
||||||
|
defun("info", cLambda {
|
||||||
|
try {
|
||||||
|
arguments_length = list_length(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length != 1) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
print(arguments->value.pair->first);
|
||||||
|
|
||||||
|
Ast_Node* type = eval_expr(
|
||||||
|
create_ast_node_pair(
|
||||||
|
create_ast_node_symbol("type"),
|
||||||
|
create_ast_node_pair(arguments->value.pair->first, create_ast_node_nil())),
|
||||||
|
env);
|
||||||
|
|
||||||
|
if (type) {
|
||||||
|
printf(" is of type ");
|
||||||
|
print(type);
|
||||||
|
printf("\n");
|
||||||
|
// just make sure type was not redefined and
|
||||||
|
// returns something that is not a keyword
|
||||||
|
if (type->type == Ast_Node_Type_Keyword &&
|
||||||
|
(string_equal(type->value.keyword->identifier, "dynamic-function") ||
|
||||||
|
string_equal(type->value.keyword->identifier, "dynamic-macro")))
|
||||||
|
{
|
||||||
|
Ast_Node* fun = eval_expr(arguments->value.pair->first, env);
|
||||||
|
printf("\nMacro? %s\n", (fun->value.function->is_macro) ? "yes" : "no");
|
||||||
|
if (fun->value.function->docstring)
|
||||||
|
printf("Docstring:\n==========\n%s\n\n", fun->value.function->docstring);
|
||||||
|
else
|
||||||
|
printf("No docstring avaliable\n");
|
||||||
|
|
||||||
|
printf("Arguments:\n==========\n");
|
||||||
|
printf("Postitional: {");
|
||||||
|
if (fun->value.function->positional_arguments->next_index != 0) {
|
||||||
|
printf("%s", fun->value.function->positional_arguments->identifiers[0]);
|
||||||
|
for (int i = 1; i < fun->value.function->positional_arguments->next_index; ++i) {
|
||||||
|
printf(", %s", fun->value.function->positional_arguments->identifiers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("}\n");
|
||||||
|
printf("Keyword: {");
|
||||||
|
if (fun->value.function->keyword_arguments->next_index != 0) {
|
||||||
|
printf("%s", fun->value.function->keyword_arguments->identifiers[0]);
|
||||||
|
if (fun->value.function->keyword_arguments->values->data[0]) {
|
||||||
|
printf(" (");
|
||||||
|
print(fun->value.function->keyword_arguments->values->data[0]);
|
||||||
|
printf(")");
|
||||||
|
}
|
||||||
|
for (int i = 1; i < fun->value.function->keyword_arguments->next_index; ++i) {
|
||||||
|
printf(", %s", fun->value.function->keyword_arguments->identifiers[i]);
|
||||||
|
if (fun->value.function->keyword_arguments->values->data[i]) {
|
||||||
|
printf(" (");
|
||||||
|
print(fun->value.function->keyword_arguments->values->data[i]);
|
||||||
|
printf(")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("}\n");
|
||||||
|
printf("Rest: {");
|
||||||
|
if (fun->value.function->rest_argument)
|
||||||
|
printf("%s", fun->value.function->rest_argument);
|
||||||
|
printf("}\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf(" is not defined\n");
|
||||||
|
delete_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
return create_ast_node_nil();
|
||||||
|
});
|
||||||
|
defun("print", cLambda {
|
||||||
|
try {
|
||||||
|
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
if (arguments_length != 1) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
print(evaluated_arguments->value.pair->first);
|
||||||
|
printf("\n");
|
||||||
|
return create_ast_node_nil();
|
||||||
|
});
|
||||||
|
defun("read", cLambda {
|
||||||
|
try {
|
||||||
|
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length > 1) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length == 1) {
|
||||||
|
Ast_Node* prompt = evaluated_arguments->value.pair->first;
|
||||||
|
/* if (prompt->type == Ast_Node_Type_String) */
|
||||||
|
/* printf("%s", prompt->value.string->value); */
|
||||||
|
/* else */
|
||||||
|
print(evaluated_arguments->value.pair->first);
|
||||||
|
}
|
||||||
|
char* line = read_line();
|
||||||
|
return create_ast_node_string(line, (int)strlen(line));
|
||||||
|
});
|
||||||
|
defun("exit", cLambda {
|
||||||
|
try {
|
||||||
|
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length > 1) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length == 1) {
|
||||||
|
Ast_Node* error_code = evaluated_arguments->value.pair->first;
|
||||||
|
if (error_code->type != Ast_Node_Type_Number)
|
||||||
|
report_error(Error_Type_Type_Missmatch);
|
||||||
|
|
||||||
|
exit((int)error_code->value.number->value);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
});
|
||||||
|
defun("break", cLambda {
|
||||||
|
print_environment(env);
|
||||||
|
#ifdef _DEBUG
|
||||||
|
__debugbreak();
|
||||||
|
#endif
|
||||||
|
return create_ast_node_nil();
|
||||||
|
});
|
||||||
|
defun("try", cLambda {
|
||||||
|
try {
|
||||||
|
arguments_length = list_length(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length != 2) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* try_part = arguments->value.pair->first;
|
||||||
|
Ast_Node* catch_part = arguments->value.pair->rest->value.pair->first;
|
||||||
|
Ast_Node* result;
|
||||||
|
|
||||||
|
result = eval_expr(try_part, env);
|
||||||
|
if (error) {
|
||||||
|
delete_error();
|
||||||
|
try {
|
||||||
|
result = eval_expr(catch_part, env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
defun("load", cLambda {
|
||||||
|
try {
|
||||||
|
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length != 1)
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
|
||||||
|
if (evaluated_arguments->value.pair->first->type != Ast_Node_Type_String)
|
||||||
|
report_error(Error_Type_Type_Missmatch);
|
||||||
|
|
||||||
|
Ast_Node* result;
|
||||||
|
try {
|
||||||
|
result = built_in_load(
|
||||||
|
evaluated_arguments->value.pair->first->value.string->value, env);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
|
||||||
|
});
|
||||||
|
defun("copy", cLambda {
|
||||||
|
try {
|
||||||
|
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_length != 1)
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
|
||||||
|
if (evaluated_arguments->value.pair->first->type == Ast_Node_Type_Nil ||
|
||||||
|
evaluated_arguments->value.pair->first->type == Ast_Node_Type_Keyword)
|
||||||
|
{
|
||||||
|
report_error(Error_Type_Type_Missmatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* target = new(Ast_Node);
|
||||||
|
Ast_Node* source = evaluated_arguments->value.pair->first;
|
||||||
|
|
||||||
|
*target = *source;
|
||||||
|
return target;
|
||||||
|
});
|
||||||
|
defun("error", cLambda {
|
||||||
|
try {
|
||||||
|
evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
if (arguments_length != 0) {
|
||||||
|
report_error(Error_Type_Wrong_Number_Of_Arguments);
|
||||||
|
}
|
||||||
|
report_error(Error_Type_Unknown_Error);
|
||||||
|
});
|
||||||
|
|
||||||
|
#undef report_error
|
||||||
|
#undef cLambda
|
||||||
|
}
|
||||||
|
|
||||||
|
Environment* create_built_ins_environment() {
|
||||||
|
Environment* ret = create_child_environment(nullptr, Environment_Type_Let);
|
||||||
|
load_built_ins_into_environment(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -144,10 +144,13 @@ Ast_Node* lookup_symbol(Ast_Node* node, Environment* env) {
|
|||||||
return create_ast_node_t();
|
return create_ast_node_t();
|
||||||
}
|
}
|
||||||
|
|
||||||
result = create_ast_node_built_in_function(sym->identifier);
|
// we should not need this anymore when we have c lambdas as built
|
||||||
result->sourceCodeLocation = node->sourceCodeLocation;
|
// in functions
|
||||||
if (result)
|
|
||||||
return result;
|
/* result = create_ast_node_built_in_function(sym->identifier); */
|
||||||
|
/* result->sourceCodeLocation = node->sourceCodeLocation; */
|
||||||
|
/* if (result) */
|
||||||
|
/* return result; */
|
||||||
|
|
||||||
create_error(Error_Type_Symbol_Not_Defined, node->sourceCodeLocation);
|
create_error(Error_Type_Symbol_Not_Defined, node->sourceCodeLocation);
|
||||||
/* printf("%s\n", sym->identifier); */
|
/* printf("%s\n", sym->identifier); */
|
||||||
@@ -15,7 +15,7 @@ typedef enum {
|
|||||||
Error_Type_Wrong_Number_Of_Arguments,
|
Error_Type_Wrong_Number_Of_Arguments,
|
||||||
} Error_Type;
|
} Error_Type;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct Error_t {
|
||||||
Error_Type type;
|
Error_Type type;
|
||||||
Source_Code_Location* location;
|
Source_Code_Location* location;
|
||||||
} Error;
|
} Error;
|
||||||
@@ -31,7 +31,7 @@ void delete_error() {
|
|||||||
|
|
||||||
void create_error(Error_Type type, Source_Code_Location* location) {
|
void create_error(Error_Type type, Source_Code_Location* location) {
|
||||||
delete_error();
|
delete_error();
|
||||||
|
__debugbreak();
|
||||||
error = new(Error);
|
error = new(Error);
|
||||||
error->type = type;
|
error->type = type;
|
||||||
error->location = location;
|
error->location = location;
|
||||||
-1015
File diff suppressed because it is too large
Load Diff
+444
@@ -0,0 +1,444 @@
|
|||||||
|
Ast_Node* eval_expr(Ast_Node* node, Environment* env);
|
||||||
|
|
||||||
|
Ast_Node* apply_arguments_to_function(Ast_Node* arguments, Function* function, Environment* parent) {
|
||||||
|
// NOTE(Felix): if it is a macro, we will set it later, so we can
|
||||||
|
// use the default "define_symbol" method here instead of
|
||||||
|
// switching between "define_symbol" and "define_macro_symbol" all
|
||||||
|
// the time
|
||||||
|
|
||||||
|
Environment* new_env = create_child_environment(parent, Environment_Type_Lambda);
|
||||||
|
|
||||||
|
// positional arguments
|
||||||
|
for (int i = 0; i < function->positional_arguments->next_index; ++i) {
|
||||||
|
if (arguments->type == Ast_Node_Type_Pair) {
|
||||||
|
// TODO(Felix): here we create new ast_node_symbols from
|
||||||
|
// their identifiers but before we converted them to
|
||||||
|
// strings from symbols... Wo maybe just use the symbols?
|
||||||
|
define_symbol(
|
||||||
|
create_ast_node_symbol(function->positional_arguments->identifiers[i]),
|
||||||
|
arguments->value.pair->first, new_env);
|
||||||
|
} else {
|
||||||
|
// not enough arguments given
|
||||||
|
create_error(Error_Type_Ill_Formed_Arguments, arguments->sourceCodeLocation);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments->type == Ast_Node_Type_Nil)
|
||||||
|
goto eval_time;
|
||||||
|
|
||||||
|
String_Array_List* read_in_keywords = create_String_array_list(16);
|
||||||
|
// keyword arguments: use all given ones and keep track of the
|
||||||
|
// added ones (array list), if end of parameters in encountered or
|
||||||
|
// something that is not a keyword is encountered or a keyword
|
||||||
|
// that is not recognized is encoutered, jump out of the loop.
|
||||||
|
|
||||||
|
while (arguments->value.pair->first->type == Ast_Node_Type_Keyword) {
|
||||||
|
// check if this one is even an accepted keyword
|
||||||
|
bool accepted = false;
|
||||||
|
for (int i = 0; i < function->keyword_arguments->next_index; ++i) {
|
||||||
|
if (string_equal(
|
||||||
|
arguments->value.pair->first->value.keyword->identifier,
|
||||||
|
function->keyword_arguments->identifiers[i]))
|
||||||
|
{
|
||||||
|
accepted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!accepted) {
|
||||||
|
create_error(Error_Type_Ill_Formed_Arguments, arguments->sourceCodeLocation);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if it was already read in
|
||||||
|
for (int i = 0; i < read_in_keywords->next_index; ++i) {
|
||||||
|
if (string_equal(
|
||||||
|
arguments->value.pair->first->value.keyword->identifier,
|
||||||
|
read_in_keywords->data[i]))
|
||||||
|
{
|
||||||
|
// TODO(Felix): if we are actually done with all the
|
||||||
|
// necessary keywords then we have to count the rest
|
||||||
|
// as :rest here, instead od always creating an error
|
||||||
|
// (special case with default variables)
|
||||||
|
create_error(Error_Type_Ill_Formed_Arguments, arguments->sourceCodeLocation);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// okay so we found a keyword that has to be read in and was
|
||||||
|
// not already read in, is there a next element to actually
|
||||||
|
// set it to?
|
||||||
|
if (arguments->value.pair->rest->type != Ast_Node_Type_Pair) {
|
||||||
|
create_error(Error_Type_Ill_Formed_Arguments, arguments->sourceCodeLocation);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not set it and then add it to the array list
|
||||||
|
define_symbol(
|
||||||
|
create_ast_node_symbol(arguments->value.pair->first->value.keyword->identifier),
|
||||||
|
arguments->value.pair->rest->value.pair->first,
|
||||||
|
new_env);
|
||||||
|
|
||||||
|
append_to_String_array_list(read_in_keywords, arguments->value.pair->first->value.keyword->identifier);
|
||||||
|
|
||||||
|
// overstep both for next one
|
||||||
|
arguments = arguments->value.pair->rest->value.pair->rest;
|
||||||
|
|
||||||
|
if (arguments->type == Ast_Node_Type_Nil) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// check if all necessary keywords have been read in
|
||||||
|
for (int i = 0; i < function->keyword_arguments->next_index; ++i) {
|
||||||
|
char* defined_keyword = function->keyword_arguments->identifiers[i];
|
||||||
|
bool was_set = false;
|
||||||
|
for (int j = 0; j < read_in_keywords->next_index; ++j) {
|
||||||
|
if (string_equal(
|
||||||
|
read_in_keywords->data[j],
|
||||||
|
defined_keyword))
|
||||||
|
{
|
||||||
|
was_set = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (function->keyword_arguments->values->data[i] == nullptr) {
|
||||||
|
// if this one does not have a default value
|
||||||
|
if (!was_set) {
|
||||||
|
create_error(Error_Type_Ill_Formed_Arguments, arguments->sourceCodeLocation);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// this one does have a default value, lets see if we have
|
||||||
|
// to use it or if the user supplied his own
|
||||||
|
if (!was_set) {
|
||||||
|
define_symbol(
|
||||||
|
create_ast_node_symbol(defined_keyword),
|
||||||
|
copy_ast_node(function->keyword_arguments->values->data[i]), new_env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (arguments->type == Ast_Node_Type_Nil) {
|
||||||
|
if (function->rest_argument) {
|
||||||
|
define_symbol(
|
||||||
|
create_ast_node_symbol(function->rest_argument),
|
||||||
|
create_ast_node_nil(), new_env);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (function->rest_argument) {
|
||||||
|
define_symbol(
|
||||||
|
create_ast_node_symbol(function->rest_argument),
|
||||||
|
arguments, new_env);
|
||||||
|
} else {
|
||||||
|
// rest was not declared but additional arguments were found
|
||||||
|
create_error(Error_Type_Ill_Formed_Arguments, arguments->sourceCodeLocation);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eval_time: {
|
||||||
|
Ast_Node* result;
|
||||||
|
|
||||||
|
// don't have to check every time if it is macro environment or
|
||||||
|
// not
|
||||||
|
if (function->is_macro)
|
||||||
|
new_env->type = Environment_Type_Macro;
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = eval_expr(function->body, new_env);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
(prog
|
||||||
|
(define type--before type)
|
||||||
|
(define type
|
||||||
|
(lambda (e)
|
||||||
|
(if (and (= (type--before e) :pair) (= (first e) :my-type))
|
||||||
|
:my-type
|
||||||
|
(type--before e))))
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
This parses the argument specification of funcitons into their
|
||||||
|
Function struct. It dois this by allocating new
|
||||||
|
positional_arguments, keyword_arguments and rest_argument and
|
||||||
|
filling it in
|
||||||
|
*/
|
||||||
|
void parse_argument_list(Ast_Node* arguments, Function* function) {
|
||||||
|
// first init the fields
|
||||||
|
function->positional_arguments = create_positional_argument_list(16);
|
||||||
|
function->keyword_arguments = create_keyword_argument_list(16);
|
||||||
|
function->rest_argument = nullptr;
|
||||||
|
|
||||||
|
// okay let's try to read some positional arguments
|
||||||
|
while (arguments->type == Ast_Node_Type_Pair) {
|
||||||
|
if (arguments->value.pair->first->type == Ast_Node_Type_Keyword) {
|
||||||
|
if (string_equal(arguments->value.pair->first->value.keyword->identifier, "keys") ||
|
||||||
|
string_equal(arguments->value.pair->first->value.keyword->identifier, "rest"))
|
||||||
|
break;
|
||||||
|
else {
|
||||||
|
create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments->value.pair->first->type != Ast_Node_Type_Symbol) {
|
||||||
|
create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// okay wow we found an actual symbol
|
||||||
|
append_to_positional_argument_list(
|
||||||
|
function->positional_arguments,
|
||||||
|
arguments->value.pair->first->value.symbol->identifier);
|
||||||
|
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// okay we are done with positional arguments, lets check for
|
||||||
|
// keywords,
|
||||||
|
if (arguments->type != Ast_Node_Type_Pair) {
|
||||||
|
if (arguments->type != Ast_Node_Type_Nil)
|
||||||
|
create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments->value.pair->first->type == Ast_Node_Type_Keyword &&
|
||||||
|
string_equal(arguments->value.pair->first->value.keyword->identifier, "keys"))
|
||||||
|
{
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
if (arguments->type != Ast_Node_Type_Pair ||
|
||||||
|
arguments->value.pair->first->type != Ast_Node_Type_Symbol)
|
||||||
|
{
|
||||||
|
create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (arguments->type == Ast_Node_Type_Pair) {
|
||||||
|
if (arguments->value.pair->first->type == Ast_Node_Type_Keyword) {
|
||||||
|
if (string_equal(arguments->value.pair->first->value.keyword->identifier, "rest"))
|
||||||
|
break;
|
||||||
|
else {
|
||||||
|
create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments->value.pair->first->type != Ast_Node_Type_Symbol) {
|
||||||
|
create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we found a symbol (arguments->value.pair->first) for
|
||||||
|
// the keyword args! Let's check if the next arguement is
|
||||||
|
// :defaults-to
|
||||||
|
Ast_Node* next = arguments->value.pair->rest;
|
||||||
|
if (next->type == Ast_Node_Type_Pair &&
|
||||||
|
next->value.pair->first->type == Ast_Node_Type_Keyword &&
|
||||||
|
string_equal(next->value.pair->first->value.keyword->identifier,
|
||||||
|
"defaults-to"))
|
||||||
|
{
|
||||||
|
// check if there is a next argument too, otherwise it
|
||||||
|
// would be an error
|
||||||
|
next = next->value.pair->rest;
|
||||||
|
if (next->type == Ast_Node_Type_Pair) {
|
||||||
|
append_to_keyword_argument_list(function->keyword_arguments,
|
||||||
|
arguments->value.pair->first->value.symbol->identifier,
|
||||||
|
next->value.pair->first);
|
||||||
|
arguments = next->value.pair->rest;
|
||||||
|
} else {
|
||||||
|
create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No :defaults-to, so just add it to the list
|
||||||
|
append_to_keyword_argument_list(function->keyword_arguments,
|
||||||
|
arguments->value.pair->first->value.symbol->identifier,
|
||||||
|
nullptr);
|
||||||
|
arguments = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Now we are also done with keyword arguments, lets check for
|
||||||
|
// if there is a rest argument
|
||||||
|
if (arguments->type != Ast_Node_Type_Pair) {
|
||||||
|
if (arguments->type != Ast_Node_Type_Nil)
|
||||||
|
create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments->value.pair->first->type == Ast_Node_Type_Keyword &&
|
||||||
|
string_equal(arguments->value.pair->first->value.keyword->identifier, "rest"))
|
||||||
|
{
|
||||||
|
arguments = arguments->value.pair->rest;
|
||||||
|
if (arguments->type != Ast_Node_Type_Pair ||
|
||||||
|
arguments->value.pair->first->type != Ast_Node_Type_Symbol)
|
||||||
|
{
|
||||||
|
create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
function->rest_argument = arguments->value.pair->first->value.symbol->identifier;
|
||||||
|
if (arguments->value.pair->rest->type != Ast_Node_Type_Nil) {
|
||||||
|
create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("this should not happen?");
|
||||||
|
create_error(Error_Type_Unknown_Error, arguments->sourceCodeLocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int list_length(Ast_Node* node) {
|
||||||
|
if (node->type == Ast_Node_Type_Nil)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (node->type != Ast_Node_Type_Pair) {
|
||||||
|
create_error(Error_Type_Type_Missmatch, node->sourceCodeLocation);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = 0;
|
||||||
|
while (node->type == Ast_Node_Type_Pair) {
|
||||||
|
++len;
|
||||||
|
node = node->value.pair->rest;
|
||||||
|
if (node->type == Ast_Node_Type_Nil)
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
create_error(Error_Type_Ill_Formed_List, node->sourceCodeLocation);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_truthy (Ast_Node* expression, Environment* env);
|
||||||
|
|
||||||
|
Ast_Node* extract_keyword_value(char* keyword, Parsed_Arguments* args) {
|
||||||
|
// NOTE(Felix): This will be a hashmap lookup later
|
||||||
|
for (int i = 0; i < args->keyword_keys->next_index; ++i) {
|
||||||
|
if (string_equal(args->keyword_keys->data[i]->value.keyword->identifier, keyword))
|
||||||
|
return args->keyword_values->data[i];
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* eval_arguments(Ast_Node* arguments, Environment* env, int *out_arguments_length) {
|
||||||
|
*out_arguments_length = 0;
|
||||||
|
if (arguments->type == Ast_Node_Type_Nil) {
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* evaluated_arguments = create_ast_node_pair(nullptr, nullptr);
|
||||||
|
Ast_Node* evaluated_arguments_head = evaluated_arguments;
|
||||||
|
Ast_Node* current_head = arguments;
|
||||||
|
while (current_head->type == Ast_Node_Type_Pair) {
|
||||||
|
try {
|
||||||
|
evaluated_arguments_head->value.pair->first =
|
||||||
|
eval_expr(current_head->value.pair->first, env);
|
||||||
|
}
|
||||||
|
current_head = current_head->value.pair->rest;
|
||||||
|
|
||||||
|
if (current_head->type == Ast_Node_Type_Pair) {
|
||||||
|
evaluated_arguments_head->value.pair->rest = create_ast_node_pair(nullptr, nullptr);
|
||||||
|
evaluated_arguments_head = evaluated_arguments_head->value.pair->rest;
|
||||||
|
} else if (current_head->type == Ast_Node_Type_Nil) {
|
||||||
|
evaluated_arguments_head->value.pair->rest = current_head;
|
||||||
|
} else {
|
||||||
|
create_error(Error_Type_Ill_Formed_Arguments, arguments->sourceCodeLocation);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
++(*out_arguments_length);
|
||||||
|
}
|
||||||
|
return evaluated_arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
|
||||||
|
#define report_error(_type) { \
|
||||||
|
create_error(_type, node->sourceCodeLocation); \
|
||||||
|
return nullptr; \
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
Ast_Node* ret = new(Ast_Node);
|
||||||
|
switch (node->type) {
|
||||||
|
case Ast_Node_Type_T:
|
||||||
|
case Ast_Node_Type_Nil:
|
||||||
|
return node;
|
||||||
|
case Ast_Node_Type_Symbol: {
|
||||||
|
Ast_Node* symbol;
|
||||||
|
try {
|
||||||
|
symbol = lookup_symbol(node, env);
|
||||||
|
}
|
||||||
|
return symbol;
|
||||||
|
}
|
||||||
|
case Ast_Node_Type_Number:
|
||||||
|
case Ast_Node_Type_Keyword:
|
||||||
|
case Ast_Node_Type_String:
|
||||||
|
return node;
|
||||||
|
case Ast_Node_Type_Pair: {
|
||||||
|
Ast_Node* lispOperator;
|
||||||
|
if (node->value.pair->first->type != Ast_Node_Type_CFunction &&
|
||||||
|
node->value.pair->first->type != Ast_Node_Type_Function)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
lispOperator = eval_expr(node->value.pair->first, env);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lispOperator = node->value.pair->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* arguments = node->value.pair->rest;
|
||||||
|
int arguments_length;
|
||||||
|
|
||||||
|
// check for c function
|
||||||
|
if (lispOperator->type == Ast_Node_Type_CFunction) {
|
||||||
|
Ast_Node* result = lispOperator->value.cfunction->function(arguments, env);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for list function
|
||||||
|
if (lispOperator->type == Ast_Node_Type_Function) {
|
||||||
|
if (!lispOperator->value.function->is_macro) {
|
||||||
|
try {
|
||||||
|
arguments = eval_arguments(arguments, env, &arguments_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast_Node* result;
|
||||||
|
try {
|
||||||
|
result = apply_arguments_to_function(arguments, lispOperator->value.function, env);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
#ifdef _DEBUG
|
||||||
|
__debugbreak();
|
||||||
|
#endif
|
||||||
|
report_error(Error_Type_Not_A_Function);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#undef report_error
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_truthy (Ast_Node* expression, Environment* env) {
|
||||||
|
Ast_Node* result;
|
||||||
|
try {
|
||||||
|
result = eval_expr(expression, env);
|
||||||
|
}
|
||||||
|
if (result->type == Ast_Node_Type_Nil)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#define new(type) (type*)malloc(sizeof(type))
|
#define new(type) new type
|
||||||
#define nullptr NULL
|
#define nullptr NULL
|
||||||
|
|
||||||
#define concat_( a, b) a##b
|
#define concat_( a, b) a##b
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
define_array_list(char*, String);
|
define_array_list(char*, String);
|
||||||
|
|
||||||
|
|
||||||
typedef enum { false, true } bool;
|
//typedef enum { false, true } bool;
|
||||||
|
|
||||||
int string_equal(char* a, char* b) {
|
int string_equal(char* a, char* b) {
|
||||||
return !strcmp(a, b);
|
return !strcmp(a, b);
|
||||||
@@ -74,7 +74,7 @@ int _vscprintf_so(const char * format, va_list pargs) {
|
|||||||
int vasprintf(char **strp, const char *fmt, va_list ap) {
|
int vasprintf(char **strp, const char *fmt, va_list ap) {
|
||||||
int len = _vscprintf_so(fmt, ap);
|
int len = _vscprintf_so(fmt, ap);
|
||||||
if (len == -1) return -1;
|
if (len == -1) return -1;
|
||||||
char *str = malloc((size_t) len + 1);
|
char *str = (char *)malloc((size_t) len + 1);
|
||||||
if (!str) return -1;
|
if (!str) return -1;
|
||||||
int r = vsnprintf(str, len + 1, fmt, ap); /* "secure" version of vsprintf */
|
int r = vsnprintf(str, len + 1, fmt, ap); /* "secure" version of vsprintf */
|
||||||
if (r == -1) return free(str), -1;
|
if (r == -1) return free(str), -1;
|
||||||
@@ -99,6 +99,9 @@ static char get_nibble(char c) {
|
|||||||
return (c - '0');
|
return (c - '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define true 1
|
||||||
|
#define false 0
|
||||||
|
#define bool char
|
||||||
bool unescape_string(char* in) {
|
bool unescape_string(char* in) {
|
||||||
if (!in)
|
if (!in)
|
||||||
return true;
|
return true;
|
||||||
@@ -190,8 +193,8 @@ char* read_entire_file (char* filename) {
|
|||||||
/* Don't forget to call free() later! */
|
/* Don't forget to call free() later! */
|
||||||
}
|
}
|
||||||
|
|
||||||
char* read_line() {
|
char* read_expression() {
|
||||||
char* line = malloc(100), * linep = line;
|
char* line = (char*)malloc(100), * linep = line;
|
||||||
size_t lenmax = 100, len = lenmax;
|
size_t lenmax = 100, len = lenmax;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
@@ -207,7 +210,7 @@ char* read_line() {
|
|||||||
|
|
||||||
if(--len == 0) {
|
if(--len == 0) {
|
||||||
len = lenmax;
|
len = lenmax;
|
||||||
char * linen = realloc(linep, lenmax *= 2);
|
char * linen = (char*)realloc(linep, lenmax *= 2);
|
||||||
|
|
||||||
if(linen == NULL) {
|
if(linen == NULL) {
|
||||||
free(linep);
|
free(linep);
|
||||||
@@ -228,7 +231,50 @@ char* read_line() {
|
|||||||
}
|
}
|
||||||
(*line)--; // we dont want the \n actually
|
(*line)--; // we dont want the \n actually
|
||||||
*line = '\0';
|
*line = '\0';
|
||||||
return linep;
|
// BUG(Felix): Why do we have to add 1 here?
|
||||||
|
return linep + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* read_line() {
|
||||||
|
char* line = (char*)malloc(100), * linep = line;
|
||||||
|
size_t lenmax = 100, len = lenmax;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
int nesting = 0;
|
||||||
|
|
||||||
|
if(line == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
c = fgetc(stdin);
|
||||||
|
if(c == EOF)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(--len == 0) {
|
||||||
|
len = lenmax;
|
||||||
|
char* linen = (char*)realloc(linep, lenmax *= 2);
|
||||||
|
|
||||||
|
if(linen == NULL) {
|
||||||
|
free(linep);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
line = linen + (line - linep);
|
||||||
|
linep = linen;
|
||||||
|
}
|
||||||
|
|
||||||
|
*line++;
|
||||||
|
if((*line = (char)c) == '(')
|
||||||
|
++nesting;
|
||||||
|
else if((*line = (char)c) == ')')
|
||||||
|
--nesting;
|
||||||
|
else if((*line = (char)c) == '\n')
|
||||||
|
if (nesting == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(*line)--; // we dont want the \n actually
|
||||||
|
*line = '\0';
|
||||||
|
// BUG(Felix): Why do we have to add 1 here?
|
||||||
|
return linep + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
-18
@@ -1,18 +0,0 @@
|
|||||||
// for colored console output
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#pragma comment(lib, "Kernel32.lib")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void init () {
|
|
||||||
// On Windows: manually enable colored console output
|
|
||||||
#ifdef _WIN32
|
|
||||||
SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE),
|
|
||||||
ENABLE_PROCESSED_OUTPUT |
|
|
||||||
ENABLE_WRAP_AT_EOL_OUTPUT |
|
|
||||||
ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// TODO(Felix): Init the constants here (Bulit-in function
|
|
||||||
// ast_nodes, nil, type_keywords ..more?)
|
|
||||||
}
|
|
||||||
@@ -66,8 +66,8 @@ void print(Ast_Node* node) {
|
|||||||
else
|
else
|
||||||
printf("[lambda]");
|
printf("[lambda]");
|
||||||
} break;
|
} break;
|
||||||
case (Ast_Node_Type_Built_In_Function): {
|
case (Ast_Node_Type_CFunction): {
|
||||||
printf("[built-in-function %s]", Built_In_Name_to_string(node->value.built_in_function->type));
|
printf("[C-function]");
|
||||||
} break;
|
} break;
|
||||||
case (Ast_Node_Type_Pair): {
|
case (Ast_Node_Type_Pair): {
|
||||||
Ast_Node* head = node;
|
Ast_Node* head = node;
|
||||||
@@ -112,6 +112,6 @@ void log_error() {
|
|||||||
Error_Type_to_string(error->type),
|
Error_Type_to_string(error->type),
|
||||||
console_normal);
|
console_normal);
|
||||||
printf(" in: %s", console_cyan);
|
printf(" in: %s", console_cyan);
|
||||||
print_error_location(error->location);
|
print_error_location();
|
||||||
printf("%s\n", console_normal);
|
printf("%s\n", console_normal);
|
||||||
}
|
}
|
||||||
+22
-19
@@ -1,3 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
#define _CRT_SECURE_NO_DEPRECATE
|
#define _CRT_SECURE_NO_DEPRECATE
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -5,48 +6,52 @@
|
|||||||
#include <stdarg.h> /* needed for va_list */
|
#include <stdarg.h> /* needed for va_list */
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include "./helpers.c"
|
#include "./helpers.cpp"
|
||||||
#include "./ast.c"
|
#include "./ast.cpp"
|
||||||
#include "./error.c"
|
#include "./error.cpp"
|
||||||
#include "./io.c"
|
#include "./io.cpp"
|
||||||
#include "./assert.c"
|
#include "./assert.cpp"
|
||||||
#include "./parse.c"
|
#include "./parse.cpp"
|
||||||
#include "./env.c"
|
#include "./env.cpp"
|
||||||
#include "./built_ins.c"
|
#include "./built_ins.cpp"
|
||||||
#include "./eval.c"
|
#include "./eval.cpp"
|
||||||
#include "./testing.c"
|
#include "./testing.cpp"
|
||||||
#include "./init.c"
|
|
||||||
|
|
||||||
void interprete_file (char* file_name) {
|
Ast_Node* interprete_file (char* file_name) {
|
||||||
char* file_content = read_entire_file(file_name);
|
char* file_content = read_entire_file(file_name);
|
||||||
if (!file_content) {
|
if (!file_content) {
|
||||||
create_error(Error_Type_Unknown_Error, nullptr);
|
create_error(Error_Type_Unknown_Error, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast_Node_Array_List* program;
|
Ast_Node_Array_List* program;
|
||||||
try_void {
|
try {
|
||||||
parse_program(file_name, file_content);
|
program = parse_program(file_name, file_content);
|
||||||
}
|
}
|
||||||
|
|
||||||
Environment* env = create_empty_environment(Environment_Type_Let);
|
Environment* env = create_empty_environment(Environment_Type_Let);
|
||||||
|
load_built_ins_into_environment(env);
|
||||||
|
|
||||||
try_void {
|
try {
|
||||||
built_in_load("pre.slime", env);
|
built_in_load("pre.slime", env);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast_Node* result = create_ast_node_nil();
|
Ast_Node* result = create_ast_node_nil();
|
||||||
for (int i = 0; i < program->next_index; ++i) {
|
for (int i = 0; i < program->next_index; ++i) {
|
||||||
try_void {
|
try {
|
||||||
result = eval_expr(program->data[i], env);
|
result = eval_expr(program->data[i], env);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int interprete_stdin () {
|
int interprete_stdin () {
|
||||||
printf("Welcome to the lispy interpreter.\n");
|
printf("Welcome to the lispy interpreter.\n");
|
||||||
char* line;
|
char* line;
|
||||||
Environment* env = create_empty_environment(Environment_Type_Let);
|
Environment* env = create_empty_environment(Environment_Type_Let);
|
||||||
|
load_built_ins_into_environment(env);
|
||||||
|
|
||||||
built_in_load("pre.slime", env);
|
built_in_load("pre.slime", env);
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -57,7 +62,7 @@ int interprete_stdin () {
|
|||||||
Ast_Node* parsed, * evaluated;
|
Ast_Node* parsed, * evaluated;
|
||||||
while (true) {
|
while (true) {
|
||||||
printf(">");
|
printf(">");
|
||||||
line = read_line();
|
line = read_expression();
|
||||||
parsed = parse_single_expression(line);
|
parsed = parse_single_expression(line);
|
||||||
if (error) {
|
if (error) {
|
||||||
log_error();
|
log_error();
|
||||||
@@ -77,8 +82,6 @@ int interprete_stdin () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main (int argc, char *argv[]) {
|
int main (int argc, char *argv[]) {
|
||||||
init();
|
|
||||||
|
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
interprete_file(argv[1]);
|
interprete_file(argv[1]);
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -89,7 +89,7 @@ testresult test_eval_operands() {
|
|||||||
char operands_string[] = "((eval 1) (+ 1 2) \"okay\" (eval :haha))";
|
char operands_string[] = "((eval 1) (+ 1 2) \"okay\" (eval :haha))";
|
||||||
Ast_Node* operands = parse_single_expression(operands_string);
|
Ast_Node* operands = parse_single_expression(operands_string);
|
||||||
int operands_length;
|
int operands_length;
|
||||||
operands = eval_arguments(operands, create_empty_environment(Environment_Type_Let), &operands_length);
|
operands = eval_arguments(operands, create_built_ins_environment(), &operands_length);
|
||||||
|
|
||||||
assert_no_error(error);
|
assert_no_error(error);
|
||||||
assert_equal_int(list_length(operands), 4);
|
assert_equal_int(list_length(operands), 4);
|
||||||
@@ -233,7 +233,7 @@ testresult test_parse_expression() {
|
|||||||
testresult test_built_in_add() {
|
testresult test_built_in_add() {
|
||||||
char exp_string[] = "(+ 10 4)";
|
char exp_string[] = "(+ 10 4)";
|
||||||
Ast_Node* expression = parse_single_expression(exp_string);
|
Ast_Node* expression = parse_single_expression(exp_string);
|
||||||
Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let));
|
Ast_Node* result = eval_expr(expression, create_built_ins_environment());
|
||||||
|
|
||||||
assert_no_error(error);
|
assert_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -246,7 +246,7 @@ testresult test_built_in_add() {
|
|||||||
testresult test_built_in_substract() {
|
testresult test_built_in_substract() {
|
||||||
char exp_string[] = "(- 10 4)";
|
char exp_string[] = "(- 10 4)";
|
||||||
Ast_Node* expression = parse_single_expression(exp_string);
|
Ast_Node* expression = parse_single_expression(exp_string);
|
||||||
Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let));
|
Ast_Node* result = eval_expr(expression, create_built_ins_environment());
|
||||||
|
|
||||||
assert_no_error(error);
|
assert_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -260,7 +260,7 @@ testresult test_built_in_substract() {
|
|||||||
testresult test_built_in_multiply() {
|
testresult test_built_in_multiply() {
|
||||||
char exp_string[] = "(* 10 4)";
|
char exp_string[] = "(* 10 4)";
|
||||||
Ast_Node* expression = parse_single_expression(exp_string);
|
Ast_Node* expression = parse_single_expression(exp_string);
|
||||||
Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let));
|
Ast_Node* result = eval_expr(expression, create_built_ins_environment());
|
||||||
|
|
||||||
assert_no_error(error);
|
assert_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -274,7 +274,7 @@ testresult test_built_in_multiply() {
|
|||||||
testresult test_built_in_divide() {
|
testresult test_built_in_divide() {
|
||||||
char exp_string[] = "(/ 20 4)";
|
char exp_string[] = "(/ 20 4)";
|
||||||
Ast_Node* expression = parse_single_expression(exp_string);
|
Ast_Node* expression = parse_single_expression(exp_string);
|
||||||
Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let));
|
Ast_Node* result = eval_expr(expression, create_built_ins_environment());
|
||||||
|
|
||||||
assert_null(error);
|
assert_null(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -288,7 +288,7 @@ testresult test_built_in_divide() {
|
|||||||
testresult test_built_in_if() {
|
testresult test_built_in_if() {
|
||||||
char exp_string1[] = "(if 1 4 5)";
|
char exp_string1[] = "(if 1 4 5)";
|
||||||
Ast_Node* expression = parse_single_expression(exp_string1);
|
Ast_Node* expression = parse_single_expression(exp_string1);
|
||||||
Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let));
|
Ast_Node* result = eval_expr(expression, create_built_ins_environment());
|
||||||
|
|
||||||
assert_no_error(error);
|
assert_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -297,7 +297,7 @@ testresult test_built_in_if() {
|
|||||||
|
|
||||||
char exp_string2[] = "(if () 4 5)";
|
char exp_string2[] = "(if () 4 5)";
|
||||||
expression = parse_single_expression(exp_string2);
|
expression = parse_single_expression(exp_string2);
|
||||||
result = eval_expr(expression, create_empty_environment(Environment_Type_Let));
|
result = eval_expr(expression, create_built_ins_environment());
|
||||||
|
|
||||||
assert_no_error(error);
|
assert_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -310,7 +310,7 @@ testresult test_built_in_if() {
|
|||||||
testresult test_built_in_and() {
|
testresult test_built_in_and() {
|
||||||
char exp_string1[] = "(and 1 \"asd\" 4)";
|
char exp_string1[] = "(and 1 \"asd\" 4)";
|
||||||
Ast_Node* expression = parse_single_expression(exp_string1);
|
Ast_Node* expression = parse_single_expression(exp_string1);
|
||||||
Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let));
|
Ast_Node* result = eval_expr(expression, create_built_ins_environment());
|
||||||
|
|
||||||
assert_no_error(error);
|
assert_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -319,7 +319,7 @@ testresult test_built_in_and() {
|
|||||||
// a false case
|
// a false case
|
||||||
char exp_string2[] = "(and () \"asd\" 4)";
|
char exp_string2[] = "(and () \"asd\" 4)";
|
||||||
expression = parse_single_expression(exp_string2);
|
expression = parse_single_expression(exp_string2);
|
||||||
result = eval_expr(expression, create_empty_environment(Environment_Type_Let));
|
result = eval_expr(expression, create_built_ins_environment());
|
||||||
|
|
||||||
assert_no_error(error);
|
assert_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -331,7 +331,7 @@ testresult test_built_in_and() {
|
|||||||
testresult test_built_in_or() {
|
testresult test_built_in_or() {
|
||||||
char exp_string1[] = "(or \"asd\" nil)";
|
char exp_string1[] = "(or \"asd\" nil)";
|
||||||
Ast_Node* expression = parse_single_expression(exp_string1);
|
Ast_Node* expression = parse_single_expression(exp_string1);
|
||||||
Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let));
|
Ast_Node* result = eval_expr(expression, create_built_ins_environment());
|
||||||
|
|
||||||
assert_no_error(error);
|
assert_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -340,7 +340,7 @@ testresult test_built_in_or() {
|
|||||||
// a false case
|
// a false case
|
||||||
char exp_string2[] = "(or () ())";
|
char exp_string2[] = "(or () ())";
|
||||||
expression = parse_single_expression(exp_string2);
|
expression = parse_single_expression(exp_string2);
|
||||||
result = eval_expr(expression, create_empty_environment(Environment_Type_Let));
|
result = eval_expr(expression, create_built_ins_environment());
|
||||||
|
|
||||||
assert_no_error(error);
|
assert_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -353,7 +353,7 @@ testresult test_built_in_or() {
|
|||||||
testresult test_built_in_not() {
|
testresult test_built_in_not() {
|
||||||
char exp_string1[] = "(not ())";
|
char exp_string1[] = "(not ())";
|
||||||
Ast_Node* expression = parse_single_expression(exp_string1);
|
Ast_Node* expression = parse_single_expression(exp_string1);
|
||||||
Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let));
|
Ast_Node* result = eval_expr(expression, create_built_ins_environment());
|
||||||
|
|
||||||
// a true case
|
// a true case
|
||||||
assert_no_error(error);
|
assert_no_error(error);
|
||||||
@@ -363,7 +363,7 @@ testresult test_built_in_not() {
|
|||||||
// a false case
|
// a false case
|
||||||
char exp_string2[] = "(not \"asd xD\")";
|
char exp_string2[] = "(not \"asd xD\")";
|
||||||
expression = parse_single_expression(exp_string2);
|
expression = parse_single_expression(exp_string2);
|
||||||
result = eval_expr(expression, create_empty_environment(Environment_Type_Let));
|
result = eval_expr(expression, create_built_ins_environment());
|
||||||
|
|
||||||
assert_no_error(error);
|
assert_no_error(error);
|
||||||
assert_not_null(result);
|
assert_not_null(result);
|
||||||
@@ -4,7 +4,7 @@ pushd %~dp0\bin
|
|||||||
call ..\build.bat
|
call ..\build.bat
|
||||||
if %errorlevel% == 0 (
|
if %errorlevel% == 0 (
|
||||||
echo ---------- Testing ----------
|
echo ---------- Testing ----------
|
||||||
call timecmd slime.exe test.slime
|
call timecmd ..\build\slime.exe test.slime
|
||||||
)
|
)
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|||||||
+1
-1
@@ -168,7 +168,7 @@
|
|||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\src\main.c" />
|
<ClCompile Include="..\src\main.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
|||||||
Reference in New Issue
Block a user