diff --git a/make.sh b/make.sh index 80f8179..f273625 100644 --- a/make.sh +++ b/make.sh @@ -5,4 +5,4 @@ gcc `find src/ -name '*.c'` \ submodules/utilitiec/build/lib/liballocator-interface.a \ submodules/utilitiec/build/lib/libStringView.a \ submodules/utilitiec/build/lib/libScratchpad.a \ - -lm -ggdb -Wall -o bin/flup + -lm -ggdb -Wall -Wextra -pedantic -o bin/flup diff --git a/src/interpreter.c b/src/interpreter.c index 38832bd..53d218e 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -2,18 +2,31 @@ #include -static int CallFrame_Create(CallFrame* self, FlupFunctionAlternative* f) +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->function = f; - self->instruction_pointer = f->body_token_start; + 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) { @@ -66,6 +79,7 @@ int Interpreter_ParseFunction(Interpreter* self, CallFrame* top_frame, size_t st Token* function_name = Interpreter_GetToken(self, top_frame->instruction_pointer); + memset(f, 0, sizeof(*f)); f->name = function_name->get.identifier; top_frame->instruction_pointer++; @@ -122,7 +136,7 @@ int Interpreter_ParseFunction(Interpreter* self, CallFrame* top_frame, size_t st return EXIT_FAILURE; } - FlupFunctionAlternative** fdef = &f->alternative; + FlupFunctionAlternative** fdef = &f->alternatives; while (alternative_start->type == TOKENTYPE_PIPE) { top_frame->instruction_pointer++; size_t condition_start = top_frame->instruction_pointer; @@ -134,17 +148,19 @@ 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); - size_t condition_end = top_frame->instruction_pointer - 1; + size_t condition_end = top_frame->instruction_pointer - 2; - size_t body_start = top_frame->instruction_pointer + 1; + size_t body_start = top_frame->instruction_pointer; do { current = Interpreter_ExpectToken(self, top_frame, stop_index); top_frame->instruction_pointer++; if (current == NULL) { return EXIT_FAILURE; } - } while (current->type != TOKENTYPE_PIPE && current->type != TOKENTYPE_COLON); + // TODO: Count inner colon and semicolon to allow for nested functions + } while (current->type != TOKENTYPE_PIPE && current->type != TOKENTYPE_SEMICOLON); top_frame->instruction_pointer--; size_t body_end = top_frame->instruction_pointer; @@ -168,9 +184,239 @@ int Interpreter_ParseFunction(Interpreter* self, CallFrame* top_frame, size_t st // one token colon top_frame->instruction_pointer++; + f->next = top_frame->functions; + top_frame->functions = f; + return EXIT_SUCCESS; } +FlupFunction* Interpreter_FindFunction(Interpreter* self, StringView name) +{ + DYNAMICARRAY_REVERSEITERATE(self->call_frames, CallFrame, frame) { + for (FlupFunction* function = frame->functions; function != NULL; function = function->next) { + if (StringView_Equal(function->name, name)) { + return function; + } + } + } + + return NULL; +} + +bool _are_types_compatible(enum ValueType type, StringView type_name) +{ + const enum ValueType type_mapping[] = {VALUETYPE_INT64, VALUETYPE_DOUBLE}; + const char* name_mapping[] = {"int", "double"}; + + 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; +} + +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 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; + } + + CallFrame* new_frame; + DynamicArray_AppendEmpty(&self->call_frames, (void**) &new_frame); + parent_frame = DynamicArray_GetPointer(&self->call_frames, DynamicArray_GetLength(&self->call_frames) - 2); + if (new_frame == NULL) { + // TODO: Error message + return ENOMEM; + } + CallFrame_Create(new_frame, function); + + size_t argument_count = 0; + size_t stack_size = DynamicArray_GetLength(&parent_frame->stack); + for ( + ParameterDefinition* d = function->parameters; + d != NULL; + d = d->next, + argument_count++); + if (stack_size < argument_count) { + // TODO: Error message + return EXIT_FAILURE; + } + + 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)) { + // TODO: Error message + return EXIT_FAILURE; + } + + int define_code = CallFrame_DefineVariable(new_frame, parameter_def->name, *parameter_value); + if (define_code != EXIT_SUCCESS) { + return define_code; + } + } + + for (size_t i = 0; i < argument_count; i++) { + DynamicArray_Remove(&parent_frame->stack, DynamicArray_GetLength(&parent_frame->stack) - 1); + } + + 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_ExecuteNext(Interpreter* self, size_t stop_token) { size_t frame_count = DynamicArray_GetLength(&self->call_frames); @@ -200,24 +446,82 @@ int Interpreter_ExecuteNext(Interpreter* self, size_t stop_token) case TOKENTYPE_IDENTIFIER: { Token* next = Interpreter_GetToken(self, top_frame->instruction_pointer + 1); + FlupVariable* var; if (next != NULL && next->type == TOKENTYPE_COLON) { // function definition return Interpreter_ParseFunction(self, top_frame, stop_token); + } else if ((var = CallFrame_FindVariable(top_frame, t->get.identifier)) != NULL) { + Value* stack_value; + DynamicArray_AppendEmpty(&top_frame->stack, (void**) &stack_value); + if (stack_value == NULL) { + return ENOMEM; + } + *stack_value = var->value; + top_frame->instruction_pointer++; } else { + top_frame->instruction_pointer++; + return Interpreter_CallFunction(self, top_frame, t); // regular call } + + break; + } + + case TOKENTYPE_EQUALITY: + { + return BuiltinFunction_Equality(top_frame); + } + + case TOKENTYPE_PIPE: + case TOKENTYPE_SEMICOLON: + { + // return from function body + if (StringView_Equal(top_frame->self_function->return_type, StringView_FromString("void"))) { + DynamicArray_Remove(&self->call_frames, frame_count - 1); + return EXIT_SUCCESS; + } + + CallFrame* parent_frame = DynamicArray_GetPointer(&self->call_frames, frame_count - 2); + Value return_value; + if (CallFrame_StackPop(top_frame, &return_value)) { + // TODO: Error message + return EXIT_FAILURE; + } + + 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); + + if (! _are_types_compatible(return_value.type, top_frame->self_function->return_type)) { + // TODO: Error message + return EXIT_FAILURE; + } + + CallFrame_Destroy(top_frame); + DynamicArray_Remove(&self->call_frames, frame_count - 1); + if (CallFrame_StackPush(parent_frame, &return_value)) { + return ENOMEM; + } + + break; } case TOKENTYPE_LEFT_BRACE: case TOKENTYPE_RIGHT_BRACE: case TOKENTYPE_AMPERSAND: case TOKENTYPE_PLUS: + { + BuiltinFunction_Plus(top_frame); + break; + } case TOKENTYPE_MINUS: + { + BuiltinFunction_Minus(top_frame); + break; + } case TOKENTYPE_MULTIPLY: case TOKENTYPE_DIVIDE: - case TOKENTYPE_PIPE: case TOKENTYPE_ARROW: case TOKENTYPE_COLON: - case TOKENTYPE_EQUALITY: case TOKENTYPE_INEQUALITY: case TOKENTYPE_LESSTHAN: case TOKENTYPE_LESSEQUAL: @@ -228,9 +532,24 @@ int Interpreter_ExecuteNext(Interpreter* self, size_t stop_token) case TOKENTYPE_NONE: case TOKENTYPE_ERROR: - default: 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; } #define RPN(a,b,c) a c b @@ -239,22 +558,50 @@ int Interpreter_RunFrame(Interpreter* self) size_t frame_count = DynamicArray_GetLength(&self->call_frames); CallFrame* top_frame = DynamicArray_GetPointer(&self->call_frames, frame_count - 1); if ( - top_frame->instruction_pointer >= top_frame->function->condition_token_start - && top_frame->instruction_pointer <= top_frame->function->condition_token_end + top_frame->instruction_pointer >= top_frame->alternative->condition_token_start + && top_frame->instruction_pointer <= top_frame->alternative->condition_token_end ) { // alternative executing - while (top_frame->instruction_pointer <= top_frame->function->condition_token_end) { - Interpreter_ExecuteNext(self, top_frame->function->condition_token_end); + while (top_frame->instruction_pointer <= top_frame->alternative->condition_token_end) { + int execute_code = Interpreter_ExecuteNext(self, top_frame->alternative->condition_token_end); + if (execute_code != EXIT_SUCCESS) { + return execute_code; + } if (RPN(frame_count, DynamicArray_GetLength(&self->call_frames), !=)) { return EXIT_SUCCESS; } } + } else if (top_frame->instruction_pointer == top_frame->alternative->condition_token_end + 1) { + // alternative evaluation done + Value alternative_success; + if (CallFrame_StackPop(top_frame, &alternative_success) != EXIT_SUCCESS) { + // TODO: Error message + return EXIT_FAILURE; + } + if (Value_IsTruthy(&alternative_success)) { + // into body + top_frame->instruction_pointer = top_frame->alternative->body_token_start; + } else { + if (top_frame->alternative->next == NULL) { + // TODO: Error message + return EXIT_FAILURE; + } + + top_frame->alternative = top_frame->alternative->next; + top_frame->instruction_pointer = top_frame->alternative->condition_token_start; + // next alternative + } + + return EXIT_SUCCESS; } else { // function executing - while (top_frame->instruction_pointer <= top_frame->function->body_token_end) { - Interpreter_ExecuteNext(self, top_frame->function->body_token_end); + while (top_frame->instruction_pointer <= top_frame->alternative->body_token_end) { + int execute_code = Interpreter_ExecuteNext(self, top_frame->alternative->body_token_end); + if (execute_code != EXIT_SUCCESS) { + return execute_code; + } if (RPN(frame_count, DynamicArray_GetLength(&self->call_frames), !=)) { return EXIT_SUCCESS; @@ -268,15 +615,14 @@ int Interpreter_RunFrame(Interpreter* self) int Interpreter_Run(Interpreter* self) { - size_t frame_count; + size_t frame_count = 1; do { - frame_count = DynamicArray_GetLength(&self->call_frames); - if (Interpreter_RunFrame(self)) { // TODO: Return the error code return EXIT_FAILURE; } + frame_count = DynamicArray_GetLength(&self->call_frames); } while (frame_count != 0); return EXIT_SUCCESS; } @@ -284,18 +630,28 @@ int Interpreter_Run(Interpreter* self) int Interpreter_Interpret(Interpreter* self) { CallFrame* first_frame; - FlupFunctionAlternative* file_function = FlupFunctionAlternative_Malloc(SIZE_MAX, SIZE_MAX, 0, DynamicArray_GetLength(self->tokens)); - if (file_function == NULL) { + FlupFunctionAlternative* main_alternative = FlupFunctionAlternative_Malloc(SIZE_MAX-1, SIZE_MAX-1, 0, DynamicArray_GetLength(self->tokens)); + FlupFunction main_function = { + .name = StringView_FromString("
"), + .alternatives = main_alternative, + .return_type = StringView_FromString("void"), + .next = NULL, + .parameters = NULL, + }; + if (main_alternative == NULL) { return ENOMEM; } DynamicArray_AppendEmpty(&self->call_frames, (void**) &first_frame); - if (CallFrame_Create(first_frame, file_function)) { + if (CallFrame_Create(first_frame, &main_function)) { return ENOMEM; } + first_frame->instruction_pointer = 0; Interpreter_Run(self); + free(main_alternative); + return EXIT_SUCCESS; } diff --git a/src/interpreter.h b/src/interpreter.h index 47a44da..0ec4454 100644 --- a/src/interpreter.h +++ b/src/interpreter.h @@ -25,17 +25,20 @@ typedef struct FlupFunction_s { StringView name; ParameterDefinition* parameters; StringView return_type; - FlupFunctionAlternative* alternative; + FlupFunctionAlternative* alternatives; + struct FlupFunction_s* next; } FlupFunction; enum ValueType { VALUETYPE_INT64, - VALUETYPE_DOUBLE + VALUETYPE_DOUBLE, + VALUETYPE_BOOLEAN, }; union ValueContent { int64_t i64; double f64; + bool boolean; }; typedef struct Value_s { @@ -43,13 +46,22 @@ typedef struct Value_s { 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 * */ - FlupFunctionAlternative* function; + 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; diff --git a/src/main.c b/src/main.c index e9ebfd3..f90263d 100644 --- a/src/main.c +++ b/src/main.c @@ -64,9 +64,9 @@ char* load_file_string(StringView path) return NULL; } - size_t bytes_read = fread(buffer, 1, length + 1, stream); - if (bytes_read != length) { - fprintf(stderr, "Fatal Error: Failed read %li bytes from script file, got only %li\n", length, bytes_read); + size_t objects_read = fread(buffer, length, 1, stream); + if (objects_read != 1) { + fprintf(stderr, "Fatal Error: Failed read %li bytes from script file, got only %li\n", length, objects_read); free(buffer); return NULL; } diff --git a/src/tokenizer.c b/src/tokenizer.c index 1677205..4f23327 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -96,7 +96,7 @@ static Token _Tokenizer_IdentifierToken(StringView* source) static Token _Tokenizer_SimpleToken(StringView* source) { - const char* literal_table[] = { "{", "}", "&", ":", "+", "->", "-", "*", "/", "|", "==", "!=", "<", "<=", ">", ">=", "," }; + const char* literal_table[] = { "{", "}", "&", ":", "+", "->", "-", "*", "/", "|", "==", "!=", "<", "<=", ">", ">=", ",", ";" }; const enum TokenType type_table[] = { TOKENTYPE_LEFT_BRACE, TOKENTYPE_RIGHT_BRACE, @@ -115,6 +115,7 @@ static Token _Tokenizer_SimpleToken(StringView* source) TOKENTYPE_GREATERTHAN, TOKENTYPE_GREATEREQUAL, TOKENTYPE_COMMA, + TOKENTYPE_SEMICOLON, }; for (size_t i = 0; i < sizeof(literal_table) / sizeof(literal_table[0]); i++) { @@ -205,5 +206,10 @@ const char* TokenType_ToString(enum TokenType type) return "TOKENTYPE_GREATEREQUAL"; case TOKENTYPE_COMMA: return "TOKENTYPE_COMMA"; + case TOKENTYPE_SEMICOLON: + return "TOKENTYPE_SEMICOLON"; + } + + return "INVALID"; } diff --git a/src/tokenizer.h b/src/tokenizer.h index 35ae203..be20d2f 100644 --- a/src/tokenizer.h +++ b/src/tokenizer.h @@ -19,6 +19,7 @@ enum TokenType { TOKENTYPE_PIPE, TOKENTYPE_ARROW, TOKENTYPE_COLON, + TOKENTYPE_SEMICOLON, TOKENTYPE_EQUALITY, TOKENTYPE_INEQUALITY, TOKENTYPE_LESSTHAN,