diff --git a/.gitignore b/.gitignore index e660fd9..965abf2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ bin/ +build/ +.idea/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..52109fb --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.21) +project(Flup C) + +set(CMAKE_CXX_STANDARD 14) + +include_directories(src) +include_directories(submodules/utilitiec/src) +include_directories(templates) + +add_executable(Flup + src/interpreter.c + src/main.c + src/tokenizer.c + src/builtin-functions.c + src/callframe.c + src/value.c + submodules/utilitiec/src/allocator-interface/allocator-interface.c + submodules/utilitiec/src/allocator-interface/allocator-interface.h + submodules/utilitiec/src/allocator-interface/CMakeLists.txt + submodules/utilitiec/src/argumentc/argumentc.c + submodules/utilitiec/src/argumentc/argumentc.h + submodules/utilitiec/src/argumentc/CMakeLists.txt + submodules/utilitiec/src/arraylist/arraylist.c + submodules/utilitiec/src/arraylist/arraylist.h + submodules/utilitiec/src/arraylist/CMakeLists.txt + submodules/utilitiec/src/dynamicarray/CMakeLists.txt + submodules/utilitiec/src/dynamicarray/dynamicarray.c + submodules/utilitiec/src/dynamicarray/dynamicarray.h + submodules/utilitiec/src/dynamicbuffer/CMakeLists.txt + submodules/utilitiec/src/dynamicbuffer/dynamicbuffer.c + submodules/utilitiec/src/dynamicbuffer/dynamicbuffer.h + submodules/utilitiec/src/pointers/CMakeLists.txt + submodules/utilitiec/src/pointers/pointers.c + submodules/utilitiec/src/pointers/pointers.h + submodules/utilitiec/src/Scratchpad/CMakeLists.txt + submodules/utilitiec/src/Scratchpad/Scratchpad.c + submodules/utilitiec/src/Scratchpad/Scratchpad.h + submodules/utilitiec/src/siphash/CMakeLists.txt + submodules/utilitiec/src/siphash/siphash.c + submodules/utilitiec/src/siphash/siphash.h + submodules/utilitiec/src/StringView/CMakeLists.txt + submodules/utilitiec/src/StringView/StringView.c + submodules/utilitiec/src/StringView/StringView.h + submodules/utilitiec/src/utf8/CMakeLists.txt + submodules/utilitiec/src/utf8/utf-8.c + submodules/utilitiec/src/utf8/utf-8.h + submodules/utilitiec/src/cmakegen.sh + submodules/utilitiec/src/CMakeLists.txt + submodules/utilitiec/src/errorcodes.h) + +target_link_libraries(Flup m) diff --git a/src/builtin-functions.c b/src/builtin-functions.c new file mode 100644 index 0000000..119e75b --- /dev/null +++ b/src/builtin-functions.c @@ -0,0 +1,154 @@ +/* + * This code is part of the programming language flup + * flup comes with ABSOLUTELY NO WARRANTY and is licensed under AGPL-3.0 or later. + * Copyright (C) 2024 VegOwOtenks + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "builtin-functions.h" + +#include "value.h" + +#include +#include + +int BuiltinFunction_Equality(CallFrame* top_frame) +{ + Value v1; + Value v2; + + CallFrame_StackPop(top_frame, &v1); + if (CallFrame_StackPop(top_frame, &v2) != EXIT_SUCCESS) { + // TODO: Error message + return EXIT_FAILURE; + } + + Value result; + result.type = VALUETYPE_BOOLEAN; + result.get.boolean = Value_Equal(&v1, &v2); + + top_frame->instruction_pointer++; + return CallFrame_StackPush(top_frame, &result); + +} + +int BuiltinFunction_Plus(CallFrame* top_frame) +{ + Value v1; + Value v2; + + CallFrame_StackPop(top_frame, &v1); + if (CallFrame_StackPop(top_frame, &v2) != EXIT_SUCCESS) { + // TODO: Error message + return EXIT_FAILURE; + } + if (v1.type != v2.type) { + // TODO: Error message + return EXIT_FAILURE; + } + + Value result; + result.type = v1.type; + switch (v1.type) { + case VALUETYPE_INT64: + result.get.i64 = v2.get.i64 + v1.get.i64; + break; + case VALUETYPE_DOUBLE: + result.get.f64 = v2.get.f64 + v1.get.f64; + break; + case VALUETYPE_BOOLEAN: + result.get.boolean = v2.get.boolean + v1.get.boolean; + break; + } + + top_frame->instruction_pointer++; + return CallFrame_StackPush(top_frame, &result); +} + +int BuiltinFunction_Minus(CallFrame* top_frame) +{ + Value v1; + Value v2; + + CallFrame_StackPop(top_frame, &v1); + if (CallFrame_StackPop(top_frame, &v2) != EXIT_SUCCESS) { + // TODO: Error message + return EXIT_FAILURE; + } + if (v1.type != v2.type) { + // TODO: Error message + return EXIT_FAILURE; + } + + Value result; + result.type = v1.type; + switch (v1.type) { + case VALUETYPE_INT64: + result.get.i64 = v2.get.i64 - v1.get.i64; + break; + case VALUETYPE_DOUBLE: + result.get.f64 = v2.get.f64 - v1.get.f64; + break; + case VALUETYPE_BOOLEAN: + result.get.boolean = v2.get.boolean - v1.get.boolean; + break; + } + + top_frame->instruction_pointer++; + return CallFrame_StackPush(top_frame, &result); +} + + +int BuiltinFunction_PrintLine(CallFrame* top_frame) +{ + Value v1; + + if (CallFrame_StackPop(top_frame, &v1) != EXIT_SUCCESS) { + return EXIT_FAILURE; + } + + switch (v1.type) { + case VALUETYPE_INT64: + printf("%i\n", v1.get.i64); + break; + case VALUETYPE_DOUBLE: + printf("%f\n", v1.get.f64); + break; + case VALUETYPE_BOOLEAN: + if (v1.get.boolean) { + printf("true\n"); + } else { + printf("false\n"); + } + break; + } + + return EXIT_SUCCESS; +} + +int BuiltinFunction_Duplicate(CallFrame* top_frame) +{ + Value v1; + + if (CallFrame_StackPop(top_frame, &v1) != EXIT_SUCCESS) { + return EXIT_FAILURE; + } + + if (CallFrame_StackPush(top_frame, &v1) || CallFrame_StackPush(top_frame, &v1)) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/src/builtin-functions.h b/src/builtin-functions.h new file mode 100644 index 0000000..cd690a3 --- /dev/null +++ b/src/builtin-functions.h @@ -0,0 +1,34 @@ +/* + * This code is part of the programming language flup + * flup comes with ABSOLUTELY NO WARRANTY and is licensed under AGPL-3.0 or later. + * Copyright (C) 2024 VegOwOtenks + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef FLUP_BUILTINFUNCTION_H +#define FLUP_BUILTINFUNCTION_H + +#include "callframe.h" +#include "tokenizer.h" + + +int BuiltinFunction_Minus(CallFrame* top_frame); +int BuiltinFunction_Plus(CallFrame* top_frame); +int BuiltinFunction_Equality(CallFrame* top_frame); +int BuiltinFunction_PrintLine(CallFrame* top_frame); +int BuiltinFunction_Duplicate(CallFrame* top_frame); + + +#endif \ No newline at end of file diff --git a/src/callframe.c b/src/callframe.c new file mode 100644 index 0000000..608e2be --- /dev/null +++ b/src/callframe.c @@ -0,0 +1,125 @@ +/* + * This code is part of the programming language flup + * flup comes with ABSOLUTELY NO WARRANTY and is licensed under AGPL-3.0 or later. + * Copyright (C) 2024 VegOwOtenks + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "callframe.h" + +#include + +int CallFrame_Create(CallFrame* self, FlupFunction* self_function) +{ + if (DynamicArray_Create(&self->stack, sizeof(Value), 8, NULL)) { + return ENOMEM; + } + self->memory_pad.memory = NULL; + self->self_function = self_function; + self->alternative = self->self_function->alternatives; + self->instruction_pointer = self->alternative->condition_token_start; + + self->variables = NULL; + self->functions = NULL; + + return EXIT_SUCCESS; +} + +int CallFrame_DefineVariable(CallFrame* self, StringView name, Value value) +{ + FlupVariable* v = CallFrame_Reserve(self, sizeof(FlupVariable), alignof(FlupVariable)); + if (v == NULL) { + return ENOMEM; + } + + v->name = name; + v->value = value; + + v->next = self->variables; + self->variables = v; + + return EXIT_SUCCESS; +} + + +int CallFrame_StackPop(CallFrame* self, Value* dest) +{ + size_t stack_size = DynamicArray_GetLength(&self->stack); + Value* popped = DynamicArray_GetPointer(&self->stack, stack_size - 1); + if (popped == NULL) { + return ENOTFOUND; + } + + *dest = *popped; + DynamicArray_Remove(&self->stack, stack_size - 1); + + return EXIT_SUCCESS; +} + +int CallFrame_StackPush(CallFrame* self, Value* value) +{ + return DynamicArray_Append(&self->stack, value); +} + +FlupVariable* CallFrame_FindVariable(CallFrame* self, StringView name) +{ + for (FlupVariable* variable = self->variables; variable != NULL; variable = variable->next) { + if (StringView_Equal(variable->name, name)) { + return variable; + } + } + + return NULL; +} + +void CallFrame_Destroy(CallFrame* self) +{ + if (self->memory_pad.memory != NULL) { + Scratchpad_Destroy(&self->memory_pad); + } + + DynamicArray_Destroy(&self->stack); +} + +void* CallFrame_Reserve(CallFrame* self, size_t amount, size_t alignment) +{ + if (self->memory_pad.memory == NULL) { + if (Scratchpad_Create(&self->memory_pad, 1024, NULL)) return NULL; + } + + return Scratchpad_ReserveAligned(&self->memory_pad, amount, alignment); +} + +bool CallFrame_IsExecutingCondition(CallFrame* frame) +{ + return frame->instruction_pointer >= frame->alternative->condition_token_start + && frame->instruction_pointer <= frame->alternative->condition_token_end; +} + +bool CallFrame_IsSelectingCondition(CallFrame* frame) +{ + return frame->instruction_pointer == frame->alternative->condition_token_end + 1; +} + +bool CallFrame_IsExecutingBody(CallFrame* frame) +{ + return frame->instruction_pointer < frame->alternative->body_token_end; +} + +bool CallFrame_IsReturningBody(CallFrame* frame) +{ + return frame->instruction_pointer == frame->alternative->body_token_end; +} + diff --git a/src/callframe.h b/src/callframe.h new file mode 100644 index 0000000..9f18051 --- /dev/null +++ b/src/callframe.h @@ -0,0 +1,59 @@ +/* + * This code is part of the programming language flup + * flup comes with ABSOLUTELY NO WARRANTY and is licensed under AGPL-3.0 or later. + * Copyright (C) 2024 VegOwOtenks + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef FLUP_CALLFRAME_H +#define FLUP_CALLFRAME_H + +#include "function.h" +#include "variable.h" +#include "value.h" +#include "dynamicarray/dynamicarray.h" +#include "StringView/StringView.h" +#include "Scratchpad/Scratchpad.h" + +typedef struct CallFrame_s { + size_t instruction_pointer; + /* ip = condition start : start + * ip = condition_end + 1 : done + * + */ + FlupFunction* self_function; + FlupFunctionAlternative* alternative; + FlupFunction* functions; // functions defined in this callframe + FlupVariable* variables; // variables defined in this callframe + DynamicArray stack; // Value + Scratchpad memory_pad; +} CallFrame; + +int CallFrame_Create(CallFrame* self, FlupFunction* self_function); +void CallFrame_Destroy(CallFrame* self); + +int CallFrame_DefineVariable(CallFrame* self, StringView name, Value value); +int CallFrame_StackPop(CallFrame* self, Value* dest); +int CallFrame_StackPush(CallFrame* self, Value* value); +FlupVariable* CallFrame_FindVariable(CallFrame* self, StringView name); +void* CallFrame_Reserve(CallFrame* self, size_t amount, size_t alignment); +bool CallFrame_IsExecutingCondition(CallFrame* frame); +bool CallFrame_IsSelectingCondition(CallFrame* frame); +bool CallFrame_IsExecutingBody(CallFrame* frame); +bool CallFrame_IsReturningBody(CallFrame* frame); + + + +#endif \ No newline at end of file diff --git a/src/function.h b/src/function.h new file mode 100644 index 0000000..1022709 --- /dev/null +++ b/src/function.h @@ -0,0 +1,48 @@ +/* + * This code is part of the programming language flup + * flup comes with ABSOLUTELY NO WARRANTY and is licensed under AGPL-3.0 or later. + * Copyright (C) 2024 VegOwOtenks + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef FLUP_FUNCTION_H +#define FLUP_FUNCTION_H + +#include +#include "StringView/StringView.h" + +typedef struct FlupFunctionAlternative_s { + size_t condition_token_start; + size_t condition_token_end; + size_t body_token_start; + size_t body_token_end; + struct FlupFunctionAlternative_s* next; +} FlupFunctionAlternative; + +typedef struct ParameterDefinition_s { + StringView type; + StringView name; + struct ParameterDefinition_s* next; +} ParameterDefinition; + +typedef struct FlupFunction_s { + StringView name; + ParameterDefinition* parameters; + StringView return_type; + FlupFunctionAlternative* alternatives; + struct FlupFunction_s* next; +} FlupFunction; + +#endif \ No newline at end of file diff --git a/src/interpreter.c b/src/interpreter.c index 747389d..1630364 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -1,41 +1,9 @@ #include "interpreter.h" +#include "builtin-functions.h" #include #include -static int CallFrame_Create(CallFrame* self, FlupFunction* self_function) -{ - if (DynamicArray_Create(&self->stack, sizeof(Value), 8, NULL)) { - return ENOMEM; - } - self->memory_pad.memory = NULL; - self->self_function = self_function; - self->alternative = self->self_function->alternatives; - self->instruction_pointer = self->alternative->condition_token_start; - - self->variables = NULL; - self->functions = NULL; - - return EXIT_SUCCESS; -} - -static void CallFrame_Destroy(CallFrame* self) -{ - if (self->memory_pad.memory != NULL) { - Scratchpad_Destroy(&self->memory_pad); - } - - DynamicArray_Destroy(&self->stack); -} - -static void* CallFrame_Reserve(CallFrame* self, size_t amount, size_t alignment) -{ - if (self->memory_pad.memory == NULL) { - if (Scratchpad_Create(&self->memory_pad, 1024, NULL)) return NULL; - } - - return Scratchpad_ReserveAligned(&self->memory_pad, amount, alignment); -} static FlupFunctionAlternative* FlupFunctionAlternative_Malloc(size_t condition_token_start, size_t condition_token_end, size_t body_token_start, size_t body_token_end) { @@ -49,6 +17,17 @@ static FlupFunctionAlternative* FlupFunctionAlternative_Malloc(size_t condition_ return a; } +const char* standard_builtin_names[] = { + "println", + "duplicate", + NULL +}; + +const BuiltinFunction standard_builtin_functions[] = { + BuiltinFunction_PrintLine, + BuiltinFunction_Duplicate, +}; + int Interpreter_Create(Interpreter* self, DynamicArray* tokens) { if (DynamicArray_Create(&self->call_frames, sizeof(CallFrame), 16, NULL)) { @@ -56,6 +35,9 @@ int Interpreter_Create(Interpreter* self, DynamicArray* tokens) } self->tokens = tokens; + self->builtin_names = standard_builtin_names; + self->builtin_functions = standard_builtin_functions; + return EXIT_SUCCESS; } @@ -73,6 +55,16 @@ Token* Interpreter_ExpectToken(Interpreter* self, CallFrame* top_frame, size_t s return Interpreter_GetToken(self, top_frame->instruction_pointer); } +bool is_begin_of_nested_function_definition(Interpreter* self, CallFrame* top_frame, size_t stop_index) { + Token* possible_colon = Interpreter_ExpectToken(self, top_frame, stop_index); + return possible_colon != NULL && possible_colon->type == TOKENTYPE_COLON; +} + +bool is_end_of_nested_function_definition(Interpreter* self, CallFrame* top_frame, size_t stop_index) { + Token* possible_semicolon = Interpreter_ExpectToken(self, top_frame, stop_index); + return possible_semicolon != NULL && possible_semicolon->type == TOKENTYPE_SEMICOLON; +} + int Interpreter_ParseFunction(Interpreter* self, CallFrame* top_frame, size_t stop_index) { FlupFunction* f = CallFrame_Reserve(top_frame, sizeof(FlupFunction), alignof(FlupFunction)); @@ -103,7 +95,7 @@ int Interpreter_ParseFunction(Interpreter* self, CallFrame* top_frame, size_t st return EXIT_FAILURE; } - *pdef = CallFrame_Reserve(top_frame, sizeof(**pdef), alignof(**pdef)); + *pdef = CallFrame_Reserve(top_frame, sizeof(ParameterDefinition), alignof(ParameterDefinition)); if (*pdef == NULL) { return ENOMEM; } @@ -138,11 +130,14 @@ int Interpreter_ParseFunction(Interpreter* self, CallFrame* top_frame, size_t st return EXIT_FAILURE; } + int nested_functions_count = 0; + FlupFunctionAlternative** fdef = &f->alternatives; while (alternative_start->type == TOKENTYPE_PIPE) { - top_frame->instruction_pointer++; + top_frame->instruction_pointer++; // pipe Token überspringen size_t condition_start = top_frame->instruction_pointer; + // condition parsing Token* current; do { current = Interpreter_ExpectToken(self, top_frame, stop_index); @@ -150,23 +145,36 @@ int Interpreter_ParseFunction(Interpreter* self, CallFrame* top_frame, size_t st if (current == NULL) { return EXIT_FAILURE; } - // TODO: Count inner colon and semicolon to allow for nested functions - } while (current->type != TOKENTYPE_ARROW); + if (is_begin_of_nested_function_definition(self, top_frame, stop_index)) { + nested_functions_count++; + } + if (is_end_of_nested_function_definition(self, top_frame, stop_index)) { + nested_functions_count--; + } + } while (current->type != TOKENTYPE_ARROW || nested_functions_count != 0); size_t condition_end = top_frame->instruction_pointer - 2; + // condition body parsing + nested_functions_count = 0; size_t body_start = top_frame->instruction_pointer; + current = Interpreter_ExpectToken(self, top_frame, stop_index); do { - current = Interpreter_ExpectToken(self, top_frame, stop_index); - top_frame->instruction_pointer++; if (current == NULL) { return EXIT_FAILURE; } - // TODO: Count inner colon and semicolon to allow for nested functions - } while (current->type != TOKENTYPE_PIPE && current->type != TOKENTYPE_SEMICOLON); - top_frame->instruction_pointer--; + if (current->type == TOKENTYPE_COLON) { + nested_functions_count++; + } + if (current->type == TOKENTYPE_SEMICOLON && nested_functions_count > 0) { + nested_functions_count--; + } + top_frame->instruction_pointer++; + current = Interpreter_ExpectToken(self, top_frame, stop_index); + } while ((current->type != TOKENTYPE_PIPE && current->type != TOKENTYPE_SEMICOLON) || + nested_functions_count > 0); size_t body_end = top_frame->instruction_pointer; - *fdef = CallFrame_Reserve(top_frame, sizeof(**pdef), alignof(**pdef)); + *fdef = CallFrame_Reserve(top_frame, sizeof(ParameterDefinition), alignof(ParameterDefinition)); if (*fdef == NULL) { return ENOMEM; } @@ -182,7 +190,7 @@ int Interpreter_ParseFunction(Interpreter* self, CallFrame* top_frame, size_t st return EXIT_FAILURE; } } - + // one token colon top_frame->instruction_pointer++; @@ -205,49 +213,23 @@ FlupFunction* Interpreter_FindFunction(Interpreter* self, StringView name) return NULL; } -bool _are_types_compatible(enum ValueType type, StringView type_name) +int Interpreter_CallBuiltinFunction(Interpreter* self, CallFrame* parent_frame, Token* name) { - if (StringView_Equal(type_name, StringView_FromString("any"))) { - return true; - } - - const enum ValueType type_mapping[] = {VALUETYPE_INT64, VALUETYPE_DOUBLE, VALUETYPE_BOOLEAN, VALUETYPE_BOOLEAN}; - const char* name_mapping[] = {"int", "double", "bool", "boolean"}; - - for (size_t i = 0; i < sizeof(name_mapping) / sizeof(name_mapping[0]); i++) { - if (1==1 - && type == type_mapping[i] - && StringView_Equal(type_name, StringView_FromString(name_mapping[i])) - ) { - return true; + StringView function_name = name->get.identifier; + for (size_t i = 0; self->builtin_names[i] != NULL; i++) { + if (StringView_Equal(function_name, StringView_FromString(self->builtin_names[i]))) { + return self->builtin_functions[i](parent_frame); } } - return false; -} - -int CallFrame_DefineVariable(CallFrame* self, StringView name, Value value) -{ - FlupVariable* v = CallFrame_Reserve(self, sizeof(FlupVariable), alignof(FlupVariable)); - if (v == NULL) { - return ENOMEM; - } - - v->name = name; - v->value = value; - - v->next = self->variables; - self->variables = v; - - return EXIT_SUCCESS; + return EXIT_FAILURE; } int Interpreter_CallFunction(Interpreter* self, CallFrame* parent_frame, Token* name) { FlupFunction* function = Interpreter_FindFunction(self, name->get.identifier); if (function == NULL) { - // TODO: Error message - return EXIT_FAILURE; + return Interpreter_CallBuiltinFunction(self, parent_frame, name); } CallFrame* new_frame; @@ -274,7 +256,7 @@ int Interpreter_CallFunction(Interpreter* self, CallFrame* parent_frame, Token* ParameterDefinition* parameter_def = function->parameters; for (size_t stack_index = stack_size - argument_count; stack_index != stack_size; stack_index++, parameter_def = parameter_def->next) { Value* parameter_value = DynamicArray_GetPointer(&parent_frame->stack, stack_index); - if (! _are_types_compatible(parameter_value->type, parameter_def->type)) { + if (!Value_TypeMatchesStringName(parameter_value->type, parameter_def->type)) { // TODO: Error message return EXIT_FAILURE; } @@ -292,137 +274,6 @@ int Interpreter_CallFunction(Interpreter* self, CallFrame* parent_frame, Token* return EXIT_SUCCESS; } -FlupVariable* CallFrame_FindVariable(CallFrame* self, StringView name) -{ - for (FlupVariable* variable = self->variables; variable != NULL; variable = variable->next) { - if (StringView_Equal(variable->name, name)) { - return variable; - } - } - - return NULL; -} - -int CallFrame_StackPop(CallFrame* self, Value* dest) -{ - size_t stack_size = DynamicArray_GetLength(&self->stack); - Value* popped = DynamicArray_GetPointer(&self->stack, stack_size - 1); - if (popped == NULL) { - return ENOTFOUND; - } - - *dest = *popped; - DynamicArray_Remove(&self->stack, stack_size - 1); - - return EXIT_SUCCESS; -} - -int CallFrame_StackPush(CallFrame* self, Value* value) -{ - return DynamicArray_Append(&self->stack, value); -} - -bool Value_Equal(Value* v1, Value* v2) -{ - if (v1->type != v2->type) return false; - switch (v1->type) { - case VALUETYPE_INT64: - return v1->get.i64 == v2->get.i64; - case VALUETYPE_DOUBLE: - return v1->get.f64 == v2->get.f64; - case VALUETYPE_BOOLEAN: - return v1->get.boolean = v2->get.boolean; - } - - return false; -} - -int BuiltinFunction_Equality(CallFrame* top_frame) -{ - Value v1; - Value v2; - - CallFrame_StackPop(top_frame, &v1); - if (CallFrame_StackPop(top_frame, &v2) != EXIT_SUCCESS) { - // TODO: Error message - return EXIT_FAILURE; - } - - Value result; - result.type = VALUETYPE_BOOLEAN; - result.get.boolean = Value_Equal(&v1, &v2); - - top_frame->instruction_pointer++; - return CallFrame_StackPush(top_frame, &result); - -} - -int BuiltinFunction_Plus(CallFrame* top_frame) -{ - Value v1; - Value v2; - - CallFrame_StackPop(top_frame, &v1); - if (CallFrame_StackPop(top_frame, &v2) != EXIT_SUCCESS) { - // TODO: Error message - return EXIT_FAILURE; - } - if (v1.type != v2.type) { - // TODO: Error message - return EXIT_FAILURE; - } - - Value result; - result.type = v1.type; - switch (v1.type) { - case VALUETYPE_INT64: - result.get.i64 = v2.get.i64 + v1.get.i64; - break; - case VALUETYPE_DOUBLE: - result.get.f64 = v2.get.f64 + v1.get.f64; - break; - case VALUETYPE_BOOLEAN: - result.get.boolean = v2.get.boolean + v1.get.boolean; - break; - } - - top_frame->instruction_pointer++; - return CallFrame_StackPush(top_frame, &result); -} - -int BuiltinFunction_Minus(CallFrame* top_frame) -{ - Value v1; - Value v2; - - CallFrame_StackPop(top_frame, &v1); - if (CallFrame_StackPop(top_frame, &v2) != EXIT_SUCCESS) { - // TODO: Error message - return EXIT_FAILURE; - } - if (v1.type != v2.type) { - // TODO: Error message - return EXIT_FAILURE; - } - - Value result; - result.type = v1.type; - switch (v1.type) { - case VALUETYPE_INT64: - result.get.i64 = v2.get.i64 - v1.get.i64; - break; - case VALUETYPE_DOUBLE: - result.get.f64 = v2.get.f64 - v1.get.f64; - break; - case VALUETYPE_BOOLEAN: - result.get.boolean = v2.get.boolean - v1.get.boolean; - break; - } - - top_frame->instruction_pointer++; - return CallFrame_StackPush(top_frame, &result); -} - int Interpreter_GetParenthesizedRange(Interpreter* self, CallFrame* top_frame, size_t* paren_start, size_t* paren_end, size_t stop_token) { size_t open_paren_count = 0; @@ -601,9 +452,14 @@ int Interpreter_ExecuteNext(Interpreter* self, size_t stop_token) // TODO: Type-check the token StringView variable_name = identifier_token->get.identifier; - if (CallFrame_DefineVariable(top_frame, variable_name, variable_value)) { - // TODO: Error message - return EXIT_FAILURE; + FlupVariable* var = CallFrame_FindVariable(top_frame, variable_name); + if (var != NULL) { + var->value = variable_value; + } else { + if (CallFrame_DefineVariable(top_frame, variable_name, variable_value)) { + // TODO: Error message + return EXIT_FAILURE; + } } top_frame->instruction_pointer += 2; // points to opening paren @@ -641,45 +497,11 @@ int Interpreter_ExecuteNext(Interpreter* self, size_t stop_token) case TOKENTYPE_NONE: case TOKENTYPE_ERROR: return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - -bool Value_IsTruthy(Value* v) -{ - switch (v->type) { - case VALUETYPE_INT64: - return v->get.i64 != 0; - case VALUETYPE_DOUBLE: - return v->get.f64 != 0.0; - case VALUETYPE_BOOLEAN: - return v->get.boolean; } - return false; + return EXIT_SUCCESS; } -static bool CallFrame_IsExecutingCondition(CallFrame* frame) -{ - return frame->instruction_pointer >= frame->alternative->condition_token_start - && frame->instruction_pointer <= frame->alternative->condition_token_end; -} - -static bool CallFrame_IsSelectingCondition(CallFrame* frame) -{ - return frame->instruction_pointer == frame->alternative->condition_token_end + 1; -} - -static bool CallFrame_IsExecutingBody(CallFrame* frame) -{ - return frame->instruction_pointer < frame->alternative->body_token_end; -} - -static bool CallFrame_IsReturningBody(CallFrame* frame) -{ - return frame->instruction_pointer == frame->alternative->body_token_end; -} #define RPN(a,b,c) a c b int Interpreter_RunFrame(Interpreter* self) @@ -709,7 +531,7 @@ int Interpreter_RunFrame(Interpreter* self) if (Value_IsTruthy(&alternative_success)) { // into body top_frame->instruction_pointer = top_frame->alternative->body_token_start; - } else { + } else { if (top_frame->alternative->next == NULL) { // TODO: Error message return EXIT_FAILURE; @@ -751,8 +573,9 @@ int Interpreter_RunFrame(Interpreter* self) // char function_name[top_frame->self_function->name.length + 1]; // memset(function_name, 0, sizeof(function_name)); // StringView_Paste(function_name, top_frame->self_function->name); + // printf("%s\n", function_name); - if (! _are_types_compatible(return_value.type, top_frame->self_function->return_type)) { + if (!Value_TypeMatchesStringName(return_value.type, top_frame->self_function->return_type)) { // TODO: Error message return EXIT_FAILURE; } @@ -775,7 +598,7 @@ int Interpreter_RunFrame(Interpreter* self) int Interpreter_Run(Interpreter* self) { size_t frame_count = 1; - do { + do { if (Interpreter_RunFrame(self)) { // TODO: Return the error code return EXIT_FAILURE; @@ -807,11 +630,11 @@ int Interpreter_Interpret(Interpreter* self) } first_frame->instruction_pointer = 0; - Interpreter_Run(self); + int run_code = Interpreter_Run(self); free(main_alternative); - return EXIT_SUCCESS; + return run_code; } void Interpreter_Destroy(Interpreter* self) diff --git a/src/interpreter.h b/src/interpreter.h index 0ec4454..49f1b6a 100644 --- a/src/interpreter.h +++ b/src/interpreter.h @@ -5,74 +5,24 @@ #include "../submodules/utilitiec/src/Scratchpad/Scratchpad.h" #include "tokenizer.h" +#include "callframe.h" +#include "value.h" #include -typedef struct FlupFunctionAlternative_s { - size_t condition_token_start; - size_t condition_token_end; - size_t body_token_start; - size_t body_token_end; - struct FlupFunctionAlternative_s* next; -} FlupFunctionAlternative; +typedef struct Interpreter_s Interpreter; -typedef struct ParameterDefinition_s { - StringView type; - StringView name; - struct ParameterDefinition_s* next; -} ParameterDefinition; +typedef int (*BuiltinFunction)(CallFrame *); -typedef struct FlupFunction_s { - StringView name; - ParameterDefinition* parameters; - StringView return_type; - FlupFunctionAlternative* alternatives; - struct FlupFunction_s* next; -} FlupFunction; +struct Interpreter_s { + const char** builtin_names; + const BuiltinFunction* builtin_functions; -enum ValueType { - VALUETYPE_INT64, - VALUETYPE_DOUBLE, - VALUETYPE_BOOLEAN, -}; - -union ValueContent { - int64_t i64; - double f64; - bool boolean; -}; - -typedef struct Value_s { - enum ValueType type; - union ValueContent get; -} Value; - -typedef struct FlupVariable_s { - StringView name; - Value value; - struct FlupVariable_s* next; -} FlupVariable; - -typedef struct CallFrame_s { - size_t instruction_pointer; - /* ip = condition start : start - * ip = condition_end + 1 : done - * - */ - FlupFunction* self_function; - FlupFunctionAlternative* alternative; - FlupFunction* functions; // functions defined in this callframe - FlupVariable* variables; // variables defined in this callframe - DynamicArray stack; // Value - Scratchpad memory_pad; -} CallFrame; - -typedef struct Interpreter_s { DynamicArray* tokens; DynamicArray call_frames; // stores CallFrame -} Interpreter; +}; int Interpreter_Create(Interpreter* self, DynamicArray* tokens); int Interpreter_Interpret(Interpreter* self); void Interpreter_Destroy(Interpreter* self); -#endif //header guard +#endif //FLUP_INTERPRETER_H diff --git a/src/main.c b/src/main.c index f90263d..4e9bba1 100644 --- a/src/main.c +++ b/src/main.c @@ -39,7 +39,7 @@ int tokenize_all(StringView source, DynamicArray* a) char* load_file_string(StringView path) { - FILE* stream = fopen(path.source, "r"); + FILE* stream = fopen(path.source, "rb"); if (stream == NULL) { fprintf(stderr, "Fatal Error: Failed to open file at %*s\n", (int) path.length, path.source); return NULL; @@ -56,7 +56,10 @@ char* load_file_string(StringView path) return NULL; } - rewind(stream); + if (fseek(stream, 0, SEEK_SET)) { + perror("fseek"); + return NULL; + } char* buffer = malloc(length + 1); if (buffer == NULL) { @@ -64,9 +67,10 @@ char* load_file_string(StringView path) return NULL; } - size_t objects_read = fread(buffer, length, 1, stream); - if (objects_read != 1) { + size_t objects_read = fread(buffer, 1, length, stream); + if (objects_read != length) { fprintf(stderr, "Fatal Error: Failed read %li bytes from script file, got only %li\n", length, objects_read); + ; free(buffer); return NULL; } @@ -109,12 +113,12 @@ int main(int argc, const char* argv []) Interpreter_Create(&interpreter, &tokens); - Interpreter_Interpret(&interpreter); + int interpret_code = Interpreter_Interpret(&interpreter); Interpreter_Destroy(&interpreter); DynamicArray_Destroy(&tokens); free(script_string); - return EXIT_SUCCESS; + return interpret_code; } diff --git a/src/value.c b/src/value.c new file mode 100644 index 0000000..61e7835 --- /dev/null +++ b/src/value.c @@ -0,0 +1,72 @@ +/* + * This code is part of the programming language flup + * flup comes with ABSOLUTELY NO WARRANTY and is licensed under AGPL-3.0 or later. + * Copyright (C) 2024 VegOwOtenks + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "value.h" + + +bool Value_IsTruthy(Value* v) +{ + switch (v->type) { + case VALUETYPE_INT64: + return v->get.i64 != 0; + case VALUETYPE_DOUBLE: + return v->get.f64 != 0.0; + case VALUETYPE_BOOLEAN: + return v->get.boolean; + } + + return false; +} + +bool Value_Equal(Value* v1, Value* v2) +{ + if (v1->type != v2->type) return false; + switch (v1->type) { + case VALUETYPE_INT64: + return v1->get.i64 == v2->get.i64; + case VALUETYPE_DOUBLE: + return v1->get.f64 == v2->get.f64; + case VALUETYPE_BOOLEAN: + return v1->get.boolean = v2->get.boolean; + } + + return false; +} + +bool Value_TypeMatchesStringName(enum ValueType type, StringView type_name) +{ + if (StringView_Equal(type_name, StringView_FromString("any"))) { + return true; + } + + const enum ValueType type_mapping[] = {VALUETYPE_INT64, VALUETYPE_DOUBLE, VALUETYPE_BOOLEAN, VALUETYPE_BOOLEAN}; + const char* name_mapping[] = {"int", "double", "bool", "boolean"}; + + for (size_t i = 0; i < sizeof(name_mapping) / sizeof(name_mapping[0]); i++) { + if (1 == 1 + && type == type_mapping[i] + && StringView_Equal(type_name, StringView_FromString(name_mapping[i])) + ) { + return true; + } + } + + return false; +} + diff --git a/src/value.h b/src/value.h new file mode 100644 index 0000000..1ac4f26 --- /dev/null +++ b/src/value.h @@ -0,0 +1,49 @@ +/* + * This code is part of the programming language flup + * flup comes with ABSOLUTELY NO WARRANTY and is licensed under AGPL-3.0 or later. + * Copyright (C) 2024 VegOwOtenks + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef FLUP_VALUE_H +#define FLUP_VALUE_H + +#include +#include +#include "StringView/StringView.h" + +enum ValueType { + VALUETYPE_INT64, + VALUETYPE_DOUBLE, + VALUETYPE_BOOLEAN, +}; + +union ValueContent { + int64_t i64; + double f64; + bool boolean; +}; + +typedef struct Value_s { + enum ValueType type; + union ValueContent get; +} Value; + +bool Value_IsTruthy(Value* v); +bool Value_Equal(Value* v1, Value* v2); +bool Value_TypeMatchesStringName(enum ValueType type, StringView type_name); + + +#endif diff --git a/src/variable.h b/src/variable.h new file mode 100644 index 0000000..149e171 --- /dev/null +++ b/src/variable.h @@ -0,0 +1,32 @@ +/* + * This code is part of the programming language flup + * flup comes with ABSOLUTELY NO WARRANTY and is licensed under AGPL-3.0 or later. + * Copyright (C) 2024 VegOwOtenks + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef FLUP_VARIABLE_H +#define FLUP_VARIABLE_H + +#include "value.h" +#include "StringView/StringView.h" + +typedef struct FlupVariable_s { + StringView name; + Value value; + struct FlupVariable_s* next; +} FlupVariable; + +#endif \ No newline at end of file diff --git a/test-inputs/efficient-fibonacci.flup b/test-inputs/efficient-fibonacci.flup new file mode 100644 index 0000000..9ff7b63 --- /dev/null +++ b/test-inputs/efficient-fibonacci.flup @@ -0,0 +1,9 @@ +fib: int n -> int + | 1 -> fib: int a int b int iterations -> int + | iterations 0 == -> a b + + | 1 -> a b + println b a b + iterations 1 - fib + ; + 1 1 n fib + ; + +50 fib println diff --git a/test-inputs/fibonacci.flup b/test-inputs/fibonacci.flup new file mode 100644 index 0000000..eb742d0 --- /dev/null +++ b/test-inputs/fibonacci.flup @@ -0,0 +1,7 @@ +fib: int a -> int + | a 0 == -> 1 + | a 1 == -> 1 + | 1 1 == -> a 1 - fib a 2 - fib + duplicate println + ; + +10 fib \ No newline at end of file diff --git a/test-inputs/innere_function_definition.flup b/test-inputs/innere_function_definition.flup new file mode 100644 index 0000000..0a56733 --- /dev/null +++ b/test-inputs/innere_function_definition.flup @@ -0,0 +1,24 @@ + +test: int a -> int + | a 1 == -> inner: int b -> int + | b 0 == -> 5 fuenf + | 1 1 == -> 7 sieben + ; + a inner + | 1 1 == -> 3 drei + ; + +fuenf: int a -> int + | 1 -> a + ; + +drei: int a -> int + | 1 -> a + ; + + +sieben: int a -> int + | 1 -> a + ; + +0 test \ No newline at end of file