diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e660fd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bin/ diff --git a/include/utilitiec b/include/utilitiec new file mode 120000 index 0000000..1679026 --- /dev/null +++ b/include/utilitiec @@ -0,0 +1 @@ +/home/vego/Git/utilitiec/src/ \ No newline at end of file diff --git a/lib/utilitiec b/lib/utilitiec new file mode 120000 index 0000000..8945509 --- /dev/null +++ b/lib/utilitiec @@ -0,0 +1 @@ +/home/vego/Git/utilitiec/build/lib/ \ No newline at end of file diff --git a/make.sh b/make.sh new file mode 100644 index 0000000..9f07a21 --- /dev/null +++ b/make.sh @@ -0,0 +1 @@ +gcc `find src/ -name '*.c'` lib/utilitiec/libargumentc.a lib/utilitiec/libdynamicarray.a lib/utilitiec/libpointers.a lib/utilitiec/liballocator-interface.a lib/utilitiec/libStringView.a -lm -ggdb -o bin/flup diff --git a/src/interpreter.c b/src/interpreter.c new file mode 100644 index 0000000..227bfc3 --- /dev/null +++ b/src/interpreter.c @@ -0,0 +1,50 @@ +#include "interpreter.h" + +static int CallFrame_Create(CallFrame* self, FlupFunctionAlternative* f) +{ + if (DynamicArray_Create(&self->stack, sizeof(Value), 8, NULL)) { + return ENOMEM; + } + self->function = f; + self->instruction_pointer = f->body_token_start; + + return EXIT_SUCCESS; +} + +static FlupFunctionAlternative* FlupFunctionAlternative_Malloc(size_t condition_token_start, size_t condition_token_end, size_t body_token_start, size_t body_token_end) +{ + FlupFunctionAlternative* a = malloc(sizeof(FlupFunctionAlternative)); + a->next = NULL; + a->condition_token_start = condition_token_start; + a->condition_token_end = condition_token_end; + a->body_token_start = body_token_start; + a->body_token_end = body_token_end; + + return a; +} + +int Interpreter_Create(Interpreter* self, DynamicArray* tokens) +{ + if (DynamicArray_Create(&self->call_frames, sizeof(CallFrame), 16, NULL)) { + return ENOMEM; + } + self->tokens = tokens; + + return EXIT_SUCCESS; +} + +int Interpreter_Interpret(Interpreter* self) +{ + CallFrame* first_frame; + DynamicArray_AppendEmpty(&self->call_frames, (void**) &first_frame); + if (CallFrame_Create(first_frame, )) { + return ENOMEM; + } + + return EXIT_SUCCESS; +} + +void Interpreter_Destroy(Interpreter* self) +{ + DynamicArray_Destroy(&self->call_frames); +} diff --git a/src/interpreter.h b/src/interpreter.h new file mode 100644 index 0000000..05cb840 --- /dev/null +++ b/src/interpreter.h @@ -0,0 +1,59 @@ +#ifndef FLUP_INTERPRETER_H +#define FLUP_INTERPRETER_H + +#include "../include/utilitiec/dynamicarray/dynamicarray.h" + +#include "tokenizer.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 ParameterDefinition_s { + StringView name; + StringView type; +} ParameterDefinition; + +typedef struct FlupFunction_s { + StringView name; + DynamicArray argument_defs; // ParameterDefinition + StringView return_type; + FlupFunctionAlternative* alternative; +} FlupFunction; + +enum ValueType { + VALUETYPE_INT64, + VALUETYPE_DOUBLE +}; + +union ValueContent { + int64_t i64; + double f64; +}; + +typedef struct Value_s { + enum ValueType type; + union ValueContent get; +} Value; + +typedef struct CallFrame_s { + size_t instruction_pointer; + FlupFunctionAlternative* function; + DynamicArray stack; // Value +} 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 diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..2489e08 --- /dev/null +++ b/src/main.c @@ -0,0 +1,120 @@ +/* + * 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 +#include + +#include "../include/utilitiec/argumentc/argumentc.h" +#include "interpreter.h" +#include "tokenizer.h" + +int tokenize_all(StringView source, DynamicArray* a) +{ + Token t; + while ((t = Tokenizer_NextToken(&source)).type != TOKENTYPE_NONE) { + int append_code = DynamicArray_Append(a, &t); + if (append_code) return append_code; + if (t.type == TOKENTYPE_ERROR) break; + } + + return EXIT_SUCCESS; +} + +char* load_file_string(StringView path) +{ + FILE* stream = fopen(path.source, "r"); + if (stream == NULL) { + fprintf(stderr, "Fatal Error: Failed to open file at %*s\n", (int) path.length, path.source); + return NULL; + } + + if (fseek(stream, 0, SEEK_END)) { + perror("fseek"); + return NULL; + } + + long length = ftell(stream); + if (length == -1) { + perror("ftell"); + return NULL; + } + + rewind(stream); + + char* buffer = malloc(length + 1); + if (buffer == NULL) { + fprintf(stderr, "Fatal Error: Failed to allocate %li bytes\n", length); + 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); + free(buffer); + return NULL; + } + + fclose(stream); + + buffer[length] = '\0'; + + return buffer; +} + +int main(int argc, const char* argv []) +{ + Argumentc arguments; + Argumentc_Create(&arguments, argc, argv); + + Option script_file = Argumentc_PopLongArgument(&arguments, StringView_FromString("file")).argument; + if (script_file.type == OPTIONTYPE_NONE) { + fprintf(stderr, "Usage: [program] --file path/to/script_file\n"); + return 1; + } + Argumentc_Destroy(&arguments); + + char* script_string = load_file_string(script_file.content); + if (script_string == NULL) return 1; + + StringView source = StringView_FromString(script_string); + DynamicArray tokens; + if (DynamicArray_Create(&tokens, sizeof(Token), 128, NULL)) { + fprintf(stderr, "Fatal Error: Failed to create dynamicarray\n"); + return 1; + } + + if (tokenize_all(source, &tokens)) { + fprintf(stderr, "Fatal Error: Out of Memory in tokenizing\n"); + return 1; + } + + Interpreter interpreter; + Interpreter_Create(&interpreter, &tokens); + + + Interpreter_Interpret(&interpreter); + + Interpreter_Destroy(&interpreter); + DynamicArray_Destroy(&tokens); + + free(script_string); + + return EXIT_SUCCESS; +} diff --git a/src/tokenizer.c b/src/tokenizer.c new file mode 100644 index 0000000..1677205 --- /dev/null +++ b/src/tokenizer.c @@ -0,0 +1,209 @@ +#include "tokenizer.h" +#include +#include + +static StringView StringViewOfNumberTillNextNonDigit(StringView* source) { + StringView stringViewOfNumber = StringView_Slice(*source, 0, 0); + while (source->length != 0 && isdigit(source->source[0])) { + *source = StringView_Drop(*source, 1); + stringViewOfNumber.length ++; + } + return stringViewOfNumber; +} + +static int64_t* _StringView_FoldInt64(char c, int64_t* i) +{ + *i = *i * 10 + (c - '0'); + return i; +} + +static double* _StringView_FoldDouble(char c, double* d) +{ + *d = *d * 10 + (c - '0'); + return d; +} + +static Token _Tokenizer_ParseInt64(bool negative, StringView integerPart) +{ + int64_t theInt64 = 0; + StringView_FoldLeft(integerPart, &theInt64, (StringViewFoldFunction) _StringView_FoldInt64); + + return (Token) { + .type = TOKENTYPE_INTEGER, + .get = { + .integer = theInt64 * (negative ? -1 : 1) + } + }; +} + +static Token _Tokenizer_ParseDouble(bool negative, StringView integerPart, StringView decimalPart) +{ + double theDouble = 0.0; + StringView_FoldLeft(integerPart, &theDouble, (StringViewFoldFunction) _StringView_FoldDouble); + double theDecimal = 0.0; + StringView_FoldLeft(decimalPart, &theDecimal, (StringViewFoldFunction) _StringView_FoldDouble); + + double result = (negative ? -1 : 1) * (theDouble + theDecimal / pow(10.0, decimalPart.length)); + + return (Token) { + .type = TOKENTYPE_DOUBLE, + .get = { + .decimal = result + } + }; +} + +static Token _Tokenizer_NumberToken(StringView* source) +{ + bool negative = false; + if (StringView_StartsWith(*source, StringView_FromString("-"))) { + negative = true; + *source = StringView_Drop(*source, 1); + } + + StringView integerPart = StringViewOfNumberTillNextNonDigit(source); + bool has_point = false; + if (source->length != 0 && source->source[0] == '.') { + *source = StringView_Drop(*source, 1); + has_point = true; + } + StringView decimalPart = StringViewOfNumberTillNextNonDigit(source); + + if (has_point) { + return _Tokenizer_ParseDouble(negative, integerPart, decimalPart); + } else { + return _Tokenizer_ParseInt64(negative, integerPart); + } +} + +static bool _Tokenizer_IdentifierLetter(char c) +{ + return isalnum(c); +} + +static Token _Tokenizer_IdentifierToken(StringView* source) +{ + StringView identifier = StringView_TakeWhile(*source, _Tokenizer_IdentifierLetter); + *source = StringView_Drop(*source, identifier.length); + + return (Token) { + .type = TOKENTYPE_IDENTIFIER, + .get = { + .identifier = identifier, + } + }; +} + +static Token _Tokenizer_SimpleToken(StringView* source) +{ + const char* literal_table[] = { "{", "}", "&", ":", "+", "->", "-", "*", "/", "|", "==", "!=", "<", "<=", ">", ">=", "," }; + const enum TokenType type_table[] = { + TOKENTYPE_LEFT_BRACE, + TOKENTYPE_RIGHT_BRACE, + TOKENTYPE_AMPERSAND, + TOKENTYPE_COLON, + TOKENTYPE_PLUS, + TOKENTYPE_ARROW, + TOKENTYPE_MINUS, + TOKENTYPE_MULTIPLY, + TOKENTYPE_DIVIDE, + TOKENTYPE_PIPE, + TOKENTYPE_EQUALITY, + TOKENTYPE_INEQUALITY, + TOKENTYPE_LESSTHAN, + TOKENTYPE_LESSEQUAL, + TOKENTYPE_GREATERTHAN, + TOKENTYPE_GREATEREQUAL, + TOKENTYPE_COMMA, + }; + + for (size_t i = 0; i < sizeof(literal_table) / sizeof(literal_table[0]); i++) { + StringView literal_view = StringView_FromString(literal_table[i]); + if (StringView_StartsWith(*source, literal_view)) { + *source = StringView_Drop(*source, literal_view.length); + return (Token) { + .type = type_table[i], + .get = { .identifier = STRINGVIEW_NONE } + }; + } + } + return TOKEN_NONE; +} + +Token Tokenizer_NextToken(StringView* source) +{ + while (source->length != 0 && isspace(source->source[0])) { + 0[source] = StringView_Slice(*source, 1, source->length); + } + + if (source->length == 0) { + return TOKEN_NONE; + } + + { + Token simple_token = _Tokenizer_SimpleToken(source); + if (simple_token.type != TOKENTYPE_NONE) { + return simple_token; + } + } + + if (isdigit(source->source[0]) || StringView_StartsWith(*source, StringView_FromString("-"))) { + // parse int/double + return _Tokenizer_NumberToken(source); + } else if (isalpha(source->source[0])) { + // parse name + return _Tokenizer_IdentifierToken(source); + } else { + return (Token) {.type = TOKENTYPE_ERROR, .get = {.error = *source } }; + } +} + +const char* TokenType_ToString(enum TokenType type) +{ + switch (type) { + case TOKENTYPE_NONE: + return "TOKENTYPE_NONE"; + case TOKENTYPE_INTEGER: + return "TOKENTYPE_INTEGER"; + case TOKENTYPE_DOUBLE: + return "TOKENTYPE_DOUBLE"; + case TOKENTYPE_IDENTIFIER: + return "TOKENTYPE_IDENTIFIER"; + case TOKENTYPE_LEFT_BRACE: + return "TOKENTYPE_LEFT_BRACE"; + case TOKENTYPE_RIGHT_BRACE: + return "TOKENTYPE_RIGHT_BRACE"; + case TOKENTYPE_AMPERSAND: + return "TOKENTYPE_AMPERSAND"; + case TOKENTYPE_PLUS: + return "TOKENTYPE_PLUS"; + case TOKENTYPE_MINUS: + return "TOKENTYPE_MINUS"; + case TOKENTYPE_MULTIPLY: + return "TOKENTYPE_MULTIPLY"; + case TOKENTYPE_DIVIDE: + return "TOKENTYPE_DIVIDE"; + case TOKENTYPE_PIPE: + return "TOKENTYPE_PIPE"; + case TOKENTYPE_ARROW: + return "TOKENTYPE_ARROW"; + case TOKENTYPE_COLON: + return "TOKENTYPE_COLON"; + case TOKENTYPE_ERROR: + return "TOKENTYPE_ERROR"; + case TOKENTYPE_EQUALITY: + return "TOKENTYPE_EQUALITY"; + case TOKENTYPE_INEQUALITY: + return "TOKENTYPE_INEQUALITY"; + case TOKENTYPE_LESSTHAN: + return "TOKENTYPE_LESSTHAN"; + case TOKENTYPE_LESSEQUAL: + return "TOKENTYPE_LESSEQUAL"; + case TOKENTYPE_GREATERTHAN: + return "TOKENTYPE_GREATERTHAN"; + case TOKENTYPE_GREATEREQUAL: + return "TOKENTYPE_GREATEREQUAL"; + case TOKENTYPE_COMMA: + return "TOKENTYPE_COMMA"; + } +} diff --git a/src/tokenizer.h b/src/tokenizer.h new file mode 100644 index 0000000..c794417 --- /dev/null +++ b/src/tokenizer.h @@ -0,0 +1,50 @@ +#ifndef FLUP_TOKENIZER_H +#define FLUP_TOKENIZER_H + +#include "../include/utilitiec/StringView/StringView.h" +#include + +enum TokenType { + TOKENTYPE_NONE, + TOKENTYPE_INTEGER, + TOKENTYPE_DOUBLE, + TOKENTYPE_IDENTIFIER, + TOKENTYPE_LEFT_BRACE, + TOKENTYPE_RIGHT_BRACE, + TOKENTYPE_AMPERSAND, + TOKENTYPE_PLUS, + TOKENTYPE_MINUS, + TOKENTYPE_MULTIPLY, + TOKENTYPE_DIVIDE, + TOKENTYPE_PIPE, + TOKENTYPE_ARROW, + TOKENTYPE_COLON, + TOKENTYPE_EQUALITY, + TOKENTYPE_INEQUALITY, + TOKENTYPE_LESSTHAN, + TOKENTYPE_LESSEQUAL, + TOKENTYPE_GREATERTHAN, + TOKENTYPE_GREATEREQUAL, + TOKENTYPE_COMMA, + TOKENTYPE_ERROR, +}; + +union TokenContent { + StringView identifier; + StringView error; + int64_t integer; + double decimal; +}; + +typedef struct Token_s { + enum TokenType type; + union TokenContent get; +} Token; + + +#define TOKEN_NONE ((Token) {.type = TOKENTYPE_NONE, .get = {.error = STRINGVIEW_NONE } } ) + +Token Tokenizer_NextToken(StringView* source); +const char* TokenType_ToString(enum TokenType type); + +#endif //header guard diff --git a/templates/C-Header.h b/templates/C-Header.h new file mode 100644 index 0000000..38c9b91 --- /dev/null +++ b/templates/C-Header.h @@ -0,0 +1,19 @@ +/* + * 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. + */ diff --git a/templates/C-Source.c b/templates/C-Source.c new file mode 100644 index 0000000..38c9b91 --- /dev/null +++ b/templates/C-Source.c @@ -0,0 +1,19 @@ +/* + * 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. + */