From c811a743a1528db1d05970e1aa14162ef7c70b75 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 21 Sep 2019 13:59:39 +0200 Subject: Implement vararg, verify arguments to parameters --- include/ast.h | 7 ++++ include/bytecode/bytecode.h | 8 +++- include/compiler.h | 3 +- include/program.h | 9 ++--- include/tokenizer.h | 4 +- src/ast.c | 75 +++++++++++++++++++++++++++++++---- src/bytecode/bytecode.c | 40 +++++++++++++------ src/compiler.c | 18 ++++++++- src/parser.c | 87 +++++++++++++++++++++++++++++++---------- src/program.c | 26 +++++++----- src/tokenizer.c | 46 +++++++++++++++------- tests/bytecode.amal | 30 +------------- tests/errors/return_borrow.amal | 1 + tests/main.c | 4 ++ 14 files changed, 256 insertions(+), 102 deletions(-) create mode 100644 tests/errors/return_borrow.amal 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 #include +#include #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(¶m->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(¶m->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) { -- cgit v1.2.3