diff options
-rw-r--r-- | include/ast.h | 67 | ||||
-rw-r--r-- | include/binop_type.h | 3 | ||||
-rw-r--r-- | include/ssa/ssa.h | 28 | ||||
-rw-r--r-- | include/std/misc.h | 6 | ||||
-rw-r--r-- | include/tokenizer.h | 8 | ||||
-rw-r--r-- | src/ast.c | 210 | ||||
-rw-r--r-- | src/bytecode/bytecode.c | 212 | ||||
-rw-r--r-- | src/compiler.c | 3 | ||||
-rw-r--r-- | src/parser.c | 392 | ||||
-rw-r--r-- | src/ssa/ssa.c | 233 | ||||
-rw-r--r-- | src/tokenizer.c | 111 | ||||
-rw-r--r-- | tests/b.amal.z | 6 | ||||
-rw-r--r-- | tests/bytecode.amal | 12 | ||||
-rw-r--r-- | tests/bytecode.amal.z | 11 | ||||
-rw-r--r-- | tests/errors/const_assign.amal | 4 | ||||
-rw-r--r-- | tests/errors/const_assign.amal.z | 6 | ||||
-rw-r--r-- | tests/io.amal.z | 6 | ||||
-rw-r--r-- | tests/main.amal | 22 | ||||
-rw-r--r-- | tests/main.amal.z | 69 | ||||
-rw-r--r-- | tests/main.c | 45 | ||||
-rw-r--r-- | tests/sub/a.amal.z | 2 |
21 files changed, 1149 insertions, 307 deletions
diff --git a/include/ast.h b/include/ast.h index c40be22..34e62b8 100644 --- a/include/ast.h +++ b/include/ast.h @@ -17,16 +17,22 @@ #define AST_ERR -1 #define AST_ERR_DEF_DUP -20 +typedef struct FunctionSignature FunctionSignature; typedef struct FunctionCall FunctionCall; typedef struct StructDecl StructDecl; typedef struct StructField StructField; typedef struct LhsExpr LhsExpr; +typedef struct AssignmentExpr AssignmentExpr; typedef struct Import Import; typedef struct String String; typedef struct Variable Variable; typedef struct Number Number; typedef struct Binop Binop; +typedef struct IfStatement IfStatement; +typedef struct ElseIfStatement ElseIfStatement; +typedef struct WhileStatement WhileStatement; +/* TODO: Instead of using pointers, use the data directly */ typedef union { void *data; FunctionDecl *func_decl; @@ -34,11 +40,14 @@ typedef union { StructDecl *struct_decl; StructField *struct_field; LhsExpr *lhs_expr; + AssignmentExpr *assign_expr; Import *import; String *string; Number *number; Variable *variable; Binop *binop; + IfStatement *if_stmt; + WhileStatement *while_stmt; } AstValue; typedef enum { @@ -47,11 +56,14 @@ typedef enum { AST_STRUCT_DECL, AST_STRUCT_FIELD, AST_LHS, + AST_ASSIGN, AST_IMPORT, AST_STRING, AST_NUMBER, AST_VARIABLE, - AST_BINOP + AST_BINOP, + AST_IF_STATEMENT, + AST_WHILE_STATEMENT } AstType; typedef enum { @@ -88,9 +100,15 @@ struct FileScopeReference { struct Variable { BufferView name; + Ast *resolved_var; /* resolved_var will always be a LhsExpr after resolved */ +}; + +struct FunctionSignature { + int params; /* TODO: Implement signatures */ }; struct FunctionDecl { + FunctionSignature *signature; Scope body; SsaFuncIndex ssa_func_index; }; @@ -109,11 +127,25 @@ struct StructField { Variable type; }; +typedef struct { + enum { + VARIABLE_TYPE_NONE, + VARIABLE_TYPE_VARIABLE, + VARIABLE_TYPE_SIGNATURE + } type; + + union { + Variable *variable; + FunctionSignature *signature; + } value; +} VariableType; + struct LhsExpr { + bool is_extern; bool is_pub; bool is_const; BufferView var_name; - Variable type; + VariableType type; Ast *rhs_expr; /* TODO: Find a better way to store this. This most likely will use too much memory. @@ -121,6 +153,11 @@ struct LhsExpr { amal_mutex *mutex; }; +struct AssignmentExpr { + Ast *lhs_expr; + Ast *rhs_expr; +}; + struct Import { BufferView path; FileScopeReference *file_scope; @@ -147,6 +184,24 @@ struct Binop { bool grouped; }; +struct IfStatement { + Ast *condition; + Scope body; + ElseIfStatement *else_if_stmt; +}; + +/* ElseIfStatement where condition == NULL is an Else statement */ +struct ElseIfStatement { + Ast *condition; + Scope body; + ElseIfStatement *next_else_if_stmt; +}; + +struct WhileStatement { + Ast *condition; + Scope body; +}; + typedef struct { jmp_buf env; amal_compiler *compiler; @@ -156,16 +211,20 @@ typedef struct { CHECK_RESULT int ast_create(ScopedAllocator *allocator, void *value, AstType type, Ast **result); BufferView ast_get_name(Ast *self); -CHECK_RESULT int funcdecl_init(FunctionDecl *self, Scope *parent, ScopedAllocator *allocator); +CHECK_RESULT int funcdecl_init(FunctionDecl *self, FunctionSignature *signature, Scope *parent, ScopedAllocator *allocator); CHECK_RESULT int funccall_init(FunctionCall *self, BufferView name, ScopedAllocator *allocator); CHECK_RESULT int structdecl_init(StructDecl *self, Scope *parent, ScopedAllocator *allocator); void structfield_init(StructField *self, BufferView name, BufferView type_name); -CHECK_RESULT int lhsexpr_init(LhsExpr *self, bool is_pub, bool is_const, BufferView var_name, ScopedAllocator *allocator); +CHECK_RESULT int lhsexpr_init(LhsExpr *self, bool is_extern, bool is_pub, bool is_const, BufferView var_name, ScopedAllocator *allocator); +void assignmentexpr_init(AssignmentExpr *self, Ast *lhs_expr, Ast *rhs_expr); void import_init(Import *self, BufferView path); CHECK_RESULT int string_init(String *self, BufferView str); void number_init(Number *self, i64 value, bool is_integer, BufferView code_ref); void variable_init(Variable *self, BufferView name); void binop_init(Binop *self); +CHECK_RESULT int if_statement_init(IfStatement *self, Scope *parent, ScopedAllocator *allocator); +CHECK_RESULT int else_if_statement_init(ElseIfStatement *self, Scope *parent, ScopedAllocator *allocator); +CHECK_RESULT int while_statement_init(WhileStatement *self, Scope *parent, ScopedAllocator *allocator); CHECK_RESULT int scope_init(Scope *self, Scope *parent, ScopedAllocator *allocator); CHECK_RESULT int file_scope_reference_init(FileScopeReference *self, BufferView canonical_path, ScopedAllocator *allocator); diff --git a/include/binop_type.h b/include/binop_type.h index e747102..502392c 100644 --- a/include/binop_type.h +++ b/include/binop_type.h @@ -6,7 +6,8 @@ typedef enum { BINOP_SUB, BINOP_MUL, BINOP_DIV, - BINOP_DOT + BINOP_DOT, + BINOP_EQUALS } BinopType; #endif diff --git a/include/ssa/ssa.h b/include/ssa/ssa.h index e1e2d01..15a9f78 100644 --- a/include/ssa/ssa.h +++ b/include/ssa/ssa.h @@ -16,10 +16,13 @@ typedef enum { SSA_SUB, SSA_MUL, SSA_DIV, + SSA_EQUALS, SSA_FUNC_START, SSA_FUNC_END, SSA_PUSH, - SSA_CALL + SSA_CALL, + SSA_JUMP_ZERO, + SSA_JUMP } SsaInstruction; typedef enum { @@ -35,10 +38,11 @@ typedef struct { SsaNumberType type; } SsaNumber; +typedef i16 JumpOffset; typedef u16 SsaRegister; typedef u16 SsaIntermediateIndex; typedef u16 SsaStringIndex; -typedef usize SsaFuncIndex; +typedef u16 SsaFuncIndex; typedef struct { Buffer/*instruction data*/ instructions; @@ -73,6 +77,15 @@ typedef struct { FunctionDecl *func_decl; } SsaInsFuncCall; +typedef struct { + SsaRegister condition_reg; + JumpOffset jump_offset; +} SsaInsJumpZero; + +typedef struct { + JumpOffset jump_offset; +} SsaInsJump; + /* None of these functions are thread-safe */ SsaNumber create_ssa_integer(i64 value); SsaNumber create_ssa_float(f64 value); @@ -81,20 +94,11 @@ SsaNumber ssa_get_intermediate(Ssa *self, SsaIntermediateIndex index); BufferView ssa_get_string(Ssa *self, SsaStringIndex index); CHECK_RESULT int ssa_init(Ssa *self, ScopedAllocator *allocator); -CHECK_RESULT int ssa_get_unique_reg(Ssa *self, SsaRegister *result); -CHECK_RESULT int ssa_ins_assign_inter(Ssa *self, SsaRegister dest, SsaNumber number); -CHECK_RESULT int ssa_ins_assign_string(Ssa *self, SsaRegister dest, BufferView str); -CHECK_RESULT int ssa_ins_assign_reg(Ssa *self, SsaRegister dest, SsaRegister src); -CHECK_RESULT int ssa_ins_binop(Ssa *self, SsaInstruction binop_type, SsaRegister lhs, SsaRegister rhs, SsaRegister *result); -CHECK_RESULT int ssa_ins_func_start(Ssa *self, u8 num_args, SsaFuncIndex *result); -CHECK_RESULT int ssa_ins_func_end(Ssa *self); -CHECK_RESULT int ssa_ins_push(Ssa *self, SsaRegister reg); -CHECK_RESULT int ssa_ins_call(Ssa *self, FunctionDecl *func_decl, SsaRegister *result); - typedef struct { jmp_buf env; Ssa *ssa; + amal_compiler *compiler; } SsaCompilerContext; /* longjump to context->env on failure */ diff --git a/include/std/misc.h b/include/std/misc.h index 2549c22..b1932d5 100644 --- a/include/std/misc.h +++ b/include/std/misc.h @@ -8,15 +8,15 @@ #ifdef AMAL_PEDANTIC #define throw_debug_msg do {} while(0) #else - #define throw_debug_msg do { amal_log_error("Throwing from %s:%d", __FUNCTION__, __LINE__); } while(0) + #define throw_debug_msg do { amal_log_error("Throwing from %s:%d", __FILE__, __LINE__); } while(0) #endif #ifdef AMAL_PEDANTIC #define return_if_debug_msg do {} while(0) #define cleanup_if_debug_msg do {} while(0) #else - #define return_if_debug_msg do { amal_log_error("Return from %s:%d", __FUNCTION__, __LINE__); } while(0) - #define cleanup_if_debug_msg do { amal_log_error("cleanup from %s:%d", __FUNCTION__, __LINE__); } while(0) + #define return_if_debug_msg do { amal_log_error("Return from %s:%d", __FILE__, __LINE__); } while(0) + #define cleanup_if_debug_msg do { amal_log_error("cleanup from %s:%d", __FILE__, __LINE__); } while(0) #endif #define return_if_error(result) \ diff --git a/include/tokenizer.h b/include/tokenizer.h index d10b789..4018e3a 100644 --- a/include/tokenizer.h +++ b/include/tokenizer.h @@ -6,6 +6,7 @@ #include "std/defs.h" #include "binop_type.h" #include "compiler_options.h" +#include <stdarg.h> #define TOKENIZER_OK 0 /* General error */ @@ -32,7 +33,11 @@ typedef enum { TOK_SEMICOLON, TOK_COLON, TOK_BINOP, - TOK_PUB + TOK_PUB, + TOK_IF, + TOK_ELSE, + TOK_WHILE, + TOK_EXTERN } Token; typedef struct { @@ -71,6 +76,7 @@ CHECK_RESULT int tokenizer_accept(Tokenizer *self, Token expected_token); otherwise @result is set to 1 */ CHECK_RESULT int tokenizer_consume_if(Tokenizer *self, Token expected_token, bool *result); +void tokenizer_print_error_args(Tokenizer *self, int index, const char *fmt, va_list args); void tokenizer_print_error(Tokenizer *self, int index, const char *fmt, ...); void tokenizer_print_error_object(Tokenizer *self, TokenizerError *error); TokenizerError tokenizer_create_error(Tokenizer *self, int index, const char *fmt, ...); @@ -44,6 +44,8 @@ BufferView ast_get_name(Ast *self) { case AST_IMPORT: case AST_STRING: case AST_BINOP: + case AST_IF_STATEMENT: + case AST_WHILE_STATEMENT: name = create_buffer_view_null(); break; case AST_NUMBER: @@ -52,6 +54,9 @@ BufferView ast_get_name(Ast *self) { case AST_LHS: name = self->value.lhs_expr->var_name; break; + case AST_ASSIGN: + name = ast_get_name(self->value.assign_expr->lhs_expr); + break; case AST_FUNCTION_CALL: name = self->value.func_call->func.name; break; @@ -69,7 +74,8 @@ static BufferView ast_get_code_reference(Ast *self) { return ast_get_name(self); } -int funcdecl_init(FunctionDecl *self, Scope *parent, ScopedAllocator *allocator) { +int funcdecl_init(FunctionDecl *self, FunctionSignature *signature, Scope *parent, ScopedAllocator *allocator) { + self->signature = signature; self->ssa_func_index = 0; return scope_init(&self->body, parent, allocator); } @@ -88,16 +94,23 @@ void structfield_init(StructField *self, BufferView name, BufferView type_name) variable_init(&self->type, type_name); } -int lhsexpr_init(LhsExpr *self, bool is_pub, bool is_const, BufferView var_name, ScopedAllocator *allocator) { +int lhsexpr_init(LhsExpr *self, bool is_extern, bool is_pub, bool is_const, BufferView var_name, ScopedAllocator *allocator) { + self->is_extern = is_extern; self->is_pub = is_pub; self->is_const = is_const; - variable_init(&self->type, create_buffer_view_null()); + self->type.type = VARIABLE_TYPE_NONE; + self->type.value.variable = NULL; self->var_name = var_name; self->rhs_expr = NULL; return_if_error(scoped_allocator_create_mutex(allocator, &self->mutex)); return 0; } +void assignmentexpr_init(AssignmentExpr *self, Ast *lhs_expr, Ast *rhs_expr) { + self->lhs_expr = lhs_expr; + self->rhs_expr = rhs_expr; +} + void import_init(Import *self, BufferView path) { self->path = path; self->file_scope = NULL; @@ -117,6 +130,7 @@ void number_init(Number *self, i64 value, bool is_integer, BufferView code_ref) void variable_init(Variable *self, BufferView name) { self->name = name; + self->resolved_var = NULL; } void binop_init(Binop *self) { @@ -126,6 +140,23 @@ void binop_init(Binop *self) { self->grouped = bool_false; } +int if_statement_init(IfStatement *self, Scope *parent, ScopedAllocator *allocator) { + self->condition = NULL; + self->else_if_stmt = NULL; + return scope_init(&self->body, parent, allocator); +} + +int else_if_statement_init(ElseIfStatement *self, Scope *parent, ScopedAllocator *allocator) { + self->condition = NULL; + self->next_else_if_stmt = NULL; + return scope_init(&self->body, parent, allocator); +} + +int while_statement_init(WhileStatement *self, Scope *parent, ScopedAllocator *allocator) { + self->condition = NULL; + return scope_init(&self->body, parent, allocator); +} + int scope_init(Scope *self, Scope *parent, ScopedAllocator *allocator) { return_if_error(buffer_init(&self->ast_objects, allocator)); return_if_error(hash_map_init(&self->named_objects, allocator, sizeof(Ast*), hash_compare_string, amal_hash_string)); @@ -195,17 +226,18 @@ static void parser_print_error(Parser *parser, const char *ref, const char *fmt, va_list args; va_start(args, fmt); if(parser) { - tokenizer_print_error(&parser->tokenizer, + tokenizer_print_error_args(&parser->tokenizer, tokenizer_get_code_reference_index(&parser->tokenizer, ref), fmt, args); } else { /* TODO: Redirect error to compiler error callback if set */ + amal_log_error("TODO: Fix this:"); amal_log_error(fmt, args); } va_end(args); } -static Ast* scope_get_resolved_variable(Scope *self, AstCompilerContext *context, BufferView name) { +static Ast* __scope_get_resolved_variable(Scope *self, Scope *start, AstCompilerContext *context, BufferView name) { Ast *result; bool exists; Scope *prev_scope; @@ -215,16 +247,16 @@ static Ast* scope_get_resolved_variable(Scope *self, AstCompilerContext *context if(!exists) { Parser *parser; if(self->parent) - return scope_get_resolved_variable(self->parent, context, name); + return __scope_get_resolved_variable(self->parent, start, context, name); - parser = scope_get_parser(self); + parser = scope_get_parser(start); parser_print_error(parser, name.data, "Undefined reference to variable \"%.*s\"", name.size, name.data); throw(AST_ERR); } /* Need to change scope here because we are changing the visible scope - and the ast object may be in another scope than the current + and the ast object may be in another scope than the current resolving ast. */ prev_scope = context->scope; @@ -236,9 +268,58 @@ static Ast* scope_get_resolved_variable(Scope *self, AstCompilerContext *context return result; } +static Ast* scope_get_resolved_variable(Scope *self, AstCompilerContext *context, BufferView name) { + return __scope_get_resolved_variable(self, self, context, name); +} + +static void function_signature_resolve(FunctionSignature *self, AstCompilerContext *context) { + /* TODO: Implement */ + (void)self; + (void)context; +} + static void variable_resolve(Variable *self, AstCompilerContext *context, AstResolveData *resolve_data) { - if(!resolve_data->type) - resolve_data->type = scope_get_resolved_variable(context->scope, context, self->name)->resolve_data.type; + if(!self->resolved_var) { + self->resolved_var = scope_get_resolved_variable(context->scope, context, self->name); + resolve_data->type = self->resolved_var->resolve_data.type; + } +} + +static LhsExpr* variable_get_resolved_type(Variable *self) { + assert(self->resolved_var); + return self->resolved_var->value.lhs_expr; +} + +static void variable_type_resolve(VariableType *self, AstCompilerContext *context, AstResolveData *resolve_data) { + switch(self->type) { + case VARIABLE_TYPE_NONE: + return; + case VARIABLE_TYPE_VARIABLE: + variable_resolve(self->value.variable, context, resolve_data); + break; + case VARIABLE_TYPE_SIGNATURE: + function_signature_resolve(self->value.signature, context); + break; + } +} + +static BufferView variable_type_get_name(VariableType *self) { + BufferView result; + switch(self->type) { + case VARIABLE_TYPE_NONE: + result.data = NULL; + result.size = 0; + break; + case VARIABLE_TYPE_VARIABLE: + result = self->value.variable->name; + break; + case VARIABLE_TYPE_SIGNATURE: + /* TODO: Save reference to signature in code in the FunctionSignature type */ + result.data = "fn()"; + result.size = 4; + break; + } + return result; } static LhsExpr* lhsexpr_resolve_rhs(LhsExpr *self, AstCompilerContext *context) { @@ -259,8 +340,7 @@ static void lhsexpr_resolve(Ast *ast, AstCompilerContext *context) { self = ast->value.lhs_expr; rhs_resolve_type = NULL; - if(self->type.name.data) - variable_resolve(&self->type, context, &ast->resolve_data); + variable_type_resolve(&self->type, context, &ast->resolve_data); /* TODO: When parameters and return types are implemented, AST_RESOLVE_END should be set after @@ -274,10 +354,15 @@ static void lhsexpr_resolve(Ast *ast, AstCompilerContext *context) { rhs_resolve_type = lhsexpr_resolve_rhs(self, context); /* self->rhs_expr can be null here because this is valid: var num: i32; */ + /* TODO: Add casting */ if(ast->resolve_data.type && self->rhs_expr && ast->resolve_data.type != rhs_resolve_type) { Parser *parser; parser = scope_get_parser(context->scope); - parser_print_error(parser, self->type.name.data, + /* + TODO: Instead of using self->var_name, using type name. This cant be done right now because + type can be function signature. + */ + parser_print_error(parser, self->var_name.data, "Variable type and variable assignment type (right-hand side) do not match"); throw(AST_ERR); } @@ -287,6 +372,67 @@ static void lhsexpr_resolve(Ast *ast, AstCompilerContext *context) { ast->resolve_data.type = rhs_resolve_type; } +static LhsExpr* binop_get_lhs_expr(Binop *self) { + if(self->rhs) { + if(self->rhs->type == AST_LHS) + return self->rhs->value.lhs_expr; + else if(self->rhs->type == AST_BINOP) + return binop_get_lhs_expr(self->rhs->value.binop); + } else { + if(self->lhs->type == AST_LHS) + return self->lhs->value.lhs_expr; + else if(self->lhs->type == AST_BINOP) + return binop_get_lhs_expr(self->lhs->value.binop); + } + return NULL; +} + +static void assignmentexpr_resolve(Ast *ast, AstCompilerContext *context) { + AssignmentExpr *self; + LhsExpr *lhs_source; + + assert(ast->type == AST_ASSIGN); + self = ast->value.assign_expr; + lhs_source = NULL; + + ast_resolve(self->lhs_expr, context); + ast_resolve(self->rhs_expr, context); + + if(self->lhs_expr->type == AST_VARIABLE) + lhs_source = variable_get_resolved_type(self->lhs_expr->value.variable); + else if(self->lhs_expr->type == AST_BINOP) + lhs_source = binop_get_lhs_expr(self->lhs_expr->value.binop); + + /* This also covers extern variables, since extern variables are always const */ + /* TODO: var.field type expressions should also be checked */ + if(lhs_source && lhs_source->is_const) { + Parser *parser; + parser = scope_get_parser(context->scope); + parser_print_error(parser, ast_get_code_reference(self->lhs_expr).data, "Can't assign to a const value"); + throw(AST_ERR); + } + + /* TODO: Add casting */ + if(self->lhs_expr->resolve_data.type != self->rhs_expr->resolve_data.type) { + Parser *parser; + BufferView rhs_type_name; + BufferView lhs_type_name; + + parser = scope_get_parser(context->scope); + rhs_type_name = variable_type_get_name(&self->rhs_expr->resolve_data.type->type); + lhs_type_name = variable_type_get_name(&self->lhs_expr->resolve_data.type->type); + /* + TODO: Instead of using self->var_name, using type name. This cant be done right now because + type can be function signature. + */ + parser_print_error(parser, ast_get_code_reference(self->lhs_expr).data, + "Can't cast data of type %.*s to type %.*s", rhs_type_name.size, rhs_type_name.data, lhs_type_name.size, lhs_type_name.data); + throw(AST_ERR); + } + + ast->resolve_data.type = self->lhs_expr->resolve_data.type; +} + static void import_resolve(Ast *ast, AstCompilerContext *context) { Import *self; assert(ast->type == AST_IMPORT); @@ -331,6 +477,7 @@ static void funccall_resolve(Ast *self, AstCompilerContext *context) { func_call = self->value.func_call; variable_resolve(&func_call->func, context, &self->resolve_data); + /* Attemping to use call syntax (variable_name ( ) ) with a variable that is not a function */ if(self->resolve_data.type->rhs_expr->type != AST_FUNCTION_DECL) { Parser *caller_parser; Parser *callee_parser; @@ -420,8 +567,12 @@ static void binop_resolve(Ast *ast, AstCompilerContext *context) { if(self->type == BINOP_DOT && (self->rhs->type == AST_VARIABLE || self->rhs->type == AST_FUNCTION_CALL)) { binop_resolve_dot_access(ast, context); /* Only function call has extra data that needs to be resolved (args) */ - if(self->rhs->type == AST_FUNCTION_CALL) + if(self->rhs->type == AST_FUNCTION_CALL) { + Scope *prev_scope = context->scope; + context->scope = lhsexpr_get_scope(self->lhs->resolve_data.type); ast_resolve(self->rhs, context); + context->scope = prev_scope; + } self->rhs->resolve_data.status = AST_RESOLVED; ast->resolve_data.type = self->rhs->resolve_data.type; } else { @@ -452,6 +603,28 @@ static void binop_resolve(Ast *ast, AstCompilerContext *context) { } } +static void else_if_statement_resolve(ElseIfStatement *else_if_stmt, AstCompilerContext *context) { + if(else_if_stmt->condition) + ast_resolve(else_if_stmt->condition, context); + + scope_resolve(&else_if_stmt->body, context); + if(else_if_stmt->next_else_if_stmt) + else_if_statement_resolve(else_if_stmt->next_else_if_stmt, context); +} + +static void if_statement_resolve(IfStatement *if_stmt, AstCompilerContext *context) { + assert(if_stmt->condition); + ast_resolve(if_stmt->condition, context); + scope_resolve(&if_stmt->body, context); + if(if_stmt->else_if_stmt) + else_if_statement_resolve(if_stmt->else_if_stmt, context); +} + +static void while_statement_resolve(WhileStatement *while_stmt, AstCompilerContext *context) { + ast_resolve(while_stmt->condition, context); + scope_resolve(&while_stmt->body, context); +} + void ast_resolve(Ast *self, AstCompilerContext *context) { assert(self); /* @@ -515,6 +688,9 @@ void ast_resolve(Ast *self, AstCompilerContext *context) { case AST_LHS: lhsexpr_resolve(self, context); break; + case AST_ASSIGN: + assignmentexpr_resolve(self, context); + break; case AST_IMPORT: /* TODO: When @import(...).data syntax is added, implement the resolve for it */ import_resolve(self, context); @@ -529,6 +705,12 @@ void ast_resolve(Ast *self, AstCompilerContext *context) { case AST_BINOP: binop_resolve(self, context); break; + case AST_IF_STATEMENT: + if_statement_resolve(self->value.if_stmt, context); + break; + case AST_WHILE_STATEMENT: + while_statement_resolve(self->value.while_stmt, context); + break; } /* TODO: See comment at the top of this function */ self->resolve_data.status = AST_RESOLVED; diff --git a/src/bytecode/bytecode.c b/src/bytecode/bytecode.c index 6774a54..789a9f6 100644 --- a/src/bytecode/bytecode.c +++ b/src/bytecode/bytecode.c @@ -46,6 +46,17 @@ static CHECK_RESULT usize ssa_extract_func_call(u8 *instruction_data, SsaInsFunc return sizeof(result->result) + sizeof(result->func_decl); } +static CHECK_RESULT usize ssa_extract_jump_zero(u8 *instruction_data, SsaInsJumpZero *result) { + am_memcpy(&result->condition_reg, instruction_data, sizeof(result->condition_reg)); + am_memcpy(&result->jump_offset, instruction_data + sizeof(result->condition_reg), sizeof(result->jump_offset)); + return sizeof(result->condition_reg) + sizeof(result->jump_offset); +} + +static CHECK_RESULT usize ssa_extract_jump(u8 *instruction_data, SsaInsJump *result) { + am_memcpy(&result->jump_offset, instruction_data, sizeof(result->jump_offset)); + return sizeof(result->jump_offset); +} + static void add_intermediates(BytecodeCompilerContext *self) { Ssa *ssa; Buffer *instructions; @@ -66,19 +77,44 @@ static void add_intermediates(BytecodeCompilerContext *self) { } } +#if 0 #define NUM_MAX_REGS 256 +#define NUM_MAX_FUNC_ARGS 32 + +static const char* lhs_expr_get_c_name(BytecodeCompilerContext *self, LhsExpr *lhs_expr) { + if(lhs_expr == self->parser->compiler->default_types.i64) { + return "i64"; + } else if(lhs_expr == self->parser->compiler->default_types.f64) { + return "f64"; + } else if(lhs_expr == self->parser->compiler->default_types.str) { + return"const char*"; + } else { + amal_log_error("Invalid rhs type %p", lhs_expr); + assert(bool_false && "TODO: Implement"); + return ""; + } +} +#endif static void add_instructions(BytecodeCompilerContext *self) { Ssa *ssa; u8 *instruction; u8 *instructions_end; + SsaInsForm1 ssa_ins_form1; SsaInsForm2 ssa_ins_form2; SsaInsFuncStart ssa_ins_func_start; SsaInsFuncCall ssa_ins_func_call; + SsaInsJumpZero ssa_ins_jump_zero; + SsaInsJump ssa_ins_jump; + FILE *file; char *filename; + #ifdef COMPILE_TO_C LhsExpr *reg_types[NUM_MAX_REGS]; /* TODO: Remove this. Encode this data in the register itself */ + SsaRegister func_arg_stack[NUM_MAX_FUNC_ARGS]; /* TODO: Remove this? */ + int func_arg_index; + #endif ssa = self->parser->ssa; instruction = buffer_begin(&ssa->instructions); @@ -90,15 +126,27 @@ static void add_instructions(BytecodeCompilerContext *self) { strcat(filename, ".z"); file = fopen(filename, "wb"); free(filename); + #ifdef COMPILE_TO_C #ifdef DEBUG am_memset(reg_types, 0, sizeof(reg_types)); #endif + func_arg_index = 0; fputs("typedef i64 signed long long;\n", file); fputs("typedef f64 double;\n", file); + #define ARITH_OP(op) do {\ + const char *rhs_type_name; \ + instruction += ssa_extract_form2(instruction, &ssa_ins_form2); \ + assert(ssa_ins_form2.result < NUM_MAX_REGS); \ + assert(ssa_ins_form2.lhs < NUM_MAX_REGS); \ + rhs_type_name = lhs_expr_get_c_name(self, reg_types[ssa_ins_form2.lhs]); \ + fprintf(file, "%s r%d = r%d %s r%d;\n", rhs_type_name, ssa_ins_form2.result, ssa_ins_form2.lhs, (op), ssa_ins_form2.rhs); \ + reg_types[ssa_ins_form2.result] = reg_types[ssa_ins_form2.lhs]; \ + } while(0) + while(instruction != instructions_end) { - switch(*instruction++) { + switch((SsaInstruction)*instruction++) { case SSA_ASSIGN_INTER: { SsaNumber number; instruction += ssa_extract_form1(instruction, &ssa_ins_form1); @@ -125,54 +173,32 @@ static void add_instructions(BytecodeCompilerContext *self) { break; } case SSA_ASSIGN_REG: { - LhsExpr *rhs_type; const char *rhs_type_name; instruction += ssa_extract_form1(instruction, &ssa_ins_form1); assert(ssa_ins_form1.rhs < NUM_MAX_REGS); - rhs_type = reg_types[ssa_ins_form1.rhs]; - if(rhs_type == self->parser->compiler->default_types.i64) { - rhs_type_name = "i64"; - } else if(rhs_type == self->parser->compiler->default_types.f64) { - rhs_type_name = "f64"; - } else if(rhs_type == self->parser->compiler->default_types.str) { - rhs_type_name = "const char*"; - } else { - amal_log_error("Invalid rhs type %p for reg %d", rhs_type, ssa_ins_form1.rhs); - assert(bool_false && "TODO: Implement"); - } + rhs_type_name = lhs_expr_get_c_name(self, reg_types[ssa_ins_form1.rhs]); fprintf(file, "%s r%d = r%d;\n", rhs_type_name, ssa_ins_form1.lhs, ssa_ins_form1.rhs); + reg_types[ssa_ins_form1.lhs] = reg_types[ssa_ins_form1.rhs]; break; } case SSA_ADD: { - instruction += ssa_extract_form2(instruction, &ssa_ins_form2); - fprintf(file, "r%d = r%d + r%d;\n", ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs); - assert(ssa_ins_form2.result < NUM_MAX_REGS); - assert(ssa_ins_form2.rhs < NUM_MAX_REGS); - reg_types[ssa_ins_form2.result] = reg_types[ssa_ins_form2.rhs]; + ARITH_OP("+"); break; } case SSA_SUB: { - instruction += ssa_extract_form2(instruction, &ssa_ins_form2); - fprintf(file, "r%d = r%d - r%d;\n", ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs); - assert(ssa_ins_form2.result < NUM_MAX_REGS); - assert(ssa_ins_form2.rhs < NUM_MAX_REGS); - reg_types[ssa_ins_form2.result] = reg_types[ssa_ins_form2.rhs]; + ARITH_OP("-"); break; } case SSA_MUL: { - instruction += ssa_extract_form2(instruction, &ssa_ins_form2); - fprintf(file, "r%d = r%d * r%d;\n", ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs); - assert(ssa_ins_form2.result < NUM_MAX_REGS); - assert(ssa_ins_form2.rhs < NUM_MAX_REGS); - reg_types[ssa_ins_form2.result] = reg_types[ssa_ins_form2.rhs]; + ARITH_OP("*"); break; } case SSA_DIV: { - instruction += ssa_extract_form2(instruction, &ssa_ins_form2); - fprintf(file, "r%d = r%d / r%d;\n", ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs); - assert(ssa_ins_form2.result < NUM_MAX_REGS); - assert(ssa_ins_form2.rhs < NUM_MAX_REGS); - reg_types[ssa_ins_form2.result] = reg_types[ssa_ins_form2.rhs]; + ARITH_OP("/"); + break; + } + case SSA_EQUALS: { + ARITH_OP("=="); break; } case SSA_FUNC_START: { @@ -194,20 +220,134 @@ static void add_instructions(BytecodeCompilerContext *self) { SsaRegister reg; am_memcpy(®, instruction, sizeof(SsaRegister)); instruction += sizeof(SsaRegister); - fprintf(file, "PUSH r%d ***\n", reg); + assert(func_arg_index < NUM_MAX_FUNC_ARGS); + func_arg_stack[func_arg_index++] = reg; break; } - case SSA_CALL: + case SSA_CALL: { + int i; instruction += ssa_extract_func_call(instruction, &ssa_ins_func_call); - fprintf(file, "r%d = CALL %p ***\n", ssa_ins_func_call.result, ssa_ins_func_call.func_decl); + fprintf(file, "r%d = f%p(", ssa_ins_func_call.result, ssa_ins_func_call.func_decl); + for(i = 0; i < func_arg_index; ++i) { + if(i > 0) + fputs(", ", file); + fprintf(file, "r%d", func_arg_stack[i]); + } + func_arg_index = 0; + fputs(");\n", file); + break; + } + case SSA_JUMP_ZERO: { + assert(bool_false && "Not implemented!"); + instruction += ssa_extract_jump_zero(instruction, &ssa_ins_jump_zero); + break; + } + case SSA_JUMP: { + assert(bool_false && "Not implemented!"); + instruction += ssa_extract_jump(instruction, &ssa_ins_jump); break; + } } } + #else + #define ARITH_OP(op) do {\ + instruction += ssa_extract_form2(instruction, &ssa_ins_form2); \ + fprintf(stderr, "%s r%d, r%d, r%d\n", (op), ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs); \ + } while(0) + while(instruction != instructions_end) { + SsaInstruction ins = (SsaInstruction)*instruction++; + switch(ins) { + case SSA_ASSIGN_INTER: { + instruction += ssa_extract_form1(instruction, &ssa_ins_form1); + fprintf(file, "mov r%d, i%d\n", ssa_ins_form1.lhs, ssa_ins_form1.rhs); + break; + } + case SSA_ASSIGN_STRING: { + instruction += ssa_extract_form1(instruction, &ssa_ins_form1); + fprintf(file, "mov r%d, s%d\n", ssa_ins_form1.lhs, ssa_ins_form1.rhs); + break; + } + case SSA_ASSIGN_REG: { + instruction += ssa_extract_form1(instruction, &ssa_ins_form1); + fprintf(file, "mov r%d, r%d\n", ssa_ins_form1.lhs, ssa_ins_form1.rhs); + break; + } + case SSA_ADD: { + ARITH_OP("add"); + break; + } + case SSA_SUB: { + ARITH_OP("sub"); + break; + } + case SSA_MUL: { + ARITH_OP("mul"); + break; + } + case SSA_DIV: { + ARITH_OP("div"); + break; + } + case SSA_EQUALS: { + ARITH_OP("eq"); + break; + } + case SSA_FUNC_START: { + instruction += ssa_extract_func_start(instruction, &ssa_ins_func_start); + fprintf(file, "FUNC_START %d\n", ssa_ins_func_start.num_args); + break; + } + case SSA_FUNC_END: { + fprintf(file, "FUNC_END\n"); + break; + } + case SSA_PUSH: { + SsaRegister reg; + am_memcpy(®, instruction, sizeof(SsaRegister)); + instruction += sizeof(SsaRegister); + fprintf(file, "push r%d\n", reg); + break; + } + case SSA_CALL: { + /* TODO: Add args, using number of bytes to pop after function call. */ + /* + TODO: Pass return register to function. The register should be a pointer that + has the size of the function return values so the return values can fit in it. + */ + /* + TODO: Using ssa_func_index only works correctly if the function was defined in the same + file as the function call. To make this work with calling functions in other files, + ssa_func_index should also have an offset index or something like that. + So each file has it's own function list with indices and when they need to be combined in the end, + the function indices can be increased by their block index (ssa_func_index + block index), where block index + is defined as the size of all previous files' number of functions. + */ + instruction += ssa_extract_func_call(instruction, &ssa_ins_func_call); + fprintf(file, "call %d\n", ssa_ins_func_call.func_decl->ssa_func_index); + break; + } + case SSA_JUMP_ZERO: { + instruction += ssa_extract_jump_zero(instruction, &ssa_ins_jump_zero); + fprintf(file, "jz r%d, %d\n", ssa_ins_jump_zero.condition_reg, ssa_ins_jump_zero.jump_offset); + break; + } + case SSA_JUMP: { + instruction += ssa_extract_jump(instruction, &ssa_ins_jump); + fprintf(file, "jmp %d\n", ssa_ins_jump.jump_offset); + break; + } + default: + amal_log_error("Instruction not yet implemented: %d", ins); + assert(bool_false && "Instruction not yet implemented"); + } + } + #endif /* COMPILE_TO_C */ fclose(file); } void generate_bytecode_from_ssa(BytecodeCompilerContext *self) { add_intermediates(self); + /* TODO: Also add strings in ssa, so we can index them */ add_instructions(self); } diff --git a/src/compiler.c b/src/compiler.c index 30a583a..bdcb1c8 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -40,7 +40,7 @@ static CHECK_RESULT int create_default_type(amal_compiler *compiler, const char return_if_error(structdecl_init(struct_decl, &compiler->root_scope, &compiler->allocator)); return_if_error(scoped_allocator_alloc(&compiler->allocator, sizeof(LhsExpr), (void**)lhs_expr)); - return_if_error(lhsexpr_init(*lhs_expr, bool_true, bool_true, + return_if_error(lhsexpr_init(*lhs_expr, bool_true, bool_true, bool_true, create_buffer_view(name, strnlen(name, PATH_MAX)), &compiler->allocator)); return_if_error(ast_create(&compiler->allocator, struct_decl, AST_STRUCT_DECL, &(*lhs_expr)->rhs_expr)); @@ -242,6 +242,7 @@ static CHECK_RESULT int thread_generate_ssa(Parser *parser) { return_if_error(scoped_allocator_alloc(parser->allocator, sizeof(Ssa), (void**)&compiler_context.ssa)); return_if_error(ssa_init(compiler_context.ssa, parser->allocator)); + compiler_context.compiler = parser->compiler; parser->ssa = compiler_context.ssa; amal_log_debug("Generating SSA for file: %.*s", parser->tokenizer.code_name.size, parser->tokenizer.code_name.data); result = setjmp(compiler_context.env); diff --git a/src/parser.c b/src/parser.c index f05b31d..c0d2c6d 100644 --- a/src/parser.c +++ b/src/parser.c @@ -20,9 +20,10 @@ do { \ throw(return_if_result); \ } while(0) -static CHECK_RESULT Ast* parser_parse_rhs_start(Parser *self, bool is_standalone); +static CHECK_RESULT Ast* parser_parse_rhs(Parser *self); static CHECK_RESULT Ast* parser_parse_body(Parser *self); static CHECK_RESULT Ast* parser_parse_struct_body(Parser *self); +static CHECK_RESULT Ast* parser_parse_rhs_binop(Parser *self); static void parser_queue_file(Parser *self, BufferView path, FileScopeReference **file_scope); int parser_thread_data_init(ParserThreadData *self) { @@ -61,7 +62,7 @@ int parser_init(Parser *self, amal_compiler *compiler, ScopedAllocator *allocato self->error_context = ERROR_CONTEXT_NONE; return_if_error(structdecl_init(&self->struct_decl, &compiler->root_scope, allocator)); self->struct_decl.body.parser = self; - return_if_error(lhsexpr_init(&self->file_decl, bool_true, bool_true, create_buffer_view_null(), self->allocator)); + return_if_error(lhsexpr_init(&self->file_decl, bool_true, bool_true, bool_true, create_buffer_view_null(), self->allocator)); return_if_error(ast_create(self->allocator, &self->struct_decl, AST_STRUCT_DECL, &self->file_decl.rhs_expr)); self->current_scope = &self->struct_decl.body; self->has_func_parent = bool_false; @@ -132,30 +133,78 @@ static void parser_parse_struct_body_loop(Parser *self, Scope *scope) { } /* -VAR_TYPE_DEF = ':' TOK_IDENTIFIER +TODO: Implement params and return types +FUNC_SIGNATURE = 'fn' ('(' ')')? */ -static void parser_parse_var_type_def(Parser *self, BufferView *type_name) { +static CHECK_RESULT FunctionSignature* parser_parse_function_signature(Parser *self) { + FunctionSignature *signature; bool match; + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_FN, &match)); + if(!match) + return NULL; + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_PAREN, &match)); + if(match) { + /* TODO: Parse parameters */ + throw_if_error(tokenizer_accept(&self->tokenizer, TOK_CLOSING_PAREN)); + /* TODO: Parse return types */ + } + + throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(FunctionSignature), (void**)&signature)); + signature->params = 0; + return signature; +} + +/* +VAR_TYPE_DEF = ':' TOK_IDENTIFIER|FUNC_SIGNATURE +*/ +static CHECK_RESULT int parser_parse_var_type_def(Parser *self, VariableType *result) { + bool match; + + result->type = VARIABLE_TYPE_NONE; + result->value.variable = NULL; + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_COLON, &match)); + if(!match) + return -1; + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IDENTIFIER, &match)); if(match) { - throw_if_error(tokenizer_accept(&self->tokenizer, TOK_IDENTIFIER)); - *type_name = self->tokenizer.value.identifier; + result->type = VARIABLE_TYPE_VARIABLE; + throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(Variable), (void**)&result->value.variable)); + variable_init(result->value.variable, self->tokenizer.value.identifier); + return 0; + } + + result->type = VARIABLE_TYPE_SIGNATURE; + result->value.signature = parser_parse_function_signature(self); + if(!result->value.signature) { + self->error = tokenizer_create_error(&self->tokenizer, + tokenizer_get_code_reference_index(&self->tokenizer, self->tokenizer.value.identifier.data), + "Expected type or closure signature"); + throw(PARSER_UNEXPECTED_TOKEN); } + return 0; } /* -LHS = ('pub'? 'const' TOK_IDENTIFIER VAR_TYPE_DEF? '=') | - ('pub'? 'var' TOK_IDENTIFIER VAR_TYPE_DEF? '='|';') +LHS_DECLARATION = 'extern'? 'pub'? 'const'|'var' TOK_IDENTIFIER VAR_TYPE_DEF? */ -static CHECK_RESULT LhsExpr* parser_parse_lhs(Parser *self, bool *assignment_or_rhs) { +static CHECK_RESULT LhsExpr* parser_parse_declaration_lhs(Parser *self) { LhsExpr *result; + bool is_extern; bool is_pub; bool is_const; - bool match; BufferView var_name; - result = NULL; - *assignment_or_rhs = bool_true; + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_EXTERN, &is_extern)); + if(is_extern && self->has_func_parent) { + self->error = tokenizer_create_error(&self->tokenizer, + tokenizer_get_code_reference_index(&self->tokenizer, self->tokenizer.value.identifier.data), + "Only declarations in structs can be extern"); + throw(PARSER_UNEXPECTED_TOKEN); + } throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_PUB, &is_pub)); if(is_pub && self->has_func_parent) { @@ -168,77 +217,48 @@ static CHECK_RESULT LhsExpr* parser_parse_lhs(Parser *self, bool *assignment_or_ throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_CONST, &is_const)); if(!is_const) { bool isVar; + + if(is_extern) { + self->error = tokenizer_create_error(&self->tokenizer, + tokenizer_get_code_reference_index(&self->tokenizer, self->tokenizer.value.identifier.data), + "Extern variable have to be declared with \"const\""); + throw(PARSER_UNEXPECTED_TOKEN); + } + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_VAR, &isVar)); if(!isVar) - return result; + return NULL; } throw_if_error(tokenizer_accept(&self->tokenizer, TOK_IDENTIFIER)); var_name = self->tokenizer.value.identifier; throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(LhsExpr), (void**)&result)); - throw_if_error(lhsexpr_init(result, is_pub, is_const, var_name, self->allocator)); - - parser_parse_var_type_def(self, &result->type.name); - - throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_EQUALS, &match)); - if(match) { - *assignment_or_rhs = bool_true; - return result; - } + throw_if_error(lhsexpr_init(result, is_extern, is_pub, is_const, var_name, self->allocator)); - if(!result->type.name.data) { - self->error = tokenizer_create_error(&self->tokenizer, - tokenizer_get_error_index(&self->tokenizer), - "Variable declaration requires type or right-hand side expression (Expected ':' or '=')"); - throw(PARSER_UNEXPECTED_TOKEN); - } - - *assignment_or_rhs = bool_false; - throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_SEMICOLON, &match)); - if(match && is_const) { - self->error = tokenizer_create_error(&self->tokenizer, - tokenizer_get_error_index(&self->tokenizer), - "Const variable declaration requires assignment (expected '=', got ';')"); - throw(PARSER_UNEXPECTED_TOKEN); - } - if(!match) { - self->error = tokenizer_create_error(&self->tokenizer, - tokenizer_get_error_index(&self->tokenizer), - "Expected ';'"); - throw(PARSER_UNEXPECTED_TOKEN); - } + ignore_result_int(parser_parse_var_type_def(self, &result->type)); return result; } /* -TODO: Implement params and return types -CLOSURE = 'fn' ('(' PARAM? (',' PARAM)* ')')? '{' BODY_LOOP '}' + +CLOSURE = FUNC_SIGNATURE '{' BODY_LOOP '}' */ static CHECK_RESULT FunctionDecl* parser_parse_closure(Parser *self) { + FunctionSignature *signature; FunctionDecl *result; - bool match; bool prev_has_func_parent; - result = NULL; - throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_FN, &match)); - if(!match) - return result; - - throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_BRACE, &match)); - if(!match) { - throw_if_error(tokenizer_accept(&self->tokenizer, TOK_OPEN_PAREN)); - /* TODO: Parse parameters */ - throw_if_error(tokenizer_accept(&self->tokenizer, TOK_CLOSING_PAREN)); - /* TODO: Parse return types */ - throw_if_error(tokenizer_accept(&self->tokenizer, TOK_OPEN_BRACE)); - } + signature = parser_parse_function_signature(self); + if(!signature) + return NULL; throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(FunctionDecl), (void**)&result)); - throw_if_error(funcdecl_init(result, self->current_scope, self->allocator)); + throw_if_error(funcdecl_init(result, signature, self->current_scope, self->allocator)); self->current_scope = &result->body; prev_has_func_parent = self->has_func_parent; self->has_func_parent = bool_true; + throw_if_error(tokenizer_accept(&self->tokenizer, TOK_OPEN_BRACE)); parser_parse_body_loop(self, self->current_scope, TOK_CLOSING_BRACE); self->current_scope = result->body.parent; self->has_func_parent = prev_has_func_parent; @@ -286,14 +306,14 @@ static void parser_parse_function_args(Parser *self, FunctionCall *func_call) { throw_if_error(tokenizer_accept(&self->tokenizer, TOK_COMMA)); first_arg = bool_false; - arg_expr = parser_parse_rhs_start(self, bool_false); + arg_expr = parser_parse_rhs(self); throw_if_error(buffer_append(&func_call->args, &arg_expr, sizeof(arg_expr))); } } /* VARIABLE = TOK_IDENTIFIER -FUNC_CALL_OR_VARIABLE = VARIABLE '(' FUNC_ARGS ')' +FUNC_CALL_OR_VARIABLE = VARIABLE ('(' FUNC_ARGS ')')? */ static CHECK_RESULT Ast* parser_parse_function_call_or_variable(Parser *self) { Ast *result; @@ -319,6 +339,7 @@ static CHECK_RESULT Ast* parser_parse_function_call_or_variable(Parser *self) { throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(FunctionCall), (void**)&func_call)); throw_if_error(funccall_init(func_call, self->tokenizer.value.identifier, self->allocator)); throw_if_error(ast_create(self->allocator, func_call, AST_FUNCTION_CALL, &result)); + /* Ends after TOK_CLOSING_PAREN */ parser_parse_function_args(self, func_call); return result; } @@ -340,6 +361,109 @@ static CHECK_RESULT Import* parser_parse_import(Parser *self) { return result; } +/* +ELSE_IF_STATEMENT = 'else' ('if' RHS_BINOP)? ('{' BODY_LOOP '}')|BODY +*/ +static CHECK_RESULT ElseIfStatement* parser_parse_else_if_statement(Parser *self) { + ElseIfStatement *result; + bool match; + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_ELSE, &match)); + if(!match) + return NULL; + + throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(ElseIfStatement), (void**)&result)); + throw_if_error(else_if_statement_init(result, self->current_scope, self->allocator)); + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IF, &match)); + if(match) + result->condition = parser_parse_rhs_binop(self); + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_BRACE, &match)); + if(match) { + parser_parse_body_loop(self, &result->body, TOK_CLOSING_BRACE); + } else { + Ast *body_obj; + body_obj = parser_parse_body(self); + throw_if_error(scope_add_child(&result->body, body_obj)); + } + + return result; +} + +static void parser_parse_else_if_statement_loop(Parser *self, IfStatement *if_stmt) { + ElseIfStatement *else_if_stmt; + else_if_stmt = if_stmt->else_if_stmt; + for(;;) { + ElseIfStatement *next_else_if; + next_else_if = parser_parse_else_if_statement(self); + if(!next_else_if) + break; + else_if_stmt->next_else_if_stmt = next_else_if; + /* else statement, which because they have no conditions; can't be followed by another else statement */ + if(!else_if_stmt->condition) + break; + else_if_stmt = next_else_if; + } +} + +/* +IF_STATEMENT = 'if' RHS_BINOP ('{' BODY_LOOP '}')|BODY ELSE_IF_STATEMENT* +*/ +static CHECK_RESULT IfStatement* parser_parse_if_statement(Parser *self) { + IfStatement *result; + bool match; + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IF, &match)); + if(!match) + return NULL; + + throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(IfStatement), (void**)&result)); + throw_if_error(if_statement_init(result, self->current_scope, self->allocator)); + + result->condition = parser_parse_rhs_binop(self); + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_BRACE, &match)); + if(match) { + parser_parse_body_loop(self, &result->body, TOK_CLOSING_BRACE); + } else { + Ast *body_obj; + body_obj = parser_parse_body(self); + throw_if_error(scope_add_child(&result->body, body_obj)); + } + + parser_parse_else_if_statement_loop(self, result); + return result; +} + +/* +WHILE_STATEMENT = 'while' RHS_BINOP ('{' BODY_LOOP '}')|BODY +*/ +static CHECK_RESULT WhileStatement* parser_parse_while_statement(Parser *self) { + WhileStatement *result; + bool match; + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_WHILE, &match)); + if(!match) + return NULL; + + throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(WhileStatement), (void**)&result)); + throw_if_error(while_statement_init(result, self->current_scope, self->allocator)); + + result->condition = parser_parse_rhs_binop(self); + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_BRACE, &match)); + if(match) { + parser_parse_body_loop(self, &result->body, TOK_CLOSING_BRACE); + } else { + Ast *body_obj; + body_obj = parser_parse_body(self); + throw_if_error(scope_add_child(&result->body, body_obj)); + } + + return result; +} + static CHECK_RESULT Ast* parser_parse_number(Parser *self) { Ast *result; bool match; @@ -388,8 +512,6 @@ static Ast* parser_parse_rhs_single_expr(Parser *self) { throw(PARSER_UNEXPECTED_TOKEN); } -static CHECK_RESULT Ast* parser_parse_rhs_binop(Parser *self); - /* RHS_BINOP_OPT_PAREN = RHS_S | '(' RHS_BINOP ')' */ @@ -442,7 +564,7 @@ RHS = RHS_BINOP ';' Note: Parantheses count has to match for the beginning paranthesis and the ending parenthesis. */ -static CHECK_RESULT Ast* parser_parse_rhs(Parser *self) { +Ast* parser_parse_rhs(Parser *self) { /* TODO: If binop only contains one expression, then use that directly for @rhs_expr */ Ast *result; result = parser_parse_rhs_binop(self); @@ -452,41 +574,9 @@ static CHECK_RESULT Ast* parser_parse_rhs(Parser *self) { return result; } -/* -RHS_START = CLOSURE | IMPORT | RHS -*/ -Ast* parser_parse_rhs_start(Parser *self, bool is_standalone) { - Ast *result; - - if(!is_standalone) { - FunctionDecl *func_decl; - StructDecl *struct_decl; - Import *import; - - func_decl = parser_parse_closure(self); - if(func_decl) { - throw_if_error(ast_create(self->allocator, func_decl, AST_FUNCTION_DECL, &result)); - return result; - } - - struct_decl = parser_parse_struct_decl(self); - if(struct_decl) { - throw_if_error(ast_create(self->allocator, struct_decl, AST_STRUCT_DECL, &result)); - return result; - } - - import = parser_parse_import(self); - if(import) { - parser_queue_file(self, import->path, &import->file_scope); - throw_if_error(ast_create(self->allocator, import, AST_IMPORT, &result)); - return result; - } - self->error_context = ERROR_CONTEXT_RHS_STANDALONE; - } - - result = parser_parse_rhs(self); - self->error_context = ERROR_CONTEXT_NONE; - return result; +static bool type_requires_semicolon_at_end(AstType type) { + /* TODO: Check for tables */ + return type != AST_FUNCTION_DECL && type != AST_STRUCT_DECL && type != AST_IF_STATEMENT; } /* @@ -508,37 +598,109 @@ static void parser_parse_body_semicolon(Parser *self, Ast *expr) { return; } - /* TODO: Check for tables */ - if(expr->type != AST_FUNCTION_DECL && expr->type != AST_STRUCT_DECL) + if(type_requires_semicolon_at_end(expr->type)) throw_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON)); } +/* CONDITIONAL = (IF_STATEMENT|WHILE_STATEMENT)? */ +static CHECK_RESULT Ast* parser_parse_conditional(Parser *self) { + Ast *result; + IfStatement *if_stmt; + WhileStatement *while_stmt; + + if_stmt = parser_parse_if_statement(self); + if(if_stmt) { + throw_if_error(ast_create(self->allocator, if_stmt, AST_IF_STATEMENT, &result)); + self->error_context = ERROR_CONTEXT_NONE; + return result; + } + + while_stmt = parser_parse_while_statement(self); + if(while_stmt) { + throw_if_error(ast_create(self->allocator, while_stmt, AST_WHILE_STATEMENT, &result)); + self->error_context = ERROR_CONTEXT_NONE; + return result; + } + + return NULL; +} + /* -BODY = LHS ';' | - (LHS '=' RHS_START BODY_SEMICOLON) | - (RHS_START BODY_SEMICOLON) +BODY = (LHS_DECLARATION ';') | + (LHS_DECLARATION '=' CLOSURE|STRUCT|IMPORT|(RHS BODY_SEMICOLON)) | + CONDITIONAL | + (RHS ';'|('=' RHS BODY_SEMICOLON)) */ Ast* parser_parse_body(Parser *self) { - bool assignment_or_rhs; Ast *result; LhsExpr *lhs_expr; Ast *rhs_expr; - lhs_expr = parser_parse_lhs(self, &assignment_or_rhs); - if(!assignment_or_rhs) { + lhs_expr = parser_parse_declaration_lhs(self); + if(lhs_expr) { + bool match; throw_if_error(ast_create(self->allocator, lhs_expr, AST_LHS, &result)); - return result; - } - - if(!lhs_expr) + if(lhs_expr->is_extern) { + throw_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON)); + return result; + } else { + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_SEMICOLON, &match)); + if(match) + return result; + } + + throw_if_error(tokenizer_accept(&self->tokenizer, TOK_EQUALS)); + + FunctionDecl *func_decl; + StructDecl *struct_decl; + Import *import; + + func_decl = parser_parse_closure(self); + if(func_decl) { + throw_if_error(ast_create(self->allocator, func_decl, AST_FUNCTION_DECL, &lhs_expr->rhs_expr)); + return result; + } + + struct_decl = parser_parse_struct_decl(self); + if(struct_decl) { + throw_if_error(ast_create(self->allocator, struct_decl, AST_STRUCT_DECL, &lhs_expr->rhs_expr)); + return result; + } + + import = parser_parse_import(self); + if(import) { + parser_queue_file(self, import->path, &import->file_scope); + throw_if_error(ast_create(self->allocator, import, AST_IMPORT, &lhs_expr->rhs_expr)); + parser_parse_body_semicolon(self, lhs_expr->rhs_expr); + return result; + } + } else { self->error_context = ERROR_CONTEXT_NO_LHS; + result = parser_parse_conditional(self); + if(result) + return result; + } - rhs_expr = parser_parse_rhs_start(self, !lhs_expr); + self->error_context = ERROR_CONTEXT_RHS_STANDALONE; + rhs_expr = parser_parse_rhs(self); + self->error_context = ERROR_CONTEXT_NONE; if(lhs_expr) { lhs_expr->rhs_expr = rhs_expr; - throw_if_error(ast_create(self->allocator, lhs_expr, AST_LHS, &result)); } else { - result = rhs_expr; + bool match; + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_EQUALS, &match)); + if(match) { + AssignmentExpr *assign_expr; + throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(AssignmentExpr), (void**)&assign_expr)); + throw_if_error(ast_create(self->allocator, assign_expr, AST_ASSIGN, &result)); + assign_expr->lhs_expr = rhs_expr; + + self->error_context = ERROR_CONTEXT_RHS_STANDALONE; + assign_expr->rhs_expr = parser_parse_rhs(self); + self->error_context = ERROR_CONTEXT_NONE; + } else { + result = rhs_expr; + } } parser_parse_body_semicolon(self, rhs_expr); diff --git a/src/ssa/ssa.c b/src/ssa/ssa.c index c4ed0d3..91ba185 100644 --- a/src/ssa/ssa.c +++ b/src/ssa/ssa.c @@ -4,6 +4,7 @@ #include "../../include/std/hash.h" #include "../../include/std/thread.h" #include "../../include/ast.h" +#include "../../include/compiler.h" #include <assert.h> #define throw(result) do { longjmp(context->env, (result)); } while(0) @@ -59,7 +60,7 @@ int ssa_init(Ssa *self, ScopedAllocator *allocator) { return 0; } -int ssa_get_unique_reg(Ssa *self, SsaRegister *result) { +static CHECK_RESULT int ssa_get_unique_reg(Ssa *self, SsaRegister *result) { /* Overflow */ if(self->reg_counter + 1 < self->reg_counter) return -1; @@ -144,12 +145,13 @@ static CHECK_RESULT int ssa_add_ins_form1(Ssa *self, SsaInstruction ins_type, Ss } static const char* binop_type_to_string(SsaInstruction binop_type) { - assert(binop_type >= SSA_ADD && binop_type <= SSA_DIV); + assert(binop_type >= SSA_ADD && binop_type <= SSA_EQUALS); switch(binop_type) { case SSA_ADD: return "+"; case SSA_SUB: return "-"; case SSA_MUL: return "*"; case SSA_DIV: return "/"; + case SSA_EQUALS: return "=="; default: return ""; } } @@ -173,31 +175,31 @@ static CHECK_RESULT int ssa_add_ins_form2(Ssa *self, SsaInstruction ins_type, Ss return 0; } -int ssa_ins_assign_inter(Ssa *self, SsaRegister dest, SsaNumber number) { +static CHECK_RESULT int ssa_ins_assign_inter(Ssa *self, SsaRegister dest, SsaNumber number) { SsaIntermediateIndex index; return_if_error(ssa_try_add_intermediate(self, number, &index)); amal_log_debug("r%u = i%u", dest, index); return ssa_add_ins_form1(self, SSA_ASSIGN_INTER, dest, index); } -int ssa_ins_assign_string(Ssa *self, SsaRegister dest, BufferView str) { +static CHECK_RESULT int ssa_ins_assign_string(Ssa *self, SsaRegister dest, BufferView str) { SsaStringIndex index; return_if_error(ssa_try_add_string(self, str, &index)); amal_log_debug("r%u = s%u", dest, index); return ssa_add_ins_form1(self, SSA_ASSIGN_STRING, dest, index); } -int ssa_ins_assign_reg(Ssa *self, SsaRegister dest, SsaRegister src) { +static CHECK_RESULT int ssa_ins_assign_reg(Ssa *self, SsaRegister dest, SsaRegister src) { amal_log_debug("r%u = r%u", dest, src); return ssa_add_ins_form1(self, SSA_ASSIGN_REG, dest, src); } -int ssa_ins_binop(Ssa *self, SsaInstruction binop_type, SsaRegister lhs, SsaRegister rhs, SsaRegister *result) { - assert(binop_type >= SSA_ADD && binop_type <= SSA_DIV); +static CHECK_RESULT int ssa_ins_binop(Ssa *self, SsaInstruction binop_type, SsaRegister lhs, SsaRegister rhs, SsaRegister *result) { + assert(binop_type >= SSA_ADD && binop_type <= SSA_EQUALS); return ssa_add_ins_form2(self, binop_type, lhs, rhs, result); } -int ssa_ins_func_start(Ssa *self, u8 num_args, SsaFuncIndex *result) { +static CHECK_RESULT int ssa_ins_func_start(Ssa *self, u8 num_args, SsaFuncIndex *result) { usize index; index = self->instructions.size; @@ -214,14 +216,14 @@ int ssa_ins_func_start(Ssa *self, u8 num_args, SsaFuncIndex *result) { return 0; } -int ssa_ins_func_end(Ssa *self) { +static CHECK_RESULT int ssa_ins_func_end(Ssa *self) { u8 ins; ins = SSA_FUNC_END; amal_log_debug("FUNC_END"); return buffer_append(&self->instructions, &ins, 1); } -int ssa_ins_push(Ssa *self, SsaRegister reg) { +static CHECK_RESULT int ssa_ins_push(Ssa *self, SsaRegister reg) { usize index; index = self->instructions.size; @@ -232,7 +234,7 @@ int ssa_ins_push(Ssa *self, SsaRegister reg) { return 0; } -int ssa_ins_call(Ssa *self, FunctionDecl *func_decl, SsaRegister *result) { +static CHECK_RESULT int ssa_ins_call(Ssa *self, FunctionDecl *func_decl, SsaRegister *result) { usize index; index = self->instructions.size; @@ -249,31 +251,88 @@ int ssa_ins_call(Ssa *self, FunctionDecl *func_decl, SsaRegister *result) { return 0; } +static CHECK_RESULT int ssa_ins_jumpzero(Ssa *self, SsaRegister condition_reg, JumpOffset jump_offset) { + usize index; + index = self->instructions.size; + + return_if_error(buffer_append_empty(&self->instructions, sizeof(u8) + sizeof(SsaRegister) + sizeof(JumpOffset))); + self->instructions.data[index + 0] = SSA_JUMP_ZERO; + am_memcpy(self->instructions.data + index + 1, &condition_reg, sizeof(SsaRegister)); + am_memcpy(self->instructions.data + index + 1 + sizeof(SsaRegister), &jump_offset, sizeof(JumpOffset)); + if(jump_offset == 0) + amal_log_debug("JUMP_ZERO r%u, DUMMY", condition_reg); + else + amal_log_debug("JUMP_ZERO r%u, %d", condition_reg, jump_offset); + return 0; +} + +static CHECK_RESULT int ssa_ins_jump(Ssa *self, JumpOffset jump_offset) { + usize index; + index = self->instructions.size; + + return_if_error(buffer_append_empty(&self->instructions, sizeof(u8) + sizeof(JumpOffset))); + self->instructions.data[index + 0] = SSA_JUMP; + am_memcpy(self->instructions.data + index + 1, &jump_offset, sizeof(JumpOffset)); + amal_log_debug("JUMP %d", jump_offset); + return 0; +} + +static usize ssa_ins_get_index(Ssa *self) { + return self->instructions.size; +} + +/* Set target of jump instruction to current location */ +static CHECK_RESULT int ssa_ins_jump_set_target(Ssa *self, usize jump_ins_index) { + switch(self->instructions.data[jump_ins_index]) { + case SSA_JUMP_ZERO: { + isize jump_offset = (isize)ssa_ins_get_index(self) - (isize)jump_ins_index; + /* TODO: Should something be done about this? */ + if(jump_offset < -0x7FFF || jump_offset > 0x7FFF) { + amal_log_error("Unexpected error. Jump offset has to be less than +-32767, was %d", jump_offset); + return 1; + } + am_memcpy(self->instructions.data + jump_ins_index + 1 + sizeof(SsaRegister), &jump_offset, sizeof(JumpOffset)); + break; + } + default: + assert(bool_false && "Unexpected error... jump_ins_index doesn't point to a valid index to a jump instruction"); + break; + } + return 0; +} + static CHECK_RESULT SsaRegister ast_generate_ssa(Ast *self, SsaCompilerContext *context); static CHECK_RESULT SsaRegister number_generate_ssa(Number *self, SsaCompilerContext *context) { SsaRegister reg; SsaNumber number; - if(self->is_integer) { + if(self->is_integer) number = create_ssa_integer(self->value.integer); - throw_if_error(ssa_get_unique_reg(context->ssa, ®)); - throw_if_error(ssa_ins_assign_inter(context->ssa, reg, number)); - } else { + else number = create_ssa_float(self->value.floating); - throw_if_error(ssa_get_unique_reg(context->ssa, ®)); - throw_if_error(ssa_ins_assign_inter(context->ssa, reg, number)); - } + throw_if_error(ssa_get_unique_reg(context->ssa, ®)); + throw_if_error(ssa_ins_assign_inter(context->ssa, reg, number)); return reg; } +static CHECK_RESULT SsaRegister lhsexpr_extern_generate_ssa(LhsExpr *self, SsaCompilerContext *context) { + /* TODO: SsaRegister should be extended to include static and extern data */ + (void)self; + (void)context; + amal_log_error("TODO: Implement lhsexpr_extern_generate_ssa"); + return 0; +} + static CHECK_RESULT SsaRegister lhsexpr_generate_ssa(Ast *self, SsaCompilerContext *context) { - /* TODO: Implement */ LhsExpr *lhs_expr; SsaRegister reg; assert(self->type == AST_LHS); lhs_expr = self->value.lhs_expr; + if(lhs_expr->is_extern) + return lhsexpr_extern_generate_ssa(lhs_expr, context); + if(lhs_expr->rhs_expr) { SsaRegister rhs_reg; rhs_reg = ast_generate_ssa(lhs_expr->rhs_expr, context); @@ -283,8 +342,10 @@ static CHECK_RESULT SsaRegister lhsexpr_generate_ssa(Ast *self, SsaCompilerConte Import expression also has no meaning in SSA until it's used. TODO: Shouldn't lhsexpr that have struct/function declaration as rhs be different ast expression types? */ - if(self->resolve_data.type == lhs_expr || lhs_expr->rhs_expr->type == AST_IMPORT) + if(self->resolve_data.type == lhs_expr || lhs_expr->rhs_expr->type == AST_IMPORT) { + /*assert(bool_false);*/ return 0; + } throw_if_error(ssa_get_unique_reg(context->ssa, ®)); if(reg == rhs_reg) { amal_log_error("rhs_expr is same as reg.. rhs type: %d", lhs_expr->rhs_expr->type); @@ -292,12 +353,33 @@ static CHECK_RESULT SsaRegister lhsexpr_generate_ssa(Ast *self, SsaCompilerConte assert(reg != rhs_reg); throw_if_error(ssa_ins_assign_reg(context->ssa, reg, rhs_reg)); } else { - /* TODO: assign default value to reg depending on LhsExpr type */ - reg = 0; + /* TODO: Do not assign if we dont want default value */ + SsaNumber number; + if(self->resolve_data.type == context->compiler->default_types.i64) + number = create_ssa_integer(0); + else if(self->resolve_data.type == context->compiler->default_types.f64) + number = create_ssa_float(0.0); + else + assert(bool_false && "TODO: assign default value to reg depending on LhsExpr type"); + throw_if_error(ssa_get_unique_reg(context->ssa, ®)); + throw_if_error(ssa_ins_assign_inter(context->ssa, reg, number)); } return reg; } +static CHECK_RESULT SsaRegister assignmentexpr_generate_ssa(Ast *ast, SsaCompilerContext *context) { + AssignmentExpr *self; + SsaRegister lhs_reg, rhs_reg; + + assert(ast->type == AST_ASSIGN); + self = ast->value.assign_expr; + + lhs_reg = ast_generate_ssa(self->lhs_expr, context); + rhs_reg = ast_generate_ssa(self->rhs_expr, context); + throw_if_error(ssa_ins_assign_reg(context->ssa, lhs_reg, rhs_reg)); + return lhs_reg; +} + /* TODO: Each function declaration should be in separate SSA instances so ast can be converted into ssa in any order. @@ -314,6 +396,7 @@ static CHECK_RESULT SsaRegister funcdecl_generate_ssa(FunctionDecl *self, SsaCom throw_if_error(ssa_ins_func_end(context->ssa)); context->ssa->reg_counter = prev_reg_counter; + /*assert(bool_false);*/ return 0; } @@ -348,12 +431,14 @@ static CHECK_RESULT SsaRegister funccall_generate_ssa(Ast *self, SsaCompilerCont static CHECK_RESULT SsaRegister structdecl_generate_ssa(StructDecl *self, SsaCompilerContext *context) { /* TODO: Implement */ + /*assert(bool_false);*/ scope_generate_ssa(&self->body, context); return 0; } static CHECK_RESULT SsaRegister structfield_generate_ssa(StructField *self, SsaCompilerContext *context) { /* TODO: Implement */ + /*assert(bool_false);*/ (void)self; (void)context; return 0; @@ -367,10 +452,10 @@ static CHECK_RESULT SsaRegister string_generate_ssa(String *self, SsaCompilerCon } static CHECK_RESULT SsaRegister variable_generate_ssa(Variable *self, SsaCompilerContext *context) { - /* TODO: Implement, and with cross field references */ - (void)self; - (void)context; - return 0; + /* TODO: If resolved_var refers to a variable in another file, use a cross file reference that requires no locking (not yet implemented) */ + /* This is not thread-safe:*/ + assert(self->resolved_var); + return ast_generate_ssa(self->resolved_var, context); } static SsaInstruction binop_type_to_ssa_type(BinopType binop_type) { @@ -386,6 +471,8 @@ static SsaInstruction binop_type_to_ssa_type(BinopType binop_type) { case BINOP_DOT: assert(bool_false && "Binop dot not valid for arithmetic operation and requires special functionality"); return 0; + case BINOP_EQUALS: + return SSA_EQUALS; } return 0; } @@ -405,7 +492,57 @@ static CHECK_RESULT SsaRegister binop_generate_ssa(Binop *self, SsaCompilerConte return reg; } -CHECK_RESULT SsaRegister ast_generate_ssa(Ast *self, SsaCompilerContext *context) { +static void else_if_statement_generate_ssa(ElseIfStatement *else_if_stmt, SsaCompilerContext *context) { + usize jump_ins_index; + if(else_if_stmt->condition) { + SsaRegister condition_reg; + condition_reg = ast_generate_ssa(else_if_stmt->condition, context); + jump_ins_index = ssa_ins_get_index(context->ssa); + throw_if_error(ssa_ins_jumpzero(context->ssa, condition_reg, 0)); + } + scope_generate_ssa(&else_if_stmt->body, context); + if(else_if_stmt->condition) + throw_if_error(ssa_ins_jump_set_target(context->ssa, jump_ins_index)); + if(else_if_stmt->next_else_if_stmt) + else_if_statement_generate_ssa(else_if_stmt->next_else_if_stmt, context); +} + +static void if_statement_generate_ssa(IfStatement *if_stmt, SsaCompilerContext *context) { + SsaRegister condition_reg; + usize jump_ins_index; + + condition_reg = ast_generate_ssa(if_stmt->condition, context); + jump_ins_index = ssa_ins_get_index(context->ssa); + throw_if_error(ssa_ins_jumpzero(context->ssa, condition_reg, 0)); + scope_generate_ssa(&if_stmt->body, context); + throw_if_error(ssa_ins_jump_set_target(context->ssa, jump_ins_index)); + if(if_stmt->else_if_stmt) + else_if_statement_generate_ssa(if_stmt->else_if_stmt, context); +} + +static void while_statement_generate_ssa(WhileStatement *while_stmt, SsaCompilerContext *context) { + SsaRegister condition_reg; + usize jump_back_ins_index; + usize jump_condition_ins_index; + isize jump_offset; + + jump_back_ins_index = ssa_ins_get_index(context->ssa); + condition_reg = ast_generate_ssa(while_stmt->condition, context); + jump_condition_ins_index = ssa_ins_get_index(context->ssa); + throw_if_error(ssa_ins_jumpzero(context->ssa, condition_reg, 0)); + scope_generate_ssa(&while_stmt->body, context); + /* Jump back and check condition again before running the content of the loop again */ + jump_offset = (isize)jump_back_ins_index - (isize)ssa_ins_get_index(context->ssa); + /* TODO: Should something be done about this? */ + if(jump_offset < -0x7FFF || jump_offset > 0x7FFF) { + amal_log_error("Unexpected error. Jump offset has to be less than +-32767, was %d", jump_offset); + throw(1); + } + throw_if_error(ssa_ins_jump(context->ssa, (JumpOffset)jump_offset)); + throw_if_error(ssa_ins_jump_set_target(context->ssa, jump_condition_ins_index)); +} + +static CHECK_RESULT SsaRegister ast_generate_ssa(Ast *self, SsaCompilerContext *context) { assert(self); #ifdef DEBUG if(self->resolve_data.status != AST_RESOLVED && self->resolve_data.status != AST_SSA_RESOLVED) { @@ -420,48 +557,48 @@ CHECK_RESULT SsaRegister ast_generate_ssa(Ast *self, SsaCompilerContext *context switch(self->type) { case AST_NUMBER: self->ssa_reg = number_generate_ssa(self->value.number, context); - self->resolve_data.status = AST_SSA_RESOLVED; - return self->ssa_reg; + break; case AST_FUNCTION_DECL: self->ssa_reg = funcdecl_generate_ssa(self->value.func_decl, context); - self->resolve_data.status = AST_SSA_RESOLVED; - return self->ssa_reg; + break; case AST_FUNCTION_CALL: self->ssa_reg = funccall_generate_ssa(self, context); - self->resolve_data.status = AST_SSA_RESOLVED; - return self->ssa_reg; + break; case AST_STRUCT_DECL: self->ssa_reg = structdecl_generate_ssa(self->value.struct_decl, context); - self->resolve_data.status = AST_SSA_RESOLVED; - return self->ssa_reg; + break; case AST_STRUCT_FIELD: self->ssa_reg = structfield_generate_ssa(self->value.struct_field, context); - self->resolve_data.status = AST_SSA_RESOLVED; - return self->ssa_reg; + break; case AST_LHS: self->ssa_reg = lhsexpr_generate_ssa(self, context); - self->resolve_data.status = AST_SSA_RESOLVED; - return self->ssa_reg; + break; + case AST_ASSIGN: + self->ssa_reg = assignmentexpr_generate_ssa(self, context); + break; case AST_IMPORT: /* TODO: Implement cross file references */ self->ssa_reg = 0; - self->resolve_data.status = AST_SSA_RESOLVED; - return self->ssa_reg; + break; case AST_STRING: self->ssa_reg = string_generate_ssa(self->value.string, context); - self->resolve_data.status = AST_SSA_RESOLVED; - return self->ssa_reg; + break; case AST_VARIABLE: self->ssa_reg = variable_generate_ssa(self->value.variable, context); - self->resolve_data.status = AST_SSA_RESOLVED; - return self->ssa_reg; + break; case AST_BINOP: self->ssa_reg = binop_generate_ssa(self->value.binop, context); - self->resolve_data.status = AST_SSA_RESOLVED; - return self->ssa_reg; + break; + case AST_IF_STATEMENT: + if_statement_generate_ssa(self->value.if_stmt, context); + break; + case AST_WHILE_STATEMENT: + while_statement_generate_ssa(self->value.while_stmt, context); + break; } - return 0; + self->resolve_data.status = AST_SSA_RESOLVED; + return self->ssa_reg; } void scope_generate_ssa(Scope *self, SsaCompilerContext *context) { diff --git a/src/tokenizer.c b/src/tokenizer.c index 7f6d08e..03a72a1 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -170,6 +170,9 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) { if(am_memeql(self->value.identifier.data, "fn", 2)) { *token = TOK_FN; return TOKENIZER_OK; + } else if(am_memeql(self->value.identifier.data, "if", 2)) { + *token = TOK_IF; + return TOKENIZER_OK; } break; } @@ -183,10 +186,20 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) { } break; } + case 4: { + if(am_memeql(self->value.identifier.data, "else", 4)) { + *token = TOK_ELSE; + return TOKENIZER_OK; + } + break; + } case 5: { if(am_memeql(self->value.identifier.data, "const", 5)) { *token = TOK_CONST; return TOKENIZER_OK; + } else if(am_memeql(self->value.identifier.data, "while", 5)) { + *token = TOK_WHILE; + return TOKENIZER_OK; } break; } @@ -194,6 +207,9 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) { if(am_memeql(self->value.identifier.data, "struct", 6)) { *token = TOK_STRUCT; return TOKENIZER_OK; + } else if(am_memeql(self->value.identifier.data, "extern", 6)) { + *token = TOK_EXTERN; + return TOKENIZER_OK; } break; } @@ -288,7 +304,12 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) { SET_BINOP(BINOP_DIV); } else if(c == '=') { ++self->index; - *token = TOK_EQUALS; + if(self->index < (int)self->code.size && tokenizer_get_char(self) == '=') { + ++self->index; + SET_BINOP(BINOP_EQUALS); + } else { + *token = TOK_EQUALS; + } } else if(c == '(') { ++self->index; *token = TOK_OPEN_PAREN; @@ -450,6 +471,18 @@ static BufferView tokenizer_expected_token_as_string(Token token) { case TOK_PUB: str = "pub"; break; + case TOK_IF: + str = "if"; + break; + case TOK_ELSE: + str = "else"; + break; + case TOK_WHILE: + str = "while"; + break; + case TOK_EXTERN: + str = "extern"; + break; } return create_buffer_view(str, strlen(str)); } @@ -588,18 +621,15 @@ int tokenizer_get_end_of_multiline_comment(Tokenizer *self, int index) { int comment_count; comment_count = 1; + ++index; while(index < (int)self->code.size) { c = self->code.data[index]; - if(c == '*') { - if(index - 1 >= 0 && self->code.data[index - 1] == '/') { - ++comment_count; - } - } else if(c == '/') { - if(index - 1 >= 0 && self->code.data[index - 1] == '*') { - --comment_count; - if(comment_count == 0) - return index + 1; - } + if(c == '*' && self->code.data[index - 1] == '/') { + ++comment_count; + } else if(c == '/' && self->code.data[index - 1] == '*') { + --comment_count; + if(comment_count == 0) + return index + 1; } ++index; } @@ -625,7 +655,61 @@ static int max(int a, int b) { return a > b ? a : b; } +void tokenizer_print_error_args(Tokenizer *self, int index, const char *fmt, va_list args) { + int line; + int line_start; + int line_end; + /*int code_start;*/ + int prev_column; + int i; + + line = tokenizer_get_line_by_index(self, index); + line_start = tokenizer_get_start_of_line_from_index(self, index); + line_end = tokenizer_get_end_of_line_from_index(self, index); + /*code_start = find_non_whitespace(&self->code.data[line_start], line_end - line_start); + if(code_start != -1) + line_start += code_start;*/ + prev_column = index - line_start; + + if(self->compiler_options->error_callback) { + char buffer[2048]; + int bytes_copied; + + bytes_copied = 0; + bytes_copied += max(0, snprintf(buffer + bytes_copied, sizeof(buffer) - bytes_copied, "%.*s:%d:%d: error: ", (int)self->code_name.size, self->code_name.data, line, 1 + prev_column)); + + if(sizeof(buffer) - bytes_copied > 0) { + bytes_copied += max(0, vsnprintf(buffer + bytes_copied, sizeof(buffer) - bytes_copied, fmt, args)); + } + + if(sizeof(buffer) - bytes_copied > 0) + bytes_copied += max(0, snprintf(buffer + bytes_copied, sizeof(buffer) - bytes_copied, "\n%.*s\n", line_end - line_start, self->code.data + line_start)); + + if(sizeof(buffer) - bytes_copied > 0) { + for(i = 0; i < prev_column; ++i) + bytes_copied += max(0, snprintf(buffer + bytes_copied, sizeof(buffer) - bytes_copied, " ")); + } + + if(sizeof(buffer) - bytes_copied > 0) + bytes_copied += max(0, snprintf(buffer + bytes_copied, sizeof(buffer) - bytes_copied, "^\n")); + + self->compiler_options->error_callback(buffer, bytes_copied, self->compiler_options->error_callback_userdata); + } else { + amal_mutex *mutex; + mutex = amal_log_get_mutex(); + ignore_result_int(amal_mutex_lock(mutex, "tokenizer_print_error")); + fprintf(stderr, "\x1b[1;37m%.*s:%d:%d:\x1b[0m \x1b[1;31merror:\x1b[0m ", (int)self->code_name.size, self->code_name.data, line, 1 + prev_column); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n%.*s\n", line_end - line_start, self->code.data + line_start); + for(i = 0; i < prev_column; ++i) + fprintf(stderr, " "); + fprintf(stderr, "\x1b[1;32m^\x1b[0m\n"); + ignore_result_int(amal_mutex_unlock(mutex)); + } +} + void tokenizer_print_error(Tokenizer *self, int index, const char *fmt, ...) { +#if 0 va_list args; int line; int line_start; @@ -681,6 +765,11 @@ void tokenizer_print_error(Tokenizer *self, int index, const char *fmt, ...) { va_end(args); ignore_result_int(amal_mutex_unlock(mutex)); } +#endif + va_list args; + va_start(args, fmt); + tokenizer_print_error_args(self, index, fmt, args); + va_end(args); } void tokenizer_print_error_object(Tokenizer *self, TokenizerError *error) { diff --git a/tests/b.amal.z b/tests/b.amal.z index bf8e698..b9e0013 100644 --- a/tests/b.amal.z +++ b/tests/b.amal.z @@ -1,4 +1,2 @@ -typedef i64 signed long long; -typedef f64 double; -void f0() { -} +FUNC_START 0 +FUNC_END diff --git a/tests/bytecode.amal b/tests/bytecode.amal new file mode 100644 index 0000000..c5b2cb3 --- /dev/null +++ b/tests/bytecode.amal @@ -0,0 +1,12 @@ +extern const printf: fn; + +const print = fn { + +} + +const main = fn { + var value = 23; + value = 34; + const str_value = "hello, world"; + print(); +}
\ No newline at end of file diff --git a/tests/bytecode.amal.z b/tests/bytecode.amal.z new file mode 100644 index 0000000..2809b47 --- /dev/null +++ b/tests/bytecode.amal.z @@ -0,0 +1,11 @@ +FUNC_START 0 +FUNC_END +FUNC_START 0 +mov r0, i0 +mov r1, r0 +mov r2, i1 +mov r1, r2 +mov r3, s0 +mov r4, r3 +call 0 +FUNC_END diff --git a/tests/errors/const_assign.amal b/tests/errors/const_assign.amal new file mode 100644 index 0000000..b0b0ec4 --- /dev/null +++ b/tests/errors/const_assign.amal @@ -0,0 +1,4 @@ +const main = fn { + const value = 23; + value = 34; +}
\ No newline at end of file diff --git a/tests/errors/const_assign.amal.z b/tests/errors/const_assign.amal.z new file mode 100644 index 0000000..99b1210 --- /dev/null +++ b/tests/errors/const_assign.amal.z @@ -0,0 +1,6 @@ +FUNC_START 0 +mov r0, i0 +mov r1, r0 +mov r2, i1 +mov r1, r2 +FUNC_END diff --git a/tests/io.amal.z b/tests/io.amal.z index bf8e698..b9e0013 100644 --- a/tests/io.amal.z +++ b/tests/io.amal.z @@ -1,4 +1,2 @@ -typedef i64 signed long long; -typedef f64 double; -void f0() { -} +FUNC_START 0 +FUNC_END diff --git a/tests/main.amal b/tests/main.amal index ae37816..c12ecba 100644 --- a/tests/main.amal +++ b/tests/main.amal @@ -20,8 +20,28 @@ const main = fn { episfjpseifipesf */ io.puts("lole"); + if num1 == 43 + print(2); } const print = fn { -}
\ No newline at end of file +} + +/* +// Shader main function is guaranteed to be inline if run on CPU + +const vertex_passthrough = VertexShader { + const main = fn() vec4f { + return gl_ModelViewProjectionMatrix * gl_Vertex; + } +} + +const fragment_passthrough = FragmentShader { + tex: texture2d; + + const main = fn(coord: vec2f) vec2f { + return tex.getPixel(coord); + } +} +*/ diff --git a/tests/main.amal.z b/tests/main.amal.z index 73d9dae..eac48f6 100644 --- a/tests/main.amal.z +++ b/tests/main.amal.z @@ -1,36 +1,33 @@ -typedef i64 signed long long; -typedef f64 double; -void f0() { -void f1() { -} -const char* r0 = "hello"; -const char* r1 = r0; -PUSH r0 *** -const char* r2 = "world"; -PUSH r2 *** -i64 r3 = 356; -PUSH r3 *** -f64 r4 = 13.370000; -PUSH r4 *** -r5 = CALL 0x621000010bc8 *** -i64 r6 = 23232; -i64 r7 = r6; -i64 r8 = 30; -r9 = r0 * r8; -r10 = r0 + r9; -i64 r11 = r10; -r12 = r0 + r0; -i64 r13 = 34; -i64 r14 = 32; -r15 = r13 + r14; -i64 r16 = 2; -r17 = r15 / r16; -r18 = r0 * r17; -r19 = r12 * r18; -i64 r20 = r19; -const char* r21 = "lole"; -PUSH r21 *** -r22 = CALL 0x621000006948 *** -} -void f2() { -} +FUNC_START 0 +FUNC_START 0 +FUNC_END +mov r0, s0 +mov r1, r0 +push r1 +mov r2, s1 +push r2 +mov r3, i0 +push r3 +mov r4, i1 +push r4 +call 2 +mov r6, i2 +mov r7, i3 +mov r8, r7 +mov r9, i4 +mov r12, r11 +mov r14, i5 +mov r15, i6 +mov r17, i7 +mov r21, r20 +mov r22, s2 +push r22 +call 0 +mov r24, i8 +jz r25, 24 +mov r26, i9 +push r26 +call 2 +FUNC_END +FUNC_START 0 +FUNC_END diff --git a/tests/main.c b/tests/main.c index 04f2343..3080807 100644 --- a/tests/main.c +++ b/tests/main.c @@ -7,6 +7,9 @@ #include <stdlib.h> #include <unistd.h> +static int num_successful_tests = 0; +static int num_tests_run = 0; + #define REQUIRE_EQ_INT(expected, actual) do{\ if((expected) != (actual)) {\ fprintf(stderr, "%s:%d: Assert failed (%s vs %s).\nExpected %d, got %d.\n", __FILE__, __LINE__, #expected, #actual, (expected), (actual));\ @@ -42,7 +45,7 @@ static CHECK_RESULT int test_hash_map() { return 0; } -#define FAIL_TEST(name) do { fprintf(stderr, "Test failed: %s\n", (name)); exit(1); } while(0) +#define FAIL_TEST(name) do { fprintf(stderr, "Test failed: %s\n", (name)); return; } while(0) typedef struct { char *filepath; @@ -56,8 +59,8 @@ typedef struct { static void error_callback_assert(const char *err_msg, int err_msg_len, void *userdata) { ErrorExpectedData *expected_data; - expected_data = userdata; int expected_err_msg_len; + expected_data = userdata; expected_err_msg_len = strlen(expected_data->expected_error); if(expected_data->got_expected_error) { @@ -74,14 +77,14 @@ static void error_callback_assert(const char *err_msg, int err_msg_len, void *us expected_data->got_expected_error = bool_true; } - +#if 0 static void error_callback(const char *err_msg, int err_msg_len, void *userdata) { ErrorUnexpectedData *data; data = userdata; fprintf(stderr, "Test failed: %s with error: %.*s\n", data->filepath, err_msg_len, err_msg); exit(1); } - +#endif static char* get_full_path(const char *filepath) { #define PATH_LEN 4096 char *buf; @@ -120,33 +123,39 @@ static char* join_str(const char *str1, const char *str2, char delimiter) { static void test_load(const char *filepath) { amal_compiler compiler; amal_compiler_options options; + char *full_path; int result; amal_compiler_options_init(&options); + ++num_tests_run; + full_path = get_full_path(filepath); + /* options.error_callback = error_callback; ErrorUnexpectedData data; data.filepath = get_full_path(filepath); options.error_callback_userdata = &data; + */ result = amal_compiler_init(&compiler, &options); if(result != AMAL_COMPILER_OK) { - fprintf(stderr, "Failed to initialize compiler, error code: %d\n", result); - FAIL_TEST(data.filepath); + fprintf(stderr, "Failed to initialize compiler to test %s, error code: %d\n", full_path, result); + FAIL_TEST(full_path); } result = amal_compiler_load_file(&compiler, filepath); if(result != AMAL_COMPILER_OK) { - fprintf(stderr, "Failed to load file %s, result: %d\n", data.filepath, result); - FAIL_TEST(data.filepath); + fprintf(stderr, "Failed to load file %s, result: %d\n", full_path, result); + FAIL_TEST(full_path); } if(amal_compiler_deinit(&compiler) != 0) { - fprintf(stderr, "Failed to deinitialize compiler.\n"); - FAIL_TEST(data.filepath); + fprintf(stderr, "Failed to deinitialize compiler for test %s\n", full_path); + FAIL_TEST(full_path); } - fprintf(stderr, "Test succeeded: %s\n", data.filepath); - free(data.filepath); + fprintf(stderr, "Test succeeded as expected: %s\n", full_path); + ++num_successful_tests; + free(full_path); } static void test_load_error(const char *filepath, const char *expected_error) { @@ -155,6 +164,7 @@ static void test_load_error(const char *filepath, const char *expected_error) { int result; amal_compiler_options_init(&options); + ++num_tests_run; options.error_callback = error_callback_assert; ErrorExpectedData expected_data; expected_data.filepath = get_full_path(filepath); @@ -184,7 +194,8 @@ static void test_load_error(const char *filepath, const char *expected_error) { FAIL_TEST(expected_data.filepath); } - fprintf(stderr, "Test succeeded: %s\n", expected_data.filepath); + fprintf(stderr, "Test failed as expected: %s\n", expected_data.filepath); + ++num_successful_tests; free(expected_data.filepath); free(expected_data.expected_error); } @@ -205,11 +216,17 @@ int main(int argc, char **argv) { " pub const num = 45;\n" " ^\n"); test_load_error("tests/errors/closure_no_lhs.amal", - "1:1: error: Expected variable declaration, string, variable or function call\n" + "1:1: error: Expected string, variable, closure, struct, function call or import\n" "fn {}\n" "^\n"); + test_load_error("tests/errors/const_assign.amal", + "3:5: error: Can't assign to a const value\n" + " value = 34;\n" + " ^\n"); + fprintf(stderr, "##### %d/%d tests succeeded #####\n", num_successful_tests, num_tests_run); } else if(argc == 2) { test_load(argv[1]); + fprintf(stderr, "##### %d/%d tests succeeded #####\n", num_successful_tests, num_tests_run); } else { fprintf(stderr, "usage: test [test-file-path]\n"); fprintf(stderr, "examples:\n"); diff --git a/tests/sub/a.amal.z b/tests/sub/a.amal.z index e0c566e..e69de29 100644 --- a/tests/sub/a.amal.z +++ b/tests/sub/a.amal.z @@ -1,2 +0,0 @@ -typedef i64 signed long long; -typedef f64 double; |