aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2019-06-07 10:47:47 +0200
committerdec05eba <dec05eba@protonmail.com>2020-07-25 14:36:46 +0200
commit53a331bc8b2fc33bd2b7e25a23b4128f89ee0b52 (patch)
tree2e5c0f4cda85b2afdb6142145fa7ded61d100f02
parent1b68fdcf5aebf2bc53bbd9234c77aea243c0decd (diff)
Add assignment, while, extern, function signature type, start on bytecode
-rw-r--r--include/ast.h67
-rw-r--r--include/binop_type.h3
-rw-r--r--include/ssa/ssa.h28
-rw-r--r--include/std/misc.h6
-rw-r--r--include/tokenizer.h8
-rw-r--r--src/ast.c210
-rw-r--r--src/bytecode/bytecode.c212
-rw-r--r--src/compiler.c3
-rw-r--r--src/parser.c392
-rw-r--r--src/ssa/ssa.c233
-rw-r--r--src/tokenizer.c111
-rw-r--r--tests/b.amal.z6
-rw-r--r--tests/bytecode.amal12
-rw-r--r--tests/bytecode.amal.z11
-rw-r--r--tests/errors/const_assign.amal4
-rw-r--r--tests/errors/const_assign.amal.z6
-rw-r--r--tests/io.amal.z6
-rw-r--r--tests/main.amal22
-rw-r--r--tests/main.amal.z69
-rw-r--r--tests/main.c45
-rw-r--r--tests/sub/a.amal.z2
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, ...);
diff --git a/src/ast.c b/src/ast.c
index 983b3a4..4194737 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -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(&reg, 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(&reg, 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, &reg));
- 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, &reg));
- throw_if_error(ssa_ins_assign_inter(context->ssa, reg, number));
- }
+ throw_if_error(ssa_get_unique_reg(context->ssa, &reg));
+ 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, &reg));
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, &reg));
+ 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;