diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d0251cc3..0172b843d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,6 @@ add_executable(c3c src/compiler/source_file.c src/compiler/diagnostics.c src/compiler/ast.c - src/compiler/bigint.c src/compiler/bigint.h src/compiler/context.c src/compiler/codegen.c src/compiler/expr_analysis.c src/compiler/enums.h src/compiler/casts.c src/compiler/compiler.h src/compiler/types.c) + src/compiler/bigint.c src/compiler/bigint.h src/compiler/context.c src/compiler/codegen.c src/compiler/expr_analysis.c src/compiler/enums.h src/compiler/casts.c src/compiler/compiler.h src/compiler/types.c src/compiler/module.c) target_compile_options(c3c PRIVATE -Werror -Wall -Wextra -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter) diff --git a/resources/testfragments/compilertest2.c3 b/resources/testfragments/compilertest2.c3 new file mode 100644 index 000000000..6a7c6951c --- /dev/null +++ b/resources/testfragments/compilertest2.c3 @@ -0,0 +1,272 @@ +module bigint; + +macro @max(a, b) +{ + return a > b ? a : b; +} + +// Horribly bad implementation of BigInt with add/sub. +public struct BigInt @opaque +{ + byte& number; + uint length; + char sign; +} + +public func void BigInt.init(BigInt& bigInt) +{ + bigInt.number = malloc(1); + bigInt.number[0] = 0; + bigInt.length = 1; + bigInt.sign = 1; +} + +public func void BigInt.initFromString(BigInt& bigInt, char& str) +{ + uint size = strlen(str); + bigInt.sign = 1; + switch (str[0]) + { + case '-': + bigInt.sign = -1; + size--; + str++; + case '+': + size--; + str++; + default: + break; + } + char* res = malloc(size); + for (uint i = 0; i < size; i++) + { + res[i] = str[size - i - 1] - '0'; + } + bigInt.number = res; + bigInt.length = size; +} + +public func BigInt& BigInt.newFromString(char& str) +{ + BigInt& bigInt = malloc(sizeof(BigInt)); + bigInt.initFromString(str); + return bigInt; +} + +public func void BigInt.copyTo(BigInt& source, BigInt& target) +{ + target.number = realloc(target.number, source.length); + target.sign = source.sign; + target.length = source.length; + for (uint i = 0; i < target.length; i++) target.number[i] = source.number[i]; +} + +public func void BigInt.destroy(BigInt& bigInt) +{ + free(bigInt.number); +} + +func void BigInt.addIgnoreSign(BigInt& a, BigInt& b, BigInt& result) +{ + uint length = @max(a.length, b.length) + 1; + byte* res = malloc(length); + char carry = 0; + BigInt* x; + BigInt* y; + if (a.length > b.length) + { + x = a; + y = b; + } + else + { + x = b; + y = a; + } + for (uint i = 0; i < length; i++) + { + if (i >= y.length) + { + res[i] = carry + (i >= x.length ? 0 : x.number[i]); + } + else + { + res[i] = x.number[i] + y.number[i] + carry; + } + carry = 0; + if (res[i] > 9) + { + carry = 1; + res[i] -= 10; + } + } + result.destroy(); + result.number = res; + result.length = length; +} + +public func void BigInt.getMaxVal(BigInt &bigInt, uint& pos, int& val) +{ + for (uint i = bigInt.length; i > 0; i++) + { + if (bigInt.number[i] != 0) + { + *pos = i; + *val = bigInt.number[i]; + return; + } + } + *pos = 0; + *val = 0; +} + +public func char BigInt.compare(BigInt& a, BigInt& b) +{ + if (a.sign != b.sign) return a.sign; + byte aMax; + uint aMaxPos; + a.getMaxVal(&aMaxPos, &aMax); + if (aMaxPos >= b.length) return a.sign; + byte bMax; + uint bMaxPos; + b.getMaxVal(&bMaxPos, &bMax); + if (aMaxPos > bMaxPos) return a.sign; + if (aMaxPos < bMaxPos) return -a.sign; + if (aMax > bMax) return a.sign; + if (aMax < bMax) return -a.sign; + return 0; +} + +public func char BigInt.compareNoSign(BigInt& a, BigInt& b) +{ + byte aMax; + uint aMaxPos; + a.getMaxVal(&aMaxPos, &aMax); + if (aMaxPos >= b.length) return 1; + byte bMax; + uint bMaxPos; + b.getMaxVal(&bMaxPos, &bMax); + if (aMaxPos > bMaxPos) return 1; + if (aMaxPos < bMaxPos) return -1; + if (aMax > bMax) return 1; + if (aMax < bMax) return -1; + return 0; +} + +func void BigInt.subIgnoreSign(BigInt& a, BigInt& b, BigInt& result) +{ + uint length = @max(a.length, b.length); + byte& res = malloc(length); + + BigInt& x; + BigInt& y; + if (a.compareNoSign(b) < 0) + { + result.sign = -1; + x = b; + y = a; + } + else + { + result.sign = 1; + x = a; + y = b; + } + + byte borrow = 0; + for (uint i = 0; i < length; i++) + { + byte aValue = i >= x.length ? 0 : x.number[i]; + byte bValue = borrow + (i >= y.length ? 0 : y.number[i]); + if (bValue > aValue) + { + borrow = 1; + aValue += 10; + } + else + { + borrow = 0; + } + res[i] = aValue - bValue; + } + result.destroy(); + result.number = res; + result.length = length; +} + +public func void BigInt.add(BigInt& a, BigInt& b, BigInt& result) +{ + if (a.sign == b.sign) + { + a.addIgnoreSign(b, result); + result.sign = a.sign; + return; + } + if (a.sign < 0) + { + b.subIgnoreSign(a, result); + } + else + { + a.subIgnoreSign(a, result); + } +} + +public func char* BigInt.toCharArray(BigInt* bigInt) +{ + uint charLen = bigInt.length + 1 + (bigInt.sign < 0 ? 1 : 0); + byte* out = malloc(charLen); + out[charLen - 1] = '\0'; + byte* start = out; + if (bigInt.sign < 0) + { + out[0] = '-'; + start++; + } + bool nonZeroFound = false; + for (uint i = bigInt.length; i > 0; i--) + { + byte digit = bigInt.number[i - 1]; + if (i > 1 && !nonZeroFound && digit == 0) continue; + nonZeroFound = true; + *(start++) = digit + '0'; + } + return out; +} + +public func void BigInt.print(BigInt& bigInt) +{ + char* chars = bigInt.toCharArray(); + puts(chars); + free(chars); +} + +/* +public func void BigInt.fprint(BigInt* bigInt, FILE* file) +{ + char* chars = bigInt.toCharArray(); + fputs(chars, file); + putc('\n', file); + free(chars); +}*/ + + +public func int main(int size, char*& args) +{ + BigInt minus2; + BigInt minus1; + BigInt current; + minus2.initFromString("0"); + minus1.initFromString("1"); + current.initFromString("0"); + + for (int i = 1; i <= 500; i++) + { + minus1.add(&minus2, ¤t); + printf("%d : ", i); + current.print(); + minus1.copyTo(&minus2); + current.copyTo(&minus1); + } + return 0; +} diff --git a/resources/testfragments/compiletest.c3 b/resources/testfragments/compiletest.c3 index 65edbec5d..a7d08acc0 100644 --- a/resources/testfragments/compiletest.c3 +++ b/resources/testfragments/compiletest.c3 @@ -5,6 +5,21 @@ int bob = 'HELO'; typedef Foo* as Bob; +func void testdefer(int x) +{ + defer printf("A"); + defer + { + int x = 2; + printf("B"); + x = x + 1; + } + //defer catch printf("B") + //defer catch (error e) printf("%s", @name(e)); + //if (x = 1) return throw Error.FOO; + printf("!"); +} + struct Foo { int i; diff --git a/resources/testfragments/simple_test1.c3 b/resources/testfragments/simple_test1.c3 new file mode 100644 index 000000000..88d40698c --- /dev/null +++ b/resources/testfragments/simple_test1.c3 @@ -0,0 +1,15 @@ +module foo; + +public func void gonk() +{ + printf("Bob\n"); +} +func void test() +{ + int i = 0; +} + +func void main() +{ + printf("Helo\n"); +} \ No newline at end of file diff --git a/resources/testfragments/simple_test2.c3 b/resources/testfragments/simple_test2.c3 new file mode 100644 index 000000000..2c7817f2a --- /dev/null +++ b/resources/testfragments/simple_test2.c3 @@ -0,0 +1,13 @@ +module bar; +import foo local; + +func void test() +{ + int i = 0; +} + +func void main() +{ + gronk(); + printf("Helo\n"); +} \ No newline at end of file diff --git a/src/compiler/codegen.c b/src/compiler/codegen.c index 92a808db9..cee405cd9 100644 --- a/src/compiler/codegen.c +++ b/src/compiler/codegen.c @@ -698,7 +698,7 @@ static void codegen_ast(Context *context, Ast *ast, int indent) codegen_declare_stmt(context, ast, indent); return; case AST_DEFER_STMT: - break; + return; case AST_DO_STMT: codegen_emit_do_stmt(context, ast, indent); return; diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 6a3560685..9ae49ed81 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -5,8 +5,12 @@ #include "compiler_internal.h" #include "../build/build_options.h" +Compiler compiler; + void compiler_init(void) { + stable_init(&compiler.modules, 64); + compiler.module_list = NULL; } static void compiler_lex() @@ -38,8 +42,9 @@ void compiler_parse() File *file = source_file_load(build_options.files[i], &loaded); if (loaded) continue; diag_reset(); - parse_file(file); - context_print_ast(current_context, stdout); + Context *context = context_create(file); + parse_file(context); + context_print_ast(context, stdout); } exit(EXIT_SUCCESS); } @@ -47,22 +52,32 @@ void compiler_parse() void compiler_compile() { builtin_setup(); + Context **contexts = NULL; VECEACH(build_options.files, i) { bool loaded = false; File *file = source_file_load(build_options.files[i], &loaded); if (loaded) continue; diag_reset(); - parse_file(file); - sema_analysis(current_context); + Context *context = context_create(file); + vec_add(contexts, context); + parse_file(context); + } + VECEACH(contexts, i) + { + Context *context = contexts[i]; + sema_analysis(context); if (diagnostics.errors > 0) exit(EXIT_FAILURE); - FILE *f = fopen("test.c","w"); + char buffer[255]; + sprintf(buffer, "%s_test.c", context->module_name.string); + printf("%s\n", buffer); + FILE *f = fopen(buffer,"w"); fprintf(f, "#include \n#include \n"); - current_context->codegen_output = f; - codegen(current_context); + context->codegen_output = f; + codegen(context); fclose(f); - system("cc test.c && ./a.out"); - + sprintf(buffer, "cc %s_test.c && ./a.out", context->module_name.string); + system(buffer); } exit(EXIT_SUCCESS); } @@ -86,3 +101,34 @@ void compile_file() } +Decl *compiler_find_symbol(Token token) +{ + Decl *candidate = NULL; + VECEACH(compiler.module_list, i) + { + Module *module = compiler.module_list[i]; + Decl *decl = module_find_symbol(module, token.string); + if (decl && candidate) + { + const char *previous = candidate->module->name; + const char *current = decl->module->name; + SEMA_ERROR(token, "Ambiguous use of '%s', matches both %s::%s and %s::%s.", token.string, + previous, token.string, current, token.string); + return &poisoned_decl; + } + candidate = decl; + } + return candidate; +} + +Module *compiler_find_or_create_module(const char *module_name) +{ + Module *module = stable_get(&compiler.modules, module_name); + if (module) return module; + module = CALLOCS(Module); + module->name = module_name; + stable_init(&module->symbols, 0x10000); + stable_set(&compiler.modules, module_name, module); + vec_add(compiler.module_list, module); + return module; +} diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index ddae5ac4e..3b53bfc7d 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -74,7 +74,7 @@ typedef struct } File; -typedef struct _Path +typedef struct { Token package; Token module; @@ -133,7 +133,7 @@ typedef struct ImportType type : 3; Token alias; Expr** generic_parameters; - struct _Module *module; + Module *module; } ImportDecl; typedef struct @@ -552,6 +552,11 @@ typedef struct Ast *body; } AstCtForStmt; +typedef struct +{ + Ast **defers; +} AstContinueStmt; + typedef struct _Ast { AstKind ast_kind : 8; @@ -577,6 +582,7 @@ typedef struct _Ast AstCaseStmt case_stmt; AstCtSwitchStmt ct_switch_stmt; AstCtCaseStmt ct_case_stmt; + AstContinueStmt continue_stmt; Ast* ct_default_stmt; Ast* next_stmt; AstCatchStmt catch_stmt; @@ -594,9 +600,14 @@ typedef struct _Ast } Ast; +typedef struct _Package +{ + const char *name; +} Package; typedef struct _Module { + Package *package; const char *name; bool is_external; @@ -618,8 +629,7 @@ typedef struct _DynamicScope ScopeFlags flags_created; unsigned errors; Decl **local_decl_start; - Ast *defer_stack_start; - Ast *active_defer; + unsigned defer_start; ExitType exit; } DynamicScope; @@ -638,6 +648,7 @@ typedef struct _Context Decl **functions; Decl **vars; Decl **ct_ifs; + Ast **defers; Decl *active_function_for_analysis; Type *left_type_in_assignment; FILE *codegen_output; @@ -653,8 +664,14 @@ typedef struct _Context DynamicScope scopes[MAX_SCOPE_DEPTH]; } Context; -extern Context *current_context; +typedef struct +{ + STable modules; + Module **module_list; +} Compiler; +extern Context *current_context; +extern Compiler compiler; extern Ast poisoned_ast; extern Decl poisoned_decl; extern Expr poisoned_expr; @@ -753,6 +770,9 @@ void codegen(Context *context); bool sema_analyse_expr(Context *context, Expr *expr); +Decl *compiler_find_symbol(Token token); +Module *compiler_find_or_create_module(const char *module_name); + Context *context_create(File *file); void context_push(Context *context); void context_register_global_decl(Context *context, Decl *decl); @@ -827,7 +847,7 @@ static inline void advance_and_verify(TokenType token_type) Decl *module_find_symbol(Module *module, const char *symbol); -void parse_file(File *file); +void parse_file(Context *context); #define SEMA_ERROR(_tok, ...) sema_error_range(_tok.span, __VA_ARGS__) void sema_init(File *file); diff --git a/src/compiler/context.c b/src/compiler/context.c index c7af265e5..ca1aa2b30 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -55,11 +55,8 @@ static inline bool create_module_or_check_name(Context *context, Token module_na context->module_name = module_name; if (context->module == NULL) { - context->module = malloc_arena(sizeof(Module)); - memset(context->module, 0, sizeof(Module)); - context->module->name = module_name.string; - stable_init(&(context->module)->symbols, 0x10000); - return true; + context->module = compiler_find_or_create_module(module_name.string); + return true; } else if (context->module->name != module_name.string) { diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 5bda5ca7f..ecb127033 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -254,6 +254,7 @@ typedef enum SCOPE_CONTINUE = 1 << 1, SCOPE_CONTROL = 1 << 2, SCOPE_NEXT = 1 << 3, + SCOPE_DEFER = 1 << 4, } ScopeFlags; typedef enum diff --git a/src/compiler/expr_analysis.c b/src/compiler/expr_analysis.c index f4234fa96..d076c3e0b 100644 --- a/src/compiler/expr_analysis.c +++ b/src/compiler/expr_analysis.c @@ -46,10 +46,17 @@ static inline bool sema_expr_analyse_identifier(Context *context, Expr *expr) } Decl *decl = context_find_ident(context, expr->identifier_expr.identifier.string); if (decl == NULL) + { + decl = compiler_find_symbol(expr->identifier_expr.identifier); + if (decl && !decl_ok(decl)) return false; + } + if (decl == NULL) { SEMA_ERROR(expr->loc, "Unknown identifier %s.", expr->identifier_expr.identifier.string); return false; } + if (!decl_ok(decl)) return expr_poison(expr); + expr->identifier_expr.decl = decl; expr->type = decl->var.type; return true; diff --git a/src/compiler/module.c b/src/compiler/module.c new file mode 100644 index 000000000..d0972ebd4 --- /dev/null +++ b/src/compiler/module.c @@ -0,0 +1,11 @@ +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "compiler_internal.h" + +Decl *module_find_symbol(Module *module, const char *symbol) +{ + return stable_get(&module->symbols, symbol); +} + diff --git a/src/compiler/parser.c b/src/compiler/parser.c index 1c3e6da30..ce7550236 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -693,10 +693,6 @@ static inline Ast* parse_if_stmt(void) if_ast->if_stmt.then_body = stmt; if (stmt->ast_kind != AST_COMPOUND_STMT || tok.type != TOKEN_ELSE) { - if (stmt->ast_kind != AST_COMPOUND_STMT) - { - CONSUME_OR(TOKEN_EOS, &poisoned_ast); - } return if_ast; } advance_and_verify(TOKEN_ELSE); @@ -2663,10 +2659,9 @@ void parse_current(void) } } -void parse_file(File *file) +void parse_file(Context *context) { - lexer_add_file_for_lexing(file); - Context *context = context_create(file); + lexer_add_file_for_lexing(context->file); context_push(context); parse_current(); } diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 853a648de..90fb315fe 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -19,11 +19,6 @@ void sema_shadow_error(Decl *decl, Decl *old) sema_prev_at_range(old->name.span, "The previous use of '%s' was here.", decl->name.string); } -Decl *module_find_symbol(Module *module, const char *symbol) -{ - return stable_get(&module->symbols, symbol); -} - Decl *context_find_ident(Context *context, const char *symbol) { @@ -36,7 +31,6 @@ Decl *context_find_ident(Context *context, const char *symbol) } Decl *found = module_find_symbol(context->module, symbol); if (found) return found; - // TODO check imports return NULL; } @@ -50,6 +44,7 @@ static inline void context_push_scope(Context *context) context->current_scope++; context->current_scope->exit = EXIT_NONE; context->current_scope->local_decl_start = context->last_local; + context->current_scope->defer_start = vec_size(context->defers); } static inline void context_push_scope_with_flags(Context *context, ScopeFlags flags) @@ -64,7 +59,10 @@ static inline void context_pop_scope(Context *context) { assert(context->current_scope != &context->scopes[0]); context->last_local = context->current_scope->local_decl_start; + assert(vec_size(context->defers) == context->current_scope->defer_start); + ExitType exit_type = context->current_scope->exit; + vec_resize(context->defers, context->current_scope->defer_start); context->current_scope--; if (context->current_scope->exit < exit_type) { @@ -86,6 +84,27 @@ static bool sema_resolve_ptr_type(Context *context, Type *type) return true; } +static void sema_build_defer_chain(Context *context, Ast ***statement_list) +{ + unsigned size = vec_size(context->defers); + unsigned start = context->current_scope->defer_start; + for (unsigned i = size; i > start; i--) + { + vec_add(*statement_list, context->defers[i - 1]); + } +} + +static void sema_release_defer_chain(Context *context, Ast ***statement_list) +{ + unsigned size = vec_size(context->defers); + unsigned start = context->current_scope->defer_start; + for (unsigned i = size; i > start; i--) + { + vec_add(*statement_list, context->defers[i - 1]->defer_stmt.body); + } + vec_resize(context->defers, start); +} + static bool sema_resolve_array_type(Context *context, Type *type) { if (!sema_resolve_type(context, type->base)) @@ -516,7 +535,19 @@ static inline bool sema_analyse_expr_stmt(Context *context, Ast *statement) static inline bool sema_analyse_defer_stmt(Context *context, Ast *statement) { - TODO + context_push_scope_with_flags(context, SCOPE_DEFER | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) + // Only ones allowed. + context->current_scope->flags &= SCOPE_DEFER | SCOPE_CONTINUE; // NOLINT(hicpp-signed-bitwise) + + bool success = sema_analyse_statement(context, statement->defer_stmt.body); + + context_pop_scope(context); + + if (!success) return false; + + vec_add(context->defers, statement); + + return true; } static inline bool sema_analyse_default_stmt(Context *context, Ast *statement) @@ -633,7 +664,7 @@ static bool sema_analyse_asm_stmt(Context *context, Ast *statement) static bool sema_analyse_break_stmt(Context *context, Ast *statement) { - if (!(context->current_scope->flags | SCOPE_BREAK)) // NOLINT(hicpp-signed-bitwise) + if (!(context->current_scope->flags & SCOPE_BREAK)) // NOLINT(hicpp-signed-bitwise) { SEMA_ERROR(statement->token, "'break' is not allowed here."); return false; @@ -649,11 +680,12 @@ static bool sema_analyse_case_stmt(Context *context, Ast *statement) static bool sema_analyse_continue_stmt(Context *context, Ast *statement) { - if (!(context->current_scope->flags | SCOPE_CONTINUE)) // NOLINT(hicpp-signed-bitwise) + if (!(context->current_scope->flags & SCOPE_CONTINUE)) // NOLINT(hicpp-signed-bitwise) { SEMA_ERROR(statement->token, "'continue' is not allowed here."); return false; } + sema_build_defer_chain(context, &statement->continue_stmt.defers); return true; } @@ -718,6 +750,7 @@ static bool sema_analyse_switch_case(Context *context, Ast*** prev_cases, Ast *c { if (*prev_case) { + // sema_build_defer_chain(context, prev_cases); context_pop_scope(context); *prev_case = NULL; } @@ -733,7 +766,6 @@ static bool sema_analyse_switch_case(Context *context, Ast*** prev_cases, Ast *c case_stmt->case_stmt.value_type = type_is_signed(case_expr->type->canonical) ? CASE_VALUE_INT : CASE_VALUE_UINT; uint64_t val = case_expr->const_expr.i; case_stmt->case_stmt.val = val; - context_push_scope(context); *prev_case = case_stmt; VECEACH(*prev_cases, i) { @@ -744,6 +776,7 @@ static bool sema_analyse_switch_case(Context *context, Ast*** prev_cases, Ast *c return false; } } + context_push_scope_with_flags(context, SCOPE_BREAK); vec_add(*prev_cases, case_stmt); return true; } @@ -894,10 +927,14 @@ static inline bool sema_analyse_function_body(Context *context, Decl *func) if (!context_add_local(context, params[i])) return false; } if (!sema_analyse_compound_statement_no_scope(context, func->func.body)) return false; - if (context->current_scope->exit != EXIT_RETURN && func->func.function_signature.rtype->canonical != type_void) + if (context->current_scope->exit != EXIT_RETURN) { - SEMA_ERROR(func->name, "Missing return statement at the end of the function."); - return false; + if (func->func.function_signature.rtype->canonical != type_void) + { + SEMA_ERROR(func->name, "Missing return statement at the end of the function."); + return false; + } + sema_release_defer_chain(context, &func->func.body->compound_stmt.stmts); } context_pop_scope(context); return true; diff --git a/src/utils/lib.h b/src/utils/lib.h index 84a678588..6b4a850d7 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -214,6 +214,14 @@ static inline unsigned vec_size(const void*vec) return vec ? (((_VHeader *)vec) - 1)->size : 0; } +static inline void vec_resize(const void *vec, unsigned new_size) +{ + if (vec) + { + (((_VHeader *)vec) - 1)->size = new_size; + } +} + static inline void vec_pop(const void *vec) { assert(vec); @@ -248,7 +256,7 @@ static inline void* _expand(void *vec, size_t element_size) typeof(_vec) __temp = (typeof(_vec))_expand((_vec), sizeof((_vec)[0])); \ __temp[vec_size(__temp) - 1] = _value; \ _vec = __temp; }) -#define vec_add(_vec, _value) do { _vec = VECADD(_vec, _value); } while (0) +#define vec_add(_vec, _value) do { (_vec) = VECADD((_vec), _value); } while (0) #define VECLAST(_vec) ( (_vec) ? (_vec)[vec_size(_vec) - 1] : NULL)