all repos — hex @ fa8a41c5eec49ca9a17bdcbb381330081ea8eeb3

A tiny, minimalist, slightly-esoteric concatenative programming lannguage.

Refactor hex_interpret to improve token management; ensure tokens are freed after processing to prevent memory leaks.
h3rald h3rald@h3rald.com
Thu, 11 Sep 2025 10:23:04 +0200
commit

fa8a41c5eec49ca9a17bdcbb381330081ea8eeb3

parent

ef3260c464f35545cecc7ce37cb307de2d4d4bfc

2 files changed, 28 insertions(+), 4 deletions(-)

jump to
M src/hex.csrc/hex.c

@@ -3393,13 +3393,17 @@

while (token != NULL && token->type != HEX_TOKEN_INVALID) { int result = 0; + int token_consumed = 0; // Track whether the token was consumed by a function + if (token->type == HEX_TOKEN_INTEGER) { result = hex_push_integer(ctx, hex_parse_integer(token->value)); + // Token is not consumed by hex_push_integer, we need to free it } else if (token->type == HEX_TOKEN_STRING) { result = hex_push_string(ctx, token->value); + // Token is not consumed by hex_push_string, we need to free it } else if (token->type == HEX_TOKEN_SYMBOL) {

@@ -3412,11 +3416,14 @@ }

token->position->filename = strdup(filename); } result = hex_push_symbol(ctx, token); + // hex_push_symbol now copies the token (from Patch 2), so we should free the original + // Token is not consumed, we need to free it } else if (token->type == HEX_TOKEN_QUOTATION_END) { hex_error(ctx, "(%d,%d) Unexpected end of quotation", position.line, position.column); result = 1; + // Token is not consumed, we need to free it } else if (token->type == HEX_TOKEN_QUOTATION_START) {

@@ -3425,18 +3432,21 @@ if (!quotationItem)

{ hex_error(ctx, "(%d,%d) Failed to allocate memory for quotation", position.line, position.column); result = 1; + // Token is not consumed, we need to free it } else if (hex_parse_quotation(ctx, &input, quotationItem, &position) != 0) { hex_error(ctx, "(%d,%d) Failed to parse quotation", position.line, position.column); free(quotationItem); result = 1; + // Token is not consumed, we need to free it } else { result = hex_push_quotation(ctx, quotationItem->data.quotation_value, quotationItem->quotation_size); // Don't free quotationItem here as its content is now owned by the stack free(quotationItem); // Only free the wrapper, not the contents + // Token is not consumed, we need to free it } }

@@ -3448,8 +3458,9 @@ print_stack_trace(ctx);

return result; } - // Free the token after successful processing (unless it's been stored somewhere) - if (token->type != HEX_TOKEN_SYMBOL && token->type != HEX_TOKEN_QUOTATION_START) + // Always free the token after processing since none of the above functions consume it + // With Patch 2, hex_push_symbol now copies tokens, so we always own the original + if (!token_consumed) { hex_free_token(token); }

@@ -3461,6 +3472,7 @@ {

token->position->filename = strdup(filename); add_to_stack_trace(ctx, token); print_stack_trace(ctx); + hex_free_token(token); // Make sure to free the invalid token too return 1; } return 0;
M src/interpreter.csrc/interpreter.c

@@ -76,13 +76,17 @@

while (token != NULL && token->type != HEX_TOKEN_INVALID) { int result = 0; + int token_consumed = 0; // Track whether the token was consumed by a function + if (token->type == HEX_TOKEN_INTEGER) { result = hex_push_integer(ctx, hex_parse_integer(token->value)); + // Token is not consumed by hex_push_integer, we need to free it } else if (token->type == HEX_TOKEN_STRING) { result = hex_push_string(ctx, token->value); + // Token is not consumed by hex_push_string, we need to free it } else if (token->type == HEX_TOKEN_SYMBOL) {

@@ -95,11 +99,14 @@ }

token->position->filename = strdup(filename); } result = hex_push_symbol(ctx, token); + // hex_push_symbol now copies the token (from Patch 2), so we should free the original + // Token is not consumed, we need to free it } else if (token->type == HEX_TOKEN_QUOTATION_END) { hex_error(ctx, "(%d,%d) Unexpected end of quotation", position.line, position.column); result = 1; + // Token is not consumed, we need to free it } else if (token->type == HEX_TOKEN_QUOTATION_START) {

@@ -108,18 +115,21 @@ if (!quotationItem)

{ hex_error(ctx, "(%d,%d) Failed to allocate memory for quotation", position.line, position.column); result = 1; + // Token is not consumed, we need to free it } else if (hex_parse_quotation(ctx, &input, quotationItem, &position) != 0) { hex_error(ctx, "(%d,%d) Failed to parse quotation", position.line, position.column); free(quotationItem); result = 1; + // Token is not consumed, we need to free it } else { result = hex_push_quotation(ctx, quotationItem->data.quotation_value, quotationItem->quotation_size); // Don't free quotationItem here as its content is now owned by the stack free(quotationItem); // Only free the wrapper, not the contents + // Token is not consumed, we need to free it } }

@@ -131,8 +141,9 @@ print_stack_trace(ctx);

return result; } - // Free the token after successful processing (unless it's been stored somewhere) - if (token->type != HEX_TOKEN_SYMBOL && token->type != HEX_TOKEN_QUOTATION_START) + // Always free the token after processing since none of the above functions consume it + // With Patch 2, hex_push_symbol now copies tokens, so we always own the original + if (!token_consumed) { hex_free_token(token); }

@@ -144,6 +155,7 @@ {

token->position->filename = strdup(filename); add_to_stack_trace(ctx, token); print_stack_trace(ctx); + hex_free_token(token); // Make sure to free the invalid token too return 1; } return 0;