From 785dbf06f6b3a3ba225e5cf96c1f5be899f3b503 Mon Sep 17 00:00:00 2001 From: Max Regan Date: Tue, 15 Nov 2022 15:18:37 -0500 Subject: [PATCH] Improve parser errors slightly --- src/main.c | 7 ++++++- src/parse.c | 38 +++++++++++++++++++++++++------------- src/special.c | 2 ++ test/test_parse.c | 17 +++++++++++++++++ 4 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/main.c b/src/main.c index 2889ec1..127d6ec 100644 --- a/src/main.c +++ b/src/main.c @@ -79,7 +79,12 @@ int main(int argc, const char **argv) { } struct ucl_object *sexp = ucl_parse(line); - struct ucl_object *result = ucl_evaluate(scope, ucl_car(sexp)); + struct ucl_object *result = NULL; + if (sexp->type == UCL_TYPE_ERROR) { + result = sexp; + } else { + result = ucl_evaluate(scope, ucl_car(sexp)); + } ucl_print_obj(result); printf("\n"); diff --git a/src/parse.c b/src/parse.c index 4113666..1be0073 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1,5 +1,6 @@ #include "uclisp.h" #include "internal.h" +#include "utility.h" // TODO: remove these #include @@ -20,6 +21,8 @@ static const char *reserved_symbols[] = { }; // TODO: remove malloc and strndup calls +// TODO: refresh this with newer 'object' code +// TODO: Syntatic sugar for quote static bool ucl_is_whitespace(char c) { return c == ' ' || c == '\n' || c == '\t'; @@ -68,6 +71,7 @@ struct ucl_object *ucl_token_next(const char **curr_src) { str = strndup(start + 1, *curr_src - start - 2); return ucl_cell_create(ucl_string_create(str), NULL); case '0'...'9': { + // TODO: Add support for negative integers char *end = NULL; long value = strtol(*curr_src, &end, 0); *curr_src = end; @@ -141,6 +145,9 @@ static struct ucl_object *ucl_parse_tokens_recursive(struct ucl_object **token_i assert((*token_iter)->type == UCL_TYPE_CELL); struct ucl_object *token = (*token_iter)->cell.car; + if (token == NULL) { + return NULL; + } struct ucl_object *next_sexp = ucl_parse_token_atom(token); if (next_sexp != NULL) { *token_iter = (*token_iter)->cell.cdr; @@ -154,7 +161,13 @@ static struct ucl_object *ucl_parse_tokens_recursive(struct ucl_object **token_i // Consume the START_LIST_CHAR *token_iter = (*token_iter)->cell.cdr; while (1) { + if (*token_iter == NULL) { + // Unexpected end of parsing + return ucl_error_create("Unmatched open parenthesis"); + } + token = (*token_iter)->cell.car; + if (token->type == UCL_TYPE_SYMBOL && token->symbol[0] == END_LIST_CHAR) { *token_iter = (*token_iter)->cell.cdr; if (list == NULL) { @@ -164,6 +177,7 @@ static struct ucl_object *ucl_parse_tokens_recursive(struct ucl_object **token_i } next_sexp = ucl_parse_tokens_recursive(token_iter); + UCL_RET_IF_ERROR(next_sexp); if (next_sexp == NULL) { // Error somewhere in the recursive parsing ucl_object_delete(list); @@ -174,12 +188,11 @@ static struct ucl_object *ucl_parse_tokens_recursive(struct ucl_object **token_i next_node = &(*next_node)->cell.cdr; } } else if (token->symbol[0] == END_LIST_CHAR) { - // Mismatched parens - return NULL; + return ucl_error_create("Unmatched closed parenthesis"); } // Any other symbol type should have been an atom, this shouldn't happen assert(false); - return NULL; + return ucl_error_create("Unreachable parse error"); } // parse_tokens -> doesn't care about quotes, terminates on EOF @@ -192,8 +205,8 @@ struct ucl_object *ucl_parse_tokens(struct ucl_object *tokens) { while (*token_iter != NULL) { struct ucl_object *new_sexp = ucl_parse_tokens_recursive(token_iter); - if (new_sexp == NULL) { - goto error; + if (new_sexp == NULL || new_sexp->type == UCL_TYPE_ERROR) { + return new_sexp; } *next_cell = ucl_cell_create(new_sexp, NULL); @@ -206,7 +219,6 @@ struct ucl_object *ucl_parse_tokens(struct ucl_object *tokens) { return resultl; error: - ucl_object_delete(resultl); return NULL; } @@ -214,13 +226,13 @@ struct ucl_object *ucl_parse_tokens(struct ucl_object *tokens) { // all sexps in the source (return a list of sexps)? struct ucl_object *ucl_parse(const char *source) { struct ucl_object *tokens = ucl_tokenize(source); + if (tokens == NULL) { + tokens = ucl_nil_create(); + } + UCL_RET_IF_ERROR(tokens); struct ucl_object *sexp = ucl_parse_tokens(tokens); - ucl_object_delete(tokens); + if (sexp == NULL) { + return ucl_nil_create(); + } return sexp; } - -/* struct ParseResult *ucl_parse(const char *source) { */ -/* struct Cell *tokens = ucl_tokenize(source); */ -/* struct Cell *sexp = n */ - -/* } */ diff --git a/src/special.c b/src/special.c index 170321b..68cf6af 100644 --- a/src/special.c +++ b/src/special.c @@ -5,6 +5,8 @@ #include +// TODO: Macro support? + struct ucl_object *ucl_special_let(struct ucl_scope *scope, struct ucl_object *args) { // TODO: Check arguments struct ucl_object *assignments = ucl_car(args); diff --git a/test/test_parse.c b/test/test_parse.c index fe74e17..4c1afbb 100644 --- a/test/test_parse.c +++ b/test/test_parse.c @@ -4,6 +4,7 @@ #include "uclisp.h" #include "internal.h" +#include "testing_helpers.h" /* static struct ucl_parse_result *result; */ static struct ucl_object *response; @@ -331,6 +332,20 @@ static void test_parse_nested(void) { TEST_ASSERT_EQUAL_STRING("foo", response->cell.car->cell.car->cell.car->symbol); } +static void test_parse_mismatched_open(void) { + response = ucl_parse("("); + + TEST_ASSERT_NOT_NULL(response); + TEST_ASSERT_OBJ_ERROR(response); +} + +static void test_parse_mismatched_closed(void) { + response = ucl_parse(")"); + + TEST_ASSERT_NOT_NULL(response); + TEST_ASSERT_OBJ_ERROR(response); +} + int main(void) { UNITY_BEGIN(); @@ -357,5 +372,7 @@ int main(void) { RUN_TEST(test_parse_2elem); RUN_TEST(test_parse_2elem_str); RUN_TEST(test_parse_nested); + RUN_TEST(test_parse_mismatched_open); + RUN_TEST(test_parse_mismatched_closed); return UNITY_END(); }