aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2019-09-21 13:59:39 +0200
committerdec05eba <dec05eba@protonmail.com>2020-07-25 14:36:46 +0200
commitc811a743a1528db1d05970e1aa14162ef7c70b75 (patch)
tree4560ab5d9084c385946415e9fc2dbf187e26c844
parent142a13ad3f77c0ad1bd321d1a1f864a5117896d1 (diff)
Implement vararg, verify arguments to parameters
-rw-r--r--include/ast.h7
-rw-r--r--include/bytecode/bytecode.h8
-rw-r--r--include/compiler.h3
-rw-r--r--include/program.h9
-rw-r--r--include/tokenizer.h4
-rw-r--r--src/ast.c75
-rw-r--r--src/bytecode/bytecode.c40
-rw-r--r--src/compiler.c18
-rw-r--r--src/parser.c87
-rw-r--r--src/program.c26
-rw-r--r--src/tokenizer.c46
-rw-r--r--tests/bytecode.amal30
-rw-r--r--tests/errors/return_borrow.amal1
-rw-r--r--tests/main.c4
14 files changed, 256 insertions, 102 deletions
diff --git a/include/ast.h b/include/ast.h
index 0c34d0b..c45d4aa 100644
--- a/include/ast.h
+++ b/include/ast.h
@@ -132,6 +132,12 @@ struct Scope {
FunctionSignature *function_signature; /* Borrowed from FunctionDecl. Only used if the scope belongs to FunctionDecl */
};
+typedef enum {
+ VARIABLE_TYPE_FLAG_NONE = 0,
+ VARIABLE_TYPE_FLAG_OPTIONAL = 1 << 0,
+ VARIABLE_TYPE_FLAG_BORROW = 1 << 1
+} VariableTypeFlag;
+
typedef struct {
enum {
VARIABLE_TYPE_NONE,
@@ -143,6 +149,7 @@ typedef struct {
Variable *variable;
FunctionSignature *signature;
} value;
+ VariableTypeFlag variable_type_flags;
} VariableType;
struct FileScopeReference {
diff --git a/include/bytecode/bytecode.h b/include/bytecode/bytecode.h
index 8f786a2..23bc4cf 100644
--- a/include/bytecode/bytecode.h
+++ b/include/bytecode/bytecode.h
@@ -77,7 +77,8 @@ typedef enum {
typedef enum {
FUNC_FLAG_NONE = 0,
- FUNC_FLAG_EXPORTED = 1 << 0
+ FUNC_FLAG_EXPORTED = 1 << 0,
+ FUNC_FLAG_VARARGS = 1 << 1
} amal_func_flag;
typedef u8 AmalOpcodeType;
@@ -110,10 +111,13 @@ typedef struct {
#pragma pack(push, 1)
typedef struct {
u8 num_params;
+ u8 num_return_types;
+ u8 name_len;
+ u8 flags;
+
u32 params_num_pointers;
u32 params_fixed_size;
- u8 num_return_types;
u32 return_types_num_pointers;
u32 return_types_fixed_size;
} BytecodeHeaderExternFunction;
diff --git a/include/compiler.h b/include/compiler.h
index f6599a1..164cf4e 100644
--- a/include/compiler.h
+++ b/include/compiler.h
@@ -15,7 +15,7 @@
#define AMAL_COMPILER_ERR -1
#define AMAL_COMPILER_WORK_FAIL_ABORT -2
-#define NUM_ARITHMETIC_TYPES 10
+#define NUM_ARITHMETIC_TYPES 14
typedef struct {
LhsExpr lhs_expr;
@@ -42,6 +42,7 @@ typedef struct {
amal_default_type *c_int;
amal_default_type *c_long;
amal_default_type *c_void;
+ amal_default_type *c_varargs;
amal_default_type *str;
diff --git a/include/program.h b/include/program.h
index aa318d9..dc65cb7 100644
--- a/include/program.h
+++ b/include/program.h
@@ -38,13 +38,11 @@
#define AMAL_PROGRAM_INVALID_IMPORTS -26
#define AMAL_PROGRAM_SECTION_ERROR -27
-#define AMAL_PROGRAM_ARGS_SIZE_VARARGS -1
-
#define AMAL_PROGRAM_MAX_RETURN_VALUES 128
typedef struct {
void *func;
- int args_byte_size; /* -1 if varargs (AMAL_PROGRAM_ARGS_SIZE_VARARGS) */
+ u32 args_byte_size;
} ProgramExternFunc;
typedef struct {
@@ -81,9 +79,10 @@ void amal_program_deinit(amal_program *self);
/*
returns @AMAL_PROGRAM_EXTERN_FUNC_ALREADY_EXISTS if a function with the name @name already exists
- in the program
+ in the program.
+ If the function has a vararg parameter, then @args_byte_size should be the minimum parameters size excluding the varargs.
*/
-CHECK_RESULT int amal_program_register_extern_func(amal_program *self, BufferView name, void *func_ptr, int args_byte_size);
+CHECK_RESULT int amal_program_register_extern_func(amal_program *self, BufferView name, void *func_ptr, u32 args_byte_size);
CHECK_RESULT int amal_program_append_bytecode(amal_program *self, Bytecode *bytecode);
CHECK_RESULT int amal_program_run(amal_program *self);
diff --git a/include/tokenizer.h b/include/tokenizer.h
index b6b401b..53351a4 100644
--- a/include/tokenizer.h
+++ b/include/tokenizer.h
@@ -40,7 +40,9 @@ typedef enum {
TOK_WHILE,
TOK_EXTERN,
TOK_EXPORT,
- TOK_RETURN
+ TOK_RETURN,
+ TOK_QUESTION_MARK,
+ TOK_C_VARARGS
} Token;
struct Tokenizer {
diff --git a/src/ast.c b/src/ast.c
index 0008785..90af981 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -5,6 +5,7 @@
#include "../include/std/hash.h"
#include <assert.h>
#include <stdarg.h>
+#include <stdio.h>
#define throw(result) do { throw_debug_msg; longjmp(context->env, (result)); } while(0)
#define throw_if_error(result) \
@@ -446,6 +447,8 @@ static void compiler_print_error(amal_compiler *compiler, const char *ref, const
tokenizer = amal_compiler_find_tokenizer_by_code_reference(compiler, ref);
if(!tokenizer) {
amal_log_error("Failed to find tokenizer for code reference %p. Is it an invalid reference?", ref ? ref : "(null)");
+ vfprintf(stderr, fmt, args);
+ fputc('\n', stderr);
return;
}
tokenizer_print_error_args(tokenizer, tokenizer_get_code_reference_index(tokenizer, ref), fmt, args);
@@ -726,9 +729,60 @@ static void funcdecl_resolve(Ast *self, AstCompilerContext *context) {
self->resolve_data.type.value.func_sig = self->value.func_decl->signature;
}
+static usize min(usize a, usize b) {
+ return a < b ? a : b;
+}
+
+static bool resolve_data_type_equals(AstResolvedType *self, AstResolvedType *other) {
+ return self->type == other->type && self->value.data == other->value.data;
+}
+
+static bool function_parameter_is_vararg(FunctionParameter *self, AstCompilerContext *context) {
+ amal_default_type *vararg_type = context->compiler->default_types.c_varargs;
+ return self->resolve_data.type.value.data == &vararg_type->lhs_expr;
+}
+
+static void verify_func_call_matches_func_signature(FunctionCall *func_call, FunctionSignature *func_sig, AstCompilerContext *context) {
+ Ast **arg = buffer_begin(&func_call->args);
+ Ast **arg_end = buffer_end(&func_call->args);
+
+ FunctionParameter *func_param = buffer_begin(&func_sig->parameters);
+ FunctionParameter *func_param_end = buffer_end(&func_sig->parameters);
+
+ usize num_args = arg_end - arg;
+ usize num_params = func_param_end - func_param;
+ isize num_missing_args = (isize)num_params - (isize)num_args;
+ usize num_check = min(num_args, num_params);
+ usize i = 0;
+ for(; i < num_check; ++i) {
+ if(!resolve_data_type_equals(&(*arg)->resolve_data.type, &func_param->resolve_data.type) && !function_parameter_is_vararg(func_param, context)) {
+ BufferView arg_name = ast_resolved_type_get_name(&(*arg)->resolve_data.type);
+ BufferView param_name = ast_resolved_type_get_name(&func_param->resolve_data.type);
+ compiler_print_error(context->compiler, func_call->func.name.data,
+ "Can't cast argument of type \"%.*s\" to parameter of type \"%.*s\"", arg_name.size, arg_name.data, param_name.size, param_name.data);
+ throw(AST_ERR);
+ }
+ ++arg;
+ ++func_param;
+ }
+
+ if(num_missing_args > 0) {
+ FunctionParameter *vararg_param = buffer_begin(&func_sig->parameters);
+ bool has_vararg = num_params > 0 && function_parameter_is_vararg(&vararg_param[num_params - 1], context);
+ if (has_vararg)
+ num_missing_args -= 1;
+ if(num_missing_args > 0) {
+ compiler_print_error(context->compiler, func_call->func.name.data,
+ "Missing %d argument(s) to closure \"%.*s\"", num_missing_args, func_call->func.name.size, func_call->func.name.data);
+ throw(AST_ERR);
+ }
+ }
+}
+
static void funccall_resolve(Ast *self, AstCompilerContext *context) {
Ast **ast;
Ast **ast_end;
+ FunctionSignature *func_sig;
FunctionCall *func_call = self->value.func_call;
variable_resolve(&func_call->func, context, &self->resolve_data.type);
@@ -744,7 +798,7 @@ static void funccall_resolve(Ast *self, AstCompilerContext *context) {
}
{
- FunctionSignature *func_sig = self->resolve_data.type.value.func_sig;
+ func_sig = self->resolve_data.type.value.func_sig;
FunctionReturnType *return_type = buffer_begin(&func_sig->return_types);
assert(buffer_get_size(&func_sig->return_types, FunctionReturnType) == 1);
self->resolve_data.type.type = RESOLVED_TYPE_LHS_EXPR;
@@ -756,6 +810,8 @@ static void funccall_resolve(Ast *self, AstCompilerContext *context) {
for(; ast != ast_end; ++ast) {
ast_resolve(*ast, context);
}
+
+ verify_func_call_matches_func_signature(func_call, func_sig, context);
}
static TypeSize variable_type_get_byte_size(VariableType *self) {
@@ -766,9 +822,13 @@ static TypeSize variable_type_get_byte_size(VariableType *self) {
case VARIABLE_TYPE_NONE:
assert(bool_false && "Variable type not resolved!");
break;
- case VARIABLE_TYPE_VARIABLE:
- type_size = resolved_type_get_byte_size(&self->value.variable->resolved_var.resolve_data->type);
+ case VARIABLE_TYPE_VARIABLE: {
+ if(self->variable_type_flags & VARIABLE_TYPE_FLAG_BORROW)
+ type_size.num_pointers = 1;
+ else
+ type_size = resolved_type_get_byte_size(&self->value.variable->resolved_var.resolve_data->type);
break;
+ }
case VARIABLE_TYPE_SIGNATURE:
type_size.num_pointers = 1;
break;
@@ -790,7 +850,8 @@ static void structdecl_resolve(Ast *self, AstCompilerContext *context) {
Ast **ast = buffer_begin(&body->ast_objects);
Ast **ast_end = buffer_end(&body->ast_objects);
for(; ast != ast_end; ++ast) {
- TypeSize type_size = variable_type_get_byte_size(&(*ast)->value.struct_field->type);
+ StructField *struct_field = (*ast)->value.struct_field;
+ TypeSize type_size = variable_type_get_byte_size(&struct_field->type);
struct_decl->fields_num_pointers += type_size.num_pointers;
struct_decl->fields_fixed_size_bytes += type_size.fixed_size;
}
@@ -1020,9 +1081,9 @@ void ast_resolve(Ast *self, AstCompilerContext *context) {
/* TODO: Support other number types */
self->resolve_data.type.type = RESOLVED_TYPE_LHS_EXPR;
if(number->is_integer)
- self->resolve_data.type.value.lhs_expr = (LhsExpr*)context->compiler->default_types.i64;
+ self->resolve_data.type.value.lhs_expr = &context->compiler->default_types.i64->lhs_expr;
else
- self->resolve_data.type.value.lhs_expr = (LhsExpr*)context->compiler->default_types.f64;
+ self->resolve_data.type.value.lhs_expr = &context->compiler->default_types.f64->lhs_expr;
break;
}
case AST_FUNCTION_DECL:
@@ -1050,7 +1111,7 @@ void ast_resolve(Ast *self, AstCompilerContext *context) {
case AST_STRING:
/* TODO: Convert special combinations. For example \n to newline */
self->resolve_data.type.type = RESOLVED_TYPE_LHS_EXPR;
- self->resolve_data.type.value.lhs_expr = (LhsExpr*)context->compiler->default_types.str;
+ self->resolve_data.type.value.lhs_expr = &context->compiler->default_types.str->lhs_expr;
break;
case AST_VARIABLE:
variable_resolve(self->value.variable, context, &self->resolve_data.type);
diff --git a/src/bytecode/bytecode.c b/src/bytecode/bytecode.c
index 7dbc305..a5e3abc 100644
--- a/src/bytecode/bytecode.c
+++ b/src/bytecode/bytecode.c
@@ -142,9 +142,13 @@ static TypeSize function_signature_get_params_size(FunctionSignature *self) {
params_total_size.num_pointers = 0;
params_total_size.fixed_size = 0;
for(; param != param_end; ++param) {
- TypeSize param_size = resolved_type_get_byte_size(&param->resolve_data.type);
- params_total_size.num_pointers += param_size.num_pointers;
- params_total_size.fixed_size += param_size.fixed_size;
+ if(param->type.variable_type_flags & VARIABLE_TYPE_FLAG_BORROW)
+ params_total_size.num_pointers += 1;
+ else {
+ TypeSize param_size = resolved_type_get_byte_size(&param->resolve_data.type);
+ params_total_size.num_pointers += param_size.num_pointers;
+ params_total_size.fixed_size += param_size.fixed_size;
+ }
}
return params_total_size;
}
@@ -228,20 +232,21 @@ static void add_extern_functions(BytecodeCompilerContext *self) {
|Type|Field |Description |
|----|-------------------------|-----------------------------------------------------------------------------------------------------|
|u8 |num_params |The number of parameters. |
+ |u8 |num_return_types |The number of return values. |
+ |u8 |name_len |The length of the external function name, in bytes. Excluding the null-terminate character. |
+ |u8 |flags |The flags for the external function. The values are defined in @amal_func_flag. |
|u32 |params_num_pointers |The number of pointers in the parameters. |
|u32 |params_fixed_size |The size of all non-pointer type parameters, in bytes. |
- |u8 |num_return_types |The number of return values. |
|u32 |return_types_num_pointers|The number of pointers in the return types. |
|u32 |return_types_fixed_size |The size of all non-pointer type return types, in bytes. |
- |u8 |name_len |The length of the external function name, in bytes. Excluding the null-terminate character. |
|u8[]|name |The name of the external function, where the size is defined by @name_len. Names are null-terminated.|
*/
Ssa *ssa = self->parser->ssa;
Buffer *instructions = &self->bytecode->data;
SsaExternFunc *extern_func = buffer_begin(&ssa->extern_funcs);
SsaExternFunc *extern_func_end = buffer_end(&ssa->extern_funcs);
- u32 extern_funcs_size = (u32)ssa->extern_func_counter * (sizeof(BytecodeHeaderExternFunction) + sizeof(u8));
- assert(sizeof(BytecodeHeaderExternFunction) == 18);
+ u32 extern_funcs_size = (u32)ssa->extern_func_counter * sizeof(BytecodeHeaderExternFunction);
+ assert(sizeof(BytecodeHeaderExternFunction) == 20);
for(; extern_func != extern_func_end; ++extern_func) {
extern_funcs_size += extern_func->name.size + 1; /* +1 for null-termination of string */
@@ -259,17 +264,26 @@ static void add_extern_functions(BytecodeCompilerContext *self) {
BytecodeHeaderExternFunction header_func;
header_func.num_params = buffer_get_size(&extern_func->func_sig->parameters, FunctionParameter);
+ header_func.num_return_types = buffer_get_size(&extern_func->func_sig->return_types, FunctionReturnType);
+ header_func.name_len = extern_func->name.size;
+ header_func.flags = 0;
+ if(header_func.num_params > 0) {
+ amal_default_type *vararg_type = self->parser->compiler->default_types.c_varargs;
+ FunctionParameter *last_param = buffer_begin(&extern_func->func_sig->parameters);
+ last_param += (header_func.num_params - 1);
+ if(last_param->resolve_data.type.value.data == &vararg_type->lhs_expr)
+ header_func.flags |= FUNC_FLAG_VARARGS;
+ }
+
header_func.params_num_pointers = params_total_size.num_pointers;
header_func.params_fixed_size = params_total_size.fixed_size;
- header_func.num_return_types = buffer_get_size(&extern_func->func_sig->return_types, FunctionReturnType);
header_func.return_types_num_pointers = return_types_total_size.num_pointers;
header_func.return_types_fixed_size = return_types_total_size.fixed_size;
throw_if_error(buffer_append(instructions, &header_func, sizeof(header_func)));
/* TODO: Add namespace to the function name */
/* u8 is fine, because the max length of a variable is 255 */
- throw_if_error(buffer_append(instructions, &extern_func->name.size, sizeof(u8)));
throw_if_error(buffer_append(instructions, extern_func->name.data, extern_func->name.size));
throw_if_error(buffer_append(instructions, &null_s, sizeof(char)));
}
@@ -435,10 +449,10 @@ static void add_ins6(BytecodeCompilerContext *self, AmalOpcode opcode, i8 dst_re
static void add_instructions(BytecodeCompilerContext *self) {
/*doc(Bytecode instructions)
# Instructions layout
- |Type |Field |Description |
- |-----------|-----------------|---------------------------------------------------------------------------|
- |u32 |Instructions size|The size of the instructions section, in bytes. |
- |Instruction|Instructions data|The instructions data. Each instructions begins with an opcode, see #Opcode|
+ |Type |Field |Description |
+ |-------------|-----------------|---------------------------------------------------------------------------|
+ |u32 |Instructions size|The size of the instructions section, in bytes. |
+ |Instruction[]|Instructions data|The instructions data. Each instructions begins with an opcode, see #Opcode|
*/
SsaInsForm1 ssa_ins_form1;
diff --git a/src/compiler.c b/src/compiler.c
index 4d43329..5b48aaf 100644
--- a/src/compiler.c
+++ b/src/compiler.c
@@ -82,6 +82,7 @@ static CHECK_RESULT int init_default_types(amal_compiler *compiler) {
return_if_error(create_default_type_fixed_size(compiler, "c_int", sizeof(int), &compiler->default_types.c_int));
return_if_error(create_default_type_fixed_size(compiler, "c_long", sizeof(long), &compiler->default_types.c_long));
return_if_error(create_default_type_num_pointers(compiler, "c_void", 0, &compiler->default_types.c_void));
+ return_if_error(create_default_type_num_pointers(compiler, "...", 0, &compiler->default_types.c_varargs));
compiler->default_types.arithmetic_types[0] = compiler->default_types.i8;
compiler->default_types.arithmetic_types[1] = compiler->default_types.u8;
@@ -94,6 +95,11 @@ static CHECK_RESULT int init_default_types(amal_compiler *compiler) {
compiler->default_types.arithmetic_types[8] = compiler->default_types.isize;
compiler->default_types.arithmetic_types[9] = compiler->default_types.usize;
+ compiler->default_types.arithmetic_types[10] = compiler->default_types.c_char;
+ compiler->default_types.arithmetic_types[11] = compiler->default_types.c_short;
+ compiler->default_types.arithmetic_types[12] = compiler->default_types.c_int;
+ compiler->default_types.arithmetic_types[13] = compiler->default_types.c_long;
+
compiler->default_types.i8->is_signed = bool_true;
compiler->default_types.u8->is_signed = bool_false;
compiler->default_types.i16->is_signed = bool_true;
@@ -106,7 +112,15 @@ static CHECK_RESULT int init_default_types(amal_compiler *compiler) {
compiler->default_types.usize->is_signed = bool_false;
compiler->default_types.f32->is_signed = bool_true;
compiler->default_types.f64->is_signed = bool_true;
+ compiler->default_types.bool->is_signed = bool_false;
compiler->default_types.str->is_signed = bool_false;
+
+ compiler->default_types.c_char->is_signed = bool_true;
+ compiler->default_types.c_short->is_signed = bool_true;
+ compiler->default_types.c_int->is_signed = bool_true;
+ compiler->default_types.c_long->is_signed = bool_true;
+ compiler->default_types.c_void->is_signed = bool_false;
+ compiler->default_types.c_varargs->is_signed = bool_false;
return 0;
}
@@ -199,8 +213,10 @@ static CHECK_RESULT int amal_compiler_load_in_this_thread(amal_compiler *compile
parser_allocator = NULL;
result = AMAL_COMPILER_ERR;
- return_if_error(arena_allocator_alloc(&compiler->allocator, sizeof(ArenaAllocator), (void**)&parser_allocator));
+ cleanup_if_error(amal_mutex_lock(&compiler->mutex, "amal_compiler_load_in_this_thread, create allocator for parser"));
+ cleanup_if_error(arena_allocator_alloc(&compiler->allocator, sizeof(ArenaAllocator), (void**)&parser_allocator));
cleanup_if_error(arena_allocator_init(parser_allocator));
+ amal_mutex_tryunlock(&compiler->mutex);
filepath = create_buffer_view(file_scope->canonical_path.data, file_scope->canonical_path.size);
amal_log_info("Started parsing %.*s", filepath.size, filepath.data);
diff --git a/src/parser.c b/src/parser.c
index 9da9147..dc39a6a 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -119,31 +119,45 @@ static void parser_parse_struct_body_loop(Parser *self, StructDecl *struct_decl)
}
/*
-FUNC_PARAM = TOK_IDENTIFIER VAR_TYPE_DEF
+FUNC_PARAM = (TOK_IDENTIFIER VAR_TYPE_DEF)|'...'
*/
static CHECK_RESULT bool parser_parse_function_parameter(Parser *self, FunctionParameter *result) {
bool match;
throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IDENTIFIER, &match));
- if(!match)
- return bool_false;
+ if(match) {
+ result->name = self->tokenizer.value.identifier;
+ parser_parse_var_type_def(self, &result->type);
+ if(result->type.type == VARIABLE_TYPE_NONE) {
+ self->error = tokenizer_create_error(&self->tokenizer,
+ tokenizer_get_error_index(&self->tokenizer),
+ "Expected ':' after parameter name");
+ throw(PARSER_UNEXPECTED_TOKEN);
+ }
+ return bool_true;
+ }
- result->name = self->tokenizer.value.identifier;
- parser_parse_var_type_def(self, &result->type);
- if(result->type.type == VARIABLE_TYPE_NONE) {
- self->error = tokenizer_create_error(&self->tokenizer,
- tokenizer_get_error_index(&self->tokenizer),
- "Expected ':' after parameter name");
- throw(PARSER_UNEXPECTED_TOKEN);
+ throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_C_VARARGS, &match));
+ if(match) {
+ result->name = self->tokenizer.value.identifier;
+ result->type.variable_type_flags = VARIABLE_TYPE_FLAG_NONE;
+ result->type.type = VARIABLE_TYPE_VARIABLE;
+ throw_if_error(arena_allocator_alloc(self->allocator, sizeof(Variable), (void**)&result->type.value.variable));
+ variable_init(result->type.value.variable, result->name);
+ return bool_true;
}
- return bool_true;
+ return bool_false;
}
/*
FUNC_PARAMS = FUNC_PARAM (',' FUNC_PARAM)*
*/
static void parser_parse_function_parameters(Parser *self, FunctionSignature *func_sig) {
+ amal_default_type *vararg_type = self->compiler->default_types.c_varargs;
+ int vararg_index = -1;
+ int index = 0;
+
for(;;) {
FunctionParameter func_param;
bool match;
@@ -157,6 +171,13 @@ static void parser_parse_function_parameters(Parser *self, FunctionSignature *fu
throw(PARSER_UNEXPECTED_TOKEN);
}
+ if(vararg_index != -1) {
+ self->error = tokenizer_create_error(&self->tokenizer,
+ tokenizer_get_error_index(&self->tokenizer),
+ "A closure can't have parameters after a vararg");
+ throw(PARSER_UNEXPECTED_TOKEN);
+ }
+
result = function_signature_add_parameter(func_sig, &func_param);
if(result == AST_ERR_DEF_DUP) {
self->error = tokenizer_create_error(&self->tokenizer,
@@ -169,7 +190,19 @@ static void parser_parse_function_parameters(Parser *self, FunctionSignature *fu
}
throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_COMMA, &match));
if(!match)
- return;
+ break;
+
+ if (vararg_index == -1 && buffer_view_equals(&func_param.name, &vararg_type->lhs_expr.var_name))
+ vararg_index = index;
+
+ ++index;
+ }
+
+ if (index > FUNC_MAX_PARAMS) {
+ self->error = tokenizer_create_error(&self->tokenizer,
+ tokenizer_get_error_index(&self->tokenizer),
+ "A closure can't have more than %d parameters", FUNC_MAX_PARAMS);
+ throw(PARSER_ERR);
}
}
@@ -192,19 +225,19 @@ static void parser_parse_function_return_types(Parser *self, FunctionSignature *
tokenizer_get_error_index(&self->tokenizer),
"Expected type or closure signature");
throw(PARSER_UNEXPECTED_TOKEN);
+ } else if(var_type.variable_type_flags & VARIABLE_TYPE_FLAG_BORROW) {
+ self->error = tokenizer_create_error(&self->tokenizer,
+ tokenizer_get_error_index(&self->tokenizer),
+ "A closure can't return a value as borrowed");
+ throw(PARSER_ERR);
}
+
throw_if_error(function_signature_add_return_type(func_sig, &var_type));
throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_COMMA, &match));
if(!match)
break;
- ++return_type_index;
- }
- if (buffer_get_size(&func_sig->parameters, FunctionParameter) > FUNC_MAX_PARAMS) {
- self->error = tokenizer_create_error(&self->tokenizer,
- tokenizer_get_error_index(&self->tokenizer),
- "A closure can't have more than %d parameters", FUNC_MAX_PARAMS);
- throw(PARSER_ERR);
+ ++return_type_index;
}
if (buffer_get_size(&func_sig->return_types, FunctionReturnType) > FUNC_MAX_RETURN_TYPES) {
@@ -244,13 +277,27 @@ static CHECK_RESULT FunctionSignature* parser_parse_function_signature(Parser *s
}
/*
-VAR_TYPE = TOK_IDENTIFIER|FUNC_SIGNATURE
+VAR_TYPE = '?'? TOK_IDENTIFIER|FUNC_SIGNATURE
*/
void parser_parse_var_type(Parser *self, VariableType *result) {
bool match;
result->type = VARIABLE_TYPE_NONE;
result->value.variable = NULL;
+ result->variable_type_flags = VARIABLE_TYPE_FLAG_NONE;
+
+ throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_QUESTION_MARK, &match));
+ if(match)
+ result->variable_type_flags |= VARIABLE_TYPE_FLAG_OPTIONAL;
+
+ throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_BINOP, &match));
+ if(match) {
+ if(self->tokenizer.value.binop_type != BINOP_MUL) {
+ self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), "Expected '*', type or closure signature");
+ throw(PARSER_UNEXPECTED_TOKEN);
+ }
+ result->variable_type_flags |= VARIABLE_TYPE_FLAG_BORROW;
+ }
throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IDENTIFIER, &match));
if(match) {
diff --git a/src/program.c b/src/program.c
index 128f0f9..4c0f1a2 100644
--- a/src/program.c
+++ b/src/program.c
@@ -78,7 +78,7 @@ void amal_program_deinit(amal_program *self) {
buffer_deinit(&self->data);
}
-int amal_program_register_extern_func(amal_program *self, BufferView name, void *func_ptr, int args_byte_size) {
+int amal_program_register_extern_func(amal_program *self, BufferView name, void *func_ptr, u32 args_byte_size) {
ProgramExternFunc extern_func;
extern_func.func = func_ptr;
extern_func.args_byte_size = args_byte_size;
@@ -112,15 +112,16 @@ static void amal_program_get_header_extern_function_by_index(amal_program *self,
u32 i;
u8 *extern_funcs_start = amal_program_get_extern_funcs_start_by_import_index(self, import_index);
extern_funcs_start += sizeof(u16) + sizeof(u32);
+
for(i = 0; i < (u32)index; ++i) {
- u8 name_len;
+ BytecodeHeaderExternFunction *extern_func = (BytecodeHeaderExternFunction*)extern_funcs_start;
extern_funcs_start += sizeof(BytecodeHeaderExternFunction);
- name_len = *extern_funcs_start;
- extern_funcs_start += 1 + name_len + 1; /* +1 for skipping length byte and the null-terminated character */
+ extern_funcs_start += extern_func->name_len + 1; /* +1 for skipping the null-terminated character */
}
+
am_memcpy(&result->extern_func, extern_funcs_start, sizeof(result->extern_func));
- result->name.size = *(extern_funcs_start + sizeof(result->extern_func));
- result->name.data = (const char*)extern_funcs_start + sizeof(result->extern_func) + sizeof(u8);
+ result->name.size = result->extern_func.name_len;
+ result->name.data = (const char*)extern_funcs_start + sizeof(result->extern_func);
}
@@ -133,10 +134,15 @@ static CHECK_RESULT int amal_program_get_extern_func_by_index(amal_program *self
return AMAL_PROGRAM_NO_SUCH_EXTERNAL_FUNCTION;
}
- /* TODO: This assumes all arguments are of size sizeof(isize) */
- if(result->args_byte_size != -1 && result->args_byte_size != (int)extern_func_get_total_param_size(&extern_func.extern_func)) {
- amal_log_error("Extern function %.*s was registered to take %d byte(s), but the program says it takes %d byte(s)",
- extern_func.name.size, extern_func.name.data, result->args_byte_size, (int)extern_func_get_total_param_size(&extern_func.extern_func));
+ if(extern_func.extern_func.flags & FUNC_FLAG_VARARGS) {
+ if(extern_func_get_total_param_size(&extern_func.extern_func) < result->args_byte_size) {
+ amal_log_error("Extern function %.*s was registered to take at least %u byte(s), but the program says it takes at least %u byte(s)",
+ extern_func.name.size, extern_func.name.data, result->args_byte_size, extern_func_get_total_param_size(&extern_func.extern_func));
+ return AMAL_PROGRAM_NO_SUCH_EXTERNAL_FUNCTION;
+ }
+ } else if(extern_func_get_total_param_size(&extern_func.extern_func) != result->args_byte_size) {
+ amal_log_error("Extern function %.*s was registered to take %u byte(s), but the program says it takes %u byte(s)",
+ extern_func.name.size, extern_func.name.data, result->args_byte_size, extern_func_get_total_param_size(&extern_func.extern_func));
return AMAL_PROGRAM_NO_SUCH_EXTERNAL_FUNCTION;
}
return 0;
diff --git a/src/tokenizer.c b/src/tokenizer.c
index 9ce1bba..d753b20 100644
--- a/src/tokenizer.c
+++ b/src/tokenizer.c
@@ -235,19 +235,6 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) {
}
}
*token = TOK_IDENTIFIER;
- } else if(c == '"') {
- int string_end;
- ++self->index;
- string_end = find_end_of_string(self->code, self->index);
- if(string_end == -1) {
- tokenizer_print_error(self, self->prev_index, "String end not found. Did you forget '\"' or did you have a mismatch of number of '\"'?");
- return TOKENIZER_ERR;
- }
-
- self->value.string.data = &self->code.data[self->index];
- self->value.string.size = string_end - self->index;
- self->index = string_end + 1;
- *token = TOK_STRING;
} else if(isDigit(c)) {
int number_start;
int dot_index;
@@ -290,9 +277,31 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) {
self->number_is_integer = bool_false;
}
*token = TOK_NUMBER;
+ } else if(c == '"') {
+ int string_end;
+ ++self->index;
+ string_end = find_end_of_string(self->code, self->index);
+ if(string_end == -1) {
+ tokenizer_print_error(self, self->prev_index, "String end not found. Did you forget '\"' or did you have a mismatch of number of '\"'?");
+ return TOKENIZER_ERR;
+ }
+
+ self->value.string.data = &self->code.data[self->index];
+ self->value.string.size = string_end - self->index;
+ self->index = string_end + 1;
+ *token = TOK_STRING;
} else if(c == '.') {
+ const char *start = self->code.data + self->index;
++self->index;
- SET_BINOP(BINOP_DOT);
+ /* ... */
+ if((usize)self->index + 2 < self->code.size && am_memcmp(self->code.data + self->index, "..", 2) == 0) {
+ self->index += 2;
+ self->value.identifier.data = start;
+ self->value.identifier.size = 3;
+ *token = TOK_C_VARARGS;
+ } else {
+ SET_BINOP(BINOP_DOT);
+ }
} else if(c == '+') {
++self->index;
SET_BINOP(BINOP_ADD);
@@ -351,6 +360,9 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) {
} else if(c == ':') {
++self->index;
*token = TOK_COLON;
+ } else if(c == '?') {
+ ++self->index;
+ *token = TOK_QUESTION_MARK;
} else if(c == '@') {
const char *err_msg;
++self->index;
@@ -502,6 +514,12 @@ static BufferView tokenizer_expected_token_as_string(Token token) {
case TOK_RETURN:
str = "return";
break;
+ case TOK_QUESTION_MARK:
+ str = "?";
+ break;
+ case TOK_C_VARARGS:
+ str = "...";
+ break;
}
return create_buffer_view(str, strlen(str));
}
diff --git a/tests/bytecode.amal b/tests/bytecode.amal
index 6941bc8..d27a240 100644
--- a/tests/bytecode.amal
+++ b/tests/bytecode.amal
@@ -1,31 +1,5 @@
-/*
-extern const print_extern: fn() i32;
-extern const print_extern_num: fn(num: i32) i32;
+extern const printf: fn(fmt: ?*c_char, ...) c_int;
-const print = fn() i32 {
- return 0;
-}
-
-const main = fn {
- var value = 23;
- value = 34;
- const value2: i64 = 54;
- const value3 = 2 + 5 - 1 * 10 / 2;
- const str_value = "hello, world";
- print();
- if value2 == 54
- const result = print_num(1337);
-}
-
-const print_num = fn(num: i32) i32 {
- print_extern();
- print_extern_num(num);
- print_extern_num(8080);
- return num;
-}
-*/
-
-const io = @import("../std/io.amal");
const main = fn {
- io.print();
+ printf("hello, world!\n");
}
diff --git a/tests/errors/return_borrow.amal b/tests/errors/return_borrow.amal
new file mode 100644
index 0000000..ed1a043
--- /dev/null
+++ b/tests/errors/return_borrow.amal
@@ -0,0 +1 @@
+const func = fn() *str {} \ No newline at end of file
diff --git a/tests/main.c b/tests/main.c
index 019f8d9..0b3a744 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -251,6 +251,10 @@ static void test_load(const char *filepath) {
fprintf(stderr, "Unexpected error (alloc failure)\n");
FAIL_TEST_CLEANUP(full_path);
}
+ if(amal_program_register_extern_func(&program, create_buffer_view_auto("printf"), printf, sizeof(const char*)) != 0) {
+ fprintf(stderr, "Unexpected error (alloc failure)\n");
+ FAIL_TEST_CLEANUP(full_path);
+ }
result = amal_compiler_load_file(&options, &program, filepath);
if(result != AMAL_COMPILER_OK) {