From df82b1d31a93ad57ef3ea4d95690fdc2031b215a Mon Sep 17 00:00:00 2001 From: Robotnik08 Date: Wed, 13 Nov 2024 20:27:32 +0100 Subject: [PATCH 1/2] classes start --- dosato_libraries/dosato.h | 1 + include/compiler.h | 1 + include/error.h | 1 + include/node.h | 4 + include/token.h | 4 +- include/virtual-machine.h | 2 + src/compiler.c | 189 ++++++++++++++++++++++++++++++++++++-- src/error.c | 3 +- src/lexer.c | 5 + src/node.c | 4 + src/parser.c | 42 ++++++++- src/virtual-machine.c | 1 + 12 files changed, 246 insertions(+), 11 deletions(-) diff --git a/dosato_libraries/dosato.h b/dosato_libraries/dosato.h index 91916b9..92e4a39 100644 --- a/dosato_libraries/dosato.h +++ b/dosato_libraries/dosato.h @@ -111,6 +111,7 @@ typedef enum { E_EXPECTED_NUMBER, E_CANNOT_ASSIGN_TO_CONSTANT, E_INVALID_AMOUNT_SET_EXPRESSION, + E_INVALID_IDENTIFIER, // standard library errors E_FILE_NOT_FOUND, diff --git a/include/compiler.h b/include/compiler.h index f2ad9d8..58b57c0 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -13,6 +13,7 @@ typedef struct { size_t locals_count; size_t locals_capacity; DataType return_type; + bool is_class; } ScopeData; void compile(VirtualMachine* vm, AST* ast); diff --git a/include/error.h b/include/error.h index 6d9f0f8..508cf86 100644 --- a/include/error.h +++ b/include/error.h @@ -65,6 +65,7 @@ typedef enum { E_EXPECTED_NUMBER, E_CANNOT_ASSIGN_TO_CONSTANT, E_INVALID_AMOUNT_SET_EXPRESSION, + E_INVALID_IDENTIFIER, // standard library errors E_FILE_NOT_FOUND, diff --git a/include/node.h b/include/node.h index fda9a8e..a49dc1f 100644 --- a/include/node.h +++ b/include/node.h @@ -18,6 +18,8 @@ typedef enum { NODE_MASTER_CONTINUE, NODE_MASTER_SWITCH, NODE_MASTER_CONST, + NODE_MASTER_CLASS, + NODE_MASTER_METHOD, NODE_MASTER_DO_BODY, NODE_MASTER_MAKE_BODY, @@ -30,6 +32,8 @@ typedef enum { NODE_MASTER_CONTINUE_BODY, NODE_MASTER_SWITCH_BODY, NODE_MASTER_CONST_BODY, + NODE_MASTER_CLASS_BODY, + NODE_MASTER_METHOD_BODY, NODE_WHEN_BODY, diff --git a/include/token.h b/include/token.h index 1510169..6cce608 100644 --- a/include/token.h +++ b/include/token.h @@ -3,7 +3,7 @@ #include "common.h" -#define MASTER_KEYWORDS {"DO", "MAKE", "SET", "DEFINE", "INCLUDE", "IMPORT", "RETURN", "BREAK", "CONTINUE", "SWITCH", "CONST"} +#define MASTER_KEYWORDS {"DO", "MAKE", "SET", "DEFINE", "INCLUDE", "IMPORT", "RETURN", "BREAK", "CONTINUE", "SWITCH", "CONST", "CLASS", "METHOD"} #define EXTENSION_KEYWORDS {"WHEN", "WHILE", "ELSE", "CATCH", "THEN", "FOR", "IF"} #define VAR_TYPES {"INT", "BOOL", "STRING", "FLOAT", "DOUBLE", "CHAR", "SHORT", "LONG", "BYTE", "VOID", "ARRAY", "UINT", "USHORT", "ULONG", "UBYTE", "OBJECT", "VAR", "FUNCTION"} #define BOOLEAN_KEYWORDS {"FALSE", "TRUE"} @@ -132,6 +132,8 @@ typedef enum { MASTER_CONTINUE, MASTER_SWITCH, MASTER_CONST, + MASTER_CLASS, + MASTER_METHOD, M_NULL = -1 } MasterKeywordType; diff --git a/include/virtual-machine.h b/include/virtual-machine.h index e9a6656..8032f38 100644 --- a/include/virtual-machine.h +++ b/include/virtual-machine.h @@ -21,6 +21,8 @@ typedef struct { size_t* captured_indices; size_t captured_count; + bool is_class; + bool is_compiled; // compiled or imported by external library void* func_ptr; } Function; diff --git a/src/compiler.c b/src/compiler.c index 7f2a168..536070f 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -65,6 +65,8 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop break; } + case NODE_MASTER_METHOD: + case NODE_MASTER_CLASS: case NODE_MASTER_CONST: case NODE_MASTER_IMPORT: case NODE_MASTER_INCLUDE: @@ -110,6 +112,9 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop } case NODE_MASTER_RETURN_BODY: { + if (scope == NULL) { + PRINT_ERROR(E_CANT_RETURN_OUTSIDE_FUNCTION, node.start - 1); + } if (scope->return_type != D_NULL) { if (node.body.count != 0) { compileNode(vm, ci, node.body.nodes[0], ast, scope); @@ -120,7 +125,7 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop } writeInstruction(ci, node.start, OP_RETURN, DOSATO_SPLIT_SHORT(scope->locals_count)); } else { - PRINT_ERROR(E_CANT_RETURN_OUTSIDE_FUNCTION, node.body.nodes[0].start); + PRINT_ERROR(E_CANT_RETURN_OUTSIDE_FUNCTION, node.start - 1); } break; } @@ -130,10 +135,10 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop PRINT_ERROR(E_MUST_BE_GLOBAL, node.start - 1); } - DataType type = TYPE_VAR; + DataType data_type = TYPE_VAR; int identifier_index = 0; if (node.body.count == 4) { - type = ast->tokens.tokens[node.body.nodes[0].start].carry; + data_type = ast->tokens.tokens[node.body.nodes[0].start].carry; identifier_index = 1; } @@ -166,7 +171,7 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop pushScopeData(new_scope, carry); } free(hash_map); - new_scope->return_type = type; + new_scope->return_type = data_type; compileNode(vm, instance, node.body.nodes[identifier_index + 2], ast, new_scope); @@ -194,13 +199,183 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop func.argt = types; func.arity = node.body.nodes[identifier_index + 1].body.count; func.instance = instance; - func.return_type = type; + func.return_type = data_type; + + func.captured_count = 0; + + func.captured = NULL; + func.captured_indices = NULL; + + write_FunctionList(&vm->functions, func); + + break; + } + + case NODE_MASTER_METHOD_BODY: { + if (scope != NULL) { + PRINT_ERROR(E_MUST_BE_GLOBAL, node.start - 1); + } + + DataType data_type = TYPE_VAR; + int identifier_index = 0; + if (node.body.count == 4) { + data_type = ast->tokens.tokens[node.body.nodes[0].start].carry; + identifier_index = 1; + } + + char* name = getTokenString(ast->tokens.tokens[node.body.nodes[identifier_index].start]); + // check if function is already in the function list + for (int i = 0; i < vm->functions.count; i++) { + if (strcmp(vm->functions.funcs[i].name, name) == 0) { + PRINT_ERROR(E_ALREADY_DEFINED_VARIABLE, node.body.nodes[identifier_index].start); + } + } + + size_t name_index = ast->tokens.tokens[node.body.nodes[identifier_index].start].carry; + CodeInstance* instance = malloc(sizeof(CodeInstance)); + initCodeInstance(instance); + instance->ast = ast; + ScopeData* new_scope = malloc(sizeof(ScopeData)); + initScopeData(new_scope); + + int arity = node.body.nodes[identifier_index + 1].body.count; + int* hash_map = malloc(sizeof(int) * arity); + for (int i = 0; i < arity; i++) { + Node arg = node.body.nodes[identifier_index + 1].body.nodes[i]; + int carry = ast->tokens.tokens[arg.body.nodes[arg.body.count - 1].start].carry; + for (int j = 0; j < i; j++) { + if (hash_map[j] == carry) { + PRINT_ERROR(E_ALREADY_DEFINED_VARIABLE, arg.body.nodes[arg.body.count - 1].start); + } + } + hash_map[i] = carry; + pushScopeData(new_scope, carry); + } + free(hash_map); + new_scope->return_type = data_type; + + compileNode(vm, instance, node.body.nodes[identifier_index + 2], ast, new_scope); + + freeScopeData(new_scope); + free(new_scope); + + for (int i = 0; i < arity; i++) { + writeByteCode(instance, OP_POP, node.body.nodes[identifier_index + 1].body.nodes[i].end); + } + writeByteCode(instance, OP_END_FUNC, node.body.nodes[identifier_index + 2].end); + + size_t* name_indexs = malloc(sizeof(size_t) * arity); + DataType* types = malloc(sizeof(DataType) * arity); + for (int i = 0; i < arity; i++) { + NodeList list = node.body.nodes[identifier_index + 1].body.nodes[i].body; + name_indexs[i] = ast->tokens.tokens[list.nodes[list.count - 1].start].carry; + types[i] = list.count == 1 ? TYPE_VAR : ast->tokens.tokens[list.nodes[0].start].carry; + } + + Function func; + init_Function(&func); + func.name = name; + func.name_index = name_index; + func.argv = name_indexs; + func.argt = types; + func.arity = node.body.nodes[identifier_index + 1].body.count; + func.instance = instance; + func.return_type = data_type; func.captured_count = 0; func.captured = NULL; func.captured_indices = NULL; + write_FunctionList(&vm->functions, func); + break; + } + + case NODE_MASTER_CLASS_BODY: { + if (scope != NULL) { + PRINT_ERROR(E_MUST_BE_GLOBAL, node.start - 1); + } + + char* name = getTokenString(ast->tokens.tokens[node.body.nodes[0].start]); + // check if function is already in the function list + for (int i = 0; i < vm->functions.count; i++) { + if (strcmp(vm->functions.funcs[i].name, name) == 0) { + PRINT_ERROR(E_ALREADY_DEFINED_VARIABLE, node.body.nodes[0].start); + } + } + + size_t name_index = ast->tokens.tokens[node.body.nodes[0].start].carry; + CodeInstance* instance = malloc(sizeof(CodeInstance)); + initCodeInstance(instance); + instance->ast = ast; + ScopeData* new_scope = malloc(sizeof(ScopeData)); + initScopeData(new_scope); + new_scope->is_class = true; + + int arity = node.body.nodes[1].body.count; + int* hash_map = malloc(sizeof(int) * arity); + for (int i = 0; i < arity; i++) { + Node arg = node.body.nodes[1].body.nodes[i]; + int carry = ast->tokens.tokens[arg.body.nodes[arg.body.count - 1].start].carry; + for (int j = 0; j < i; j++) { + if (hash_map[j] == carry) { + PRINT_ERROR(E_ALREADY_DEFINED_VARIABLE, arg.body.nodes[arg.body.count - 1].start); + } + } + hash_map[i] = carry; + if (carry <= 1) { + PRINT_ERROR(E_INVALID_IDENTIFIER, arg.body.nodes[arg.body.count - 1].start); + } + pushScopeData(new_scope, carry); + } + + free(hash_map); + new_scope->return_type = D_NULL; + + int self_index = 1; + + // add self to the scope + pushScopeData(new_scope, self_index); + + writeByteCode(instance, OP_PUSH_NULL, node.start); + writeInstruction(instance, node.body.nodes[2].start - 1, OP_BUILD_OBJECT, DOSATO_SPLIT_SHORT(0)); + writeByteCode(instance, OP_MARK_CONSTANT, node.start); + writeInstruction(instance, node.body.nodes[2].start - 1, OP_STORE_FAST, DOSATO_SPLIT_SHORT(arity)); + writeByteCode(instance, OP_POP, node.start); + + compileNode(vm, instance, node.body.nodes[2], ast, new_scope); + + freeScopeData(new_scope); + free(new_scope); + + writeInstruction(instance, node.body.nodes[2].end, OP_RETURN, DOSATO_SPLIT_SHORT(arity)); + writeByteCode(instance, OP_END_FUNC, node.body.nodes[2].end); + + size_t* name_indexs = malloc(sizeof(size_t) * arity); + DataType* types = malloc(sizeof(DataType) * arity); + for (int i = 0; i < arity; i++) { + NodeList list = node.body.nodes[1].body.nodes[i].body; + name_indexs[i] = ast->tokens.tokens[list.nodes[list.count - 1].start].carry; + types[i] = list.count == 1 ? TYPE_VAR : ast->tokens.tokens[list.nodes[0].start].carry; + } + + Function func; + init_Function(&func); + func.name = name; + func.name_index = name_index; + func.argv = name_indexs; + func.argt = types; + func.arity = node.body.nodes[1].body.count; + func.instance = instance; + func.return_type = TYPE_OBJECT; + + func.captured_count = 0; + + func.captured = NULL; + func.captured_indices = NULL; + + func.is_class = true; + write_FunctionList(&vm->functions, func); break; @@ -735,7 +910,6 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop break; } - if (scope == NULL) { // global scope writeInstruction(ci, node.start, OP_LOAD, DOSATO_SPLIT_SHORT(ast->tokens.tokens[node.start].carry)); // load the global variable } else { @@ -1122,7 +1296,7 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop case NODE_MASTER_BREAK_BODY: case NODE_MASTER_CONTINUE_BODY: { if (ci->loop_jump_locations.count == 0) { - PRINT_ERROR(type == NODE_MASTER_BREAK_BODY ? E_BREAK_OUTSIDE_LOOP : E_CONTINUE_OUTSIDE_LOOP, node.start); + PRINT_ERROR(type == NODE_MASTER_BREAK_BODY ? E_BREAK_OUTSIDE_LOOP : E_CONTINUE_OUTSIDE_LOOP, node.start - 1); } size_t top_jump_index = ci->loop_jump_locations.locations[ci->loop_jump_locations.count - 1]; if (ci->code[top_jump_index] == OP_JUMP_IF_FALSE && type == NODE_MASTER_CONTINUE_BODY) { @@ -1380,6 +1554,7 @@ void initScopeData(ScopeData* scope) { scope->locals_capacity = 0; scope->locals_lookup = NULL; scope->return_type = D_NULL; + scope->is_class = false; } void pushScopeData (ScopeData* list, size_t item) { diff --git a/src/error.c b/src/error.c index 604b3b9..0f7dd0e 100644 --- a/src/error.c +++ b/src/error.c @@ -32,7 +32,7 @@ static const char* ERROR_MESSAGES[] = { "Invalid Type", "Master keyword must be global (DEFINE, IMPORT, INCLUDE)", "Can't return outside of function", - "Master keyword can't have extensions (MAKE, DEFINE, IMPORT, INCLUDE, CONST)", + "Master keyword can't have extensions (MAKE, DEFINE, IMPORT, INCLUDE, CONST, CLASS, METHOD)", "Else keyword must come directly after an if or when keyword", "If keyword expects Then keyword", "Break keyword can only be used inside a loop", @@ -62,6 +62,7 @@ static const char* ERROR_MESSAGES[] = { "Expected number", "Cannot reassign constant", "Mismatch in tuple expression", + "Identifier cannot be used in this context", "File not found", "File already exists", diff --git a/src/lexer.c b/src/lexer.c index 5957948..4f0ed33 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -699,6 +699,11 @@ int tokenise (TokenList* list, char* full_code, const int code_length, VirtualMa if (!hasName(&vm->mappings, "_")) { addName(&vm->mappings, "_"); } + + // add the self to the map + if (!hasName(&vm->mappings, "self")) { + addName(&vm->mappings, "self"); + } // get identifier tokens (variables, functions, etc.) for (int i = 0; i < code_length; i++) { diff --git a/src/node.c b/src/node.c index 0ec9f68..f549c62 100644 --- a/src/node.c +++ b/src/node.c @@ -27,6 +27,8 @@ static const char* NODE_NAMES[] = { "NODE_MASTER_CONTINUE", "NODE_MASTER_SWITCH", "NODE_MASTER_CONST", + "NODE_MASTER_CLASS", + "NODE_MASTER_METHOD", "NODE_MASTER_DO_BODY", "NODE_MASTER_MAKE_BODY", @@ -39,6 +41,8 @@ static const char* NODE_NAMES[] = { "NODE_MASTER_CONTINUE_BODY", "NODE_MASTER_SWITCH_BODY", "NODE_MASTER_CONST_BODY", + "NODE_MASTER_CLASS_BODY", + "NODE_MASTER_METHOD_BODY", "NODE_WHEN_BODY", "NODE_WHILE_BODY", diff --git a/src/parser.c b/src/parser.c index 9404317..ea1da16 100644 --- a/src/parser.c +++ b/src/parser.c @@ -73,7 +73,9 @@ Node parse (const char *source, size_t length, const int start, const int end, T case NODE_MASTER_BREAK: case NODE_MASTER_CONTINUE: case NODE_MASTER_SWITCH: - case NODE_MASTER_CONST: { + case NODE_MASTER_CONST: + case NODE_MASTER_CLASS: + case NODE_MASTER_METHOD: { bool body_parsed = false; ExtensionKeywordType ext_type = EXT_NULL; int ext_start = start; @@ -304,7 +306,8 @@ Node parse (const char *source, size_t length, const int start, const int end, T break; } - case NODE_MASTER_DEFINE_BODY: { + case NODE_MASTER_DEFINE_BODY: + case NODE_MASTER_METHOD_BODY: { // defining a function // parse the TYPE @@ -498,6 +501,41 @@ Node parse (const char *source, size_t length, const int start, const int end, T write_NodeList(&root.body, parse(source, length, i + 2, k, tokens, NODE_BLOCK, file_name)); break; } + + case NODE_MASTER_CLASS_BODY: { + // first token is the class name + if (tokens.tokens[start].type != TOKEN_IDENTIFIER) { + PRINT_ERROR(start, E_EXPECTED_IDENTIFIER); + } + write_NodeList(&root.body, parse(source, length, start, start + 1, tokens, NODE_IDENTIFIER, file_name)); + if (start + 1 == end) { + break; + } + // get the arguments + if (tokens.tokens[start + 1].type != TOKEN_PARENTHESIS_OPEN || !CHECK_BRACKET_TYPE(tokens.tokens[start + 1].carry, BRACKET_ROUND)) { + PRINT_ERROR(start + 1, E_EXPECTED_BRACKET_ROUND); + } + int i = getEndOfBlock(tokens, start + 1); + if (i == -1) { + PRINT_ERROR(start + 1, E_MISSING_CLOSING_PARENTHESIS); + } + write_NodeList(&root.body, parse(source, length, start + 2, i, tokens, NODE_FUNCTION_DEFINITION_PARAMETERS, file_name)); + + // body + if (tokens.tokens[i + 1].type != TOKEN_PARENTHESIS_OPEN || !CHECK_BRACKET_TYPE(tokens.tokens[i + 1].carry, BRACKET_CURLY)) { + PRINT_ERROR(i + 1, E_EXPECTED_BRACKET_CURLY); + } + int j = getEndOfBlock(tokens, i + 1); + if (j == -1) { + PRINT_ERROR(i + 1, E_MISSING_CLOSING_PARENTHESIS); + } + write_NodeList(&root.body, parse(source, length, i + 2, j, tokens, NODE_BLOCK, file_name)); + + if (j + 1 != end) { + PRINT_ERROR(j + 1, E_UNEXPECTED_TOKEN); + } + break; + } case NODE_FUNCTION_DEFINITION_PARAMETERS: { diff --git a/src/virtual-machine.c b/src/virtual-machine.c index 8fd0841..4f0ad08 100644 --- a/src/virtual-machine.c +++ b/src/virtual-machine.c @@ -145,6 +145,7 @@ void init_Function(Function* func) { func->arity = 0; func->return_type = TYPE_VOID; func->is_compiled = false; + func->is_class = false; func->func_ptr = NULL; func->captured = NULL; func->captured_indices = NULL; From c327ad8d4c7571b89aec3e238ae2f0c22600ce62 Mon Sep 17 00:00:00 2001 From: Robotnik08 Date: Thu, 14 Nov 2024 11:46:25 +0100 Subject: [PATCH 2/2] classes added --- README.md | 2 +- SECURITY.md | 4 +- demo/oop.to | 32 ++++--- include/common.h | 2 +- include/token.h | 2 +- src/compiler.c | 133 ++++++++++++++++++++++---- src/error.c | 4 +- src/lexer.c | 13 ++- src/parser.c | 2 +- src/value.c | 2 +- windows_installer/build_installer.sfx | 2 +- 11 files changed, 151 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 3dbd44e..148bbb7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

-# CDosato version 0.5 +# CDosato version 0.5.1 CDosato is the official implementation of the DOSATO programming language.
diff --git a/SECURITY.md b/SECURITY.md index 7015df7..13e2720 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,8 +6,8 @@ All versions in the release section are supported | Version | Supported | | ------- | ------------------ | -| 0.5 | :white_check_mark: | -| < 0.5 | :x: | +| 0.5.1 | :white_check_mark: | +| < 0.5.1 | :x: | ## Reporting a Vulnerability diff --git a/demo/oop.to b/demo/oop.to index e779179..b4c3b78 100644 --- a/demo/oop.to +++ b/demo/oop.to @@ -1,17 +1,27 @@ -define object playerMaker (string name, string position, int number) { - const player = { - name, position, number - } +class Person (name, age) { + set self->name = name + set self->age = age - set player->greet = void () => { - do sayln(`Hello, I'm {player->name} and I play as {player->position} ({player->number})`) + implement greet () { + do sayln(`Hello, my name is {self->name}. I am {self->age} years old`) } +} + +class Student (name, age, grade) { + set self += Person(name, age) + set self->grade = grade - return player + implement sayGrade () { + do sayln(`My grade is {self->grade}`) + } } -make object player1 = playerMaker("Bib", "Attacker", 10) -make object player2 = playerMaker("Bob", "Defender", 5) +make student1 = Student("Bob", 20, "A") + +do student1->greet() +do student1->sayGrade() + +make student2 = Student("Bib", 21, "B") -do player1->greet() -do player2->greet() \ No newline at end of file +do student2->greet() +do student2->sayGrade() \ No newline at end of file diff --git a/include/common.h b/include/common.h index ce02783..bd1cf91 100644 --- a/include/common.h +++ b/include/common.h @@ -21,7 +21,7 @@ #include #endif -#define DOSATO_VERSION "0.5" +#define DOSATO_VERSION "0.5.1" #ifndef DOSATO_DATE #define DOSATO_DATE "Unknown date" #endif diff --git a/include/token.h b/include/token.h index 6cce608..bd70a79 100644 --- a/include/token.h +++ b/include/token.h @@ -3,7 +3,7 @@ #include "common.h" -#define MASTER_KEYWORDS {"DO", "MAKE", "SET", "DEFINE", "INCLUDE", "IMPORT", "RETURN", "BREAK", "CONTINUE", "SWITCH", "CONST", "CLASS", "METHOD"} +#define MASTER_KEYWORDS {"DO", "MAKE", "SET", "DEFINE", "INCLUDE", "IMPORT", "RETURN", "BREAK", "CONTINUE", "SWITCH", "CONST", "CLASS", "IMPLEMENT"} #define EXTENSION_KEYWORDS {"WHEN", "WHILE", "ELSE", "CATCH", "THEN", "FOR", "IF"} #define VAR_TYPES {"INT", "BOOL", "STRING", "FLOAT", "DOUBLE", "CHAR", "SHORT", "LONG", "BYTE", "VOID", "ARRAY", "UINT", "USHORT", "ULONG", "UBYTE", "OBJECT", "VAR", "FUNCTION"} #define BOOLEAN_KEYWORDS {"FALSE", "TRUE"} diff --git a/src/compiler.c b/src/compiler.c index 536070f..e730502 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -212,9 +212,7 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop } case NODE_MASTER_METHOD_BODY: { - if (scope != NULL) { - PRINT_ERROR(E_MUST_BE_GLOBAL, node.start - 1); - } + // a method is like a lambda, it assigns it to the self object after DataType data_type = TYPE_VAR; int identifier_index = 0; @@ -224,14 +222,8 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop } char* name = getTokenString(ast->tokens.tokens[node.body.nodes[identifier_index].start]); - // check if function is already in the function list - for (int i = 0; i < vm->functions.count; i++) { - if (strcmp(vm->functions.funcs[i].name, name) == 0) { - PRINT_ERROR(E_ALREADY_DEFINED_VARIABLE, node.body.nodes[identifier_index].start); - } - } + size_t name_index = -1; - size_t name_index = ast->tokens.tokens[node.body.nodes[identifier_index].start].carry; CodeInstance* instance = malloc(sizeof(CodeInstance)); initCodeInstance(instance); instance->ast = ast; @@ -241,7 +233,7 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop int arity = node.body.nodes[identifier_index + 1].body.count; int* hash_map = malloc(sizeof(int) * arity); for (int i = 0; i < arity; i++) { - Node arg = node.body.nodes[identifier_index + 1].body.nodes[i]; + Node arg = node.body.nodes[identifier_index +1].body.nodes[i]; int carry = ast->tokens.tokens[arg.body.nodes[arg.body.count - 1].start].carry; for (int j = 0; j < i; j++) { if (hash_map[j] == carry) { @@ -256,11 +248,50 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop compileNode(vm, instance, node.body.nodes[identifier_index + 2], ast, new_scope); + size_t* capture_indexs = malloc(0); + int capture_index_count = 0; + + // check for each LOAD, if it is a local variable, change it to LOAD_FAST and capture the index + for (int i = 0; i < instance->count; i += getOffset(instance->code[i])) { + if (instance->code[i] == OP_LOAD) { + size_t index = DOSATO_GET_ADDRESS_SHORT(instance->code, i + 1); + if (inScope(scope, index)) { + instance->code[i] = OP_TEMP; // temporary change to TEMP + instance->code[i + 1] = (capture_index_count + arity) & 0xFF; + instance->code[i + 2] = (capture_index_count + arity) >> 8; + + capture_indexs = realloc(capture_indexs, sizeof(size_t) * (capture_index_count + 1)); + int variable_index = getScopeIndex(scope, index); + capture_indexs[capture_index_count++] = variable_index; + } + } + } + + + // each OP_LOAD_FAST must be offset by the capture_index_count and OP_TEMP must be changed to OP_LOAD_FAST + for (int i = 0; i < instance->count; i += getOffset(instance->code[i])) { + if (instance->code[i] == OP_LOAD_FAST || instance->code[i] == OP_STORE_FAST || instance->code[i] == OP_INCREMENT_FAST || instance->code[i] == OP_DECREMENT_FAST) { + size_t index = DOSATO_GET_ADDRESS_SHORT(instance->code, i + 1); + if (index < arity) { + continue; + } + instance->code[i + 1] = (index + capture_index_count) & 0xFF; + instance->code[i + 2] = (index + capture_index_count) >> 8; + } else if (instance->code[i] == OP_RETURN) { + size_t index = DOSATO_GET_ADDRESS_SHORT(instance->code, i + 1); + instance->code[i + 1] = (index + capture_index_count) & 0xFF; + instance->code[i + 2] = (index + capture_index_count) >> 8; + } else if (instance->code[i] == OP_TEMP) { + instance->code[i] = OP_LOAD_FAST; // change back without changing the index + } + } + + freeScopeData(new_scope); free(new_scope); - for (int i = 0; i < arity; i++) { - writeByteCode(instance, OP_POP, node.body.nodes[identifier_index + 1].body.nodes[i].end); + for (int i = 0; i < arity + capture_index_count; i++) { + writeByteCode(instance, OP_POP, node.end); } writeByteCode(instance, OP_END_FUNC, node.body.nodes[identifier_index + 2].end); @@ -270,7 +301,7 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop NodeList list = node.body.nodes[identifier_index + 1].body.nodes[i].body; name_indexs[i] = ast->tokens.tokens[list.nodes[list.count - 1].start].carry; types[i] = list.count == 1 ? TYPE_VAR : ast->tokens.tokens[list.nodes[0].start].carry; - } + } Function func; init_Function(&func); @@ -282,12 +313,35 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop func.instance = instance; func.return_type = data_type; - func.captured_count = 0; + func.captured_count = capture_index_count; + func.captured_indices = capture_indexs; func.captured = NULL; - func.captured_indices = NULL; write_FunctionList(&vm->functions, func); + + writeInstruction(ci, node.start, OP_LOAD_LAMBDA, DOSATO_SPLIT_SHORT(vm->functions.count - 1)); + + // assign it to the self object + // load self + int self_index = getScopeIndex(scope, 1); + + writeInstruction(ci, node.start, OP_LOAD_FAST, DOSATO_SPLIT_SHORT(self_index)); + + // load function name as constant, add it if not exist + char* val = COPY_STRING(name); + int id = -1; + if (hasName(&vm->constants_map, val)) { + id = getName(&vm->constants_map, val); + free(val); + } else { + id = addName(&vm->constants_map, val); + write_ValueArray(&vm->constants, BUILD_STRING(val, false)); + } + writeInstruction(ci, node.body.nodes[identifier_index + 2].start, OP_LOAD_CONSTANT, DOSATO_SPLIT_SHORT(id)); + + writeByteCode(ci, OP_STORE_OBJ, node.body.nodes[identifier_index].start); + writeByteCode(ci, OP_POP, node.body.nodes[identifier_index].start); break; } @@ -890,6 +944,47 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop free(val); } else { id = addName(&vm->constants_map, val); + // parse escape sequences + for (int j = 0; j < strlen(val); j++) { + if (val[j] == '\\') { + switch (val[j + 1]) { + case 'n': + val[j] = '\n'; + break; + case 't': + val[j] = '\t'; + break; + case 'r': + val[j] = '\r'; + break; + case '0': + val[j] = '\0'; + break; + case 'b': + val[j] = '\b'; + break; + case 'f': + val[j] = '\f'; + break; + case '\\': + val[j] = '\\'; + break; + case '\'': + val[j] = '\''; + break; + case '"': + val[j] = '"'; + break; + default: + val[j] = val[j + 1]; // if it's not an escape sequence, just use the character + break; + } + for (int k = j + 1; k < strlen(val); k++) { + val[k] = val[k + 1]; + } + } + } + write_ValueArray(&vm->constants, BUILD_STRING(val, false)); } @@ -963,7 +1058,7 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop } case NODE_LAMBDA_EXPRESSION: { - DataType type = ast->tokens.tokens[node.body.nodes[0].start].carry; + DataType data_type = ast->tokens.tokens[node.body.nodes[0].start].carry; char* name = COPY_STRING("lambda"); size_t name_index = -1; @@ -988,7 +1083,7 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop pushScopeData(new_scope, carry); } free(hash_map); - new_scope->return_type = type; + new_scope->return_type = data_type; compileNode(vm, instance, node.body.nodes[2], ast, new_scope); @@ -1055,7 +1150,7 @@ int compileNode (VirtualMachine* vm, CodeInstance* ci, Node node, AST* ast, Scop func.argt = types; func.arity = node.body.nodes[1].body.count; func.instance = instance; - func.return_type = type; + func.return_type = data_type; func.captured_count = capture_index_count; func.captured_indices = capture_indexs; diff --git a/src/error.c b/src/error.c index 0f7dd0e..f3f2655 100644 --- a/src/error.c +++ b/src/error.c @@ -30,9 +30,9 @@ static const char* ERROR_MESSAGES[] = { "Invalid Expression", "Expected Colon Operator (':')", "Invalid Type", - "Master keyword must be global (DEFINE, IMPORT, INCLUDE)", + "Master keyword must be global (DEFINE, IMPORT, INCLUDE, CLASS)", "Can't return outside of function", - "Master keyword can't have extensions (MAKE, DEFINE, IMPORT, INCLUDE, CONST, CLASS, METHOD)", + "Master keyword can't have extensions (MAKE, DEFINE, IMPORT, INCLUDE, CONST, CLASS, IMPLEMENT)", "Else keyword must come directly after an if or when keyword", "If keyword expects Then keyword", "Break keyword can only be used inside a loop", diff --git a/src/lexer.c b/src/lexer.c index 4f0ed33..051b4f4 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -31,7 +31,7 @@ void trimComments (TokenList* list) { list->count = tokenCount; } -#define MAX_TEMPLETE_RECURSION 255 +#define MAX_TEMPLATE_RECURSION 255 int tokenise (TokenList* list, char* full_code, const int code_length, VirtualMachine* vm, const char* file_name) { int tokenCount = list->count; @@ -55,9 +55,9 @@ int tokenise (TokenList* list, char* full_code, const int code_length, VirtualMa int stringCount = 0; bool in_template = false; - int template_start_positions[MAX_TEMPLETE_RECURSION]; - int template_bracket_depths[MAX_TEMPLETE_RECURSION]; - int template_ids[MAX_TEMPLETE_RECURSION]; + int template_start_positions[MAX_TEMPLATE_RECURSION]; + int template_bracket_depths[MAX_TEMPLATE_RECURSION]; + int template_ids[MAX_TEMPLATE_RECURSION]; int template_id = 0; int template_start_count = 0; int bracket_depth = 0; @@ -111,7 +111,7 @@ int tokenise (TokenList* list, char* full_code, const int code_length, VirtualMa } if (full_code[i] == '`' && escapeCount % 2 == 0) { in_template = true; - if (template_start_count >= MAX_TEMPLETE_RECURSION) { + if (template_start_count >= MAX_TEMPLATE_RECURSION) { printError(full_code, start, file_name, E_TEMPLATE_RECURSION_LIMIT, 1); } template_bracket_depths[template_start_count] = bracket_depth; @@ -783,8 +783,7 @@ int tokenise (TokenList* list, char* full_code, const int code_length, VirtualMa } write_ValueArray(&vm->constants, BUILD_CHAR(val)); - } - if (quote == '"') { + } else { char* val = malloc(strlen(lit) - 1); memcpy(val, lit + 1, strlen(lit) - 2); val[strlen(lit) - 2] = '\0'; diff --git a/src/parser.c b/src/parser.c index ea1da16..ead6928 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1011,7 +1011,7 @@ Node parse (const char *source, size_t length, const int start, const int end, T } else if ((tokens.tokens[new_start].type == TOKEN_TEMPLATE || tokens.tokens[new_start].type == TOKEN_TEMPLATE_END) && (tokens.tokens[new_end - 1].type == TOKEN_TEMPLATE_END && tokens.tokens[new_end - 1].carry == tokens.tokens[new_start].carry)) { write_NodeList(&root.body, parse(source, length, new_start, new_end, tokens, NODE_TEMPLATE_LITERAL, file_name)); } else { - PRINT_ERROR(new_start, E_UNEXPECTED_TOKEN); + PRINT_ERROR(new_start + 1, E_UNEXPECTED_TOKEN); } } else { if (is_unary) { diff --git a/src/value.c b/src/value.c index 031f627..df91afa 100644 --- a/src/value.c +++ b/src/value.c @@ -580,7 +580,7 @@ char* valueToStringSafe (Value value, bool extensive, DosatoObject*** pointers, } case TYPE_FUNCTION: { - string = realloc(string, strlen(string) + 8); + string = realloc(string, strlen(string) + 16); strcat(string, ""); break; } diff --git a/windows_installer/build_installer.sfx b/windows_installer/build_installer.sfx index 4481fcb..0bc1e86 100644 --- a/windows_installer/build_installer.sfx +++ b/windows_installer/build_installer.sfx @@ -10,7 +10,7 @@ Path=%ProgramFiles%\Dosato FinishMessage=Installation complete. Press OK to exit. Text { -

Dosato Installer 0.5

+

Dosato Installer 0.5.1

This installer will install or update Dosato on your computer.
Click "Install" to begin. } \ No newline at end of file