diff --git a/LBS/a.out b/LBS/a.out new file mode 100755 index 00000000..f5278019 Binary files /dev/null and b/LBS/a.out differ diff --git a/LBS/main.c b/LBS/main.c new file mode 100644 index 00000000..fa770801 --- /dev/null +++ b/LBS/main.c @@ -0,0 +1,66 @@ +#include +#include +#include + +struct Builder { + char *output; + char *inputPath; + char **inputFiles; + char **importPaths; + char **importFiles; +}; + +int main(void) { + FILE *file = fopen("Lumabuild", "r"); + if (!file) { + perror("Failed to open file Lumabuild"); + return -1; + } + + char ch; + int startOfLine = 1; + char currentLine[256]; + int linePos = 0; + char *fileContents[256]; + int lineCount = 0; + + while ((ch = fgetc(file)) != EOF) { + if (startOfLine) { + if (ch == '#') { + // Skip the rest of the line + while ((ch = fgetc(file)) != EOF && ch != '\n') + ; + startOfLine = 1; + continue; + } + } + + // Store character in current line buffer + if (ch != '\n' && linePos < 255) { + currentLine[linePos++] = ch; + } + + // When we hit newline or EOF, store the completed line + if (ch == '\n' || ch == EOF) { + if (linePos > 0) { + currentLine[linePos] = '\0'; + fileContents[lineCount] = malloc(strlen(currentLine) + 1); + strcpy(fileContents[lineCount], currentLine); + lineCount++; + linePos = 0; + } + startOfLine = 1; + } else { + startOfLine = 0; + } + } + + // Print stored lines + for (int i = 0; i < lineCount; i++) { + printf("Line %d: %s\n", i + 1, fileContents[i]); + free(fileContents[i]); // Clean up + } + + fclose(file); + return 0; +} diff --git a/mockups/a.out b/mockups/a.out new file mode 100755 index 00000000..2de10155 Binary files /dev/null and b/mockups/a.out differ diff --git a/mockups/file.c b/mockups/file.c new file mode 100644 index 00000000..7804f6d4 --- /dev/null +++ b/mockups/file.c @@ -0,0 +1,141 @@ +#include +#include +#include + +// used to prototype options +enum { SUCCESS, ERROR }; +enum { + FAIL_OPEN = 1, + FAIL_READ = 2, + FAIL_WRITE = 3, + FAIL_ACCESS = 4, + NONE = 0 +}; + +typedef struct FILE_STRUCT { + // store the pointer to the file + FILE *fileptr; + char *file_name; + char *file_flag; // r, w, rw + // position in the file + int position; + +} File; + +// file return should return a file struct or error information +typedef struct FILE_RET { + File *file_core; + int OK_ERR; + int FAIL_ON; +} File_Re; + +// private used to init the values of the file object +File_Re *file_init(char *file_name, char *file_flag) { + File_Re *fre = (File_Re *)malloc(sizeof(File_Re)); + if (!fre) + return NULL; + + File *file = (File *)malloc(sizeof(File)); + if (!file) { + free(fre); + return NULL; + } + + // FLAGS r, w, rw, a, ab, r+, w+, + file->file_name = file_name; + file->file_flag = file_flag; + file->position = 0; + file->fileptr = NULL; + + fre->file_core = file; + fre->OK_ERR = SUCCESS; + fre->FAIL_ON = NONE; + + return fre; +} + +// used to open a specific file. Strict error handling so if a empty buffer was +// passed in we say NO +File_Re *file_open(File_Re *fre) { + if (!fre->file_core) { + if (!fre) + return NULL; + fre->OK_ERR = ERROR; + fre->FAIL_ON = FAIL_OPEN; + return fre; + } + + // always try and open file + fre->file_core->fileptr = + fopen(fre->file_core->file_name, fre->file_core->file_flag); + if (!fre->file_core->fileptr) { + fre->OK_ERR = ERROR; + fre->FAIL_ON = FAIL_OPEN; + } else { + fre->OK_ERR = SUCCESS; + fre->FAIL_ON = NONE; + } + return fre; +} + +// used to close a file object and deinit +File_Re *file_close(File_Re *fre) { + if (!fre) + return NULL; + + if (fre->file_core) { + if (fre->file_core->fileptr) { + fclose(fre->file_core->fileptr); + fre->file_core->fileptr = NULL; + } + free(fre->file_core); + fre->file_core = NULL; + } + fre->OK_ERR = SUCCESS; + fre->FAIL_ON = NONE; + return fre; +} + +char *fail_type(int type) { + char *to_return; + switch (type) { + case FAIL_OPEN: + to_return = "FAIL_OPEN"; + break; + case FAIL_READ: + to_return = "FAIL_READ"; + break; + case FAIL_WRITE: + to_return = "FAIL_WRITE"; + break; + case FAIL_ACCESS: + to_return = "FAIL_ACCESS"; + break; + case NONE: + to_return = "NONE"; + break; + } + return to_return; +} + +int main(void) { + + File_Re *fre = file_init("text.txt", "r"); + if (!fre) { + printf("Failed to init file\n"); + } + printf("after init\n"); + + fre = file_open(fre); + if (fre->OK_ERR == ERROR) + printf("Failed to open file, error: %s\n", fail_type(fre->FAIL_ON)); + else + printf("After open\n"); + + fre = file_close(fre); + printf("after close\n"); + + free(fre); + + return 0; +} diff --git a/mockups/text.txt b/mockups/text.txt new file mode 100644 index 00000000..b5fd817d --- /dev/null +++ b/mockups/text.txt @@ -0,0 +1 @@ +some stuff diff --git a/src/ast/ast.h b/src/ast/ast.h index 20eab7ee..4ab2cbd1 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -11,451 +11,467 @@ typedef struct AstNode AstNode; // Node type enumeration typedef enum { - // Preprocessor nodes - AST_PREPROCESSOR_MODULE, // Module declaration - AST_PREPROCESSOR_USE, // Use/import statement - - // Expression nodes - AST_EXPR_LITERAL, // Literal values (numbers, strings, booleans) - AST_EXPR_IDENTIFIER, // Variable/function names - AST_EXPR_BINARY, // Binary operations (+, -, *, /, etc.) - AST_EXPR_UNARY, // Unary operations (!, -, ++, --) - AST_EXPR_CALL, // Function calls - AST_EXPR_ASSIGNMENT, // Assignment expressions - AST_EXPR_TERNARY, // Conditional expressions (? :) - AST_EXPR_MEMBER, // Member access (obj.field) - AST_EXPR_INDEX, // Array/object indexing (obj[index]) - AST_EXPR_GROUPING, // Parenthesized expressions - AST_EXPR_RANGE, // range expressions '..' - AST_EXPR_ARRAY, // [ ... ] array expressions - AST_EXPR_DEREF, // *object - AST_EXPR_ADDR, // &object - AST_EXPR_ALLOC, - AST_EXPR_MEMCPY, - AST_EXPR_FREE, - AST_EXPR_CAST, - AST_EXPR_INPUT, - AST_EXPR_SIZEOF, - AST_EXPR_SYSTEM, // System Statement - - // Statement nodes - AST_PROGRAM, // Program root node - AST_STMT_EXPRESSION, // Expression statements - AST_STMT_VAR_DECL, // Variable declarations - AST_STMT_CONST_DECL, // Constant declarations - AST_STMT_FUNCTION, // Function definitions - AST_STMT_IF, // If statements - AST_STMT_LOOP, // Loop statements (while, for) - AST_STMT_BREAK_CONTINUE, // Break and continue statements - AST_STMT_RETURN, // Return statements - AST_STMT_BLOCK, // Block statements - AST_STMT_PRINT, // Print statements - AST_STMT_MODULE, // Module declarations - AST_STMT_ENUM, // Enum declarations - AST_STMT_STRUCT, // Struct declarations - AST_STMT_FIELD_DECL, // Field declarations (for structs) - AST_STMT_DEFER, // Defer statements - AST_STMT_SWITCH, // Switch statement - AST_STMT_CASE, - AST_STMT_DEFAULT, - - // Type nodes - AST_TYPE_BASIC, // Basic types (int, float, string, etc.) - AST_TYPE_POINTER, // Pointer types - AST_TYPE_ARRAY, // Array types - AST_TYPE_FUNCTION, // Function types - AST_TYPE_STRUCT, // Struct types - AST_TYPE_ENUM, // Enum types + // Preprocessor nodes + AST_PREPROCESSOR_MODULE, // Module declaration + AST_PREPROCESSOR_USE, // Use/import statement + + // Expression nodes + AST_EXPR_LITERAL, // Literal values (numbers, strings, booleans) + AST_EXPR_IDENTIFIER, // Variable/function names + AST_EXPR_BINARY, // Binary operations (+, -, *, /, etc.) + AST_EXPR_UNARY, // Unary operations (!, -, ++, --) + AST_EXPR_CALL, // Function calls + AST_EXPR_ASSIGNMENT, // Assignment expressions + AST_EXPR_TERNARY, // Conditional expressions (? :) + AST_EXPR_MEMBER, // Member access (obj.field) + AST_EXPR_INDEX, // Array/object indexing (obj[index]) + AST_EXPR_GROUPING, // Parenthesized expressions + AST_EXPR_RANGE, // range expressions '..' + AST_EXPR_ARRAY, // [ ... ] array expressions + AST_EXPR_DEREF, // *object + AST_EXPR_ADDR, // &object + AST_EXPR_ALLOC, + AST_EXPR_MEMCPY, + AST_EXPR_FREE, + AST_EXPR_CAST, + AST_EXPR_INPUT, + AST_EXPR_SIZEOF, + AST_EXPR_SYSTEM, // System Statement + + // Statement nodes + AST_PROGRAM, // Program root node + AST_STMT_EXPRESSION, // Expression statements + AST_STMT_VAR_DECL, // Variable declarations + AST_STMT_CONST_DECL, // Constant declarations + AST_STMT_FUNCTION, // Function definitions + AST_STMT_IF, // If statements + AST_STMT_LOOP, // Loop statements (while, for) + AST_STMT_BREAK_CONTINUE, // Break and continue statements + AST_STMT_RETURN, // Return statements + AST_STMT_BLOCK, // Block statements + AST_STMT_PRINT, // Print statements + AST_STMT_MODULE, // Module declarations + AST_STMT_ENUM, // Enum declarations + AST_STMT_STRUCT, // Struct declarations + AST_STMT_FIELD_DECL, // Field declarations (for structs) + AST_STMT_DEFER, // Defer statements + AST_STMT_SWITCH, // Switch statement + AST_STMT_IMPL, // impl statement + AST_STMT_CASE, + AST_STMT_DEFAULT, + + // Type nodes + AST_TYPE_BASIC, // Basic types (int, float, string, etc.) + AST_TYPE_POINTER, // Pointer types + AST_TYPE_ARRAY, // Array types + AST_TYPE_FUNCTION, // Function types + AST_TYPE_STRUCT, // Struct types + AST_TYPE_ENUM, // Enum types } NodeType; // Literal types typedef enum { - LITERAL_IDENT, - LITERAL_INT, - LITERAL_FLOAT, - LITERAL_DOUBLE, - LITERAL_STRING, - LITERAL_CHAR, - LITERAL_BOOL, - LITERAL_NULL + LITERAL_IDENT, + LITERAL_INT, + LITERAL_FLOAT, + LITERAL_DOUBLE, + LITERAL_STRING, + LITERAL_CHAR, + LITERAL_BOOL, + LITERAL_NULL } LiteralType; // Binary operators typedef enum { - BINOP_ADD, // + - BINOP_SUB, // - - BINOP_MUL, // * - BINOP_DIV, // / - BINOP_MOD, // % - BINOP_POW, // ** - BINOP_EQ, // == - BINOP_NE, // != - BINOP_LT, // < - BINOP_LE, // <= - BINOP_GT, // > - BINOP_GE, // >= - BINOP_AND, // && - BINOP_OR, // || - BINOP_BIT_AND, // & - BINOP_BIT_OR, // | - BINOP_BIT_XOR, // ^ - BINOP_SHL, // << - BINOP_SHR, // >> - BINOP_RANGE, // .. + BINOP_ADD, // + + BINOP_SUB, // - + BINOP_MUL, // * + BINOP_DIV, // / + BINOP_MOD, // % + BINOP_POW, // ** + BINOP_EQ, // == + BINOP_NE, // != + BINOP_LT, // < + BINOP_LE, // <= + BINOP_GT, // > + BINOP_GE, // >= + BINOP_AND, // && + BINOP_OR, // || + BINOP_BIT_AND, // & + BINOP_BIT_OR, // | + BINOP_BIT_XOR, // ^ + BINOP_SHL, // << + BINOP_SHR, // >> + BINOP_RANGE, // .. } BinaryOp; // Unary operators typedef enum { - UNOP_NOT, // ! - UNOP_NEG, // - - UNOP_POS, // + - UNOP_BIT_NOT, // ~ - UNOP_PRE_INC, // ++x - UNOP_PRE_DEC, // --x - UNOP_POST_INC, // x++ - UNOP_POST_DEC, // x-- - UNOP_DEREF, // *x - UNOP_ADDR, // &x + UNOP_NOT, // ! + UNOP_NEG, // - + UNOP_POS, // + + UNOP_BIT_NOT, // ~ + UNOP_PRE_INC, // ++x + UNOP_PRE_DEC, // --x + UNOP_POST_INC, // x++ + UNOP_POST_DEC, // x-- + UNOP_DEREF, // *x + UNOP_ADDR, // &x } UnaryOp; typedef enum { - Node_Category_EXPR, - Node_Category_STMT, - Node_Category_TYPE, - Node_Category_PREPROCESSOR + Node_Category_EXPR, + Node_Category_STMT, + Node_Category_TYPE, + Node_Category_PREPROCESSOR } NodeCategory; // Base AST node structure struct AstNode { - NodeType type; - size_t line; - size_t column; - NodeCategory category; // Category of the node (expression, statement, type) - - union { - struct { - union { - // Preprocessor-specific data - struct { - char *name; - int potions; - AstNode **body; - size_t body_count; - const char *file_path; - Token *tokens; - size_t token_count; - void *scope; // NEW: Add this field - } module; - - // @use "module_name" as module; - struct { - const char *module_name; - const char *alias; - } use; - }; - } preprocessor; - - struct { - // Expression-specific data - union { - // Literal expression - struct { - LiteralType lit_type; - union { - long long int_val; - double float_val; - char *string_val; - char char_val; - bool bool_val; - } value; - } literal; - - // Identifier expression - struct { - char *name; - } identifier; - - // Binary expression - struct { - BinaryOp op; - AstNode *left; // Changed from Expr* to AstNode* - AstNode *right; // Changed from Expr* to AstNode* - } binary; - - // Unary expression - struct { - UnaryOp op; - AstNode *operand; // Changed from Expr* to AstNode* - } unary; - - // Function call expression - struct { - AstNode *callee; // Changed from Expr* to AstNode* - AstNode **args; // Changed from Expr** to AstNode** - size_t arg_count; - } call; - - // Assignment expression - struct { - AstNode *target; // Changed from Expr* to AstNode* - AstNode *value; // Changed from Expr* to AstNode* - } assignment; - - // Ternary expression - struct { - AstNode *condition; // Changed from Expr* to AstNode* - AstNode *then_expr; // Changed from Expr* to AstNode* - AstNode *else_expr; // Changed from Expr* to AstNode* - } ternary; - - // Member access expression - struct { - bool is_compiletime; - AstNode *object; // Changed from Expr* to AstNode* - char *member; - } member; - - // Index expression - struct { - AstNode *object; // Changed from Expr* to AstNode* - AstNode *index; // Changed from Expr* to AstNode* - } index; - - // Grouping expression - struct { - AstNode *expr; // Changed from Expr* to AstNode* - } grouping; - - // Array expression - struct { - AstNode **elements; // Changed from Expr** to AstNode** - size_t element_count; - } array; - - // Deref expression - struct { - AstNode *object; - } deref; - - // Address experssion - struct { - AstNode *object; - } addr; - - // alloc expression - struct { - AstNode *size; - } alloc; - - // memcpy expression - struct { - AstNode *to; - AstNode *from; - AstNode *size; - } memcpy; - - // free expression - struct { - AstNode *ptr; - } free; - - // cast expression - struct { - AstNode *type; - AstNode *castee; - } cast; - - // input expression - struct { - AstNode *type; - AstNode *msg; - } input; - - // system expression - struct { - AstNode *command; - } _system; - - // sizeof expression - struct { - AstNode *object; - bool is_type; - } size_of; - }; - } expr; - - struct { - // Statement-specific data - union { - // Program root node - struct { - AstNode **modules; // Changed from Stmt** to AstNode** - size_t module_count; - } program; - - // Expression statement - struct { - AstNode *expression; // Changed from Expr* to AstNode* - } expr_stmt; - - // Variable declaration - struct { - const char *name; - AstNode *var_type; // Changed from Type* to AstNode* - AstNode *initializer; // Changed from Expr* to AstNode* - bool is_mutable; // Whether the variable is mutable - bool is_public; - } var_decl; - - // const Persion = struct { - // pub: - // name: str; - // age: int; - // priv: - // ssn: str; - // }; - // Struct declaration - struct { - const char *name; - AstNode **public_members; // Changed from Stmt** to AstNode** - size_t public_count; - AstNode **private_members; // Changed from Stmt** to AstNode** - size_t private_count; - bool is_public; // Whether the struct is public (which is true by - // default) - } struct_decl; - - struct { - const char *name; - AstNode *type; // Changed from Type* to AstNode* - AstNode *function; // Changed from Stmt* to AstNode* - bool is_public; // Whether the field is public - } field_decl; - - // Enumeration declaration - struct { - const char *name; - char **members; // Changed from char** to AstNode** - size_t member_count; - bool is_public; // same as struct, enums are public by default - } enum_decl; - - // Function declaration - struct { - const char *name; - char **param_names; - AstNode **param_types; // Changed from Type** to AstNode** - size_t param_count; - AstNode *return_type; // Changed from Type* to AstNode* - bool is_public; - AstNode *body; // Changed from Stmt* to AstNode* - bool returns_ownership; - bool takes_ownership; - } func_decl; - - // If statement - struct { - AstNode *condition; // Changed from Expr* to AstNode* - AstNode *then_stmt; // Changed from Stmt* to AstNode* - AstNode **elif_stmts; // Changed from Stmt* to AstNode* - int elif_count; // Number of elif statements - AstNode *else_stmt; // Changed from Stmt* to AstNode* - } if_stmt; - - // Loop statement (Combined while and for) - struct { - AstNode *condition; // Changed from Expr* to AstNode* - AstNode *optional; // Optional expression (e.g., for loop initializer) - AstNode *body; // Changed from Stmt* to AstNode* - // For loops can be represented as a while loop with an initializer - // and increment - AstNode **initializer; // Optional initializer for for loops (Changed - // from Stmt* to AstNode*) - size_t init_count; // Number of initializers - } loop_stmt; - - // Return statement - struct { - AstNode *value; // Changed from Expr* to AstNode* - } return_stmt; - - // Block statement - struct { - AstNode **statements; // Changed from Stmt** to AstNode** - size_t stmt_count; - } block; - - // Print statement - struct { - AstNode **expressions; // Changed from Expr** to AstNode** - size_t expr_count; - bool ln; // Whether to print with a newline - } print_stmt; - - struct { - bool is_continue; // true for continue, false for break - } break_continue; - - struct { - AstNode *statement; - } defer_stmt; - - struct { - AstNode *condition; // The switch expression - struct AstNode **cases; // Array of case clauses - size_t case_count; - struct AstNode *default_case; // Optional default case - } switch_stmt; - - // Case clause node - struct { - AstNode **values; // Array of expressions (0, 1, 2, 3, ...) - size_t value_count; // Number of values in this case - AstNode *body; // Block statement for this case - } case_clause; - - struct { - AstNode *body; // Block statement for default case - } default_clause; - }; - } stmt; - - struct { - // Type-specific data - union { - // Basic type - struct { - const char *name; - } basic; - - // Pointer type - struct { - AstNode *pointee_type; // Changed from Type* to AstNode* - } pointer; - - // Array type - struct { - AstNode *element_type; // Changed from Type* to AstNode* - AstNode *size; // Changed from Expr* to AstNode* - } array; - - // Function type - struct { - AstNode **param_types; // Changed from Type** to AstNode** - size_t param_count; - AstNode *return_type; // Changed from Type* to AstNode* - } function; - - // AST_TYPE_STRUCT - ADD THIS STRUCT - struct { - const char *name; // Name of the struct type - AstNode **member_types; // Array of member types - const char **member_names; // Array of member names - size_t member_count; // Number of members - } struct_type; - }; - } type_data; - }; + NodeType type; + size_t line; + size_t column; + NodeCategory category; // Category of the node (expression, statement, type) + + union { + struct { + union { + // Preprocessor-specific data + struct { + char *name; + int potions; + AstNode **body; + size_t body_count; + const char *file_path; + Token *tokens; + size_t token_count; + void *scope; // NEW: Add this field + } module; + + // @use "module_name" as module; + struct { + const char *module_name; + const char *alias; + } use; + }; + } preprocessor; + + struct { + // Expression-specific data + union { + // Literal expression + struct { + LiteralType lit_type; + union { + long long int_val; + double float_val; + char *string_val; + char char_val; + bool bool_val; + } value; + } literal; + + // Identifier expression + struct { + char *name; + } identifier; + + // Binary expression + struct { + BinaryOp op; + AstNode *left; // Changed from Expr* to AstNode* + AstNode *right; // Changed from Expr* to AstNode* + } binary; + + // Unary expression + struct { + UnaryOp op; + AstNode *operand; // Changed from Expr* to AstNode* + } unary; + + // Function call expression + struct { + AstNode *callee; // Changed from Expr* to AstNode* + AstNode **args; // Changed from Expr** to AstNode** + size_t arg_count; + } call; + + // Assignment expression + struct { + AstNode *target; // Changed from Expr* to AstNode* + AstNode *value; // Changed from Expr* to AstNode* + } assignment; + + // Ternary expression + struct { + AstNode *condition; // Changed from Expr* to AstNode* + AstNode *then_expr; // Changed from Expr* to AstNode* + AstNode *else_expr; // Changed from Expr* to AstNode* + } ternary; + + // Member access expression + struct { + bool is_compiletime; + AstNode *object; // Changed from Expr* to AstNode* + char *member; + } member; + + // Index expression + struct { + AstNode *object; // Changed from Expr* to AstNode* + AstNode *index; // Changed from Expr* to AstNode* + } index; + + // Grouping expression + struct { + AstNode *expr; // Changed from Expr* to AstNode* + } grouping; + + // Array expression + struct { + AstNode **elements; // Changed from Expr** to AstNode** + size_t element_count; + } array; + + // Deref expression + struct { + AstNode *object; + } deref; + + // Address experssion + struct { + AstNode *object; + } addr; + + // alloc expression + struct { + AstNode *size; + } alloc; + + // memcpy expression + struct { + AstNode *to; + AstNode *from; + AstNode *size; + } memcpy; + + // free expression + struct { + AstNode *ptr; + } free; + + // cast expression + struct { + AstNode *type; + AstNode *castee; + } cast; + + // input expression + struct { + AstNode *type; + AstNode *msg; + } input; + + // system expression + struct { + AstNode *command; + } _system; + + // sizeof expression + struct { + AstNode *object; + bool is_type; + } size_of; + }; + } expr; + + struct { + // Statement-specific data + union { + // Program root node + struct { + AstNode **modules; // Changed from Stmt** to AstNode** + size_t module_count; + } program; + + // Expression statement + struct { + AstNode *expression; // Changed from Expr* to AstNode* + } expr_stmt; + + // Variable declaration + struct { + const char *name; + AstNode *var_type; // Changed from Type* to AstNode* + AstNode *initializer; // Changed from Expr* to AstNode* + bool is_mutable; // Whether the variable is mutable + bool is_public; + } var_decl; + + // const Persion = struct { + // pub: + // name: str; + // age: int; + // priv: + // ssn: str; + // }; + // Struct declaration + struct { + const char *name; + AstNode * + *public_members; // Changed from Stmt** to AstNode** + size_t public_count; + AstNode * + *private_members; // Changed from Stmt** to AstNode** + size_t private_count; + bool is_public; // Whether the struct is public (which is + // true by default) + } struct_decl; + + struct { + const char *name; + AstNode *type; // Changed from Type* to AstNode* + AstNode *function; // Changed from Stmt* to AstNode* + bool is_public; // Whether the field is public + } field_decl; + + // Enumeration declaration + struct { + const char *name; + char **members; // Changed from char** to AstNode** + size_t member_count; + bool is_public; // same as struct, enums are public by + // default + } enum_decl; + + // Function declaration + struct { + const char *name; + char **param_names; + AstNode **param_types; // Changed from Type** to AstNode** + size_t param_count; + AstNode *return_type; // Changed from Type* to AstNode* + bool is_public; + AstNode *body; // Changed from Stmt* to AstNode* + bool returns_ownership; + bool takes_ownership; + } func_decl; + + // If statement + struct { + AstNode *condition; // Changed from Expr* to AstNode* + AstNode *then_stmt; // Changed from Stmt* to AstNode* + AstNode **elif_stmts; // Changed from Stmt* to AstNode* + int elif_count; // Number of elif statements + AstNode *else_stmt; // Changed from Stmt* to AstNode* + } if_stmt; + + // Loop statement (Combined while and for) + struct { + AstNode *condition; // Changed from Expr* to AstNode* + AstNode *optional; // Optional expression (e.g., for loop + // initializer) + AstNode *body; // Changed from Stmt* to AstNode* + // For loops can be represented as a while loop with an + // initializer and increment + AstNode **initializer; // Optional initializer for for loops + // (Changed from Stmt* to AstNode*) + size_t init_count; // Number of initializers + } loop_stmt; + + // Return statement + struct { + AstNode *value; // Changed from Expr* to AstNode* + } return_stmt; + + // Block statement + struct { + AstNode **statements; // Changed from Stmt** to AstNode** + size_t stmt_count; + } block; + + // Print statement + struct { + AstNode **expressions; // Changed from Expr** to AstNode** + size_t expr_count; + bool ln; // Whether to print with a newline + } print_stmt; + + struct { + bool is_continue; // true for continue, false for break + } break_continue; + + struct { + AstNode *statement; + } defer_stmt; + + struct { + AstNode *condition; // The switch expression + struct AstNode **cases; // Array of case clauses + size_t case_count; + struct AstNode *default_case; // Optional default case + } switch_stmt; + + // impl [fun1, fun2, ...] -> [struct1, struct2, ...] {} + struct { + char **function_name_list; + AstNode **function_type_list; + char **struct_name_list; + size_t function_name_count; + size_t struct_name_count; + + AstNode *body; + } impl_stmt; + + // Case clause node + struct { + AstNode **values; // Array of expressions (0, 1, 2, 3, ...) + size_t value_count; // Number of values in this case + AstNode *body; // Block statement for this case + } case_clause; + + struct { + AstNode *body; // Block statement for default case + } default_clause; + }; + } stmt; + + struct { + // Type-specific data + union { + // Basic type + struct { + const char *name; + } basic; + + // Pointer type + struct { + AstNode *pointee_type; // Changed from Type* to AstNode* + } pointer; + + // Array type + struct { + AstNode *element_type; // Changed from Type* to AstNode* + AstNode *size; // Changed from Expr* to AstNode* + } array; + + // Function type + struct { + AstNode **param_types; // Changed from Type** to AstNode** + size_t param_count; + AstNode *return_type; // Changed from Type* to AstNode* + } function; + + // AST_TYPE_STRUCT - ADD THIS STRUCT + struct { + const char *name; // Name of the struct type + AstNode **member_types; // Array of member types + const char **member_names; // Array of member names + size_t member_count; // Number of members + } struct_type; + }; + } type_data; + }; }; // Type aliases for cleaner code (defined AFTER the struct) @@ -476,14 +492,14 @@ AstNode *create_type_node(ArenaAllocator *arena, NodeType type, size_t line, // Helper macros for creating nodes #define create_preprocessor(arena, type, line, column) \ - create_preprocessor_node(arena, type, Node_Category_PREPROCESSOR, line, \ - column) + create_preprocessor_node(arena, type, Node_Category_PREPROCESSOR, line, \ + column) #define create_expr(arena, type, line, column) \ - create_expr_node(arena, type, line, column) + create_expr_node(arena, type, line, column) #define create_stmt(arena, type, line, column) \ - create_stmt_node(arena, type, line, column) + create_stmt_node(arena, type, line, column) #define create_type(arena, type, line, column) \ - create_type_node(arena, type, line, column) + create_type_node(arena, type, line, column) // Create the AstNode AstNode *create_ast_node(ArenaAllocator *arena, NodeType type, @@ -595,6 +611,12 @@ AstNode *create_defer_stmt(ArenaAllocator *arena, AstNode *statement, AstNode *create_switch_stmt(ArenaAllocator *arena, AstNode *condition, AstNode **cases, size_t case_count, AstNode *default_case, size_t line, size_t column); + +AstNode *create_impl_stmt(ArenaAllocator *arena, char **function_name_list, + AstNode **function_type_list, AstNode *body, + char **struct_name_list, size_t function_name_count, + size_t struct_name_count, size_t line, size_t column); + AstNode *create_case_stmt(ArenaAllocator *arena, AstNode **values, size_t value_count, AstNode *body, size_t line, size_t column); diff --git a/src/ast/ast_definistions/stmt.c b/src/ast/ast_definistions/stmt.c index fd206191..1c8cf43a 100644 --- a/src/ast/ast_definistions/stmt.c +++ b/src/ast/ast_definistions/stmt.c @@ -4,30 +4,30 @@ AstNode *create_program_node(ArenaAllocator *arena, AstNode **statements, size_t stmt_count, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_PROGRAM, line, column); - node->stmt.program.modules = statements; - node->stmt.program.module_count = stmt_count; - return node; + AstNode *node = create_stmt_node(arena, AST_PROGRAM, line, column); + node->stmt.program.modules = statements; + node->stmt.program.module_count = stmt_count; + return node; } AstNode *create_expr_stmt(ArenaAllocator *arena, Expr *expression, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_EXPRESSION, line, column); - node->stmt.expr_stmt.expression = expression; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_EXPRESSION, line, column); + node->stmt.expr_stmt.expression = expression; + return node; } AstNode *create_var_decl_stmt(ArenaAllocator *arena, const char *name, AstNode *var_type, Expr *initializer, bool is_mutable, bool is_public, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_VAR_DECL, line, column); - node->stmt.var_decl.name = name; - node->stmt.var_decl.var_type = var_type; - node->stmt.var_decl.initializer = initializer; - node->stmt.var_decl.is_mutable = is_mutable; - node->stmt.var_decl.is_public = is_public; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_VAR_DECL, line, column); + node->stmt.var_decl.name = name; + node->stmt.var_decl.var_type = var_type; + node->stmt.var_decl.initializer = initializer; + node->stmt.var_decl.is_mutable = is_mutable; + node->stmt.var_decl.is_public = is_public; + return node; } AstNode *create_func_decl_stmt(ArenaAllocator *arena, const char *name, @@ -36,17 +36,17 @@ AstNode *create_func_decl_stmt(ArenaAllocator *arena, const char *name, bool is_public, bool returns_ownership, bool takes_ownership, AstNode *body, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_FUNCTION, line, column); - node->stmt.func_decl.name = name; - node->stmt.func_decl.param_names = param_names; - node->stmt.func_decl.param_types = param_types; - node->stmt.func_decl.param_count = param_count; - node->stmt.func_decl.return_type = return_type; - node->stmt.func_decl.is_public = is_public; - node->stmt.func_decl.returns_ownership = returns_ownership; - node->stmt.func_decl.takes_ownership = takes_ownership; - node->stmt.func_decl.body = body; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_FUNCTION, line, column); + node->stmt.func_decl.name = name; + node->stmt.func_decl.param_names = param_names; + node->stmt.func_decl.param_types = param_types; + node->stmt.func_decl.param_count = param_count; + node->stmt.func_decl.return_type = return_type; + node->stmt.func_decl.is_public = is_public; + node->stmt.func_decl.returns_ownership = returns_ownership; + node->stmt.func_decl.takes_ownership = takes_ownership; + node->stmt.func_decl.body = body; + return node; } AstNode *create_struct_decl_stmt(ArenaAllocator *arena, const char *name, @@ -54,151 +54,167 @@ AstNode *create_struct_decl_stmt(ArenaAllocator *arena, const char *name, AstNode **private_members, size_t private_count, bool is_public, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_STRUCT, line, column); - node->stmt.struct_decl.name = name; - node->stmt.struct_decl.public_members = public_members; - node->stmt.struct_decl.public_count = public_count; - node->stmt.struct_decl.private_members = private_members; - node->stmt.struct_decl.private_count = private_count; - node->stmt.struct_decl.is_public = is_public; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_STRUCT, line, column); + node->stmt.struct_decl.name = name; + node->stmt.struct_decl.public_members = public_members; + node->stmt.struct_decl.public_count = public_count; + node->stmt.struct_decl.private_members = private_members; + node->stmt.struct_decl.private_count = private_count; + node->stmt.struct_decl.is_public = is_public; + return node; } AstNode *create_field_decl_stmt(ArenaAllocator *arena, const char *name, AstNode *type, AstNode *function, bool is_public, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_FIELD_DECL, line, column); - node->stmt.field_decl.name = name; - node->stmt.field_decl.type = type; - node->stmt.field_decl.function = function; - node->stmt.field_decl.is_public = is_public; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_FIELD_DECL, line, column); + node->stmt.field_decl.name = name; + node->stmt.field_decl.type = type; + node->stmt.field_decl.function = function; + node->stmt.field_decl.is_public = is_public; + return node; } AstNode *create_enum_decl_stmt(ArenaAllocator *arena, const char *name, char **members, size_t member_count, bool is_public, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_ENUM, line, column); - node->stmt.enum_decl.name = name; - node->stmt.enum_decl.members = members; - node->stmt.enum_decl.member_count = member_count; - node->stmt.enum_decl.is_public = is_public; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_ENUM, line, column); + node->stmt.enum_decl.name = name; + node->stmt.enum_decl.members = members; + node->stmt.enum_decl.member_count = member_count; + node->stmt.enum_decl.is_public = is_public; + return node; } AstNode *create_if_stmt(ArenaAllocator *arena, Expr *condition, AstNode *then_stmt, AstNode **elif_stmts, int elif_count, AstNode *else_stmt, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_IF, line, column); - node->stmt.if_stmt.condition = condition; - node->stmt.if_stmt.then_stmt = then_stmt; - node->stmt.if_stmt.elif_stmts = elif_stmts; - node->stmt.if_stmt.elif_count = elif_count; - node->stmt.if_stmt.else_stmt = else_stmt; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_IF, line, column); + node->stmt.if_stmt.condition = condition; + node->stmt.if_stmt.then_stmt = then_stmt; + node->stmt.if_stmt.elif_stmts = elif_stmts; + node->stmt.if_stmt.elif_count = elif_count; + node->stmt.if_stmt.else_stmt = else_stmt; + return node; } AstNode *create_infinite_loop_stmt(ArenaAllocator *arena, AstNode *body, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_LOOP, line, column); - node->stmt.loop_stmt.condition = NULL; - node->stmt.loop_stmt.optional = NULL; - node->stmt.loop_stmt.initializer = NULL; - node->stmt.loop_stmt.init_count = 0; - node->stmt.loop_stmt.body = body; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_LOOP, line, column); + node->stmt.loop_stmt.condition = NULL; + node->stmt.loop_stmt.optional = NULL; + node->stmt.loop_stmt.initializer = NULL; + node->stmt.loop_stmt.init_count = 0; + node->stmt.loop_stmt.body = body; + return node; } AstNode *create_for_loop_stmt(ArenaAllocator *arena, AstNode **initializers, size_t init_count, Expr *condition, Expr *optional, AstNode *body, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_LOOP, line, column); - node->stmt.loop_stmt.condition = condition; - node->stmt.loop_stmt.optional = optional; - node->stmt.loop_stmt.body = body; - node->stmt.loop_stmt.initializer = initializers; - node->stmt.loop_stmt.init_count = init_count; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_LOOP, line, column); + node->stmt.loop_stmt.condition = condition; + node->stmt.loop_stmt.optional = optional; + node->stmt.loop_stmt.body = body; + node->stmt.loop_stmt.initializer = initializers; + node->stmt.loop_stmt.init_count = init_count; + return node; } AstNode *create_loop_stmt(ArenaAllocator *arena, Expr *condition, Expr *optional, AstNode *body, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_LOOP, line, column); - node->stmt.loop_stmt.condition = condition; - node->stmt.loop_stmt.optional = optional; - node->stmt.loop_stmt.initializer = NULL; // No initializers for standard loops - node->stmt.loop_stmt.init_count = 0; - node->stmt.loop_stmt.body = body; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_LOOP, line, column); + node->stmt.loop_stmt.condition = condition; + node->stmt.loop_stmt.optional = optional; + node->stmt.loop_stmt.initializer = + NULL; // No initializers for standard loops + node->stmt.loop_stmt.init_count = 0; + node->stmt.loop_stmt.body = body; + return node; } AstNode *create_return_stmt(ArenaAllocator *arena, Expr *value, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_RETURN, line, column); - node->stmt.return_stmt.value = value; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_RETURN, line, column); + node->stmt.return_stmt.value = value; + return node; } AstNode *create_block_stmt(ArenaAllocator *arena, AstNode **statements, size_t stmt_count, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_BLOCK, line, column); - node->stmt.block.statements = statements; - node->stmt.block.stmt_count = stmt_count; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_BLOCK, line, column); + node->stmt.block.statements = statements; + node->stmt.block.stmt_count = stmt_count; + return node; } AstNode *create_print_stmt(ArenaAllocator *arena, Expr **expressions, size_t expr_count, bool ln, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_PRINT, line, column); - node->stmt.print_stmt.expressions = expressions; - node->stmt.print_stmt.expr_count = expr_count; - node->stmt.print_stmt.ln = ln; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_PRINT, line, column); + node->stmt.print_stmt.expressions = expressions; + node->stmt.print_stmt.expr_count = expr_count; + node->stmt.print_stmt.ln = ln; + return node; } AstNode *create_break_continue_stmt(ArenaAllocator *arena, bool is_continue, size_t line, size_t column) { - AstNode *node = - create_stmt_node(arena, AST_STMT_BREAK_CONTINUE, line, column); - node->stmt.break_continue.is_continue = is_continue; - return node; + AstNode *node = + create_stmt_node(arena, AST_STMT_BREAK_CONTINUE, line, column); + node->stmt.break_continue.is_continue = is_continue; + return node; } AstNode *create_defer_stmt(ArenaAllocator *arena, AstNode *statement, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_DEFER, line, column); - node->stmt.defer_stmt.statement = statement; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_DEFER, line, column); + node->stmt.defer_stmt.statement = statement; + return node; } AstNode *create_switch_stmt(ArenaAllocator *arena, AstNode *condition, AstNode **cases, size_t case_count, AstNode *default_case, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_SWITCH, line, column); - node->stmt.switch_stmt.condition = condition; - node->stmt.switch_stmt.case_count = case_count; - node->stmt.switch_stmt.cases = cases; - node->stmt.switch_stmt.default_case = default_case; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_SWITCH, line, column); + node->stmt.switch_stmt.condition = condition; + node->stmt.switch_stmt.case_count = case_count; + node->stmt.switch_stmt.cases = cases; + node->stmt.switch_stmt.default_case = default_case; + return node; +} + +AstNode *create_impl_stmt(ArenaAllocator *arena, char **function_name_list, + AstNode **function_type_list, AstNode *body, + char **struct_name_list, size_t function_name_count, + size_t struct_name_count, size_t line, + size_t column) { + AstNode *node = create_stmt_node(arena, AST_STMT_IMPL, line, column); + node->stmt.impl_stmt.function_name_list = function_name_list; + node->stmt.impl_stmt.function_type_list = function_type_list; + node->stmt.impl_stmt.struct_name_list = struct_name_list; + node->stmt.impl_stmt.function_name_count = function_name_count; + node->stmt.impl_stmt.struct_name_count = struct_name_count; + node->stmt.impl_stmt.body = body; + return node; } AstNode *create_case_stmt(ArenaAllocator *arena, AstNode **values, size_t value_count, AstNode *body, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_CASE, line, column); - node->stmt.case_clause.values = values; - node->stmt.case_clause.value_count = value_count; - node->stmt.case_clause.body = body; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_CASE, line, column); + node->stmt.case_clause.values = values; + node->stmt.case_clause.value_count = value_count; + node->stmt.case_clause.body = body; + return node; } AstNode *create_default_stmt(ArenaAllocator *arena, AstNode *body, size_t line, size_t column) { - AstNode *node = create_stmt(arena, AST_STMT_DEFAULT, line, column); - node->stmt.default_clause.body = body; - return node; + AstNode *node = create_stmt(arena, AST_STMT_DEFAULT, line, column); + node->stmt.default_clause.body = body; + return node; } diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c index a1e25e5f..078255fc 100644 --- a/src/lexer/lexer.c +++ b/src/lexer/lexer.c @@ -17,14 +17,14 @@ /** @internal Macro to compare string to a key of known length */ #define STR_EQUALS_LEN(str, key, len) \ - (strncmp(str, key, len) == 0 && key[len] == '\0') + (strncmp(str, key, len) == 0 && key[len] == '\0') /** @internal Macro to check if next two chars match given pair */ #define MATCH_NEXT(lx, a, b) (peek(lx, 0) == (a) && peek(lx, 1) == (b)) /** @internal Macro to construct a token */ #define MAKE_TOKEN(type, start, lx, length, whitespace_len) \ - make_token(type, start, lx->line, lx->col - 1, length, whitespace_len) + make_token(type, start, lx->line, lx->col - 1, length, whitespace_len) /** @internal Symbol to token type mapping */ static const SymbolEntry symbols[] = { @@ -81,6 +81,7 @@ static const KeywordEntry keywords[] = { {"defer", TOK_DEFER}, {"in", TOK_IN}, {"switch", TOK_SWITCH}, + {"impl", TOK_IMPL}, {"input", TOK_INPUT}, {"system", TOK_SYSTEM}, }; @@ -110,19 +111,19 @@ static const KeywordEntry function_attributes[] = { void report_lexer_error(Lexer *lx, const char *error_type, const char *file, const char *msg, const char *line_text, int line, int col, int tk_length) { - ErrorInformation err = { - .error_type = error_type, - .file_path = file, - .message = msg, - .line = line, - .col = col, - .line_text = arena_strdup(lx->arena, line_text), - .token_length = tk_length, - .label = "Undefined Token", - .note = NULL, - .help = NULL, - }; - error_add(err); + ErrorInformation err = { + .error_type = error_type, + .file_path = file, + .message = msg, + .line = line, + .col = col, + .line_text = arena_strdup(lx->arena, line_text), + .token_length = tk_length, + .label = "Undefined Token", + .note = NULL, + .help = NULL, + }; + error_add(err); } /** @@ -133,29 +134,30 @@ void report_lexer_error(Lexer *lx, const char *error_type, const char *file, * @return Pointer to static buffer containing the line text */ const char *get_line_text_from_source(const char *source, int target_line) { - static char line_buffer[1024]; - const char *start = source; - int current_line = 1; + static char line_buffer[1024]; + const char *start = source; + int current_line = 1; - while (current_line < target_line && *start != '\0') { - if (*start == '\n') { - current_line++; + while (current_line < target_line && *start != '\0') { + if (*start == '\n') { + current_line++; + } + start++; } - start++; - } - if (current_line != target_line) { - line_buffer[0] = '\0'; - return line_buffer; - } + if (current_line != target_line) { + line_buffer[0] = '\0'; + return line_buffer; + } - int i = 0; - while (*start != '\0' && *start != '\n' && i < (int)sizeof(line_buffer) - 1) { - line_buffer[i++] = *start++; - } - line_buffer[i] = '\0'; + int i = 0; + while (*start != '\0' && *start != '\n' && + i < (int)sizeof(line_buffer) - 1) { + line_buffer[i++] = *start++; + } + line_buffer[i] = '\0'; - return line_buffer; + return line_buffer; } /** @@ -167,12 +169,12 @@ const char *get_line_text_from_source(const char *source, int target_line) { * @return LumaTokenType keyword token if found, else TOK_IDENTIFIER */ static LumaTokenType lookup_keyword(const char *str, int length) { - for (int i = 0; i < (int)(sizeof(keywords) / sizeof(*keywords)); ++i) { - if (STR_EQUALS_LEN(str, keywords[i].text, length)) { - return keywords[i].type; + for (int i = 0; i < (int)(sizeof(keywords) / sizeof(*keywords)); ++i) { + if (STR_EQUALS_LEN(str, keywords[i].text, length)) { + return keywords[i].type; + } } - } - return TOK_IDENTIFIER; + return TOK_IDENTIFIER; } /** @@ -184,14 +186,14 @@ static LumaTokenType lookup_keyword(const char *str, int length) { * @return LumaTokenType preprocessor token if found, else TOK_SYMBOL */ static LumaTokenType lookup_preprocessor(const char *str, int length) { - for (int i = 0; i < (int)(sizeof(preprocessor_directives) / - sizeof(*preprocessor_directives)); - ++i) { - if (STR_EQUALS_LEN(str, preprocessor_directives[i].text, length)) { - return preprocessor_directives[i].type; + for (int i = 0; i < (int)(sizeof(preprocessor_directives) / + sizeof(*preprocessor_directives)); + ++i) { + if (STR_EQUALS_LEN(str, preprocessor_directives[i].text, length)) { + return preprocessor_directives[i].type; + } } - } - return TOK_SYMBOL; + return TOK_SYMBOL; } /** @@ -203,12 +205,12 @@ static LumaTokenType lookup_preprocessor(const char *str, int length) { * @return LumaTokenType symbol token if found, else TOK_SYMBOL */ static LumaTokenType lookup_symbol(const char *str, int length) { - for (int i = 0; i < (int)(sizeof(symbols) / sizeof(*symbols)); ++i) { - if (STR_EQUALS_LEN(str, symbols[i].text, length)) { - return symbols[i].type; + for (int i = 0; i < (int)(sizeof(symbols) / sizeof(*symbols)); ++i) { + if (STR_EQUALS_LEN(str, symbols[i].text, length)) { + return symbols[i].type; + } } - } - return TOK_SYMBOL; + return TOK_SYMBOL; } /** @@ -219,11 +221,11 @@ static LumaTokenType lookup_symbol(const char *str, int length) { * @param arena Arena allocator to allocate tokens and strings */ void init_lexer(Lexer *lexer, const char *source, ArenaAllocator *arena) { - lexer->arena = arena; - lexer->src = source; - lexer->current = source; - lexer->line = 1; - lexer->col = 0; + lexer->arena = arena; + lexer->src = source; + lexer->current = source; + lexer->line = 1; + lexer->col = 0; } /** @@ -254,14 +256,14 @@ bool is_at_end(Lexer *lx) { return *lx->current == '\0'; } * @return The character that was advanced past */ char advance(Lexer *lx) { - char c = *lx->current++; - if (c == '\n') { - lx->line++; - lx->col = 0; - } else if (c != '\0') { - lx->col++; - } - return c; + char c = *lx->current++; + if (c == '\n') { + lx->line++; + lx->col = 0; + } else if (c != '\0') { + lx->col++; + } + return c; } /** @@ -277,7 +279,7 @@ char advance(Lexer *lx) { */ Token make_token(LumaTokenType type, const char *start, int line, int col, int length, int whitespace_len) { - return (Token){type, start, line, col, length, whitespace_len}; + return (Token){type, start, line, col, length, whitespace_len}; } /** @@ -288,19 +290,19 @@ Token make_token(LumaTokenType type, const char *start, int line, int col, * @return Number of characters skipped */ int skip_multiline_comment(Lexer *lx) { - int count = 0; - advance(lx); // skip '/' - advance(lx); // skip '*' - count += 2; - while (!is_at_end(lx) && !MATCH_NEXT(lx, '*', '/')) { - advance(lx); - count++; - } - if (!is_at_end(lx)) { - advance(lx); // skip '*' + int count = 0; advance(lx); // skip '/' - } - return count + 2; + advance(lx); // skip '*' + count += 2; + while (!is_at_end(lx) && !MATCH_NEXT(lx, '*', '/')) { + advance(lx); + count++; + } + if (!is_at_end(lx)) { + advance(lx); // skip '*' + advance(lx); // skip '/' + } + return count + 2; } /** @@ -311,37 +313,37 @@ int skip_multiline_comment(Lexer *lx) { * @return Total count of skipped characters */ int skip_whitespace(Lexer *lx) { - int whitespace_count = 0; - - while (!is_at_end(lx)) { - char c = peek(lx, 0); - - if (c == ' ' || c == '\t') { - // Count spaces and tabs as whitespace - advance(lx); - whitespace_count++; - } else if (c == '\n' || c == '\r') { - // Newline resets whitespace count - we only want leading whitespace on - // the current line - advance(lx); - whitespace_count = 0; // Reset because we're on a new line - } else if (c == '/' && peek(lx, 1) == '/') { - // Skip single-line comment - while (!is_at_end(lx) && peek(lx, 0) != '\n') { - advance(lx); - } - // Don't count comment characters as whitespace - } else if (c == '/' && peek(lx, 1) == '*') { - // Skip multiline comment - skip_multiline_comment(lx); - // Don't count comment characters as whitespace - } else { - // Not whitespace or comment, stop skipping - break; + int whitespace_count = 0; + + while (!is_at_end(lx)) { + char c = peek(lx, 0); + + if (c == ' ' || c == '\t') { + // Count spaces and tabs as whitespace + advance(lx); + whitespace_count++; + } else if (c == '\n' || c == '\r') { + // Newline resets whitespace count - we only want leading whitespace + // on the current line + advance(lx); + whitespace_count = 0; // Reset because we're on a new line + } else if (c == '/' && peek(lx, 1) == '/') { + // Skip single-line comment + while (!is_at_end(lx) && peek(lx, 0) != '\n') { + advance(lx); + } + // Don't count comment characters as whitespace + } else if (c == '/' && peek(lx, 1) == '*') { + // Skip multiline comment + skip_multiline_comment(lx); + // Don't count comment characters as whitespace + } else { + // Not whitespace or comment, stop skipping + break; + } } - } - return whitespace_count; + return whitespace_count; } /** @@ -351,254 +353,255 @@ int skip_whitespace(Lexer *lx) { * @return Next token found in the input */ Token next_token(Lexer *lx) { - int wh_count = skip_whitespace(lx); - if (is_at_end(lx)) { - return MAKE_TOKEN(TOK_EOF, lx->current, lx, 0, wh_count); - } - - const char *start = lx->current; - char c = advance(lx); - - // Preprocessor directives (starting with @) - if (c == '@') { - if (isalpha(peek(lx, 0))) { - // Read the rest of the directive - while (isalnum(peek(lx, 0)) || peek(lx, 0) == '_') { - advance(lx); - } - int len = (int)(lx->current - start); - LumaTokenType type = lookup_preprocessor(start, len); - if (type != TOK_SYMBOL) { - return MAKE_TOKEN(type, start, lx, len, wh_count); - } - // If not a known preprocessor directive, treat as error or symbol - static char error_msg[64]; - snprintf(error_msg, sizeof(error_msg), - "Unknown preprocessor directive: '%.*s'", len, start); - report_lexer_error(lx, "LexerError", "unknown_file", error_msg, - get_line_text_from_source(lx->src, lx->line), lx->line, - lx->col - len, len); - return MAKE_TOKEN(TOK_ERROR, start, lx, len, wh_count); + int wh_count = skip_whitespace(lx); + if (is_at_end(lx)) { + return MAKE_TOKEN(TOK_EOF, lx->current, lx, 0, wh_count); } - // Just @ by itself - treat as symbol - LumaTokenType single_type = lookup_symbol(start, 1); - return MAKE_TOKEN(single_type, start, lx, 1, wh_count); - } - if (c == '#') { - if (isalpha(peek(lx, 0))) { - // Read the rest of the attribute - while (isalnum(peek(lx, 0)) || peek(lx, 0) == '_') { - advance(lx); - } - int len = (int)(lx->current - start); - for (int i = 0; i < (int)(sizeof(function_attributes) / - sizeof(*function_attributes)); - ++i) { - if (STR_EQUALS_LEN(start, function_attributes[i].text, len)) { - return MAKE_TOKEN(function_attributes[i].type, start, lx, len, - wh_count); + const char *start = lx->current; + char c = advance(lx); + + // Preprocessor directives (starting with @) + if (c == '@') { + if (isalpha(peek(lx, 0))) { + // Read the rest of the directive + while (isalnum(peek(lx, 0)) || peek(lx, 0) == '_') { + advance(lx); + } + int len = (int)(lx->current - start); + LumaTokenType type = lookup_preprocessor(start, len); + if (type != TOK_SYMBOL) { + return MAKE_TOKEN(type, start, lx, len, wh_count); + } + // If not a known preprocessor directive, treat as error or symbol + static char error_msg[64]; + snprintf(error_msg, sizeof(error_msg), + "Unknown preprocessor directive: '%.*s'", len, start); + report_lexer_error(lx, "LexerError", "unknown_file", error_msg, + get_line_text_from_source(lx->src, lx->line), + lx->line, lx->col - len, len); + return MAKE_TOKEN(TOK_ERROR, start, lx, len, wh_count); } - } - // If not a known function attribute, treat as error - static char error_msg[64]; - snprintf(error_msg, sizeof(error_msg), - "Unknown function attribute: '%.*s'", len, start); - report_lexer_error(lx, "LexerError", "unknown_file", error_msg, - get_line_text_from_source(lx->src, lx->line), lx->line, - lx->col - len, len); - return MAKE_TOKEN(TOK_ERROR, start, lx, len, wh_count); + // Just @ by itself - treat as symbol + LumaTokenType single_type = lookup_symbol(start, 1); + return MAKE_TOKEN(single_type, start, lx, 1, wh_count); } - // Just # by itself - treat as symbol - LumaTokenType single_type = lookup_symbol(start, 1); - return MAKE_TOKEN(single_type, start, lx, 1, wh_count); - } - // Identifiers and keywords - if (isalpha(c) || c == '_') { - while (isalnum(peek(lx, 0)) || peek(lx, 0) == '_') { - advance(lx); - } - int len = (int)(lx->current - start); - LumaTokenType type = lookup_keyword(start, len); - return MAKE_TOKEN(type, start, lx, len, wh_count); - } - - // Numbers - if (isdigit(c)) { - // Read the integer part - while (isdigit(peek(lx, 0))) { - advance(lx); + if (c == '#') { + if (isalpha(peek(lx, 0))) { + // Read the rest of the attribute + while (isalnum(peek(lx, 0)) || peek(lx, 0) == '_') { + advance(lx); + } + int len = (int)(lx->current - start); + for (int i = 0; i < (int)(sizeof(function_attributes) / + sizeof(*function_attributes)); + ++i) { + if (STR_EQUALS_LEN(start, function_attributes[i].text, len)) { + return MAKE_TOKEN(function_attributes[i].type, start, lx, + len, wh_count); + } + } + // If not a known function attribute, treat as error + static char error_msg[64]; + snprintf(error_msg, sizeof(error_msg), + "Unknown function attribute: '%.*s'", len, start); + report_lexer_error(lx, "LexerError", "unknown_file", error_msg, + get_line_text_from_source(lx->src, lx->line), + lx->line, lx->col - len, len); + return MAKE_TOKEN(TOK_ERROR, start, lx, len, wh_count); + } + // Just # by itself - treat as symbol + LumaTokenType single_type = lookup_symbol(start, 1); + return MAKE_TOKEN(single_type, start, lx, 1, wh_count); } - // Check for decimal point - if (peek(lx, 0) == '.' && isdigit(peek(lx, 1))) { - advance(lx); // consume the '.' + // Identifiers and keywords + if (isalpha(c) || c == '_') { + while (isalnum(peek(lx, 0)) || peek(lx, 0) == '_') { + advance(lx); + } + int len = (int)(lx->current - start); + LumaTokenType type = lookup_keyword(start, len); + return MAKE_TOKEN(type, start, lx, len, wh_count); + } - // Read the fractional part - while (isdigit(peek(lx, 0))) { - advance(lx); - } + // Numbers + if (isdigit(c)) { + // Read the integer part + while (isdigit(peek(lx, 0))) { + advance(lx); + } - int len = (int)(lx->current - start); - return MAKE_TOKEN(TOK_NUM_FLOAT, start, lx, len, - wh_count); // Make sure this is TOK_FLOAT_LITERAL - } + // Check for decimal point + if (peek(lx, 0) == '.' && isdigit(peek(lx, 1))) { + advance(lx); // consume the '.' - int len = (int)(lx->current - start); - return MAKE_TOKEN(TOK_NUMBER, start, lx, len, - wh_count); // Make sure this is TOK_NUMBER - } + // Read the fractional part + while (isdigit(peek(lx, 0))) { + advance(lx); + } - if (c == '\'') { - const char *char_start = lx->current; // Start after opening quote - char actual_char = 0; // The actual character value we'll store - int char_count = 0; // How many characters we consumed + int len = (int)(lx->current - start); + return MAKE_TOKEN(TOK_NUM_FLOAT, start, lx, len, + wh_count); // Make sure this is TOK_FLOAT_LITERAL + } - if (is_at_end(lx)) { - // Unclosed character literal - report_lexer_error(lx, "LexerError", "unknown_file", - "Unclosed character literal", - get_line_text_from_source(lx->src, lx->line), lx->line, - lx->col - 1, 1); - return MAKE_TOKEN(TOK_ERROR, start, lx, 1, wh_count); + int len = (int)(lx->current - start); + return MAKE_TOKEN(TOK_NUMBER, start, lx, len, + wh_count); // Make sure this is TOK_NUMBER } - char ch = peek(lx, 0); - - // Handle escape sequences - if (ch == '\\') { - advance(lx); // consume backslash - char_count++; - - if (is_at_end(lx)) { - report_lexer_error(lx, "LexerError", "unknown_file", - "Incomplete escape sequence in character literal", - get_line_text_from_source(lx->src, lx->line), - lx->line, lx->col - 2, 2); - return MAKE_TOKEN(TOK_ERROR, start, lx, 2, wh_count); - } - - char escaped = peek(lx, 0); - - // Convert escape sequence to actual character value - switch (escaped) { - case 'n': - actual_char = '\n'; - break; - case 't': - actual_char = '\t'; - break; - case 'r': - actual_char = '\r'; - break; - case '\\': - actual_char = '\\'; - break; - case '\'': - actual_char = '\''; - break; - case '\"': - actual_char = '\"'; - break; - case '0': - actual_char = '\0'; - break; - default: { - static char error_msg[64]; - snprintf(error_msg, sizeof(error_msg), - "Invalid escape sequence '\\%c' in character literal", - escaped); - report_lexer_error(lx, "LexerError", "unknown_file", error_msg, - get_line_text_from_source(lx->src, lx->line), - lx->line, lx->col - 2, 3); - return MAKE_TOKEN(TOK_ERROR, start, lx, 3, wh_count); - } - } - - advance(lx); // consume escaped character - char_count++; - - } else if (ch == '\'') { - // Empty character literal - report_lexer_error(lx, "LexerError", "unknown_file", - "Empty character literal", - get_line_text_from_source(lx->src, lx->line), lx->line, - lx->col - 1, 2); - advance(lx); // consume closing quote - return MAKE_TOKEN(TOK_ERROR, start, lx, 2, wh_count); - - } else if (ch == '\n' || ch == '\r') { - // Newline in character literal - report_lexer_error(lx, "LexerError", "unknown_file", - "Newline in character literal", - get_line_text_from_source(lx->src, lx->line), lx->line, - lx->col - 1, 1); - return MAKE_TOKEN(TOK_ERROR, start, lx, 1, wh_count); - - } else { - // Regular character - actual_char = ch; - advance(lx); // consume the character - char_count = 1; - } + if (c == '\'') { + const char *char_start = lx->current; // Start after opening quote + char actual_char = 0; // The actual character value we'll store + int char_count = 0; // How many characters we consumed + + if (is_at_end(lx)) { + // Unclosed character literal + report_lexer_error(lx, "LexerError", "unknown_file", + "Unclosed character literal", + get_line_text_from_source(lx->src, lx->line), + lx->line, lx->col - 1, 1); + return MAKE_TOKEN(TOK_ERROR, start, lx, 1, wh_count); + } - // Check for closing quote - if (is_at_end(lx) || peek(lx, 0) != '\'') { - report_lexer_error(lx, "LexerError", "unknown_file", - "Unclosed character literal", - get_line_text_from_source(lx->src, lx->line), lx->line, - lx->col - 1, (int)(lx->current - start)); - return MAKE_TOKEN(TOK_ERROR, start, lx, (int)(lx->current - start), - wh_count); - } + char ch = peek(lx, 0); + + // Handle escape sequences + if (ch == '\\') { + advance(lx); // consume backslash + char_count++; + + if (is_at_end(lx)) { + report_lexer_error( + lx, "LexerError", "unknown_file", + "Incomplete escape sequence in character literal", + get_line_text_from_source(lx->src, lx->line), lx->line, + lx->col - 2, 2); + return MAKE_TOKEN(TOK_ERROR, start, lx, 2, wh_count); + } + + char escaped = peek(lx, 0); + + // Convert escape sequence to actual character value + switch (escaped) { + case 'n': + actual_char = '\n'; + break; + case 't': + actual_char = '\t'; + break; + case 'r': + actual_char = '\r'; + break; + case '\\': + actual_char = '\\'; + break; + case '\'': + actual_char = '\''; + break; + case '\"': + actual_char = '\"'; + break; + case '0': + actual_char = '\0'; + break; + default: { + static char error_msg[64]; + snprintf(error_msg, sizeof(error_msg), + "Invalid escape sequence '\\%c' in character literal", + escaped); + report_lexer_error(lx, "LexerError", "unknown_file", error_msg, + get_line_text_from_source(lx->src, lx->line), + lx->line, lx->col - 2, 3); + return MAKE_TOKEN(TOK_ERROR, start, lx, 3, wh_count); + } + } + + advance(lx); // consume escaped character + char_count++; + + } else if (ch == '\'') { + // Empty character literal + report_lexer_error(lx, "LexerError", "unknown_file", + "Empty character literal", + get_line_text_from_source(lx->src, lx->line), + lx->line, lx->col - 1, 2); + advance(lx); // consume closing quote + return MAKE_TOKEN(TOK_ERROR, start, lx, 2, wh_count); + + } else if (ch == '\n' || ch == '\r') { + // Newline in character literal + report_lexer_error(lx, "LexerError", "unknown_file", + "Newline in character literal", + get_line_text_from_source(lx->src, lx->line), + lx->line, lx->col - 1, 1); + return MAKE_TOKEN(TOK_ERROR, start, lx, 1, wh_count); + + } else { + // Regular character + actual_char = ch; + advance(lx); // consume the character + char_count = 1; + } - advance(lx); // consume closing quote - int total_len = (int)(lx->current - start); + // Check for closing quote + if (is_at_end(lx) || peek(lx, 0) != '\'') { + report_lexer_error( + lx, "LexerError", "unknown_file", "Unclosed character literal", + get_line_text_from_source(lx->src, lx->line), lx->line, + lx->col - 1, (int)(lx->current - start)); + return MAKE_TOKEN(TOK_ERROR, start, lx, (int)(lx->current - start), + wh_count); + } - // CRITICAL: Store the actual character value, not the escape sequence - // We'll create a single-character string with the resolved value - char *char_storage = (char *)arena_alloc(lx->arena, 2, 1); - char_storage[0] = actual_char; - char_storage[1] = '\0'; + advance(lx); // consume closing quote + int total_len = (int)(lx->current - start); - return make_token(TOK_CHAR_LITERAL, char_storage, lx->line, - lx->col - total_len, 1, - wh_count); // length = 1 (single char) - } + // CRITICAL: Store the actual character value, not the escape sequence + // We'll create a single-character string with the resolved value + char *char_storage = (char *)arena_alloc(lx->arena, 2, 1); + char_storage[0] = actual_char; + char_storage[1] = '\0'; - // Strings - if (c == '"') { - while (!is_at_end(lx) && peek(lx, 0) != '"') { - advance(lx); + return make_token(TOK_CHAR_LITERAL, char_storage, lx->line, + lx->col - total_len, 1, + wh_count); // length = 1 (single char) } - if (!is_at_end(lx)) { - advance(lx); // Skip closing quote + + // Strings + if (c == '"') { + while (!is_at_end(lx) && peek(lx, 0) != '"') { + advance(lx); + } + if (!is_at_end(lx)) { + advance(lx); // Skip closing quote + } + int len = (int)(lx->current - start - 2); + return MAKE_TOKEN(TOK_STRING, start + 1, lx, len, wh_count); } - int len = (int)(lx->current - start - 2); - return MAKE_TOKEN(TOK_STRING, start + 1, lx, len, wh_count); - } - - // Try to match two-character symbol - if (!is_at_end(lx)) { - char two[3] = {start[0], peek(lx, 0), '\0'}; - LumaTokenType ttype = lookup_symbol(two, 2); - if (ttype != TOK_SYMBOL) { - advance(lx); - return MAKE_TOKEN(ttype, start, lx, 2, wh_count); + + // Try to match two-character symbol + if (!is_at_end(lx)) { + char two[3] = {start[0], peek(lx, 0), '\0'}; + LumaTokenType ttype = lookup_symbol(two, 2); + if (ttype != TOK_SYMBOL) { + advance(lx); + return MAKE_TOKEN(ttype, start, lx, 2, wh_count); + } } - } - - // Single-character symbol fallback - LumaTokenType single_type = lookup_symbol(start, 1); - if (single_type != TOK_SYMBOL) - return MAKE_TOKEN(single_type, start, lx, 1, wh_count); - - // Error token if none matched - static char error_msg[64]; - snprintf(error_msg, sizeof(error_msg), "Token not found: '%c'", c); - report_lexer_error(lx, "LexerError", "unknown_file", error_msg, - get_line_text_from_source(lx->src, lx->line), lx->line, - lx->col, 1); - return MAKE_TOKEN(TOK_ERROR, start, lx, 1, wh_count); + + // Single-character symbol fallback + LumaTokenType single_type = lookup_symbol(start, 1); + if (single_type != TOK_SYMBOL) + return MAKE_TOKEN(single_type, start, lx, 1, wh_count); + + // Error token if none matched + static char error_msg[64]; + snprintf(error_msg, sizeof(error_msg), "Token not found: '%c'", c); + report_lexer_error(lx, "LexerError", "unknown_file", error_msg, + get_line_text_from_source(lx->src, lx->line), lx->line, + lx->col, 1); + return MAKE_TOKEN(TOK_ERROR, start, lx, 1, wh_count); } diff --git a/src/lexer/lexer.h b/src/lexer/lexer.h index eea4f578..d14af9f7 100644 --- a/src/lexer/lexer.h +++ b/src/lexer/lexer.h @@ -17,108 +17,109 @@ * @brief Enumeration of all possible token types recognized by the lexer. */ typedef enum { - TOK_EOF, /**< End of file/input */ - TOK_ERROR, /**< Error token */ - TOK_IDENTIFIER, /**< Identifier (variable/function names) */ - TOK_KEYWORD, /**< Reserved keyword */ - TOK_NUMBER, /**< Numeric literal */ - TOK_NUM_FLOAT, /**< Floating point numeric literal */ - TOK_STRING, /**< String literal */ - TOK_CHAR_LITERAL, /**< Character literal */ - - // Primitive Types - TOK_INT, /**< int */ - TOK_DOUBLE, /**< double */ - TOK_UINT, /**< unsigned int */ - TOK_FLOAT, /**< float */ - TOK_BOOL, /**< bool */ - TOK_STRINGT, /**< str (string type) */ - TOK_VOID, /**< void */ - TOK_CHAR, /**< char */ - - // Keywords - TOK_IF, /**< if keyword */ - TOK_ELIF, /**< elif keyword */ - TOK_ELSE, /**< else keyword */ - TOK_LOOP, /**< loop keyword */ - TOK_RETURN, /**< return keyword */ - TOK_BREAK, /**< break keyword */ - TOK_CONTINUE, /**< continue keyword */ - TOK_STRUCT, /**< struct keyword */ - TOK_ENUM, /**< enum keyword */ - TOK_MOD, /**< mod keyword */ - TOK_IMPORT, /**< import keyword */ - TOK_TRUE, /**< true keyword */ - TOK_FALSE, /**< false keyword */ - TOK_PUBLIC, /**< pub keyword */ - TOK_PRIVATE, /**< private keyword */ - TOK_VAR, /**< let keyword */ - TOK_CONST, /**< const keyword */ - TOK_FN, /**< fn keyword */ - TOK_PRINT, /**< output keyword */ - TOK_PRINTLN, /**< println keyword */ - TOK_INPUT, /**< input keyword */ - TOK_ALLOC, /**< alloc(size_t size) */ - TOK_FREE, /**< free(void *ptr, size_t size) */ - TOK_CAST, /**< cast(value you want to cast too) */ - TOK_SIZE_OF, /**< size_of */ - TOK_AS, /**< as keyword (for use in modules) */ - TOK_DEFER, /**< defer keyword */ - TOK_IN, /**< in keyword */ - TOK_SWITCH, /**< switch keyword */ - TOK_SYSTEM, /**< system keyword */ - - // prepocessor directives - TOK_MODULE, /**< @module */ - TOK_USE, /**< @use */ - - // function attibutes - TOK_RETURNES_OWNERSHIP, /** #returns_ownership */ - TOK_TAKES_OWNERSHIP, /** #takes_ownership */ - - // Symbols - TOK_SYMBOL, /**< Fallback symbol */ - TOK_LPAREN, /**< ( */ - TOK_RPAREN, /**< ) */ - TOK_LBRACE, /**< { */ - TOK_RBRACE, /**< } */ - TOK_LBRACKET, /**< [ */ - TOK_RBRACKET, /**< ] */ - TOK_SEMICOLON, /**< ; */ - TOK_COMMA, /**< , */ - TOK_DOT, /**< . */ - TOK_AT, /**< @ */ - TOK_EQUAL, /**< = */ - TOK_PLUS, /**< + */ - TOK_MINUS, /**< - */ - TOK_STAR, /**< * */ - TOK_SLASH, /**< / */ - TOK_LT, /**< < */ - TOK_GT, /**< > */ - TOK_LE, /**< <= */ - TOK_GE, /**< >= */ - TOK_EQEQ, /**< == */ - TOK_NEQ, /**< != */ - TOK_AMP, /**< & */ - TOK_PIPE, /**< | */ - TOK_CARET, /**< ^ */ - TOK_TILDE, /**< ~ */ - TOK_AND, /**< && */ - TOK_OR, /**< || */ - TOK_RESOLVE, /**< :: */ - TOK_COLON, /**< : */ - TOK_BANG, /**< ! */ - TOK_QUESTION, /**< ? */ - TOK_PLUSPLUS, /**< ++ */ - TOK_MINUSMINUS, /**< -- */ - TOK_SHIFT_LEFT, /**< << */ - TOK_SHIFT_RIGHT, /**< >> */ - TOK_RANGE, /**< .. */ - TOK_RIGHT_ARROW, /**< -> */ - TOK_LEFT_ARROW, /**< <- */ - TOK_MODL, /**< % */ - TOK_WHITESPACE, /**< whitespace */ - TOK_COMMENT /**< comment */ + TOK_EOF, /**< End of file/input */ + TOK_ERROR, /**< Error token */ + TOK_IDENTIFIER, /**< Identifier (variable/function names) */ + TOK_KEYWORD, /**< Reserved keyword */ + TOK_NUMBER, /**< Numeric literal */ + TOK_NUM_FLOAT, /**< Floating point numeric literal */ + TOK_STRING, /**< String literal */ + TOK_CHAR_LITERAL, /**< Character literal */ + + // Primitive Types + TOK_INT, /**< int */ + TOK_DOUBLE, /**< double */ + TOK_UINT, /**< unsigned int */ + TOK_FLOAT, /**< float */ + TOK_BOOL, /**< bool */ + TOK_STRINGT, /**< str (string type) */ + TOK_VOID, /**< void */ + TOK_CHAR, /**< char */ + + // Keywords + TOK_IF, /**< if keyword */ + TOK_ELIF, /**< elif keyword */ + TOK_ELSE, /**< else keyword */ + TOK_LOOP, /**< loop keyword */ + TOK_RETURN, /**< return keyword */ + TOK_BREAK, /**< break keyword */ + TOK_CONTINUE, /**< continue keyword */ + TOK_STRUCT, /**< struct keyword */ + TOK_ENUM, /**< enum keyword */ + TOK_MOD, /**< mod keyword */ + TOK_IMPORT, /**< import keyword */ + TOK_TRUE, /**< true keyword */ + TOK_FALSE, /**< false keyword */ + TOK_PUBLIC, /**< pub keyword */ + TOK_PRIVATE, /**< private keyword */ + TOK_VAR, /**< let keyword */ + TOK_CONST, /**< const keyword */ + TOK_FN, /**< fn keyword */ + TOK_PRINT, /**< output keyword */ + TOK_PRINTLN, /**< println keyword */ + TOK_INPUT, /**< input keyword */ + TOK_ALLOC, /**< alloc(size_t size) */ + TOK_FREE, /**< free(void *ptr, size_t size) */ + TOK_CAST, /**< cast(value you want to cast too) */ + TOK_SIZE_OF, /**< size_of */ + TOK_AS, /**< as keyword (for use in modules) */ + TOK_DEFER, /**< defer keyword */ + TOK_IN, /**< in keyword */ + TOK_SWITCH, /**< switch keyword */ + TOK_SYSTEM, /**< system keyword */ + TOK_IMPL, /**< implement keyword */ + + // prepocessor directives + TOK_MODULE, /**< @module */ + TOK_USE, /**< @use */ + + // function attibutes + TOK_RETURNES_OWNERSHIP, /** #returns_ownership */ + TOK_TAKES_OWNERSHIP, /** #takes_ownership */ + + // Symbols + TOK_SYMBOL, /**< Fallback symbol */ + TOK_LPAREN, /**< ( */ + TOK_RPAREN, /**< ) */ + TOK_LBRACE, /**< { */ + TOK_RBRACE, /**< } */ + TOK_LBRACKET, /**< [ */ + TOK_RBRACKET, /**< ] */ + TOK_SEMICOLON, /**< ; */ + TOK_COMMA, /**< , */ + TOK_DOT, /**< . */ + TOK_AT, /**< @ */ + TOK_EQUAL, /**< = */ + TOK_PLUS, /**< + */ + TOK_MINUS, /**< - */ + TOK_STAR, /**< * */ + TOK_SLASH, /**< / */ + TOK_LT, /**< < */ + TOK_GT, /**< > */ + TOK_LE, /**< <= */ + TOK_GE, /**< >= */ + TOK_EQEQ, /**< == */ + TOK_NEQ, /**< != */ + TOK_AMP, /**< & */ + TOK_PIPE, /**< | */ + TOK_CARET, /**< ^ */ + TOK_TILDE, /**< ~ */ + TOK_AND, /**< && */ + TOK_OR, /**< || */ + TOK_RESOLVE, /**< :: */ + TOK_COLON, /**< : */ + TOK_BANG, /**< ! */ + TOK_QUESTION, /**< ? */ + TOK_PLUSPLUS, /**< ++ */ + TOK_MINUSMINUS, /**< -- */ + TOK_SHIFT_LEFT, /**< << */ + TOK_SHIFT_RIGHT, /**< >> */ + TOK_RANGE, /**< .. */ + TOK_RIGHT_ARROW, /**< -> */ + TOK_LEFT_ARROW, /**< <- */ + TOK_MODL, /**< % */ + TOK_WHITESPACE, /**< whitespace */ + TOK_COMMENT /**< comment */ } LumaTokenType; /** @@ -126,11 +127,11 @@ typedef enum { * @brief Lexer state object for scanning source code. */ typedef struct { - ArenaAllocator *arena; /**< Arena allocator for token storage */ - const char *src; /**< Pointer to the source code string */ - const char *current; /**< Current scanning position in source */ - int line; /**< Current line number */ - int col; /**< Current column number */ + ArenaAllocator *arena; /**< Arena allocator for token storage */ + const char *src; /**< Pointer to the source code string */ + const char *current; /**< Current scanning position in source */ + int line; /**< Current line number */ + int col; /**< Current column number */ } Lexer; /** @@ -138,12 +139,12 @@ typedef struct { * @brief Represents a single token extracted by the lexer. */ typedef struct { - LumaTokenType type_; /**< Token type */ - const char *value; /**< Pointer to token text start */ - int line; /**< Line number of token */ - int col; /**< Column number of token */ - int length; /**< Length of the token text */ - int whitespace_len; /**< Leading whitespace length before token */ + LumaTokenType type_; /**< Token type */ + const char *value; /**< Pointer to token text start */ + int line; /**< Line number of token */ + int col; /**< Column number of token */ + int length; /**< Length of the token text */ + int whitespace_len; /**< Leading whitespace length before token */ } Token; /** @@ -151,8 +152,8 @@ typedef struct { * @brief Maps symbol text to token type for quick lookup. */ typedef struct { - const char *text; /**< Symbol text */ - LumaTokenType type; /**< Corresponding token type */ + const char *text; /**< Symbol text */ + LumaTokenType type; /**< Corresponding token type */ } SymbolEntry; /** @@ -160,8 +161,8 @@ typedef struct { * @brief Maps keyword text to token type for quick lookup. */ typedef struct { - const char *text; /**< Keyword text */ - LumaTokenType type; /**< Corresponding token type */ + const char *text; /**< Keyword text */ + LumaTokenType type; /**< Corresponding token type */ } KeywordEntry; /** diff --git a/src/llvm/expr.c b/src/llvm/expr.c index e4ec2d1e..a44f1084 100644 --- a/src/llvm/expr.c +++ b/src/llvm/expr.c @@ -433,35 +433,82 @@ LLVMValueRef codegen_expr_unary(CodeGenContext *ctx, AstNode *node) { } LLVMValueRef codegen_expr_call(CodeGenContext *ctx, AstNode *node) { - LLVMValueRef callee = codegen_expr(ctx, node->expr.call.callee); - if (!callee) - return NULL; - - LLVMValueRef *args = (LLVMValueRef *)arena_alloc( - ctx->arena, sizeof(LLVMValueRef) * node->expr.call.arg_count, - alignof(LLVMValueRef)); - - for (size_t i = 0; i < node->expr.call.arg_count; i++) { - args[i] = codegen_expr(ctx, node->expr.call.args[i]); - if (!args[i]) + AstNode *callee = node->expr.call.callee; + LLVMValueRef callee_value = NULL; + LLVMValueRef *args = NULL; + size_t arg_count = node->expr.call.arg_count; + + // Check if this is a method call (obj.method()) + if (callee->type == AST_EXPR_MEMBER && !callee->expr.member.is_compiletime) { + // Method call: obj.method(arg1, arg2) + // The typechecker has already injected 'self' as the first argument + // So we just need to codegen all arguments as-is + + // Get the method function + const char *member_name = callee->expr.member.member; + + // Look up the method in the current module + LLVMModuleRef current_llvm_module = + ctx->current_module ? ctx->current_module->module : ctx->module; + LLVMValueRef method_func = LLVMGetNamedFunction(current_llvm_module, member_name); + + if (!method_func) { + fprintf(stderr, "Error: Method '%s' not found\n", member_name); return NULL; + } + + callee_value = method_func; + + // Allocate space for all arguments (including injected self) + args = (LLVMValueRef *)arena_alloc( + ctx->arena, sizeof(LLVMValueRef) * arg_count, + alignof(LLVMValueRef)); + + // Codegen all arguments (self is already in args[0] from typechecker) + for (size_t i = 0; i < arg_count; i++) { + args[i] = codegen_expr(ctx, node->expr.call.args[i]); + if (!args[i]) { + fprintf(stderr, "Error: Failed to generate argument %zu for method '%s'\n", + i, member_name); + return NULL; + } + } + + } else { + // Regular function call or compile-time member access (module::func) + callee_value = codegen_expr(ctx, callee); + if (!callee_value) { + return NULL; + } + + // Allocate space for arguments + args = (LLVMValueRef *)arena_alloc( + ctx->arena, sizeof(LLVMValueRef) * arg_count, + alignof(LLVMValueRef)); + + for (size_t i = 0; i < arg_count; i++) { + args[i] = codegen_expr(ctx, node->expr.call.args[i]); + if (!args[i]) { + return NULL; + } + } } // Get the function type to check return type - LLVMTypeRef func_type = LLVMGlobalGetValueType(callee); + LLVMTypeRef func_type = LLVMGlobalGetValueType(callee_value); LLVMTypeRef return_type = LLVMGetReturnType(func_type); // Check if return type is void if (LLVMGetTypeKind(return_type) == LLVMVoidTypeKind) { // For void functions, don't assign a name to the call - LLVMBuildCall2(ctx->builder, func_type, callee, args, - node->expr.call.arg_count, ""); + LLVMBuildCall2(ctx->builder, func_type, callee_value, args, + arg_count, ""); // Return a void constant since we can't return NULL return LLVMConstNull(LLVMVoidTypeInContext(ctx->context)); } else { // For non-void functions, assign a name as usual - return LLVMBuildCall2(ctx->builder, func_type, callee, args, - node->expr.call.arg_count, "call"); + return LLVMBuildCall2(ctx->builder, func_type, callee_value, args, + arg_count, "call"); } } diff --git a/src/llvm/llvm.h b/src/llvm/llvm.h index dabb9bd3..47679748 100644 --- a/src/llvm/llvm.h +++ b/src/llvm/llvm.h @@ -200,7 +200,9 @@ void add_struct_type(CodeGenContext *ctx, StructInfo *struct_info); int get_field_index(StructInfo *struct_info, const char *field_name); bool is_field_access_allowed(CodeGenContext *ctx, StructInfo *struct_info, int field_index); - +LLVMValueRef codegen_struct_method(CodeGenContext *ctx, AstNode *func_node, + StructInfo *struct_info, const char *method_name, + bool is_public); // Enhanced member access (handles both struct.field and module.symbol) LLVMValueRef codegen_expr_member_access_enhanced(CodeGenContext *ctx, AstNode *node); @@ -284,6 +286,7 @@ LLVMValueRef codegen_expr_identifier(CodeGenContext *ctx, AstNode *node); LLVMValueRef codegen_expr_binary(CodeGenContext *ctx, AstNode *node); LLVMValueRef codegen_expr_unary(CodeGenContext *ctx, AstNode *node); LLVMValueRef codegen_expr_call(CodeGenContext *ctx, AstNode *node); +// LLVMValueRef codegen_method_call(CodeGenContext *ctx, AstNode *call_node); LLVMValueRef codegen_expr_assignment(CodeGenContext *ctx, AstNode *node); LLVMValueRef codegen_expr_array(CodeGenContext *ctx, AstNode *node); LLVMValueRef codegen_expr_index(CodeGenContext *ctx, AstNode *node); diff --git a/src/llvm/struct.c b/src/llvm/struct.c index d299a78e..0f234b57 100644 --- a/src/llvm/struct.c +++ b/src/llvm/struct.c @@ -36,18 +36,32 @@ bool is_field_access_allowed(CodeGenContext *ctx, StructInfo *struct_info, return struct_info->field_is_public[field_index]; } -// Handle struct declaration LLVMValueRef codegen_stmt_struct(CodeGenContext *ctx, AstNode *node) { if (!node || node->type != AST_STMT_STRUCT) { return NULL; } const char *struct_name = node->stmt.struct_decl.name; - size_t total_members = node->stmt.struct_decl.public_count + - node->stmt.struct_decl.private_count; + size_t public_count = node->stmt.struct_decl.public_count; + size_t private_count = node->stmt.struct_decl.private_count; + + // Separate data fields from methods + size_t data_field_count = 0; + for (size_t i = 0; i < public_count; i++) { + AstNode *member = node->stmt.struct_decl.public_members[i]; + if (member->type == AST_STMT_FIELD_DECL && !member->stmt.field_decl.function) { + data_field_count++; + } + } + for (size_t i = 0; i < private_count; i++) { + AstNode *member = node->stmt.struct_decl.private_members[i]; + if (member->type == AST_STMT_FIELD_DECL && !member->stmt.field_decl.function) { + data_field_count++; + } + } - if (total_members == 0) { - fprintf(stderr, "Error: Struct %s cannot be empty\n", struct_name); + if (data_field_count == 0) { + fprintf(stderr, "Error: Struct %s must have at least one data field\n", struct_name); return NULL; } @@ -57,35 +71,34 @@ LLVMValueRef codegen_stmt_struct(CodeGenContext *ctx, AstNode *node) { return NULL; } - // Create StructInfo + // Create StructInfo for data fields only StructInfo *struct_info = (StructInfo *)arena_alloc( ctx->arena, sizeof(StructInfo), alignof(StructInfo)); struct_info->name = arena_strdup(ctx->arena, struct_name); - struct_info->field_count = total_members; + struct_info->field_count = data_field_count; struct_info->is_public = node->stmt.struct_decl.is_public; // Allocate arrays for field information struct_info->field_names = (char **)arena_alloc( - ctx->arena, sizeof(char *) * total_members, alignof(char *)); + ctx->arena, sizeof(char *) * data_field_count, alignof(char *)); struct_info->field_types = (LLVMTypeRef *)arena_alloc( - ctx->arena, sizeof(LLVMTypeRef) * total_members, alignof(LLVMTypeRef)); - struct_info->field_element_types = (LLVMTypeRef *)arena_alloc( // NEW - ctx->arena, sizeof(LLVMTypeRef) * total_members, alignof(LLVMTypeRef)); + ctx->arena, sizeof(LLVMTypeRef) * data_field_count, alignof(LLVMTypeRef)); + struct_info->field_element_types = (LLVMTypeRef *)arena_alloc( + ctx->arena, sizeof(LLVMTypeRef) * data_field_count, alignof(LLVMTypeRef)); struct_info->field_is_public = (bool *)arena_alloc( - ctx->arena, sizeof(bool) * total_members, alignof(bool)); + ctx->arena, sizeof(bool) * data_field_count, alignof(bool)); - // Process public members first + // Process public data fields size_t field_index = 0; - for (size_t i = 0; i < node->stmt.struct_decl.public_count; i++) { - AstNode *field = node->stmt.struct_decl.public_members[i]; - if (field->type != AST_STMT_FIELD_DECL) { - fprintf(stderr, "Error: Expected field declaration in struct %s\n", - struct_name); - return NULL; - } + for (size_t i = 0; i < public_count; i++) { + AstNode *member = node->stmt.struct_decl.public_members[i]; + if (member->type != AST_STMT_FIELD_DECL) continue; + + // Skip methods for now, we'll process them after the struct type is created + if (member->stmt.field_decl.function) continue; - const char *field_name = field->stmt.field_decl.name; + const char *field_name = member->stmt.field_decl.name; // Check for duplicate field names for (size_t j = 0; j < field_index; j++) { @@ -97,31 +110,27 @@ LLVMValueRef codegen_stmt_struct(CodeGenContext *ctx, AstNode *node) { } struct_info->field_names[field_index] = arena_strdup(ctx->arena, field_name); - struct_info->field_types[field_index] = codegen_type(ctx, field->stmt.field_decl.type); - struct_info->field_element_types[field_index] = extract_element_type_from_ast(ctx, field->stmt.field_decl.type); + struct_info->field_types[field_index] = codegen_type(ctx, member->stmt.field_decl.type); + struct_info->field_element_types[field_index] = extract_element_type_from_ast(ctx, member->stmt.field_decl.type); struct_info->field_is_public[field_index] = true; if (!struct_info->field_types[field_index]) { - fprintf(stderr, - "Error: Failed to resolve type for field %s in struct %s\n", + fprintf(stderr, "Error: Failed to resolve type for field %s in struct %s\n", field_name, struct_name); return NULL; } field_index++; } - // Process private members - for (size_t i = 0; i < node->stmt.struct_decl.private_count; i++) { - AstNode *field = node->stmt.struct_decl.private_members[i]; - if (field->type != AST_STMT_FIELD_DECL) { - fprintf(stderr, "Error: Expected field declaration in struct %s\n", - struct_name); - return NULL; - } + // Process private data fields + for (size_t i = 0; i < private_count; i++) { + AstNode *member = node->stmt.struct_decl.private_members[i]; + if (member->type != AST_STMT_FIELD_DECL) continue; + + if (member->stmt.field_decl.function) continue; - const char *field_name = field->stmt.field_decl.name; + const char *field_name = member->stmt.field_decl.name; - // Check for duplicate field names (including public fields) for (size_t j = 0; j < field_index; j++) { if (strcmp(struct_info->field_names[j], field_name) == 0) { fprintf(stderr, "Error: Duplicate field name '%s' in struct %s\n", @@ -131,13 +140,12 @@ LLVMValueRef codegen_stmt_struct(CodeGenContext *ctx, AstNode *node) { } struct_info->field_names[field_index] = arena_strdup(ctx->arena, field_name); - struct_info->field_types[field_index] = codegen_type(ctx, field->stmt.field_decl.type); - struct_info->field_element_types[field_index] = extract_element_type_from_ast(ctx, field->stmt.field_decl.type); - struct_info->field_is_public[field_index] = field->stmt.field_decl.is_public; + struct_info->field_types[field_index] = codegen_type(ctx, member->stmt.field_decl.type); + struct_info->field_element_types[field_index] = extract_element_type_from_ast(ctx, member->stmt.field_decl.type); + struct_info->field_is_public[field_index] = member->stmt.field_decl.is_public; if (!struct_info->field_types[field_index]) { - fprintf(stderr, - "Error: Failed to resolve type for field %s in struct %s\n", + fprintf(stderr, "Error: Failed to resolve type for field %s in struct %s\n", field_name, struct_name); return NULL; } @@ -146,29 +154,182 @@ LLVMValueRef codegen_stmt_struct(CodeGenContext *ctx, AstNode *node) { // Create LLVM struct type struct_info->llvm_type = LLVMStructTypeInContext( - ctx->context, struct_info->field_types, total_members, false); + ctx->context, struct_info->field_types, data_field_count, false); - // Note: LLVMStructSetName is not available in all LLVM versions - // The struct will still work without an explicit name - - // Add to context + // Add to context BEFORE processing methods add_struct_type(ctx, struct_info); - - // Add struct type to symbol table as a type symbol add_symbol(ctx, struct_name, NULL, struct_info->llvm_type, false); -// Debug output (can be removed in production) -#ifdef DEBUG_STRUCTS - printf("Defined struct %s with %zu fields:\n", struct_name, total_members); - for (size_t i = 0; i < total_members; i++) { - printf(" - %s: %s (%s)\n", struct_info->field_names[i], "type", - struct_info->field_is_public[i] ? "public" : "private"); + // NOW process methods with access to the struct type + for (size_t i = 0; i < public_count; i++) { + AstNode *member = node->stmt.struct_decl.public_members[i]; + if (member->type != AST_STMT_FIELD_DECL) continue; + + // Only process methods + if (!member->stmt.field_decl.function) continue; + + AstNode *func_node = member->stmt.field_decl.function; + const char *method_name = member->stmt.field_decl.name; + + // Generate the method with implicit 'self' parameter + if (!codegen_struct_method(ctx, func_node, struct_info, method_name, true)) { + fprintf(stderr, "Error: Failed to generate method '%s' for struct '%s'\n", + method_name, struct_name); + return NULL; + } + } + + // Process private methods + for (size_t i = 0; i < private_count; i++) { + AstNode *member = node->stmt.struct_decl.private_members[i]; + if (member->type != AST_STMT_FIELD_DECL) continue; + + if (!member->stmt.field_decl.function) continue; + + AstNode *func_node = member->stmt.field_decl.function; + const char *method_name = member->stmt.field_decl.name; + + if (!codegen_struct_method(ctx, func_node, struct_info, method_name, false)) { + fprintf(stderr, "Error: Failed to generate private method '%s' for struct '%s'\n", + method_name, struct_name); + return NULL; + } } -#endif return NULL; } +LLVMValueRef codegen_struct_method(CodeGenContext *ctx, AstNode *func_node, + StructInfo *struct_info, const char *method_name, + bool is_public) { + if (!func_node || func_node->type != AST_STMT_FUNCTION) { + fprintf(stderr, "Error: Invalid function node for method '%s'\n", method_name); + return NULL; + } + + // Get method signature + AstNode *return_type_node = func_node->stmt.func_decl.return_type; + size_t original_param_count = func_node->stmt.func_decl.param_count; + AstNode **original_param_type_nodes = func_node->stmt.func_decl.param_types; + char **original_param_names = func_node->stmt.func_decl.param_names; + + // CRITICAL: Methods need an implicit 'self' parameter as the FIRST parameter + // The typechecker injects 'self' when calling methods, so the method definition must match + size_t param_count = original_param_count + 1; // +1 for 'self' + + // Allocate arrays for ALL parameters (including self) + LLVMTypeRef *llvm_param_types = (LLVMTypeRef *)arena_alloc( + ctx->arena, sizeof(LLVMTypeRef) * param_count, alignof(LLVMTypeRef)); + + char **param_names = (char **)arena_alloc( + ctx->arena, sizeof(char *) * param_count, alignof(char *)); + + AstNode **param_type_nodes = (AstNode **)arena_alloc( + ctx->arena, sizeof(AstNode *) * param_count, alignof(AstNode *)); + + // First parameter is 'self' - a pointer to the struct + llvm_param_types[0] = LLVMPointerType(struct_info->llvm_type, 0); + param_names[0] = "self"; + param_type_nodes[0] = NULL; // We'll handle this specially for element type extraction + + // Copy the rest of the original parameters (shifted by 1) + for (size_t i = 0; i < original_param_count; i++) { + llvm_param_types[i + 1] = codegen_type(ctx, original_param_type_nodes[i]); + if (!llvm_param_types[i + 1]) { + fprintf(stderr, "Error: Failed to resolve parameter type %zu for method '%s'\n", + i, method_name); + return NULL; + } + param_names[i + 1] = original_param_names[i]; + param_type_nodes[i + 1] = original_param_type_nodes[i]; + } + + // Create function type + LLVMTypeRef llvm_return_type = codegen_type(ctx, return_type_node); + if (!llvm_return_type) { + fprintf(stderr, "Error: Failed to resolve return type for method '%s'\n", method_name); + return NULL; + } + + LLVMTypeRef func_type = LLVMFunctionType(llvm_return_type, llvm_param_types, + param_count, 0); + + // Get the current LLVM module + LLVMModuleRef current_llvm_module = + ctx->current_module ? ctx->current_module->module : ctx->module; + + // Create the function in the current module + LLVMValueRef func = LLVMAddFunction(current_llvm_module, method_name, func_type); + + if (!func) { + fprintf(stderr, "Error: Failed to create LLVM function for method '%s'\n", method_name); + return NULL; + } + + // Set linkage + if (is_public) { + LLVMSetLinkage(func, LLVMExternalLinkage); + } else { + LLVMSetLinkage(func, LLVMInternalLinkage); + } + + // CRITICAL: Save the old function context before starting method generation + LLVMValueRef old_function = ctx->current_function; + + // Set current function context + ctx->current_function = func; + + // Create entry basic block + LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext( + ctx->context, func, "entry"); + LLVMPositionBuilderAtEnd(ctx->builder, entry); + + // Add all parameters to symbol table (including self at index 0) + for (size_t i = 0; i < param_count; i++) { + LLVMValueRef param = LLVMGetParam(func, i); + const char *param_name = param_names[i]; + + LLVMSetValueName2(param, param_name, strlen(param_name)); + + // Allocate stack space and store parameter + LLVMValueRef alloca = LLVMBuildAlloca(ctx->builder, llvm_param_types[i], param_name); + LLVMBuildStore(ctx->builder, param, alloca); + + // Extract element type for pointer parameters (needed for self which is *Person) + LLVMTypeRef element_type = extract_element_type_from_ast(ctx, param_type_nodes[i]); + + // Add to symbol table with element type information + add_symbol_with_element_type(ctx, param_name, alloca, llvm_param_types[i], + element_type, false); + } + + // Generate method body + AstNode *body = func_node->stmt.func_decl.body; + if (body) { + codegen_stmt(ctx, body); + } + + // Add return if missing for void functions + if (LLVMGetTypeKind(llvm_return_type) == LLVMVoidTypeKind) { + if (!LLVMGetBasicBlockTerminator(LLVMGetInsertBlock(ctx->builder))) { + LLVMBuildRetVoid(ctx->builder); + } + } + + // Verify the function + if (LLVMVerifyFunction(func, LLVMReturnStatusAction)) { + fprintf(stderr, "Error: Function verification failed for method '%s'\n", method_name); + LLVMDumpValue(func); + // Restore context even on error + ctx->current_function = old_function; + return NULL; + } + + // CRITICAL: Restore the old function context + ctx->current_function = old_function; + + return func; +} // Handle individual field declarations (mainly for completeness) LLVMValueRef codegen_stmt_field(CodeGenContext *ctx, AstNode *node) { // Field declarations are handled by the parent struct diff --git a/src/parser/parser.c b/src/parser/parser.c index 6bf074eb..9ec7a5ab 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -48,20 +48,21 @@ */ void parser_error(Parser *psr, const char *error_type, const char *file, const char *msg, int line, int col, int tk_length) { - (void)file; - ErrorInformation err = { - .error_type = error_type, - .file_path = psr->file_path, - .message = msg, - .line = line, - .col = col, - .line_text = generate_line(psr->arena, psr->tks, psr->tk_count, err.line), - .token_length = tk_length, - .label = "Parser Error", - .note = NULL, - .help = NULL, - }; - error_add(err); + (void)file; + ErrorInformation err = { + .error_type = error_type, + .file_path = psr->file_path, + .message = msg, + .line = line, + .col = col, + .line_text = + generate_line(psr->arena, psr->tks, psr->tk_count, err.line), + .token_length = tk_length, + .label = "Parser Error", + .note = NULL, + .help = NULL, + }; + error_add(err); } /** @@ -85,69 +86,69 @@ void parser_error(Parser *psr, const char *error_type, const char *file, */ Stmt *parse(GrowableArray *tks, ArenaAllocator *arena, BuildConfig *config) { - // Initialize parser - Parser parser = { - .file_path = config->filepath, - .arena = arena, - .tks = (Token *)tks->data, - .tk_count = tks->count, - .capacity = (tks->count / 4) + 10, - .pos = 0, - }; - - if (!parser.tks) { - fprintf(stderr, "Failed to get tokens from GrowableArray\n"); - return NULL; - } - - // Initialize arrays - GrowableArray stmts, modules; - if (!init_parser_arrays(&parser, &stmts, &modules)) { - return NULL; - } - - // Parse module declaration - Token module_tok = p_current(&parser); - const char *module_name = parse_module_declaration(&parser); - if (!module_name) { - return NULL; - } - - // Create initial module node and add to modules array - Stmt *module_stmt = create_module_node(parser.arena, module_name, 0, NULL, 0, - module_tok.line, module_tok.col); - - Stmt **module_slot = (Stmt **)growable_array_push(&modules); - if (!module_slot) { - fprintf(stderr, "Out of memory while growing modules array\n"); - return NULL; - } - *module_slot = module_stmt; - - // Parse all statements - while (p_current(&parser).type_ != TOK_EOF) { - Stmt *stmt = parse_stmt(&parser); - if (!stmt) { - // Error already reported in parse_stmt - return NULL; + // Initialize parser + Parser parser = { + .file_path = config->filepath, + .arena = arena, + .tks = (Token *)tks->data, + .tk_count = tks->count, + .capacity = (tks->count / 4) + 10, + .pos = 0, + }; + + if (!parser.tks) { + fprintf(stderr, "Failed to get tokens from GrowableArray\n"); + return NULL; } - Stmt **slot = (Stmt **)growable_array_push(&stmts); - if (!slot) { - fprintf(stderr, "Out of memory while growing statements array\n"); - return NULL; + // Initialize arrays + GrowableArray stmts, modules; + if (!init_parser_arrays(&parser, &stmts, &modules)) { + return NULL; } - *slot = stmt; - } - // Update module with parsed statements - *module_slot = - create_module_node(parser.arena, module_name, 0, (Stmt **)stmts.data, - stmts.count, module_tok.line, module_tok.col); + // Parse module declaration + Token module_tok = p_current(&parser); + const char *module_name = parse_module_declaration(&parser); + if (!module_name) { + return NULL; + } + + // Create initial module node and add to modules array + Stmt *module_stmt = create_module_node(parser.arena, module_name, 0, NULL, + 0, module_tok.line, module_tok.col); + + Stmt **module_slot = (Stmt **)growable_array_push(&modules); + if (!module_slot) { + fprintf(stderr, "Out of memory while growing modules array\n"); + return NULL; + } + *module_slot = module_stmt; + + // Parse all statements + while (p_current(&parser).type_ != TOK_EOF) { + Stmt *stmt = parse_stmt(&parser); + if (!stmt) { + // Error already reported in parse_stmt + return NULL; + } + + Stmt **slot = (Stmt **)growable_array_push(&stmts); + if (!slot) { + fprintf(stderr, "Out of memory while growing statements array\n"); + return NULL; + } + *slot = stmt; + } + + // Update module with parsed statements + *module_slot = + create_module_node(parser.arena, module_name, 0, (Stmt **)stmts.data, + stmts.count, module_tok.line, module_tok.col); - // Create and return program node - return create_program_node(parser.arena, (AstNode **)modules.data, - modules.count, 0, 0); + // Create and return program node + return create_program_node(parser.arena, (AstNode **)modules.data, + modules.count, 0, 0); } /** * @brief Gets the binding power (precedence) for a given token type @@ -178,68 +179,68 @@ Stmt *parse(GrowableArray *tks, ArenaAllocator *arena, BuildConfig *config) { * @see BindingPower, LumaTokenType */ BindingPower get_bp(LumaTokenType kind) { - switch (kind) { - // Assignment - case TOK_EQUAL: - return BP_ASSIGN; - - // Ternary - case TOK_QUESTION: - return BP_TERNARY; - - // Logical - case TOK_OR: - return BP_LOGICAL_OR; - case TOK_AND: - return BP_LOGICAL_AND; - - // Bitwise - case TOK_PIPE: - return BP_BITWISE_OR; - case TOK_CARET: - return BP_BITWISE_XOR; - case TOK_AMP: - return BP_BITWISE_AND; - - // Equality - case TOK_EQEQ: - case TOK_NEQ: - return BP_EQUALITY; - - // Relational - case TOK_LT: - case TOK_LE: - case TOK_GT: - case TOK_GE: - return BP_RELATIONAL; - - // Arithmetic - case TOK_PLUS: - case TOK_MINUS: - return BP_SUM; - case TOK_STAR: - case TOK_SLASH: - case TOK_MODL: - return BP_PRODUCT; - - // Postfix - case TOK_PLUSPLUS: - case TOK_MINUSMINUS: - return BP_POSTFIX; - - // Call/indexing/member access - case TOK_LPAREN: - case TOK_LBRACKET: - case TOK_DOT: - case TOK_RESOLVE: - return BP_CALL; - - case TOK_RANGE: - return BP_RANGE; - - default: - return BP_NONE; - } + switch (kind) { + // Assignment + case TOK_EQUAL: + return BP_ASSIGN; + + // Ternary + case TOK_QUESTION: + return BP_TERNARY; + + // Logical + case TOK_OR: + return BP_LOGICAL_OR; + case TOK_AND: + return BP_LOGICAL_AND; + + // Bitwise + case TOK_PIPE: + return BP_BITWISE_OR; + case TOK_CARET: + return BP_BITWISE_XOR; + case TOK_AMP: + return BP_BITWISE_AND; + + // Equality + case TOK_EQEQ: + case TOK_NEQ: + return BP_EQUALITY; + + // Relational + case TOK_LT: + case TOK_LE: + case TOK_GT: + case TOK_GE: + return BP_RELATIONAL; + + // Arithmetic + case TOK_PLUS: + case TOK_MINUS: + return BP_SUM; + case TOK_STAR: + case TOK_SLASH: + case TOK_MODL: + return BP_PRODUCT; + + // Postfix + case TOK_PLUSPLUS: + case TOK_MINUSMINUS: + return BP_POSTFIX; + + // Call/indexing/member access + case TOK_LPAREN: + case TOK_LBRACKET: + case TOK_DOT: + case TOK_RESOLVE: + return BP_CALL; + + case TOK_RANGE: + return BP_RANGE; + + default: + return BP_NONE; + } } /** @@ -262,45 +263,45 @@ BindingPower get_bp(LumaTokenType kind) { * @see led(), parse_expr(), primary(), unary(), grouping(), array_expr() */ Expr *nud(Parser *parser) { - switch (p_current(parser).type_) { - case TOK_NUMBER: - case TOK_NUM_FLOAT: - case TOK_STRING: - case TOK_CHAR_LITERAL: - case TOK_IDENTIFIER: - return primary(parser); - case TOK_MINUS: - case TOK_PLUS: - case TOK_BANG: - case TOK_PLUSPLUS: - case TOK_MINUSMINUS: - return unary(parser); - case TOK_LPAREN: - return grouping(parser); - case TOK_LBRACKET: - return array_expr(parser); - case TOK_STAR: - return deref_expr(parser); - case TOK_AMP: - return addr_expr(parser); - case TOK_ALLOC: - return alloc_expr(parser); - case TOK_FREE: - return free_expr(parser); - case TOK_CAST: - return cast_expr(parser); - case TOK_INPUT: - return input_expr(parser); - case TOK_SYSTEM: - return system_expr(parser); - - // Compile time - case TOK_SIZE_OF: - return sizeof_expr(parser); - default: - p_advance(parser); - return NULL; - } + switch (p_current(parser).type_) { + case TOK_NUMBER: + case TOK_NUM_FLOAT: + case TOK_STRING: + case TOK_CHAR_LITERAL: + case TOK_IDENTIFIER: + return primary(parser); + case TOK_MINUS: + case TOK_PLUS: + case TOK_BANG: + case TOK_PLUSPLUS: + case TOK_MINUSMINUS: + return unary(parser); + case TOK_LPAREN: + return grouping(parser); + case TOK_LBRACKET: + return array_expr(parser); + case TOK_STAR: + return deref_expr(parser); + case TOK_AMP: + return addr_expr(parser); + case TOK_ALLOC: + return alloc_expr(parser); + case TOK_FREE: + return free_expr(parser); + case TOK_CAST: + return cast_expr(parser); + case TOK_INPUT: + return input_expr(parser); + case TOK_SYSTEM: + return system_expr(parser); + + // Compile time + case TOK_SIZE_OF: + return sizeof_expr(parser); + default: + p_advance(parser); + return NULL; + } } /** @@ -328,40 +329,40 @@ Expr *nud(Parser *parser) { * @see nud(), parse_expr(), binary(), call_expr(), assign_expr(), prefix_expr() */ Expr *led(Parser *parser, Expr *left, BindingPower bp) { - switch (p_current(parser).type_) { - case TOK_PLUS: - case TOK_MINUS: - case TOK_STAR: - case TOK_SLASH: - case TOK_MODL: - case TOK_EQEQ: - case TOK_NEQ: - case TOK_LT: - case TOK_LE: - case TOK_GT: - case TOK_GE: - case TOK_AMP: - case TOK_PIPE: - case TOK_CARET: - case TOK_AND: // Add logical AND - case TOK_OR: // Add logical OR - case TOK_RANGE: - return binary(parser, left, bp); - case TOK_LPAREN: - return call_expr(parser, left, bp); - case TOK_EQUAL: - return assign_expr(parser, left, bp); - case TOK_DOT: - case TOK_RESOLVE: - case TOK_PLUSPLUS: - case TOK_MINUSMINUS: - case TOK_LBRACKET: - // Handle member access, postfix increment/decrement, and indexing - return prefix_expr(parser, left, bp); - default: - p_advance(parser); - return left; // No valid LED found, return left expression - } + switch (p_current(parser).type_) { + case TOK_PLUS: + case TOK_MINUS: + case TOK_STAR: + case TOK_SLASH: + case TOK_MODL: + case TOK_EQEQ: + case TOK_NEQ: + case TOK_LT: + case TOK_LE: + case TOK_GT: + case TOK_GE: + case TOK_AMP: + case TOK_PIPE: + case TOK_CARET: + case TOK_AND: // Add logical AND + case TOK_OR: // Add logical OR + case TOK_RANGE: + return binary(parser, left, bp); + case TOK_LPAREN: + return call_expr(parser, left, bp); + case TOK_EQUAL: + return assign_expr(parser, left, bp); + case TOK_DOT: + case TOK_RESOLVE: + case TOK_PLUSPLUS: + case TOK_MINUSMINUS: + case TOK_LBRACKET: + // Handle member access, postfix increment/decrement, and indexing + return prefix_expr(parser, left, bp); + default: + p_advance(parser); + return left; // No valid LED found, return left expression + } } /** @@ -386,13 +387,13 @@ Expr *led(Parser *parser, Expr *left, BindingPower bp) { * @see nud(), led(), get_bp(), BindingPower */ Expr *parse_expr(Parser *parser, BindingPower bp) { - Expr *left = nud(parser); + Expr *left = nud(parser); - while (p_has_tokens(parser) && get_bp(p_current(parser).type_) > bp) { - left = led(parser, left, get_bp(p_current(parser).type_)); - } + while (p_has_tokens(parser) && get_bp(p_current(parser).type_) > bp) { + left = led(parser, left, get_bp(p_current(parser).type_)); + } - return left; + return left; } /** @@ -418,61 +419,65 @@ Expr *parse_expr(Parser *parser, BindingPower bp) { * loop_stmt(), print_stmt(), break_continue_stmt(), expr_stmt() */ Stmt *parse_stmt(Parser *parser) { - bool returns_ownership = false; - bool takes_ownership = false; - bool is_public = false; - - // Check for ownership modifiers - while (p_current(parser).type_ == TOK_RETURNES_OWNERSHIP || - p_current(parser).type_ == TOK_TAKES_OWNERSHIP) { - if (p_current(parser).type_ == TOK_RETURNES_OWNERSHIP) { - returns_ownership = true; - p_advance(parser); - } else if (p_current(parser).type_ == TOK_TAKES_OWNERSHIP) { - takes_ownership = true; - p_advance(parser); + bool returns_ownership = false; + bool takes_ownership = false; + bool is_public = false; + + // Check for ownership modifiers + while (p_current(parser).type_ == TOK_RETURNES_OWNERSHIP || + p_current(parser).type_ == TOK_TAKES_OWNERSHIP) { + if (p_current(parser).type_ == TOK_RETURNES_OWNERSHIP) { + returns_ownership = true; + p_advance(parser); + } else if (p_current(parser).type_ == TOK_TAKES_OWNERSHIP) { + takes_ownership = true; + p_advance(parser); + } + } + + // Check for visibility modifiers + if (p_current(parser).type_ == TOK_PUBLIC) { + is_public = true; + p_advance(parser); // Advance past the public token + } else if (p_current(parser).type_ == TOK_PRIVATE) { + is_public = false; + p_advance(parser); // Advance past the private token + } + + switch (p_current(parser).type_) { + case TOK_USE: + return use_stmt(parser); + case TOK_CONST: + return const_stmt(parser, is_public, returns_ownership, + takes_ownership); + case TOK_VAR: + return var_stmt(parser, is_public); + case TOK_RETURN: + return return_stmt(parser); + case TOK_LBRACE: + return block_stmt(parser); + case TOK_IF: + return if_stmt(parser); + case TOK_LOOP: + return loop_stmt(parser); + case TOK_PRINT: + return print_stmt(parser, false); + case TOK_PRINTLN: + return print_stmt(parser, true); + case TOK_CONTINUE: + case TOK_BREAK: + return break_continue_stmt(parser, + p_current(parser).type_ == TOK_CONTINUE); + case TOK_DEFER: + return defer_stmt(parser); + case TOK_SWITCH: + return switch_stmt(parser); + case TOK_IMPL: + return impl_stmt(parser); + default: + return expr_stmt( + parser); // expression statements handle their own semicolon } - } - - // Check for visibility modifiers - if (p_current(parser).type_ == TOK_PUBLIC) { - is_public = true; - p_advance(parser); // Advance past the public token - } else if (p_current(parser).type_ == TOK_PRIVATE) { - is_public = false; - p_advance(parser); // Advance past the private token - } - - switch (p_current(parser).type_) { - case TOK_USE: - return use_stmt(parser); - case TOK_CONST: - return const_stmt(parser, is_public, returns_ownership, takes_ownership); - case TOK_VAR: - return var_stmt(parser, is_public); - case TOK_RETURN: - return return_stmt(parser); - case TOK_LBRACE: - return block_stmt(parser); - case TOK_IF: - return if_stmt(parser); - case TOK_LOOP: - return loop_stmt(parser); - case TOK_PRINT: - return print_stmt(parser, false); - case TOK_PRINTLN: - return print_stmt(parser, true); - case TOK_CONTINUE: - case TOK_BREAK: - return break_continue_stmt(parser, p_current(parser).type_ == TOK_CONTINUE); - case TOK_DEFER: - return defer_stmt(parser); - case TOK_SWITCH: - return switch_stmt(parser); - default: - return expr_stmt( - parser); // expression statements handle their own semicolon - } } /** @@ -497,28 +502,28 @@ Stmt *parse_stmt(Parser *parser) { * @see tnud(), tled(), LumaTokenType */ Type *parse_type(Parser *parser) { - LumaTokenType tok = p_current(parser).type_; - - switch (tok) { - case TOK_INT: - case TOK_UINT: - case TOK_DOUBLE: - case TOK_FLOAT: - case TOK_BOOL: - case TOK_STRINGT: - case TOK_VOID: - case TOK_CHAR: - case TOK_STAR: // Pointer type - case TOK_LBRACKET: // Array type - return tnud(parser); - - // Optionally: handle identifiers like 'MyStruct' or user-defined types - case TOK_IDENTIFIER: - return create_basic_type(parser->arena, get_name(parser), parser->tks->line, - parser->tks->col); - - default: - fprintf(stderr, "[parse_type] Unexpected token for type: %d\n", tok); - return NULL; - } + LumaTokenType tok = p_current(parser).type_; + + switch (tok) { + case TOK_INT: + case TOK_UINT: + case TOK_DOUBLE: + case TOK_FLOAT: + case TOK_BOOL: + case TOK_STRINGT: + case TOK_VOID: + case TOK_CHAR: + case TOK_STAR: // Pointer type + case TOK_LBRACKET: // Array type + return tnud(parser); + + // Optionally: handle identifiers like 'MyStruct' or user-defined types + case TOK_IDENTIFIER: + return create_basic_type(parser->arena, get_name(parser), + parser->tks->line, parser->tks->col); + + default: + fprintf(stderr, "[parse_type] Unexpected token for type: %d\n", tok); + return NULL; + } } diff --git a/src/parser/parser.h b/src/parser/parser.h index 1beb2a83..47dfaa93 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -51,26 +51,26 @@ * Used to control operator precedence and associativity in Pratt parsing. */ typedef enum { - BP_NONE = 0, /**< No binding power */ - BP_LOWEST, /**< Lowest binding power */ - BP_ASSIGN, /**< Assignment operators (=, +=, etc.) */ - BP_TERNARY, /**< Ternary conditional operator (? :) */ - BP_LOGICAL_OR, /**< Logical OR operator (||) */ - BP_LOGICAL_AND, /**< Logical AND operator (&&) */ - BP_BITWISE_OR, /**< Bitwise OR operator (|) */ - BP_BITWISE_XOR, /**< Bitwise XOR operator (^) */ - BP_BITWISE_AND, /**< Bitwise AND operator (&) */ - BP_EQUALITY, /**< Equality operators (==, !=) */ - BP_RELATIONAL, /**< Relational operators (<, >, <=, >=) */ - BP_RANGE, /**< Range operations (..) */ - BP_SHIFT, /**< Shift operators (<<, >>) */ - BP_SUM, /**< Addition and subtraction (+, -) */ - BP_PRODUCT, /**< Multiplication, division, modulo (*, /, %) */ - BP_EXPONENT, /**< Exponentiation operator (**) */ - BP_UNARY, /**< Unary operators (!, ~, +, -, prefix ++/--) */ - BP_POSTFIX, /**< Postfix operators (++/-- postfix) */ - BP_CALL, /**< Function call or indexing */ - BP_PRIMARY /**< Primary expressions (literals, variables) */ + BP_NONE = 0, /**< No binding power */ + BP_LOWEST, /**< Lowest binding power */ + BP_ASSIGN, /**< Assignment operators (=, +=, etc.) */ + BP_TERNARY, /**< Ternary conditional operator (? :) */ + BP_LOGICAL_OR, /**< Logical OR operator (||) */ + BP_LOGICAL_AND, /**< Logical AND operator (&&) */ + BP_BITWISE_OR, /**< Bitwise OR operator (|) */ + BP_BITWISE_XOR, /**< Bitwise XOR operator (^) */ + BP_BITWISE_AND, /**< Bitwise AND operator (&) */ + BP_EQUALITY, /**< Equality operators (==, !=) */ + BP_RELATIONAL, /**< Relational operators (<, >, <=, >=) */ + BP_RANGE, /**< Range operations (..) */ + BP_SHIFT, /**< Shift operators (<<, >>) */ + BP_SUM, /**< Addition and subtraction (+, -) */ + BP_PRODUCT, /**< Multiplication, division, modulo (*, /, %) */ + BP_EXPONENT, /**< Exponentiation operator (**) */ + BP_UNARY, /**< Unary operators (!, ~, +, -, prefix ++/--) */ + BP_POSTFIX, /**< Postfix operators (++/-- postfix) */ + BP_CALL, /**< Function call or indexing */ + BP_PRIMARY /**< Primary expressions (literals, variables) */ } BindingPower; /** @@ -113,12 +113,12 @@ static const UnaryOp TOKEN_TO_UNOP_MAP[] = { * @brief Parser state holding token stream and current position. */ typedef struct { - const char *file_path; - ArenaAllocator *arena; /**< Memory arena for AST node allocations */ - Token *tks; /**< Array of tokens to parse */ - size_t tk_count; /**< Number of tokens in the array */ - size_t capacity; /**< Capacity for statements and expressions */ - size_t pos; /**< Current token position */ + const char *file_path; + ArenaAllocator *arena; /**< Memory arena for AST node allocations */ + Token *tks; /**< Array of tokens to parse */ + size_t tk_count; /**< Number of tokens in the array */ + size_t capacity; /**< Capacity for statements and expressions */ + size_t pos; /**< Current token position */ } Parser; /** @@ -217,3 +217,4 @@ Stmt *if_stmt(Parser *parser); Stmt *break_continue_stmt(Parser *parser, bool is_continue); Stmt *defer_stmt(Parser *parser); Stmt *switch_stmt(Parser *parser); +Stmt *impl_stmt(Parser *parser); diff --git a/src/parser/stmt.c b/src/parser/stmt.c index 18603895..09ba8fe1 100644 --- a/src/parser/stmt.c +++ b/src/parser/stmt.c @@ -350,10 +350,22 @@ Stmt *struct_stmt(Parser *parser, const char *name, bool is_public) { // TODO: Add in a check to see if we have any function modifiers like // returns_ownership or takes_ownership - // Method: field_name = fn(...) - if (p_current(parser).type_ == TOK_EQUAL) { - p_consume(parser, TOK_EQUAL, "Expected '=' after field name"); - field_function = fn_stmt(parser, field_name, public_member, false, false); + bool takes_ownership, returns_ownership = false; + while (p_current(parser).type_ == TOK_RETURNES_OWNERSHIP || + p_current(parser).type_ == TOK_TAKES_OWNERSHIP) { + if (p_current(parser).type_ == TOK_RETURNES_OWNERSHIP) { + returns_ownership = true; + p_advance(parser); + } else if (p_current(parser).type_ == TOK_TAKES_OWNERSHIP) { + takes_ownership = true; + p_advance(parser); + } + } + + // Method: field_name -> fn(...) + if (p_current(parser).type_ == TOK_RIGHT_ARROW) { + p_consume(parser, TOK_RIGHT_ARROW, "Expected '->' after field name"); + field_function = fn_stmt(parser, field_name, public_member, returns_ownership, takes_ownership); } else { // Data field: field_name: Type p_consume(parser, TOK_COLON, "Expected ':' after field name"); @@ -1014,3 +1026,100 @@ Stmt *switch_stmt(Parser *parser) { return create_switch_stmt(parser->arena, condition, (AstNode **)cases.data, cases.count, (AstNode *)default_case, line, col); } +// Impl [fun1: void, fun2: void, ...] -> [struct1, struct2, ...] +Stmt *impl_stmt(Parser *parser) { + int line = p_current(parser).line; + int col = p_current(parser).col; + + GrowableArray function_name_list, function_name_types; + GrowableArray struct_name_list; + + p_consume(parser, TOK_IMPL, "Expected 'impl' keyword"); + p_consume(parser, TOK_LBRACKET, "Expected '[' after 'impl' keyword"); + + if (!growable_array_init(&function_name_list, parser->arena, 4, + sizeof(Stmt *)) || + !growable_array_init(&function_name_types, parser->arena, 4, + sizeof(Stmt *))) { + fprintf(stderr, "Failed to initialize impl function array list\n"); + return NULL; + } + + if (!growable_array_init(&struct_name_list, parser->arena, 4, + sizeof(Stmt *))) { + fprintf(stderr, "Failed to initialize impl struct array list"); + return NULL; + } + + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACKET) { + int list_line = p_current(parser).line; + int list_col = p_current(parser).col; + + if (p_current(parser).type_ != TOK_IDENTIFIER) { + parser_error(parser, "SyntaxError", parser->file_path, + "Required identifier", list_line, list_col, + p_current(parser).length); + return NULL; + } + + char *function_list_name = get_name(parser); + p_advance(parser); + p_consume(parser, TOK_COLON, "Expected ':' after parameter name"); + + Type *function_list_type = parse_type(parser); + if (!function_list_type) { + fprintf(stderr, "Failed to parse type for parameter '%s'\n", + function_list_name); + return NULL; + } + p_advance(parser); + + char **name_identifier = (char **)growable_array_push(&function_name_list); + Type **type_specifier = (Type **)growable_array_push(&function_name_types); + + if (!name_identifier || !type_specifier) { + fprintf(stderr, "Out of memory while growing function list for impl\n"); + return NULL; + } + *name_identifier = function_list_name; + *type_specifier = function_list_type; + + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); + } + } + p_consume(parser, TOK_RBRACKET, "Expected ']' after function parameters"); + p_consume(parser, TOK_RIGHT_ARROW, "Expected '->' after function list"); + p_consume(parser, TOK_LBRACKET, "Expected '[' after '->'"); + + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACKET) { + int list_line = p_current(parser).line; + int list_col = p_current(parser).col; + + if (p_current(parser).type_ != TOK_IDENTIFIER) { + parser_error(parser, "SyntaxError", parser->file_path, + "Required identifier", list_line, list_col, + p_current(parser).length); + return NULL; + } + char *struct_list_name = get_name(parser); + p_advance(parser); + char **struct_identifier = (char **)growable_array_push(&struct_name_list); + + if (!struct_identifier) { + fprintf(stderr, "Out of memory while growing struct list for impl\n"); + return NULL; + } + *struct_identifier = struct_list_name; + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); + } + } + p_consume(parser, TOK_RBRACKET, "Expected ']' after struct list"); + Stmt *body = block_stmt(parser); + return create_impl_stmt(parser->arena, (char **)function_name_list.data, + (AstNode **)function_name_types.data, body, + (char **)struct_name_list.data, + function_name_list.count, struct_name_list.count, + line, col); +} diff --git a/src/typechecker/expr.c b/src/typechecker/expr.c index 884e3e27..8115c1df 100644 --- a/src/typechecker/expr.c +++ b/src/typechecker/expr.c @@ -200,6 +200,7 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, Symbol *func_symbol = NULL; const char *func_name = NULL; + bool is_method_call = false; // Track if this is a method call with injected self if (callee->type == AST_EXPR_IDENTIFIER) { // Simple function call: func() @@ -207,6 +208,7 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, func_symbol = scope_lookup(scope, func_name); } else if (callee->type == AST_EXPR_MEMBER) { + // Member function call: could be module::func() or obj.method() const char *base_name = callee->expr.member.object->expr.identifier.name; const char *member_name = callee->expr.member.member; @@ -272,7 +274,6 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, } AstNode *base_type = base_symbol->type; - // Handle pointer dereference: if we have a pointer to struct, // automatically dereference it if (base_type->type == AST_TYPE_POINTER) { @@ -304,17 +305,98 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, // Now check if it's a struct type and get the member if (base_type->type == AST_TYPE_STRUCT) { AstNode *member_type = get_struct_member_type(base_type, member_name); + if (member_type && member_type->type == AST_TYPE_FUNCTION) { // This is a method call on a struct instance func_symbol = arena_alloc(arena, sizeof(Symbol), alignof(Symbol)); + if (!func_symbol) { + tc_error(expr, "Memory Error", "Failed to allocate symbol for method '%s'", member_name); + return NULL; + } + func_symbol->name = member_name; + func_symbol->type = member_type; + func_symbol->is_public = true; func_symbol->is_mutable = false; func_symbol->scope_depth = 0; - func_symbol->returns_ownership = false; // ADD THIS - func_symbol->takes_ownership = false; // ADD THIS + func_symbol->returns_ownership = false; + func_symbol->takes_ownership = false; func_name = member_name; + + // CRITICAL: For method calls, we need to inject 'self' as the first argument + // Verify we have a valid object before injectin + if (!callee->expr.member.object) { + tc_error(expr, "Internal Error", "Method call has no object"); + return NULL; + } + + // Create a new arguments array with 'self' prepended + size_t new_arg_count = arg_count + 1; + AstNode **new_arguments = arena_alloc(arena, new_arg_count * sizeof(AstNode *), + alignof(AstNode *)); + + if (!new_arguments) { + tc_error(expr, "Memory Error", "Failed to allocate arguments array for method call"); + return NULL; + } + + // CRITICAL: Check if we need to take the address of the object + // The method expects a pointer to the struct (self is Person*) + // But obj.method() gives us the struct value (Person) + // We need to automatically inject &obj instead of just obj + + // Get the method's parameter types to check what self expects + if (!member_type || member_type->type != AST_TYPE_FUNCTION) { + tc_error(expr, "Internal Error", "Method type is not a function"); + return NULL; + } + + AstNode **method_param_types = member_type->type_data.function.param_types; + if (!method_param_types || member_type->type_data.function.param_count == 0) { + tc_error(expr, "Internal Error", "Method has no parameters (missing self?)"); + return NULL; + } + + AstNode *self_param_type = method_param_types[0]; // First param is always self + AstNode *object_node = callee->expr.member.object; + + // Check what the method expects for self + bool expects_pointer = (self_param_type->type == AST_TYPE_POINTER); + + // Check what we have + Symbol *obj_symbol = scope_lookup(scope, base_name); + bool have_pointer = (obj_symbol && obj_symbol->type && + obj_symbol->type->type == AST_TYPE_POINTER); + + if (expects_pointer && !have_pointer) { + // Method expects pointer but we have value - take address + AstNode *addr_expr = arena_alloc(arena, sizeof(AstNode), alignof(AstNode)); + addr_expr->type = AST_EXPR_ADDR; + addr_expr->category = Node_Category_EXPR; + addr_expr->line = object_node->line; + addr_expr->column = object_node->column; + addr_expr->expr.addr.object = object_node; + new_arguments[0] = addr_expr; + } else { + // Either method expects value, or we already have pointer + new_arguments[0] = object_node; + } + + // Copy the rest of the user-provided arguments + for (size_t i = 0; i < arg_count; i++) { + new_arguments[i + 1] = arguments[i]; + } + + // Update the arguments and count for the rest of the function + arguments = new_arguments; + arg_count = new_arg_count; + is_method_call = true; // Mark that we injected self + + expr->expr.call.args = new_arguments; + expr->expr.call.arg_count = new_arg_count; + } else if (member_type) { tc_error(expr, "Runtime Call Error", "Cannot call non-function member '%s' on struct '%s'", @@ -338,7 +420,7 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, tc_error(expr, "Call Error", "Unsupported callee type for function call"); return NULL; } - + if (!func_symbol) { tc_error(expr, "Call Error", "Undefined function '%s'", func_name ? func_name : "unknown"); @@ -362,22 +444,51 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, return NULL; } - for (size_t i = 0; i < arg_count; i++) { + // For method calls, start from index 1 to skip the injected 'self' parameter + size_t start_index = is_method_call ? 1 : 0; + + for (size_t i = start_index; i < arg_count; i++) { + // Validate the argument pointer before dereferencing + if (!arguments) { + tc_error(expr, "Call Error", + "Arguments array is NULL for call to '%s'", func_name); + return NULL; + } + + if (!arguments[i]) { + tc_error(expr, "Call Error", + "Argument %zu in call to '%s' is NULL", i + 1, func_name); + return NULL; + } + AstNode *arg_type = typecheck_expression(arguments[i], scope, arena); + if (!arg_type) { tc_error(expr, "Call Error", "Failed to type-check argument %zu in call to '%s'", i + 1, func_name); return NULL; } - + + // Validate param_types array + if (!param_types || !param_types[i]) { + tc_error(expr, "Call Error", + "Parameter %zu type in function '%s' is NULL", i + 1, func_name); + return NULL; + } + TypeMatchResult match = types_match(param_types[i], arg_type); + if (match == TYPE_MATCH_NONE) { + fprintf(stderr, "DEBUG: Type mismatch - NONE - about to report error\n"); + + const char *param_str = type_to_string(param_types[i], arena); + const char *arg_str = type_to_string(arg_type, arena); + tc_error_help(expr, "Call Error", "Argument %zu to function '%s' has wrong type.", "Expected '%s', got '%s'", i + 1, func_name, - type_to_string(param_types[i], arena), - type_to_string(arg_type, arena)); + param_str, arg_str); return NULL; } } @@ -385,7 +496,7 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, // Handle ownership transfer from caller to function if (func_symbol->takes_ownership) { for (size_t i = 0; i < arg_count; i++) { - if (is_pointer_type(param_types[i])) { + if (param_types[i] && is_pointer_type(param_types[i])) { const char *arg_var = extract_variable_name(arguments[i]); if (arg_var) { StaticMemoryAnalyzer *analyzer = get_static_analyzer(scope); @@ -397,8 +508,7 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, } } } - // ========== NEW CODE ENDS HERE ========== - + return return_type; } diff --git a/src/typechecker/stmt.c b/src/typechecker/stmt.c index 979448c2..8fec9082 100644 --- a/src/typechecker/stmt.c +++ b/src/typechecker/stmt.c @@ -354,11 +354,6 @@ bool typecheck_struct_decl(AstNode *node, Scope *scope, ArenaAllocator *arena) { size_t private_count = node->stmt.struct_decl.private_count; bool is_public = node->stmt.struct_decl.is_public; - // printf("DEBUG: Processing struct '%s' with %zu public members, %zu private - // " - // "members\n", - // struct_name, public_count, private_count); - // Validate struct name if (!struct_name) { tc_error(node, "Struct Error", "Struct declaration missing name"); @@ -385,47 +380,19 @@ bool typecheck_struct_decl(AstNode *node, Scope *scope, ArenaAllocator *arena) { GrowableArray seen_names; growable_array_init(&seen_names, arena, total_members, sizeof(char *)); - // Process public members + // FIRST PASS: Collect all data fields (non-methods) to create the struct type for (size_t i = 0; i < public_count; i++) { AstNode *member = public_members[i]; - // printf("DEBUG: Processing public member %zu: %p\n", i, (void *)member); - - if (!member) { - tc_error(node, "Struct Error", "Public member %zu is NULL in struct '%s'", + if (!member || member->type != AST_STMT_FIELD_DECL) { + tc_error(node, "Struct Error", "Invalid public member %zu in struct '%s'", i, struct_name); return false; } - // printf("DEBUG: Member type: %d (expected AST_STMT_FIELD_DECL)\n", - // member->type); - - if (member->type != AST_STMT_FIELD_DECL) { - tc_error(node, "Struct Error", - "Invalid public member %zu in struct '%s' (type=%d)", i, - struct_name, member->type); - return false; - } - const char *field_name = member->stmt.field_decl.name; AstNode *field_type = member->stmt.field_decl.type; AstNode *field_function = member->stmt.field_decl.function; - // printf("DEBUG: Field name: '%s', type: %p, function: %p\n", - // field_name ? field_name : "NULL", (void *)field_type, - // (void *)field_function); - - // Additional debugging for the type - // if (field_type) { - // printf("DEBUG: Field type category: %d, type: %d\n", - // field_type->category, - // field_type->type); - // if (field_type->type == AST_TYPE_BASIC) { - // printf("DEBUG: Basic type name: '%s'\n", - // field_type->type_data.basic.name); - // } - // } - - // Validate field name if (!field_name) { tc_error(node, "Struct Field Error", "Field %zu in struct '%s' has no name", i, struct_name); @@ -444,85 +411,77 @@ bool typecheck_struct_decl(AstNode *node, Scope *scope, ArenaAllocator *arena) { } } - // Add to seen names char **name_slot = (char **)growable_array_push(&seen_names); *name_slot = (char *)field_name; - // Handle method vs data field - if (field_function) { - // printf("DEBUG: Processing method '%s'\n", field_name); - // This is a method - validate the function - if (!typecheck_statement(field_function, scope, arena)) { - tc_error(node, "Struct Method Error", - "Method '%s' in struct '%s' failed type checking", field_name, - struct_name); - return false; - } - - // Get the function's type from the symbol table - Symbol *method_symbol = scope_lookup_current_only(scope, field_name); - if (method_symbol && method_symbol->type) { - all_member_types[member_index] = method_symbol->type; - // printf("DEBUG: Method type found: %p\n", (void - // *)method_symbol->type); - } else { - tc_error(node, "Struct Method Error", - "Could not find type for method '%s' in struct '%s'", - field_name, struct_name); - return false; - } - } else if (field_type) { - // printf("DEBUG: Processing data field '%s'\n", field_name); - // This is a data field - validate the type + // If it's a data field (not a method), add it now + if (field_type && !field_function) { if (field_type->category != Node_Category_TYPE) { tc_error(node, "Struct Field Error", - "Field '%s' in struct '%s' has invalid type category (%d, " - "expected %d)", - field_name, struct_name, field_type->category, - Node_Category_TYPE); + "Field '%s' in struct '%s' has invalid type category", + field_name, struct_name); return false; } + all_member_types[member_index] = field_type; - // printf("DEBUG: Data field type assigned: %p\n", (void *)field_type); - } else { - tc_error(node, "Struct Field Error", - "Field '%s' in struct '%s' has neither type nor function", - field_name, struct_name); + all_member_names[member_index] = field_name; + member_index++; + } + } + + // Process private data fields + for (size_t i = 0; i < private_count; i++) { + AstNode *member = private_members[i]; + if (!member || member->type != AST_STMT_FIELD_DECL) { + tc_error(node, "Struct Error", "Invalid private member %zu in struct '%s'", + i, struct_name); return false; } - // Validate that we actually got a valid type - if (!all_member_types[member_index]) { + const char *field_name = member->stmt.field_decl.name; + AstNode *field_type = member->stmt.field_decl.type; + AstNode *field_function = member->stmt.field_decl.function; + + if (!field_name) { tc_error(node, "Struct Field Error", - "Failed to determine type for field '%s' in struct '%s'", - field_name, struct_name); + "Field %zu in struct '%s' has no name", i, struct_name); return false; } - // printf("DEBUG: Final type for member %zu ('%s'): %p\n", member_index, - // field_name, (void *)all_member_types[member_index]); + // Check for duplicate member names + for (size_t j = 0; j < seen_names.count; j++) { + char **existing_name = + (char **)((char *)seen_names.data + j * sizeof(char *)); + if (strcmp(*existing_name, field_name) == 0) { + tc_error_id(node, field_name, "Duplicate Member", + "Struct member '%s' is already declared in struct '%s'", + field_name, struct_name); + return false; + } + } - all_member_names[member_index] = field_name; - member_index++; - } + char **name_slot = (char **)growable_array_push(&seen_names); + *name_slot = (char *)field_name; - // Process private members (similar logic, but abbreviated for brevity) - for (size_t i = 0; i < private_count; i++) { - AstNode *member = private_members[i]; - // ... similar processing with debug prints ... - // (You can add the same debug logic here) - } + if (field_type && !field_function) { + if (field_type->category != Node_Category_TYPE) { + tc_error(node, "Struct Field Error", + "Field '%s' in struct '%s' has invalid type category", + field_name, struct_name); + return false; + } - // printf("DEBUG: Creating struct type with %zu members\n", total_members); - for (size_t i = 0; i < total_members; i++) { - // printf("DEBUG: Member %zu: '%s' -> type %p\n", i, all_member_names[i], - // (void *)all_member_types[i]); + all_member_types[member_index] = field_type; + all_member_names[member_index] = field_name; + member_index++; + } } - // Create struct type + // Create the struct type with just data fields + size_t data_field_count = member_index; AstNode *struct_type = create_struct_type(arena, struct_name, all_member_types, all_member_names, - total_members, node->line, node->column); + data_field_count, node->line, node->column); if (!struct_type) { tc_error(node, "Struct Creation Error", @@ -530,22 +489,230 @@ bool typecheck_struct_decl(AstNode *node, Scope *scope, ArenaAllocator *arena) { return false; } - // printf("DEBUG: Struct type created: %p\n", (void *)struct_type); - - // Add struct type to scope - if (!scope_add_symbol(scope, struct_name, struct_type, is_public, false, - arena)) { + // Add struct type to scope BEFORE processing methods + if (!scope_add_symbol(scope, struct_name, struct_type, is_public, false, arena)) { tc_error_id(node, struct_name, "Symbol Error", "Failed to add struct '%s' to scope", struct_name); return false; } - // printf("DEBUG: About to print struct type:\n"); - // debug_print_struct_type(struct_type, 0); + // Now we need to reserve space for methods in the arrays BEFORE processing them + // Count total fields (data + methods) + size_t method_count = 0; + for (size_t i = 0; i < public_count; i++) { + if (public_members[i]->stmt.field_decl.function) { + method_count++; + } + } + for (size_t i = 0; i < private_count; i++) { + if (private_members[i]->stmt.field_decl.function) { + method_count++; + } + } + + size_t total_member_count = data_field_count + method_count; + + // Reallocate the arrays to hold all members (data + methods) + AstNode **full_member_types = arena_alloc(arena, total_member_count * sizeof(AstNode *), + alignof(AstNode *)); + const char **full_member_names = arena_alloc(arena, total_member_count * sizeof(char *), + alignof(char *)); + + // Copy existing data fields + for (size_t i = 0; i < data_field_count; i++) { + full_member_types[i] = all_member_types[i]; + full_member_names[i] = all_member_names[i]; + } + + // Update struct type to use new arrays + struct_type->type_data.struct_type.member_types = full_member_types; + struct_type->type_data.struct_type.member_names = full_member_names; + struct_type->type_data.struct_type.member_count = data_field_count; // Start with data fields + + member_index = data_field_count; // Continue from where we left off + + // SECOND PASS: Process methods with explicit 'self' parameter + for (size_t i = 0; i < public_count; i++) { + AstNode *member = public_members[i]; + const char *field_name = member->stmt.field_decl.name; + AstNode *field_function = member->stmt.field_decl.function; + + if (field_function) { + // This is a method - typecheck it with 'self' parameter + + AstNode *func_node = field_function; + if (func_node->type != AST_STMT_FUNCTION) { + tc_error(node, "Internal Error", "Expected function node for method"); + return false; + } + + // Create the method's scope + Scope *method_scope = create_child_scope(scope, field_name, arena); + method_scope->is_function_scope = true; + method_scope->associated_node = func_node; + + // **KEY ADDITION**: Add 'self' as the first parameter + // 'self' is a reference to the struct instance (could be pointer or value) + // For simplicity, let's make it a pointer to the struct + AstNode *self_type = create_pointer_type(arena, struct_type, + func_node->line, func_node->column); + + if (!scope_add_symbol(method_scope, "self", self_type, false, true, arena)) { + tc_error(func_node, "Method Error", + "Failed to add 'self' parameter to method '%s'", field_name); + return false; + } + + // Add method parameters to the scope + size_t param_count = func_node->stmt.func_decl.param_count; + char **param_names = func_node->stmt.func_decl.param_names; + AstNode **param_types = func_node->stmt.func_decl.param_types; + + for (size_t j = 0; j < param_count; j++) { + if (!scope_add_symbol(method_scope, param_names[j], param_types[j], + false, true, arena)) { + tc_error(func_node, "Method Parameter Error", + "Could not add parameter '%s' to method '%s' scope", + param_names[j], field_name); + return false; + } + } + + // Typecheck the method body + AstNode *body = func_node->stmt.func_decl.body; + if (body) { + if (!typecheck_statement(body, method_scope, arena)) { + tc_error(node, "Struct Method Error", + "Method '%s' in struct '%s' failed type checking", + field_name, struct_name); + return false; + } + } + + // Register the method in the parent scope + // CRITICAL: Include 'self' as the first parameter in the method type + AstNode *return_type = func_node->stmt.func_decl.return_type; + + size_t method_param_count = param_count + 1; // +1 for self + AstNode **method_param_types = arena_alloc(arena, method_param_count * sizeof(AstNode *), + alignof(AstNode *)); + + // First parameter is self + method_param_types[0] = self_type; + + // Copy the rest of the parameters + for (size_t j = 0; j < param_count; j++) { + method_param_types[j + 1] = param_types[j]; + } + + AstNode *method_type = create_function_type( + arena, method_param_types, method_param_count, return_type, + func_node->line, func_node->column); + + if (!scope_add_symbol(scope, field_name, method_type, is_public, false, arena)) { + tc_error(func_node, "Method Registration Error", + "Failed to register method '%s' in scope", field_name); + return false; + } + + // Add method to struct type's member list + full_member_types[member_index] = method_type; + full_member_names[member_index] = field_name; + member_index++; + + // Update the struct's member count to include this method + struct_type->type_data.struct_type.member_count = member_index; + } + } + + // Process private methods similarly + for (size_t i = 0; i < private_count; i++) { + AstNode *member = private_members[i]; + const char *field_name = member->stmt.field_decl.name; + AstNode *field_function = member->stmt.field_decl.function; + + if (field_function) { + AstNode *func_node = field_function; + if (func_node->type != AST_STMT_FUNCTION) { + tc_error(node, "Internal Error", "Expected function node for method"); + return false; + } + + Scope *method_scope = create_child_scope(scope, field_name, arena); + method_scope->is_function_scope = true; + method_scope->associated_node = func_node; + + // Add 'self' parameter + AstNode *self_type = create_pointer_type(arena, struct_type, + func_node->line, func_node->column); + + if (!scope_add_symbol(method_scope, "self", self_type, false, true, arena)) { + tc_error(func_node, "Method Error", + "Failed to add 'self' parameter to method '%s'", field_name); + return false; + } + + size_t param_count = func_node->stmt.func_decl.param_count; + char **param_names = func_node->stmt.func_decl.param_names; + AstNode **param_types = func_node->stmt.func_decl.param_types; + + for (size_t j = 0; j < param_count; j++) { + if (!scope_add_symbol(method_scope, param_names[j], param_types[j], + false, true, arena)) { + tc_error(func_node, "Method Parameter Error", + "Could not add parameter '%s' to method '%s' scope", + param_names[j], field_name); + return false; + } + } + + AstNode *body = func_node->stmt.func_decl.body; + if (body) { + if (!typecheck_statement(body, method_scope, arena)) { + tc_error(node, "Struct Method Error", + "Method '%s' in struct '%s' failed type checking", + field_name, struct_name); + return false; + } + } + + AstNode *return_type = func_node->stmt.func_decl.return_type; + + // CRITICAL: Include 'self' as the first parameter in the method type + size_t method_param_count = param_count + 1; // +1 for self + AstNode **method_param_types = arena_alloc(arena, method_param_count * sizeof(AstNode *), + alignof(AstNode *)); + + // First parameter is self + method_param_types[0] = self_type; + + // Copy the rest of the parameters + for (size_t j = 0; j < param_count; j++) { + method_param_types[j + 1] = param_types[j]; + } + + AstNode *method_type = create_function_type( + arena, method_param_types, method_param_count, return_type, + func_node->line, func_node->column); + + if (!scope_add_symbol(scope, field_name, method_type, false, false, arena)) { + tc_error(func_node, "Method Registration Error", + "Failed to register method '%s' in scope", field_name); + return false; + } + + // Add method to struct type's member list + full_member_types[member_index] = method_type; + full_member_names[member_index] = field_name; + member_index++; + + // Update the struct's member count to include this method + struct_type->type_data.struct_type.member_count = member_index; + } + } return true; } - bool typecheck_enum_decl(AstNode *node, Scope *scope, ArenaAllocator *arena) { const char *enum_name = node->stmt.enum_decl.name; char **member_names = node->stmt.enum_decl.members; diff --git a/std/ffi/bindgen.lx b/std/ffi/bindgen.lx deleted file mode 100644 index e69de29b..00000000 diff --git a/std/ffi/c_types.lx b/std/ffi/c_types.lx deleted file mode 100644 index e69de29b..00000000 diff --git a/std/ffi/stdlib.lx b/std/ffi/stdlib.lx deleted file mode 100644 index e69de29b..00000000 diff --git a/std/ffi/syscall.lx b/std/ffi/syscall.lx deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/terminal_fps.lx b/tests/terminal_fps.lx new file mode 100644 index 00000000..216337f9 --- /dev/null +++ b/tests/terminal_fps.lx @@ -0,0 +1,156 @@ +@module "main" + +@use "math" as math +@use "memory" as mem +@use "string" as string +@use "termfx" as fx + +let A: double; +let B: double; +let C: double; + +let zBuffer: *double; +let _colorBuffer: **char; +let _buffer: *char; + +let cube_width: double; +const width: int = 160; +const height: int = 44; + +const distance_from_cam: int = 100; + +const increment_speed: double = 0.6; +let horizontal_offset: double; +const k1: double = 40.0; + +let cube_gradient_size: int = 12; + +const calculateX -> fn (i: int, j: int, k: int) double { + let sA: double = math::sin(A); + let cA: double = math::cos(A); + let sB: double = math::sin(B); + let cB: double = math::cos(B); + let sC: double = math::sin(C); + let cC: double = math::cos(C); + + return j * sA * sB * cC - k * cA * sB * cC + + j * cA * sC + k * sA * sC + i * cB * cC; +} + +const calculateY -> fn (i: int, j: int, k: int) double { + let sA: double = math::sin(A); + let cA: double = math::cos(A); + let sB: double = math::sin(B); + let cB: double = math::cos(B); + let sC: double = math::sin(C); + let cC: double = math::cos(C); + + return j * cA * cC + k * sA * cC - + j * sA * sB * sC + k * cA * sB * sC - + i * cB * sC; +} + +const calculateZ -> fn (i: int, j: int, k: int) double { + let sA: double = math::sin(A); + let cA: double = math::cos(A); + let sB: double = math::sin(B); + let cB: double = math::cos(B); + + return k * cA * cB - j * sA * cB + i * sB; +} + +const calculateForSurface -> fn (cX: double, cY: double, cZ: double, + ch: char, color: *char) void { + let x: double = calculateX(cast(cX), cast(cY), cast(cZ)); + let y: double = calculateY(cast(cX), cast(cY), cast(cZ)); + let z: double = calculateZ(cast(cX), cast(cY), cast(cZ)) + distance_from_cam; + + if (z <= 0.001) { return; } + + let ooz: double = 1.0 / z; + + if (ooz > 1000.0 || ooz < -1000.0) { return; } + + let xp: int = cast(width / 2 + horizontal_offset + k1 * ooz * x * 2); + let yp: int = cast(height / 2 + k1 * ooz * y); + + if (xp >= 0 && xp < width && yp >= 0 && yp < height) { + let idx: int = xp + yp * width; + if (ooz > zBuffer[idx]) { + zBuffer[idx] = ooz; + _buffer[idx] = ch; + _colorBuffer[idx] = color; + } + } +} + +const print_cube -> fn () void { + loop [cubeX: double = -cube_width](cubeX < cube_width) : (cubeX = cubeX + increment_speed) { + loop [cubeY: double = -cube_width](cubeY < cube_width) : (cubeY = cubeY + increment_speed) { + calculateForSurface(cubeX, cubeY, -cube_width, '@', fx::BRIGHT_RED); + calculateForSurface(cube_width, cubeY, cubeX, '+', fx::BRIGHT_GREEN); + calculateForSurface(-cube_width, cubeY, -cubeX, '~', fx::BRIGHT_YELLOW); + calculateForSurface(-cubeX, cubeY, cube_width, '#', fx::BRIGHT_BLUE); + calculateForSurface(cubeX, -cube_width, -cubeY, '*', fx::BRIGHT_CYAN); + calculateForSurface(cubeX, cube_width, cubeY, '-', fx::BRIGHT_MAGENTA); + } + } +} + +pub const main -> fn () int { + zBuffer = cast<*double>(alloc(160 * 44 * sizeof)); + _colorBuffer = cast<**char>(alloc(160 * 44 * 8)); + _buffer = cast<*char>(alloc(160 * 44)); + + defer { free(zBuffer); free(_colorBuffer); free(_buffer); } + + output(fx::CLEAR_SCREEN, fx::CURSOR_HOME, fx::BRIGHT_MAGENTA, "3D", fx::RESET, + fx::BRIGHT_CYAN, " Spinning Cubes in Luma\n", fx::RESET, fx::CURSOR_HIDE); + + let frame_count: int = 0; + + loop { + mem::memset(cast<*void>(_buffer), 32, width * height); + mem::memset(cast<*void>(_colorBuffer), 0, width * height * 8); + mem::memset(cast<*void>(zBuffer), 0, width * height * sizeof); + + cube_width = 20.0; + horizontal_offset = -2 * cube_width; + print_cube(); + + cube_width = 10.0; + horizontal_offset = 1 * cube_width; + print_cube(); + + cube_width = 5.0; + horizontal_offset = 8 * cube_width; + print_cube(); + + output(fx::CURSOR_HOME); + loop [k: int = 0](k < width * height) : (++k) { + if (k % width == 0) { output("\n"); } + + if (_colorBuffer[k] != cast<*char>(0)) { + output(_colorBuffer[k]); + } + output(string::from_char(_buffer[k]), fx::RESET); + } + + output( + fx::move_cursor(1, 29), + fx::BOLD, fx::WHITE, " Frame: ", + fx::BRIGHT_GREEN, frame_count, " ", + fx::RESET + ); + + // Rotate cube + A = (A + 0.05) % math::TWO_PI; + B = (B + 0.05) % math::TWO_PI; + C = (C + 0.01) % math::TWO_PI; + + // Increment frame count + ++frame_count; + } + + return 0; +} diff --git a/tests/test.lx b/tests/test.lx index cb35f662..b25eaba8 100644 --- a/tests/test.lx +++ b/tests/test.lx @@ -1,20 +1,24 @@ @module "main" -@use "stack" as st +const Person -> struct { + name: *char, + age: int, -pub const main -> fn () int { - let stack: *Stack = st::createStack(20); + init -> fn (age: int, name: *char) void { + self.name = name; + self.age = age; + }, + + greet -> fn () void { + output("I am ", self.name, " and I am ", self.age, " years old.\n"); + } +}; - st::push(stack, 20); - st::push(stack, 30); - st::push(stack, 50); - st::push(stack, 12); - st::push(stack, 311); - output("Peeked: ", st::peek(stack), "\n"); - st::pop(stack); - st::pop(stack); +pub const main -> fn () int { + let p: Person; - st::freeStack(stack); + p.init(20, "Connor"); + p.greet(); return 0; -} +} \ No newline at end of file diff --git a/tests/tetris.lx b/tests/tetris.lx index b15c5b7c..26c70258 100644 --- a/tests/tetris.lx +++ b/tests/tetris.lx @@ -342,4 +342,4 @@ pub const main -> fn () int { term::disable_raw_mode(); output(tx::BRIGHT_CYAN, "\n\nGame Over! Final Score: ", score, "\n", tx::RESET); return 0; -} \ No newline at end of file +}