Parser: Do a better job at cleaning up temporary allocations

-fsanitize=memory no longer finds any memory leak in the parser test suite.
We don't yet test error cases, but still pretty good :).
This commit is contained in:
Laria 2021-12-18 21:39:48 +01:00
parent d312500a5b
commit 0995739ca8
2 changed files with 120 additions and 77 deletions

View file

@ -73,6 +73,7 @@ void
apfl_expr_call_deinit(struct apfl_expr_call *call)
{
DESTROY(call->callee, apfl_expr_deinit);
apfl_expr_list_deinit(&call->arguments);
}
void

View file

@ -138,7 +138,7 @@ fragment_deinit(struct fragment *fragment)
deinit_fragment_lhs_rhs(&fragment->at);
break;
case FRAG_PREDICATE:
deinit_fragment_lhs_rhs(&fragment->at);
deinit_fragment_lhs_rhs(&fragment->predicate);
break;
case FRAG_EXPR:
apfl_expr_deinit(&fragment->expr);
@ -373,7 +373,7 @@ parse_fragment_into_list(apfl_parser_ptr p, struct fragment_list *list, bool nee
struct fragment *elem = &list->children[list->len];
enum parse_fragment_result result = parse_fragment(p, elem, need, flags);
if (result != PF_OK) {// \mystuff\TODO:even more flags?
if (result != PF_OK) {
return result;
}
@ -393,7 +393,7 @@ err_unexpected_eof_after(enum apfl_token_type token_type, struct apfl_position p
static bool fragments_to_call(
apfl_parser_ptr,
struct fragment_list *,
struct fragment_list,
struct apfl_position position,
struct apfl_expr *
);
@ -465,7 +465,7 @@ parse_parens(apfl_parser_ptr p, struct fragment *out, struct apfl_position posit
out->type = FRAG_EXPR;
out->position = position;
if (!fragments_to_call(p, &children, position, &out->expr)) {
if (!fragments_to_call(p, children, position, &out->expr)) {
goto error;
}
@ -569,62 +569,60 @@ static bool fragment_to_list_item(
static struct apfl_expr *fragment_to_expr_allocated(apfl_parser_ptr, struct fragment);
static bool
fragment_to_expr(apfl_parser_ptr p, struct fragment fragment, struct apfl_expr *out)
fragment_to_expr_inner(apfl_parser_ptr p, struct fragment *fragment, struct apfl_expr *out)
{
switch (fragment.type) {
switch (fragment->type) {
case FRAG_EXPAND:
p->error = err_unexpected_token(APFL_TOK_EXPAND, fragment.position);
p->error = err_unexpected_token(APFL_TOK_EXPAND, fragment->position);
return false;
case FRAG_CONSTANT:
out->type = APFL_EXPR_CONSTANT;
out->constant = fragment.constant;
out->position = fragment.position;
out->constant = apfl_expr_const_move(&fragment->constant);
out->position = fragment->position;
return true;
case FRAG_NAME:
out->type = APFL_EXPR_VAR;
out->var = apfl_string_move(&fragment.name);
out->position = fragment.position;
out->var = apfl_string_move(&fragment->name);
out->position = fragment->position;
return true;
case FRAG_DOT:
out->type = APFL_EXPR_DOT;
if ((out->dot.lhs = fragment_to_expr_allocated(p, fragment_move(fragment.dot.lhs))) == NULL) {
if ((out->dot.lhs = fragment_to_expr_allocated(p, fragment_move(fragment->dot.lhs))) == NULL) {
return false;
}
out->dot.rhs = apfl_string_move(&fragment.dot.rhs);
out->position = fragment.position;
out->dot.rhs = apfl_string_move(&fragment->dot.rhs);
out->position = fragment->position;
return true;
case FRAG_AT:
out->type = APFL_EXPR_AT;
if ((out->at.lhs = fragment_to_expr_allocated(p, fragment_move(fragment.at.lhs))) == NULL) {
if ((out->at.lhs = fragment_to_expr_allocated(p, fragment_move(fragment->at.lhs))) == NULL) {
return false;
}
if ((out->at.rhs = fragment_to_expr_allocated(p, fragment_move(fragment.at.rhs))) == NULL) {
if ((out->at.rhs = fragment_to_expr_allocated(p, fragment_move(fragment->at.rhs))) == NULL) {
return false;
}
out->position = fragment.position;
out->position = fragment->position;
return true;
case FRAG_PREDICATE:
p->error = err_unexpected_token(APFL_TOK_QUESTION_MARK, fragment.position);
p->error = err_unexpected_token(APFL_TOK_QUESTION_MARK, fragment->position);
return false;
case FRAG_EXPR:
*out = apfl_expr_move(&fragment.expr);
*out = apfl_expr_move(&fragment->expr);
return true;
case FRAG_LIST:
out->type = APFL_EXPR_LIST;
out->position = fragment.position;
out->position = fragment->position;
out->list.len = 0;
if (fragment.list.len == 0) {
if (fragment->list.len == 0) {
out->list.items = NULL;
return true;
}
if ((out->list.items = ALLOC_LIST(struct apfl_expr_list_item, fragment.list.len)) == NULL) {
if ((out->list.items = ALLOC_LIST(struct apfl_expr_list_item, fragment->list.len)) == NULL) {
p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
fragment_deinit(&fragment);
return false;
}
for (size_t i = 0; i < fragment.list.len; i++) {
if (!fragment_to_list_item(p, fragment_move(&fragment.list.children[i]), &out->list.items[i])) {
fragment_deinit(&fragment);
for (size_t i = 0; i < fragment->list.len; i++) {
if (!fragment_to_list_item(p, fragment_move(&fragment->list.children[i]), &out->list.items[i])) {
return false;
}
out->list.len++;
@ -635,6 +633,14 @@ fragment_to_expr(apfl_parser_ptr p, struct fragment fragment, struct apfl_expr *
assert(false);
}
static bool
fragment_to_expr(apfl_parser_ptr p, struct fragment fragment, struct apfl_expr *out)
{
bool result = fragment_to_expr_inner(p, &fragment, out);
fragment_deinit(&fragment);
return result;
}
static struct apfl_expr *
fragment_to_expr_allocated(apfl_parser_ptr p, struct fragment fragment)
{
@ -801,6 +807,7 @@ parse_list(
if (!append_fragment(&list, first)) {
fragment_deinit(&first);
p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
fragment_list_deinit(&list);
return false;
}
@ -836,7 +843,7 @@ maybe_end:
return true;
error:
// \mystuff\TODO:clean up list
fragment_list_deinit(&list);
return false;
}
@ -965,7 +972,7 @@ parse_stringify(apfl_parser_ptr p, struct fragment *fragment, struct apfl_positi
static bool fragment_to_param_recursive(apfl_parser_ptr, struct fragment *, struct apfl_expr_param *);
static bool fragments_to_params(apfl_parser_ptr, struct fragment_list *, struct apfl_expr_params *);
static bool fragments_to_params(apfl_parser_ptr, struct fragment_list, struct apfl_expr_params *);
static bool
predicate_fragment_to_param(
@ -1033,7 +1040,7 @@ fragment_to_param_recursive(
return false;
case FRAG_LIST:
out->type = APFL_EXPR_PARAM_LIST;
return fragments_to_params(p, &fragment->list, &out->list);
return fragments_to_params(p, fragment_list_move(&fragment->list), &out->list);
}
assert(false);
@ -1064,28 +1071,28 @@ fragment_to_param(
static bool
fragments_to_params(
apfl_parser_ptr p,
struct fragment_list *fragments,
struct fragment_list fragments,
struct apfl_expr_params *out
) {
bool result = true;
if (fragments->len == 0) {
if (fragments.len == 0) {
out->len = 0;
out->params = NULL;
goto ok;
}
out->params = ALLOC_LIST(struct apfl_expr_param, fragments->len);
out->params = ALLOC_LIST(struct apfl_expr_param, fragments.len);
out->len = 0;
if (out->params == NULL) {
goto error;
}
bool seen_expand = false;
for (size_t i = 0; i < fragments->len; i++) {
for (size_t i = 0; i < fragments.len; i++) {
if (!fragment_to_param(
p,
&fragments->children[i],
&fragments.children[i],
&out->params[i],
&seen_expand
)) {
@ -1101,7 +1108,7 @@ error:
result = false;
apfl_expr_params_deinit(out);
ok:
fragment_list_deinit(fragments);
fragment_list_deinit(&fragments);
return result;
}
@ -1111,13 +1118,21 @@ fragment_to_assignable(
bool expand_ok,
struct fragment fragment,
struct apfl_expr_assignable *out
);
static bool
fragment_to_assignable_inner(
apfl_parser_ptr p,
bool expand_ok,
struct fragment *fragment,
struct apfl_expr_assignable *out
) {
switch (fragment.type) {
switch (fragment->type) {
case FRAG_EXPAND:
if (!expand_ok) {
p->error = (struct apfl_error) {
.type = APFL_ERR_INVALID_ASSIGNMENT_LHS,
.position = fragment.position,
.position = fragment->position,
};
goto error;
}
@ -1131,7 +1146,7 @@ fragment_to_assignable(
if (!fragment_to_assignable(
p,
false,
fragment_move(fragment.expand),
fragment_move(fragment->expand),
out->expand
)) {
goto error;
@ -1140,16 +1155,16 @@ fragment_to_assignable(
return true;
case FRAG_CONSTANT:
out->type = APFL_EXPR_ASSIGNABLE_CONSTANT;
out->constant = apfl_expr_const_move(&fragment.constant);
out->constant = apfl_expr_const_move(&fragment->constant);
return true;
case FRAG_NAME:
out->type = APFL_EXPR_ASSIGNABLE_VAR;
out->var = apfl_string_move(&fragment.name);
out->var = apfl_string_move(&fragment->name);
return true;
case FRAG_DOT:
out->type = APFL_EXPR_ASSIGNABLE_DOT;
out->dot.rhs = apfl_string_move(&fragment.dot.rhs);
out->dot.rhs = apfl_string_move(&fragment->dot.rhs);
if ((out->dot.lhs = malloc(sizeof(struct apfl_expr_assignable))) == NULL) {
p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
@ -1159,7 +1174,7 @@ fragment_to_assignable(
if (!fragment_to_assignable(
p,
expand_ok,
fragment_move(fragment.dot.lhs),
fragment_move(fragment->dot.lhs),
out->dot.lhs
)) {
goto error;
@ -1180,7 +1195,7 @@ fragment_to_assignable(
if (!fragment_to_assignable(
p,
expand_ok,
fragment_move(fragment.at.lhs),
fragment_move(fragment->at.lhs),
out->at.lhs
)) {
free(out->at.rhs);
@ -1190,7 +1205,7 @@ fragment_to_assignable(
if (!fragment_to_expr(
p,
fragment_move(fragment.at.rhs),
fragment_move(fragment->at.rhs),
out->at.rhs
)) {
goto error;
@ -1211,7 +1226,7 @@ fragment_to_assignable(
if (!fragment_to_assignable(
p,
expand_ok,
fragment_move(fragment.at.lhs),
fragment_move(fragment->at.lhs),
out->predicate.lhs
)) {
free(out->predicate.rhs);
@ -1221,7 +1236,7 @@ fragment_to_assignable(
if (!fragment_to_expr(
p,
fragment_move(fragment.at.rhs),
fragment_move(fragment->at.rhs),
out->predicate.rhs
)) {
goto error;
@ -1231,22 +1246,22 @@ fragment_to_assignable(
case FRAG_EXPR:
p->error = (struct apfl_error) {
.type = APFL_ERR_INVALID_ASSIGNMENT_LHS,
.position = fragment.position,
.position = fragment->position,
};
goto error;
case FRAG_LIST:
out->type = APFL_EXPR_ASSIGNABLE_LIST;
out->list.len = 0;
out->list.children = malloc(sizeof(struct apfl_expr_assignable) * fragment.list.len);
out->list.children = malloc(sizeof(struct apfl_expr_assignable) * fragment->list.len);
expand_ok = true;
for (size_t i = 0; i < fragment.list.len; i++) {
for (size_t i = 0; i < fragment->list.len; i++) {
struct apfl_expr_assignable *cur = &out->list.children[i];
if (!fragment_to_assignable(
p,
expand_ok,
fragment_move(&fragment.list.children[i]),
fragment_move(&fragment->list.children[i]),
cur
)) {
goto error;
@ -1267,6 +1282,18 @@ error:
return false;
}
static bool
fragment_to_assignable(
apfl_parser_ptr p,
bool expand_ok,
struct fragment fragment,
struct apfl_expr_assignable *out
) {
bool result = fragment_to_assignable_inner(p, expand_ok, &fragment, out);
fragment_deinit(&fragment);
return result;
}
struct partial_assignment {
struct apfl_expr_assignable lhs;
bool local;
@ -1311,11 +1338,11 @@ fragment_to_list_item(
static bool
fragments_to_call(
apfl_parser_ptr p,
struct fragment_list *fragments, // \mystuff\TODO:really a pointer? Why?
struct fragment_list fragments,
struct apfl_position position,
struct apfl_expr *out
) {
assert(fragments->len > 0); // \mystuff\TODO: Or should we check this here?
assert(fragments.len > 0);
out->type = APFL_EXPR_CALL;
out->position = position;
@ -1324,28 +1351,31 @@ fragments_to_call(
.len = 0,
};
out->call.callee = fragment_to_expr_allocated(p, fragment_move(&fragments->children[0]));
out->call.callee = fragment_to_expr_allocated(p, fragment_move(&fragments.children[0]));
if (out->call.callee == NULL) {
goto error;
}
if (fragments->len == 1) {
if (fragments.len == 1) {
fragment_list_deinit(&fragments);
return true;
}
out->call.arguments.items = ALLOC_LIST(struct apfl_expr_list_item, (fragments->len - 1));
out->call.arguments.items = ALLOC_LIST(struct apfl_expr_list_item, (fragments.len - 1));
for (size_t i = 1; i < fragments->len; i++) {
if (!fragment_to_list_item(p, fragment_move(&fragments->children[i]), &out->call.arguments.items[i-1])) {
for (size_t i = 1; i < fragments.len; i++) {
if (!fragment_to_list_item(p, fragment_move(&fragments.children[i]), &out->call.arguments.items[i-1])) {
goto error;
}
out->call.arguments.len++;
}
fragment_list_deinit(&fragments);
return true;
error:
fragment_list_deinit(&fragments);
apfl_expr_deinit(out);
return false;
}
@ -1353,11 +1383,11 @@ error:
static enum parse_body_or_toplevel_finalize_result
parse_body_or_toplevel_finalize(
apfl_parser_ptr p,
struct fragment_list *fragments,
struct fragment_list fragments,
struct partial_assignment_list partial_assignments,
struct apfl_expr *out
) {
if (fragments->len == 0) {
if (fragments.len == 0) {
if (partial_assignments.len > 0) {
p->error = (struct apfl_error) {
.type = APFL_ERR_EMPTY_ASSIGNMENT,
@ -1366,6 +1396,8 @@ parse_body_or_toplevel_finalize(
goto error;
}
fragment_list_deinit(&fragments);
DEINIT_LIST(partial_assignments.items, partial_assignments.len, partial_assignment_deinit);
return BODY_FINALIZE_EMPTY;
}
@ -1383,22 +1415,25 @@ parse_body_or_toplevel_finalize(
dest = dest->assignment.rhs;
}
if (fragments->len == 1) {
if (!fragment_to_expr(p, fragment_move(&fragments->children[0]), dest)) {
DEINIT_LIST(partial_assignments.items, partial_assignments.len, partial_assignment_deinit);
if (fragments.len == 1) {
if (!fragment_to_expr(p, fragment_move(&fragments.children[0]), dest)) {
goto error;
}
} else {
if (!fragments_to_call(p, fragments, fragments->children[0].position, dest)) {
struct apfl_position position = fragments.children[0].position;
if (!fragments_to_call(p, fragment_list_move(&fragments), position, dest)) {
goto error;
}
}
fragment_list_deinit(fragments);
DEINIT_LIST(partial_assignments.items, partial_assignments.len, partial_assignment_deinit);
fragment_list_deinit(&fragments);
return BODY_FINALIZE_OK;
error:
fragment_list_deinit(&fragments);
DEINIT_LIST(partial_assignments.items, partial_assignments.len, partial_assignment_deinit);
apfl_expr_deinit(out);
return BODY_FINALIZE_ERROR;
@ -1437,7 +1472,7 @@ parse_body_or_toplevel(
if (handle_eof) {
switch (parse_body_or_toplevel_finalize(
p,
fragments,
fragment_list_move(fragments),
partial_assignment_list_move(&partial_assignments),
out
)) {
@ -1502,6 +1537,7 @@ break_inner:
fragment_list_deinit(fragments); // Reset fragment list
if (!fragment_to_assignable(p, false, fragment, &cur_partial->lhs)) {
fragment_deinit(&fragment);
goto error;
}
@ -1516,7 +1552,7 @@ break_inner:
case APFL_TOK_SEMICOLON:
switch (parse_body_or_toplevel_finalize(
p,
fragments,
fragment_list_move(fragments),
partial_assignment_list_move(&partial_assignments),
out
)) {
@ -1609,6 +1645,8 @@ parse_braces(
switch (p->token.type) {
case APFL_TOK_RBRACE:
fragment_list_deinit(&fragments);
// TODO: Rather fulgly duplication
if (has_params) {
// Finalize previous subfunc and append
@ -1618,8 +1656,8 @@ parse_braces(
&subfuncs_len,
&subfuncs_cap,
&(struct apfl_expr_subfunc) {
.params = params,
.body = body,
.params = apfl_expr_params_move(&params),
.body = apfl_expr_body_move(&body),
},
1
)) {
@ -1652,8 +1690,6 @@ parse_braces(
body_cap = 0;
}
DEINIT_LIST(fragments.children, fragments.len, fragment_deinit);
return true;
case APFL_TOK_MAPSTO:
if (body.len > 0 && !has_params) {
@ -1672,8 +1708,8 @@ parse_braces(
&subfuncs_len,
&subfuncs_cap,
&(struct apfl_expr_subfunc) {
.params = params,
.body = body,
.params = apfl_expr_params_move(&params),
.body = apfl_expr_body_move(&body),
},
1
)) {
@ -1686,7 +1722,7 @@ parse_braces(
body_cap = 0;
}
if (!fragments_to_params(p, &fragments, &params)) {
if (!fragments_to_params(p, fragment_list_move(&fragments), &params)) {
goto error;
}
has_params = true;
@ -1702,7 +1738,7 @@ parse_braces(
}
error:
// \mystuff\TODO:cleanup
fragment_list_deinit(&fragments);
return false;
}
@ -1891,7 +1927,7 @@ apfl_parser_next(apfl_parser_ptr p)
apfl_expr_deinit(&p->expr);
}
struct fragment_list fragments; // TODO: Clean me up!
struct fragment_list fragments;
apfl_resizable_init(APFL_RESIZABLE_ARGS(fragments, children));
switch (parse_body_or_toplevel(
@ -1903,18 +1939,24 @@ apfl_parser_next(apfl_parser_ptr p)
)) {
case PF_OK:
p->has_expr = true;
fragment_list_deinit(&fragments);
return APFL_PARSE_OK;
case PF_ERROR:
return APFL_PARSE_ERROR;
goto error;
case PF_EOF:
fragment_list_deinit(&fragments);
return APFL_PARSE_EOF;
case PF_CANT_HANDLE:
read_token_after_cant_handle(p);
p->error = ERR_UNEXPECTED_TOKEN(p->token);
return APFL_PARSE_ERROR;
goto error;
}
assert(false);
error:
fragment_list_deinit(&fragments);
return APFL_PARSE_ERROR;
}
struct apfl_error