2022-01-08 22:20:29 +00:00
|
|
|
#include <stdio.h>
|
2021-12-10 20:22:16 +00:00
|
|
|
|
|
|
|
|
#include "apfl.h"
|
|
|
|
|
|
|
|
|
|
#define POSFMT "%d:%d"
|
|
|
|
|
#define POSARGS error.position.line, error.position.col
|
|
|
|
|
#define POS2ARGS error.position2.line, error.position2.col
|
|
|
|
|
|
2021-12-18 23:27:34 +00:00
|
|
|
const char *
|
|
|
|
|
apfl_error_type_name(enum apfl_error_type type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case APFL_ERR_MALLOC_FAILED:
|
|
|
|
|
return "APFL_ERR_MALLOC_FAILED";
|
|
|
|
|
case APFL_ERR_INPUT_ERROR:
|
|
|
|
|
return "APFL_ERR_INPUT_ERROR";
|
|
|
|
|
case APFL_ERR_UNEXPECTED_EOF:
|
|
|
|
|
return "APFL_ERR_UNEXPECTED_EOF";
|
|
|
|
|
case APFL_ERR_EXPECTED_EQ_AFTER_COLON:
|
|
|
|
|
return "APFL_ERR_EXPECTED_EQ_AFTER_COLON";
|
2022-01-07 22:39:06 +00:00
|
|
|
case APFL_ERR_UNEXPECTED_BYTE:
|
|
|
|
|
return "APFL_ERR_UNEXPECTED_BYTE";
|
2021-12-18 23:27:34 +00:00
|
|
|
case APFL_ERR_UNEXPECTED_BYTE_IN_NUMBER:
|
|
|
|
|
return "APFL_ERR_UNEXPECTED_BYTE_IN_NUMBER";
|
|
|
|
|
case APFL_ERR_EXPECTED_DIGIT:
|
|
|
|
|
return "APFL_ERR_EXPECTED_DIGIT";
|
|
|
|
|
case APFL_ERR_EXPECTED_HEX_IN_HEX_ESCAPE:
|
|
|
|
|
return "APFL_ERR_EXPECTED_HEX_IN_HEX_ESCAPE";
|
|
|
|
|
case APFL_ERR_INVALID_ESCAPE_SEQUENCE:
|
|
|
|
|
return "APFL_ERR_INVALID_ESCAPE_SEQUENCE";
|
|
|
|
|
case APFL_ERR_NO_LINEBREAK_AFTER_CONTINUE_LINE:
|
|
|
|
|
return "APFL_ERR_NO_LINEBREAK_AFTER_CONTINUE_LINE";
|
|
|
|
|
case APFL_ERR_UNEXPECTED_TOKEN:
|
|
|
|
|
return "APFL_ERR_UNEXPECTED_TOKEN";
|
|
|
|
|
case APFL_ERR_MISMATCHING_CLOSING_BRACKET:
|
|
|
|
|
return "APFL_ERR_MISMATCHING_CLOSING_BRACKET";
|
|
|
|
|
case APFL_ERR_UNEXPECTED_EOF_AFTER_TOKEN:
|
|
|
|
|
return "APFL_ERR_UNEXPECTED_EOF_AFTER_TOKEN";
|
|
|
|
|
case APFL_ERR_STATEMENTS_BEFORE_PARAMETERS:
|
|
|
|
|
return "APFL_ERR_STATEMENTS_BEFORE_PARAMETERS";
|
|
|
|
|
case APFL_ERR_EMPTY_ASSIGNMENT_BEFORE_PARAMETERS:
|
|
|
|
|
return "APFL_ERR_EMPTY_ASSIGNMENT_BEFORE_PARAMETERS";
|
|
|
|
|
case APFL_ERR_UNEXPECTED_EXPRESSION:
|
|
|
|
|
return "APFL_ERR_UNEXPECTED_EXPRESSION";
|
|
|
|
|
case APFL_ERR_INVALID_ASSIGNMENT_LHS:
|
|
|
|
|
return "APFL_ERR_INVALID_ASSIGNMENT_LHS";
|
|
|
|
|
case APFL_ERR_EMPTY_ASSIGNMENT:
|
|
|
|
|
return "APFL_ERR_EMPTY_ASSIGNMENT";
|
2022-01-07 22:08:25 +00:00
|
|
|
case APFL_ERR_ONLY_ONE_EXPAND_ALLOWED:
|
|
|
|
|
return "APFL_ERR_ONLY_ONE_EXPAND_ALLOWED";
|
2022-01-08 21:55:24 +00:00
|
|
|
case APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS:
|
|
|
|
|
return "APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS";
|
|
|
|
|
case APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS:
|
|
|
|
|
return "APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS";
|
2022-01-08 22:20:29 +00:00
|
|
|
case APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS:
|
|
|
|
|
return "APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS";
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
case APFL_ERR_NOT_IMPLEMENTED:
|
|
|
|
|
return "APFL_ERR_NOT_IMPLEMENTED";
|
2021-12-18 23:27:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "<unknown error>";
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 20:22:16 +00:00
|
|
|
void
|
|
|
|
|
apfl_error_print(struct apfl_error error, FILE *file)
|
|
|
|
|
{
|
|
|
|
|
switch (error.type) {
|
|
|
|
|
case APFL_ERR_MALLOC_FAILED:
|
|
|
|
|
fprintf(file, "Could not allocate memory\n");
|
|
|
|
|
return;
|
|
|
|
|
case APFL_ERR_INPUT_ERROR:
|
|
|
|
|
fprintf(file, "Input error while parsing\n");
|
|
|
|
|
return;
|
|
|
|
|
case APFL_ERR_UNEXPECTED_EOF:
|
|
|
|
|
fprintf(file, "Unexpected end of file\n");
|
|
|
|
|
return;
|
|
|
|
|
case APFL_ERR_EXPECTED_EQ_AFTER_COLON:
|
|
|
|
|
fprintf(file, "Expected '=' after ':' at " POSFMT "\n", POSARGS);
|
2022-01-07 22:39:06 +00:00
|
|
|
return;
|
|
|
|
|
case APFL_ERR_UNEXPECTED_BYTE:
|
|
|
|
|
fprintf(file, "Unexpected byte '%c' (0x%X) at " POSFMT "\n", error.byte, (unsigned)error.byte, POSARGS);
|
2021-12-10 20:22:16 +00:00
|
|
|
return;
|
|
|
|
|
case APFL_ERR_UNEXPECTED_BYTE_IN_NUMBER:
|
|
|
|
|
fprintf(file, "Unexpected byte '%c' while parsing number at " POSFMT "\n", error.byte, POSARGS);
|
|
|
|
|
return;
|
|
|
|
|
case APFL_ERR_EXPECTED_DIGIT:
|
|
|
|
|
fprintf(file, "Expected a digit at " POSFMT "\n", POSARGS);
|
|
|
|
|
return;
|
|
|
|
|
case APFL_ERR_EXPECTED_HEX_IN_HEX_ESCAPE:
|
|
|
|
|
fprintf(file, "Expected a hex-digit in hex escape at " POSFMT "\n", POSARGS);
|
|
|
|
|
return;
|
|
|
|
|
case APFL_ERR_INVALID_ESCAPE_SEQUENCE:
|
|
|
|
|
fprintf(file, "Invalid escape sequence \\%c at " POSFMT "\n", error.byte, POSARGS);
|
|
|
|
|
return;
|
|
|
|
|
case APFL_ERR_NO_LINEBREAK_AFTER_CONTINUE_LINE:
|
|
|
|
|
fprintf(file, "No line break (after optional comments) after \\ at " POSFMT "\n", POSARGS);
|
|
|
|
|
return;
|
|
|
|
|
case APFL_ERR_UNEXPECTED_TOKEN:
|
|
|
|
|
fprintf(file, "Unexpected `%s` token at " POSFMT "\n", apfl_token_type_name(error.token_type), POSARGS);
|
|
|
|
|
return;
|
|
|
|
|
case APFL_ERR_MISMATCHING_CLOSING_BRACKET:
|
|
|
|
|
fprintf(
|
|
|
|
|
file,
|
|
|
|
|
"Closing `%s` token at " POSFMT " does not match opening `%s` at " POSFMT "\n",
|
|
|
|
|
apfl_token_type_name(error.token_type),
|
|
|
|
|
POSARGS,
|
|
|
|
|
apfl_token_type_name(error.token_type2),
|
|
|
|
|
POS2ARGS
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
case APFL_ERR_UNEXPECTED_EOF_AFTER_TOKEN:
|
|
|
|
|
fprintf(
|
|
|
|
|
file,
|
|
|
|
|
"Unexpected end of file after `%s` token at " POSFMT "\n",
|
|
|
|
|
apfl_token_type_name(error.token_type),
|
|
|
|
|
POSARGS
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
case APFL_ERR_STATEMENTS_BEFORE_PARAMETERS:
|
|
|
|
|
fprintf(
|
|
|
|
|
file,
|
|
|
|
|
"Unexpected statements before parameters near " POSFMT "\n",
|
|
|
|
|
POSARGS
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
case APFL_ERR_EMPTY_ASSIGNMENT_BEFORE_PARAMETERS:
|
|
|
|
|
fprintf(
|
|
|
|
|
file,
|
|
|
|
|
"Unexpected empty assignment before parameters near " POSFMT "\n",
|
|
|
|
|
POSARGS
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
case APFL_ERR_UNEXPECTED_EXPRESSION:
|
|
|
|
|
fprintf(
|
|
|
|
|
file,
|
|
|
|
|
"Unexpected expression near " POSFMT "\n",
|
|
|
|
|
POSARGS
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
case APFL_ERR_INVALID_ASSIGNMENT_LHS:
|
|
|
|
|
fprintf(
|
|
|
|
|
file,
|
|
|
|
|
"Invalid left hand side of assignment near " POSFMT "\n",
|
|
|
|
|
POSARGS
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
case APFL_ERR_EMPTY_ASSIGNMENT:
|
|
|
|
|
fprintf(
|
|
|
|
|
file,
|
|
|
|
|
"Empty assignment at " POSFMT "\n",
|
|
|
|
|
POSARGS
|
|
|
|
|
);
|
|
|
|
|
return;
|
2022-01-07 22:08:25 +00:00
|
|
|
case APFL_ERR_ONLY_ONE_EXPAND_ALLOWED:
|
|
|
|
|
fprintf(
|
|
|
|
|
file,
|
|
|
|
|
"Only one expansion (~) is allowed per level, near " POSFMT "\n",
|
|
|
|
|
POSARGS
|
|
|
|
|
);
|
2022-01-08 21:55:24 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS:
|
|
|
|
|
fprintf(
|
|
|
|
|
file,
|
|
|
|
|
"Unexpected constant in member access near " POSFMT "\n",
|
|
|
|
|
POSARGS
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS:
|
|
|
|
|
fprintf(
|
|
|
|
|
file,
|
|
|
|
|
"Unexpected expression in member access near " POSFMT "\n",
|
|
|
|
|
POSARGS
|
|
|
|
|
);
|
|
|
|
|
break;
|
2022-01-08 22:20:29 +00:00
|
|
|
case APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS:
|
|
|
|
|
fprintf(
|
|
|
|
|
file,
|
|
|
|
|
"Unexpected blank (\"_\") in member access near " POSFMT "\n",
|
|
|
|
|
POSARGS
|
|
|
|
|
);
|
|
|
|
|
break;
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
case APFL_ERR_NOT_IMPLEMENTED:
|
|
|
|
|
fprintf(
|
|
|
|
|
file,
|
|
|
|
|
"Feature not implemented\n"
|
|
|
|
|
);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(file, "Unknown error %d\n", (int)error.type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_error
|
|
|
|
|
apfl_error_simple(enum apfl_error_type type)
|
|
|
|
|
{
|
|
|
|
|
return (struct apfl_error) { .type = type };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
apfl_error_is_fatal_type(enum apfl_error_type type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case APFL_ERR_MALLOC_FAILED:
|
|
|
|
|
case APFL_ERR_INPUT_ERROR:
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|