diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bb13075..a5a3859 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,4 +33,4 @@ jobs: - name: Run tests with valgrind memory leak check run: | cd build - valgrind --leak-check=full --error-exitcode=1 ./tests/minissd_tests + valgrind --leak-check=full --track-origins=yes --error-exitcode=1 ./tests/minissd_tests diff --git a/.vscode/settings.json b/.vscode/settings.json index c262886..67e2582 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ "cSpell.words": [ "libgtest", "MINISSD", + "STREQ", "valgrind" ], "files.associations": { diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d31dac..7fed69a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,10 +5,17 @@ set(CMAKE_C_STANDARD 99) option(MINISSD_BUILD_EXAMPLE "Build example" ON) option(MINISSD_BUILD_TESTS "Build tests" ON) +option(MINISSD_BUILD_SHARED "Build shared library" OFF) set(SOURCES src/minissd.c include/minissd.h) -add_library(${PROJECT_NAME} STATIC ${SOURCES}) +if(MINISSD_BUILD_SHARED) + add_library(${PROJECT_NAME} SHARED ${SOURCES}) + target_compile_definitions(${PROJECT_NAME} PRIVATE MINISSD_SHARED) + target_compile_definitions(${PROJECT_NAME} PRIVATE MINISSD_EXPORTS) +else() + add_library(${PROJECT_NAME} STATIC ${SOURCES}) +endif() target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) diff --git a/example/minissd_print.c b/example/minissd_print.c index ed5c58f..e6c97e1 100644 --- a/example/minissd_print.c +++ b/example/minissd_print.c @@ -5,18 +5,18 @@ #include #include -void print_attributes(Attribute *attr) +void print_attributes(Attribute const *attr) { while (attr) { printf(" Attribute: %s\n", minissd_get_attribute_name(attr)); - for (Argument *arg = minissd_get_attribute_arguments(attr); arg; arg = minissd_get_next_argument(arg)) + for (AttributeParameter const *arg = minissd_get_attribute_parameters(attr); arg; arg = minissd_get_next_attribute_parameter(arg)) { - printf(" Argument: %s", arg->key); - if (arg->value) + printf(" Parameter: %s", arg->key); + if (arg->opt_value) { - printf(" = %s", arg->value); + printf(" = %s", arg->opt_value); } printf("\n"); } @@ -61,10 +61,18 @@ int main(int argc, char **argv) return 1; } - for (AstNode *node = ast; node; node = minissd_get_next_node(node)) + for (AstNode const *node = ast; node; node = minissd_get_next_node(node)) { printf("Node Type: "); - switch (minissd_get_node_type(node)) + NodeType const *type = minissd_get_node_type(node); + + if (!type) + { + printf("Unknown Node Type\n"); + continue; + } + + switch (*type) { case NODE_IMPORT: printf("Import\n"); @@ -77,7 +85,7 @@ int main(int argc, char **argv) printf(" Name: %s\n", minissd_get_data_name(node)); print_attributes(minissd_get_attributes(node)); - for (Property *prop = minissd_get_properties(node); prop; prop = minissd_get_next_property(prop)) + for (Property const *prop = minissd_get_properties(node); prop; prop = minissd_get_next_property(prop)) { printf(" Property: %s : %s\n", minissd_get_property_name(prop), minissd_get_property_type(prop)); print_attributes(prop->attributes); @@ -89,11 +97,11 @@ int main(int argc, char **argv) printf(" Name: %s\n", minissd_get_enum_name(node)); print_attributes(minissd_get_attributes(node)); - for (EnumVariant *value = minissd_get_enum_variants(node); value; value = minissd_get_next_enum_value(value)) + for (EnumVariant const *value = minissd_get_enum_variants(node); value; value = minissd_get_next_enum_variant(value)) { bool has_value; - int val = minissd_get_enum_variant(value, &has_value); - printf(" Enum Value: %s", minissd_get_enum_variant_name(value)); + int val = minissd_get_enum_variant_value(value, &has_value); + printf(" Enum Variant: %s", minissd_get_enum_variant_name(value)); if (has_value) { printf(" = %d", val); @@ -103,8 +111,43 @@ int main(int argc, char **argv) } break; - default: - printf("Unknown Node Type\n"); + case NODE_SERVICE: + printf("Service\n"); + printf(" Name: %s\n", minissd_get_service_name(node)); + print_attributes(minissd_get_attributes(node)); + + for (Dependency const *dep = minissd_get_dependencies(node); dep; dep = minissd_get_next_dependency(dep)) + { + printf(" Depends: %s\n", minissd_get_dependency_path(dep)); + print_attributes(dep->opt_ll_attributes); + } + + for (Handler const *handler = minissd_get_handlers(node); handler; handler = minissd_get_next_handler(handler)) + { + printf(" Handler: %s\n", handler->name); + if (handler->opt_return_type) + { + printf(" Return Type: %s\n", handler->opt_return_type); + } + for (Argument const *arg = minissd_get_handler_arguments(handler); arg; arg = minissd_get_next_argument(arg)) + { + printf(" Argument: %s : %s\n", minissd_get_argument_name(arg), minissd_get_argument_type(arg)); + print_attributes(minissd_get_argument_attributes(arg)); + } + print_attributes(handler->opt_ll_attributes); + } + + for (Event const *event = minissd_get_events(node); event; event = minissd_get_next_event(event)) + { + printf(" Event: %s\n", event->name); + for (Argument const *arg = minissd_get_event_arguments(event); arg; arg = minissd_get_next_argument(arg)) + { + printf(" Argument: %s : %s\n", minissd_get_argument_name(arg), minissd_get_argument_type(arg)); + print_attributes(minissd_get_argument_attributes(arg)); + } + print_attributes(event->opt_ll_attributes); + } + break; } } diff --git a/include/minissd.h b/include/minissd.h index 735e007..cefd4a2 100644 --- a/include/minissd.h +++ b/include/minissd.h @@ -2,6 +2,7 @@ #define MINISSD_H #include +#include #ifndef MAX_ERROR_SIZE #define MAX_ERROR_SIZE 512 @@ -11,21 +12,36 @@ #define MAX_TOKEN_SIZE 512 #endif +// DLL Export/Import Macros +#ifdef _WIN32 +#ifdef MINISSD_SHARED +#ifdef MINISSD_EXPORTS +#define MINISSD_API __declspec(dllexport) +#else +#define MINISSD_API __declspec(dllimport) +#endif +#else +#define MINISSD_API +#endif +#else +#define MINISSD_API +#endif + #ifdef __cplusplus extern "C" { #endif - typedef struct Argument + typedef struct AttributeParameter { char *key; - char *value; // Nullable - struct Argument *next; - } Argument; + char *opt_value; // Nullable + struct AttributeParameter *next; + } AttributeParameter; typedef struct Attribute { char *name; - Argument *ll_arguments; + AttributeParameter *opt_ll_arguments; struct Attribute *next; } Attribute; @@ -41,10 +57,42 @@ extern "C" { Attribute *attributes; char *name; - int *value; // Nullable + int *opt_value; // Nullable struct EnumVariant *next; } EnumVariant; + typedef struct Argument + { + Attribute *attributes; + char *name; + char *type; + struct Argument *next; + } Argument; + + typedef struct Handler + { + Attribute *opt_ll_attributes; + char *name; + Argument *opt_ll_arguments; + char *opt_return_type; + struct Handler *next; + } Handler; + + typedef struct Event + { + Attribute *opt_ll_attributes; + char *name; + Argument *opt_ll_arguments; + struct Event *next; + } Event; + + typedef struct Dependency + { + Attribute *opt_ll_attributes; + char *path; + struct Dependency *next; + } Dependency; + typedef struct Import { char *path; @@ -62,22 +110,32 @@ extern "C" EnumVariant *ll_variants; } Enum; + typedef struct Service + { + char *name; + Dependency *opt_ll_dependencies; + Handler *opt_ll_handlers; + Event *opt_ll_events; + } Service; + typedef enum { NODE_IMPORT, NODE_DATA, - NODE_ENUM + NODE_ENUM, + NODE_SERVICE } NodeType; typedef struct AstNode { NodeType type; - Attribute *ll_attributes; + Attribute *opt_ll_attributes; union { - Import importNode; - Data dataNode; - Enum enumNode; + Import import_node; + Data data_node; + Enum enum_node; + Service service_node; } node; struct AstNode *next; } AstNode; @@ -85,6 +143,7 @@ extern "C" typedef struct { const char *input; + size_t input_length; char error[MAX_ERROR_SIZE]; char current; int index; @@ -93,41 +152,137 @@ extern "C" } Parser; // Parser creation and destruction - Parser *minissd_create_parser(const char *input); + MINISSD_API Parser *minissd_create_parser(const char *input); void minissd_free_parser(Parser *p); // Parsing function - AstNode *minissd_parse(Parser *p); + MINISSD_API AstNode *minissd_parse(Parser *p); void minissd_free_ast(AstNode *ast); - // AST Node accessors - NodeType minissd_get_node_type(const AstNode *node); - const char *minissd_get_import_path(const AstNode *node); - const char *minissd_get_data_name(const AstNode *node); - const char *minissd_get_enum_name(const AstNode *node); - - // Attribute accessors - Attribute *minissd_get_attributes(const AstNode *node); - const char *minissd_get_attribute_name(const Attribute *attr); - Argument *minissd_get_attribute_arguments(const Attribute *attr); - - // Property and EnumVariant accessors - Property *minissd_get_properties(const AstNode *node); - const char *minissd_get_property_name(const Property *prop); - Attribute *minissd_get_property_attributes(const Property *prop); - const char *minissd_get_property_type(const Property *prop); - - EnumVariant *minissd_get_enum_variants(const AstNode *node); - const char *minissd_get_enum_variant_name(const EnumVariant *value); - Attribute *minissd_get_enum_variant_attributes(const EnumVariant *value); - int minissd_get_enum_variant(const EnumVariant *value, bool *has_value); - - // Traversal functions - AstNode *minissd_get_next_node(const AstNode *node); - Property *minissd_get_next_property(const Property *prop); - EnumVariant *minissd_get_next_enum_value(const EnumVariant *value); - Attribute *minissd_get_next_attribute(const Attribute *attr); - Argument *minissd_get_next_argument(const Argument *arg); + // AST Node Accessors + MINISSD_API NodeType const * + minissd_get_node_type(AstNode const *node); + + MINISSD_API char const * + minissd_get_import_path(AstNode const *node); + + MINISSD_API char const * + minissd_get_data_name(AstNode const *node); + + MINISSD_API char const * + minissd_get_enum_name(AstNode const *node); + + MINISSD_API char const * + minissd_get_service_name(AstNode const *node); + + MINISSD_API Property const * + minissd_get_properties(AstNode const *node); + + MINISSD_API EnumVariant const * + minissd_get_enum_variants(AstNode const *node); + + MINISSD_API Dependency const * + minissd_get_dependencies(AstNode const *node); + + MINISSD_API Handler const * + minissd_get_handlers(AstNode const *node); + + MINISSD_API Event const * + minissd_get_events(AstNode const *node); + + MINISSD_API Attribute const * + minissd_get_attributes(AstNode const *node); + + MINISSD_API AstNode const * + minissd_get_next_node(AstNode const *node); + + // Handler Accessors + MINISSD_API char const * + minissd_get_handler_name(Handler const *node); + + MINISSD_API char const * + minissd_get_handler_return_type(Handler const *handler); + + MINISSD_API Argument const * + minissd_get_handler_arguments(Handler const *node); + + MINISSD_API Handler const * + minissd_get_next_handler(Handler const *handler); + + // Event Accessors + MINISSD_API char const * + minissd_get_event_name(Event const *event); + + MINISSD_API Argument const * + minissd_get_event_arguments(Event const *event); + + MINISSD_API Event const * + minissd_get_next_event(Event const *event); + + // Dependency Accessors + MINISSD_API char const * + minissd_get_dependency_path(Dependency const *dep); + + MINISSD_API Dependency const * + minissd_get_next_dependency(Dependency const *dep); + + // Property Accessors + MINISSD_API char const * + minissd_get_property_name(Property const *prop); + + MINISSD_API char const * + minissd_get_property_type(Property const *prop); + + MINISSD_API Attribute const * + minissd_get_property_attributes(Property const *prop); + + MINISSD_API Property const * + minissd_get_next_property(Property const *prop); + + // Enum Variant Accessors + MINISSD_API char const * + minissd_get_enum_variant_name(EnumVariant const *value); + + MINISSD_API int minissd_get_enum_variant_value(EnumVariant const *value, bool *has_value); + + MINISSD_API Attribute const * + minissd_get_enum_variant_attributes(EnumVariant const *value); + + MINISSD_API EnumVariant const * + minissd_get_next_enum_variant(EnumVariant const *value); + + // Argument Accessors + MINISSD_API char const * + minissd_get_argument_name(Argument const *prop); + + MINISSD_API char const * + minissd_get_argument_type(Argument const *prop); + + MINISSD_API Attribute const * + minissd_get_argument_attributes(Argument const *prop); + + MINISSD_API Argument const * + minissd_get_next_argument(Argument const *arg); + + // Attribute Accessors + MINISSD_API char const * + minissd_get_attribute_name(Attribute const *attr); + + MINISSD_API AttributeParameter const * + minissd_get_attribute_parameters(Attribute const *attr); + + MINISSD_API Attribute const * + minissd_get_next_attribute(Attribute const *attr); + + // Attribute Parameter Accessors + MINISSD_API char const * + minissd_get_attribute_parameter_name(AttributeParameter const *arg); + + MINISSD_API char const * + minissd_get_attribute_parameter_value(AttributeParameter const *arg); + + MINISSD_API AttributeParameter const * + minissd_get_next_attribute_parameter(AttributeParameter const *arg); #ifdef __cplusplus } diff --git a/src/minissd.c b/src/minissd.c index e7ba542..a20c839 100644 --- a/src/minissd.c +++ b/src/minissd.c @@ -7,46 +7,43 @@ #include #include +#define CHECKED_DECLARE(type, obj, func) \ + type obj = func(p); \ + if (!obj) \ + { \ + return NULL; \ + } + static char * strdup_c99(const char *s) { - if (s == NULL) - return NULL; + assert(s); size_t len = strlen(s) + 1; char *dup = (char *)malloc(len); + assert(dup); - if (dup) - { - memcpy(dup, s, len); - } + memcpy(dup, s, len); return dup; } -#define CHECKED_DECLARE(type, obj, func) \ - type obj = func(p); \ - if (!obj) \ - { \ - return NULL; \ - } - // Free functions static void -free_arguments(Argument *args) +free_attribute_parameters(AttributeParameter *args) { - Argument *current = args; + AttributeParameter *current = args; while (current) { if (current->key) { free(current->key); } - if (current->value) + if (current->opt_value) { - free(current->value); + free(current->opt_value); } - Argument *next = current->next; + AttributeParameter *next = current->next; free(current); current = next; }; @@ -62,13 +59,34 @@ free_attributes(Attribute *attrs) { free(current_attr->name); } - free_arguments(current_attr->ll_arguments); + free_attribute_parameters(current_attr->opt_ll_arguments); Attribute *next_attr = current_attr->next; free(current_attr); current_attr = next_attr; }; } +static void +free_arguments(Argument *args) +{ + Argument *current = args; + while (current) + { + if (current->name) + { + free(current->name); + } + if (current->type) + { + free(current->type); + } + free_attributes(current->attributes); + Argument *next = current->next; + free(current); + current = next; + }; +} + static void free_properties(Property *prop) { @@ -100,9 +118,9 @@ free_enum_variants(EnumVariant *variants) { free(current->name); } - if (current->value) + if (current->opt_value) { - free(current->value); + free(current->opt_value); } free_attributes(current->attributes); EnumVariant *next = current->next; @@ -111,34 +129,99 @@ free_enum_variants(EnumVariant *variants) }; } +static void +free_dependencies(Dependency *deps) +{ + Dependency *current = deps; + while (current) + { + if (current->path) + { + free(current->path); + } + free_attributes(current->opt_ll_attributes); + Dependency *next = current->next; + free(current); + current = next; + }; +} +static void +free_handlers(Handler *handlers) +{ + Handler *current = handlers; + while (current) + { + if (current->name) + { + free(current->name); + } + if (current->opt_return_type) + { + free(current->opt_return_type); + } + free_attributes(current->opt_ll_attributes); + free_arguments(current->opt_ll_arguments); + Handler *next = current->next; + free(current); + current = next; + }; +} + +static void +free_events(Event *events) +{ + Event *current = events; + while (current) + { + if (current->name) + { + free(current->name); + } + free_attributes(current->opt_ll_attributes); + free_arguments(current->opt_ll_arguments); + Event *next = current->next; + free(current); + current = next; + }; +} + static void free_ast(AstNode *ast) { AstNode *current = ast; while (current) { - free_attributes(current->ll_attributes); + free_attributes(current->opt_ll_attributes); switch (current->type) { case NODE_IMPORT: - if (current->node.importNode.path) + if (current->node.import_node.path) { - free(current->node.importNode.path); + free(current->node.import_node.path); } break; case NODE_DATA: - if (current->node.dataNode.name) + if (current->node.data_node.name) { - free(current->node.dataNode.name); + free(current->node.data_node.name); } - free_properties(current->node.dataNode.ll_properties); + free_properties(current->node.data_node.ll_properties); break; case NODE_ENUM: - if (current->node.enumNode.name) + if (current->node.enum_node.name) { - free(current->node.enumNode.name); + free(current->node.enum_node.name); } - free_enum_variants(current->node.enumNode.ll_variants); + free_enum_variants(current->node.enum_node.ll_variants); + break; + case NODE_SERVICE: + if (current->node.service_node.name) + { + free(current->node.service_node.name); + } + free_dependencies(current->node.service_node.opt_ll_dependencies); + free_handlers(current->node.service_node.opt_ll_handlers); + free_events(current->node.service_node.opt_ll_events); break; default: break; @@ -156,6 +239,25 @@ error(Parser *p, const char *message) snprintf(p->error, MAX_ERROR_SIZE, "Error: %s at line %d, column %d", message, p->line, p->column); } +static char +peek(const Parser *p) +{ + if (p->index < p->input_length) + { + return p->input[p->index]; + } + return '\0'; +} + +void debug(const Parser *p) +{ + printf("Current: %c\n", p->current); + printf("Next: %c\n", peek(p)); + printf("Index: %d\n", p->index); + printf("Line: %d\n", p->line); + printf("Column: %d\n", p->column); +} + static void advance(Parser *p) { @@ -177,11 +279,24 @@ advance(Parser *p) } static void -eat_whitespace(Parser *p) +eat_whitespaces_and_comments(Parser *p) { - while (isspace(p->current)) + bool comment = true; + while (comment) { - advance(p); + comment = false; + while (isspace(p->current)) + { + advance(p); + } + if (p->current == '/' && peek(p) == '/') + { + comment = true; + while (p->current != '\n' && p->current != '\0') + { + advance(p); + } + } } } @@ -196,7 +311,7 @@ parse_path(Parser *p) { char buffer[MAX_TOKEN_SIZE + 1]; int length = 0; - eat_whitespace(p); + eat_whitespaces_and_comments(p); while (length <= MAX_TOKEN_SIZE && p->current != '\0' && (is_alphanumeric(p->current) || p->current == ':')) { if (length == MAX_TOKEN_SIZE) @@ -303,11 +418,11 @@ static Attribute * parse_attributes(Parser *p) { Attribute *head = NULL, *tail = NULL; - eat_whitespace(p); + eat_whitespaces_and_comments(p); while (p->current == '#') { advance(p); - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current != '[') { free_attributes(head); @@ -315,13 +430,13 @@ parse_attributes(Parser *p) return NULL; } advance(p); - eat_whitespace(p); + eat_whitespaces_and_comments(p); while (p->current != ']') { Attribute *attr = (Attribute *)calloc(1, sizeof(Attribute)); assert(attr); - eat_whitespace(p); + eat_whitespaces_and_comments(p); attr->name = parse_identifier(p); if (!attr->name) { @@ -330,39 +445,39 @@ parse_attributes(Parser *p) return NULL; }; - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current == '(') { advance(p); - Argument *arg_head = NULL, *arg_tail = NULL; + AttributeParameter *arg_head = NULL, *arg_tail = NULL; - eat_whitespace(p); + eat_whitespaces_and_comments(p); while (p->current != ')') { - Argument *arg = (Argument *)calloc(1, sizeof(Argument)); + AttributeParameter *arg = (AttributeParameter *)calloc(1, sizeof(AttributeParameter)); assert(arg); - eat_whitespace(p); + eat_whitespaces_and_comments(p); arg->key = parse_identifier(p); if (!arg->key) { - free_arguments(arg); - free_arguments(arg_head); + free_attribute_parameters(arg); + free_attribute_parameters(arg_head); free_attributes(attr); free_attributes(head); return NULL; }; - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current == '=') { advance(p); - eat_whitespace(p); - arg->value = parse_string(p); - if (!arg->value) + eat_whitespaces_and_comments(p); + arg->opt_value = parse_string(p); + if (!arg->opt_value) { - free_arguments(arg); - free_arguments(arg_head); + free_attribute_parameters(arg); + free_attribute_parameters(arg_head); free_attributes(attr); free_attributes(head); return NULL; @@ -379,7 +494,7 @@ parse_attributes(Parser *p) } arg_tail = arg; - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current != ',') { break; @@ -387,18 +502,18 @@ parse_attributes(Parser *p) advance(p); } - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current != ')') { error(p, "Expected ')' after attribute argument"); - free_arguments(arg_head); + free_attribute_parameters(arg_head); free_attributes(attr); free_attributes(head); return NULL; } advance(p); - attr->ll_arguments = arg_head; + attr->opt_ll_arguments = arg_head; } if (!head) @@ -411,7 +526,7 @@ parse_attributes(Parser *p) } tail = attr; - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current != ',') { break; @@ -419,7 +534,7 @@ parse_attributes(Parser *p) advance(p); } - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current != ']') { free_attributes(head); @@ -427,7 +542,7 @@ parse_attributes(Parser *p) return NULL; } advance(p); - eat_whitespace(p); + eat_whitespaces_and_comments(p); } return head; } @@ -444,17 +559,17 @@ parse_enum_variants(Parser *p) EnumVariant *head = NULL, *tail = NULL; - eat_whitespace(p); + eat_whitespaces_and_comments(p); while (p->current != '}') { EnumVariant *ev = (EnumVariant *)calloc(1, sizeof(EnumVariant)); assert(ev); - eat_whitespace(p); + eat_whitespaces_and_comments(p); ev->attributes = parse_attributes(p); - eat_whitespace(p); + eat_whitespaces_and_comments(p); ev->name = parse_identifier(p); if (!ev->name) { @@ -463,14 +578,14 @@ parse_enum_variants(Parser *p) return NULL; }; - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current == '=') { advance(p); - eat_whitespace(p); - ev->value = parse_int(p); - if (!ev->value) + eat_whitespaces_and_comments(p); + ev->opt_value = parse_int(p); + if (!ev->opt_value) { free_enum_variants(ev); free_enum_variants(head); @@ -488,16 +603,16 @@ parse_enum_variants(Parser *p) } tail = ev; - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current != ',') { break; } advance(p); - eat_whitespace(p); + eat_whitespaces_and_comments(p); } - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current != '}') { error(p, "Expected ',' after enum value"); @@ -525,17 +640,17 @@ parse_properties(Parser *p) Property *head = NULL, *tail = NULL; - eat_whitespace(p); + eat_whitespaces_and_comments(p); while (p->current != '}') { Property *prop = (Property *)calloc(1, sizeof(Property)); assert(prop); - eat_whitespace(p); + eat_whitespaces_and_comments(p); prop->attributes = parse_attributes(p); - eat_whitespace(p); + eat_whitespaces_and_comments(p); prop->name = parse_identifier(p); if (!prop->name) { @@ -544,7 +659,7 @@ parse_properties(Parser *p) return NULL; }; - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current != ':') { error(p, "Expected ':' after property name"); @@ -554,7 +669,7 @@ parse_properties(Parser *p) } advance(p); - eat_whitespace(p); + eat_whitespaces_and_comments(p); prop->type = parse_identifier(p); if (!prop->type) { @@ -573,16 +688,16 @@ parse_properties(Parser *p) } tail = prop; - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current != ',') { break; } advance(p); - eat_whitespace(p); + eat_whitespaces_and_comments(p); } - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current != '}') { error(p, "Expected ',' after property"); @@ -590,16 +705,327 @@ parse_properties(Parser *p) return NULL; } advance(p); + + if (!head) + { + error(p, "Expected property"); + return NULL; + } + + return head; +} + +static Argument * +parse_handler_arguments(Parser *p) +{ + Argument *head = NULL, *tail = NULL; + while (p->current != ')') + { + Argument *arg = (Argument *)calloc(1, sizeof(Argument)); + assert(arg); + eat_whitespaces_and_comments(p); + arg->name = parse_identifier(p); + eat_whitespaces_and_comments(p); + if (!arg->name) + { + error(p, "Expected argument name"); + free_arguments(arg); + free_arguments(head); + return NULL; + }; + if (p->current != ':') + { + error(p, "Expected ':' after argument name"); + free_arguments(arg); + free_arguments(head); + return NULL; + } + advance(p); + eat_whitespaces_and_comments(p); + arg->type = parse_identifier(p); + eat_whitespaces_and_comments(p); + if (!arg->type) + { + error(p, "Expected argument type"); + free_arguments(arg); + free_arguments(head); + return NULL; + }; + + if (!head) + { + head = arg; + } + else + { + tail->next = arg; + } + tail = arg; + + if (p->current != ',') + { + break; + } + advance(p); + eat_whitespaces_and_comments(p); + } return head; } +typedef struct ServiceComponents +{ + Handler *opt_ll_handlers; + Dependency *opt_ll_dependencies; + Event *opt_ll_events; +} ServiceComponents; + +static void +free_service_components(ServiceComponents *sc) +{ + free_handlers(sc->opt_ll_handlers); + free_dependencies(sc->opt_ll_dependencies); + free_events(sc->opt_ll_events); + free(sc); +} + +static ServiceComponents * +parse_service(Parser *p) +{ + if (p->current != '{') + { + error(p, "Expected '{' after service name"); + return NULL; + } + advance(p); + + Handler *handler_head = NULL, *handler_tail = NULL; + Dependency *dep_head = NULL, *dep_tail = NULL; + Event *event_head = NULL, *event_tail = NULL; + + while (p->current != '}') + { + eat_whitespaces_and_comments(p); + Attribute *attributes = parse_attributes(p); + eat_whitespaces_and_comments(p); + char *ident = parse_identifier(p); + if (!ident) + { + error(p, "Expected 'depends' or 'fn' keyword"); + free(ident); + free_attributes(attributes); + free_dependencies(dep_head); + free_handlers(handler_head); + free_events(event_head); + return NULL; + }; + + if (strcmp(ident, "depends") == 0) + { + Dependency *dep = (Dependency *)calloc(1, sizeof(Dependency)); + assert(dep); + + dep->opt_ll_attributes = attributes; + dep->path = parse_path(p); + if (!dep->path) + { + error(p, "Expected dependency path"); + free(ident); + free_dependencies(dep); + free_dependencies(dep_head); + free_handlers(handler_head); + free_events(event_head); + return NULL; + }; + + if (!dep_head) + { + dep_head = dep; + } + else + { + dep_tail->next = dep; + } + dep_tail = dep; + } + else if (strcmp(ident, "fn") == 0) + { + Handler *handler = (Handler *)calloc(1, sizeof(Handler)); + assert(handler); + + handler->opt_ll_attributes = attributes; + + eat_whitespaces_and_comments(p); + handler->name = parse_identifier(p); + + if (!handler->name) + { + error(p, "Expected handler name"); + free(ident); + free_handlers(handler); + free_handlers(handler_head); + free_events(event_head); + free_dependencies(dep_head); + return NULL; + }; + + eat_whitespaces_and_comments(p); + if (p->current != '(') + { + error(p, "Expected '(' after handler name"); + free(ident); + free_handlers(handler); + free_handlers(handler_head); + free_events(event_head); + free_dependencies(dep_head); + return NULL; + } + advance(p); + + eat_whitespaces_and_comments(p); + handler->opt_ll_arguments = parse_handler_arguments(p); + eat_whitespaces_and_comments(p); + + if (p->current != ')') + { + error(p, "Expected ')' after handler arguments"); + free(ident); + free_handlers(handler); + free_handlers(handler_head); + free_events(event_head); + free_dependencies(dep_head); + return NULL; + } + advance(p); + + eat_whitespaces_and_comments(p); + if (p->current == '-' && peek(p) == '>') + { + advance(p); + advance(p); + eat_whitespaces_and_comments(p); + handler->opt_return_type = parse_identifier(p); + eat_whitespaces_and_comments(p); + if (!handler->opt_return_type) + { + error(p, "Expected return type after ':'"); + free(ident); + free_handlers(handler); + free_handlers(handler_head); + free_events(event_head); + free_dependencies(dep_head); + return NULL; + }; + } + + if (!handler_head) + { + handler_head = handler; + } + else + { + handler_tail->next = handler; + } + handler_tail = handler; + } + else if (strcmp(ident, "event") == 0) + { + Event *event = (Event *)calloc(1, sizeof(Event)); + assert(event); + event->opt_ll_attributes = attributes; + + eat_whitespaces_and_comments(p); + event->name = parse_identifier(p); + if (!event->name) + { + error(p, "Expected event name"); + free(ident); + free_events(event); + free_events(event_head); + free_dependencies(dep_head); + free_handlers(handler_head); + return NULL; + }; + + eat_whitespaces_and_comments(p); + if (p->current != '(') + { + error(p, "Expected '(' after event name"); + free(ident); + free_events(event); + free_events(event_head); + free_dependencies(dep_head); + free_handlers(handler_head); + return NULL; + } + advance(p); + + eat_whitespaces_and_comments(p); + event->opt_ll_arguments = parse_handler_arguments(p); + eat_whitespaces_and_comments(p); + if (p->current != ')') + { + error(p, "Expected ')' after event arguments"); + free(ident); + free_events(event); + free_events(event_head); + free_dependencies(dep_head); + free_handlers(handler_head); + return NULL; + } + advance(p); + eat_whitespaces_and_comments(p); + + if (!event_head) + { + event_head = event; + } + else + { + event_tail->next = event; + } + event_tail = event; + } + else + { + error(p, "Expected 'depends' or 'fn' keyword"); + free(ident); + free_events(event_head); + free_attributes(attributes); + free_dependencies(dep_head); + free_handlers(handler_head); + return NULL; + } + + free(ident); + + eat_whitespaces_and_comments(p); + if (p->current != ';') + { + error(p, "Expected ';' after service component"); + free_events(event_head); + free_dependencies(dep_head); + free_handlers(handler_head); + return NULL; + } + advance(p); + + eat_whitespaces_and_comments(p); + } + advance(p); + ServiceComponents *sc = (ServiceComponents *)calloc(1, sizeof(ServiceComponents)); + sc->opt_ll_handlers = handler_head; + sc->opt_ll_dependencies = dep_head; + sc->opt_ll_events = event_head; + return sc; +} + static AstNode * parse_node(Parser *p) { - eat_whitespace(p); + eat_whitespaces_and_comments(p); Attribute *attributes = parse_attributes(p); - eat_whitespace(p); + eat_whitespaces_and_comments(p); char *ident = parse_identifier(p); if (!ident) { @@ -609,14 +1035,15 @@ parse_node(Parser *p) AstNode *node = (AstNode *)calloc(1, sizeof(AstNode)); assert(node); - eat_whitespace(p); - node->ll_attributes = attributes; + eat_whitespaces_and_comments(p); + node->opt_ll_attributes = attributes; if (strcmp(ident, "import") == 0) { node->type = NODE_IMPORT; - node->node.importNode.path = parse_path(p); - if (!node->node.importNode.path) + node->node.import_node.path = parse_path(p); + if (!node->node.import_node.path) { + error(p, "Expected import path"); free(ident); free_ast(node); return NULL; @@ -625,16 +1052,17 @@ parse_node(Parser *p) else if (strcmp(ident, "data") == 0) { node->type = NODE_DATA; - node->node.dataNode.name = parse_identifier(p); - if (!node->node.dataNode.name) + node->node.data_node.name = parse_identifier(p); + if (!node->node.data_node.name) { + error(p, "Expected data name"); free(ident); free_ast(node); return NULL; }; - eat_whitespace(p); - node->node.dataNode.ll_properties = parse_properties(p); - if (!node->node.dataNode.ll_properties) + eat_whitespaces_and_comments(p); + node->node.data_node.ll_properties = parse_properties(p); + if (!node->node.data_node.ll_properties) { free(ident); free_ast(node); @@ -644,22 +1072,55 @@ parse_node(Parser *p) else if (strcmp(ident, "enum") == 0) { node->type = NODE_ENUM; - node->node.enumNode.name = parse_identifier(p); - if (!node->node.enumNode.name) + node->node.enum_node.name = parse_identifier(p); + if (!node->node.enum_node.name) { + error(p, "Expected enum name"); free(ident); free_ast(node); return NULL; }; - eat_whitespace(p); - node->node.enumNode.ll_variants = parse_enum_variants(p); - if (!node->node.enumNode.ll_variants) + eat_whitespaces_and_comments(p); + node->node.enum_node.ll_variants = parse_enum_variants(p); + if (!node->node.enum_node.ll_variants) { free(ident); free_ast(node); return NULL; }; } + else if (strcmp(ident, "service") == 0) + { + node->type = NODE_SERVICE; + node->node.service_node.name = parse_identifier(p); + if (!node->node.service_node.name) + { + error(p, "Expected service name"); + free(ident); + free_ast(node); + return NULL; + }; + eat_whitespaces_and_comments(p); + ServiceComponents *sc = parse_service(p); + if (!sc) + { + free(ident); + free_ast(node); + return NULL; + }; + if (!sc->opt_ll_handlers && !sc->opt_ll_events) + { + error(p, "Service must have at least one handler or event"); + free_service_components(sc); + free(ident); + free_ast(node); + return NULL; + } + node->node.service_node.opt_ll_handlers = sc->opt_ll_handlers; + node->node.service_node.opt_ll_dependencies = sc->opt_ll_dependencies; + node->node.service_node.opt_ll_events = sc->opt_ll_events; + free(sc); + } else { error(p, "Unknown node type"); @@ -669,7 +1130,7 @@ parse_node(Parser *p) free(ident); if (node) { - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current != ';') { switch (node->type) @@ -683,6 +1144,9 @@ parse_node(Parser *p) case NODE_ENUM: error(p, "Expected ';' after enum declaration"); break; + case NODE_SERVICE: + error(p, "Expected ';' after service declaration"); + break; } free_ast(node); return NULL; @@ -729,34 +1193,15 @@ create_parser(const char *input) Parser *p = (Parser *)calloc(1, sizeof(Parser)); assert(p); p->input = input; + p->input_length = strlen(input); p->line = 1; p->column = 1; return p; } -static void -print_ast(AstNode *ast) -{ - while (ast) - { - switch (ast->type) - { - case NODE_IMPORT: - printf("Import: %s\n", ast->node.importNode.path); - break; - case NODE_DATA: - printf("Data: %s\n", ast->node.dataNode.name); - break; - case NODE_ENUM: - printf("Enum: %s\n", ast->node.enumNode.name); - break; - } - ast = ast->next; - } -} - // Parser functions -Parser *minissd_create_parser(const char *input) +Parser * +minissd_create_parser(const char *input) { return create_parser(input); } @@ -767,7 +1212,8 @@ void minissd_free_parser(Parser *p) } // Parsing -AstNode *minissd_parse(Parser *p) +AstNode * +minissd_parse(Parser *p) { return parse(p); } @@ -778,114 +1224,249 @@ void minissd_free_ast(AstNode *ast) } // AST Node accessors -NodeType minissd_get_node_type(const AstNode *node) +NodeType const * +minissd_get_node_type(AstNode const *node) +{ + return node ? &node->type : NULL; +} + +char const * +minissd_get_import_path(AstNode const *node) +{ + return (node && node->type == NODE_IMPORT) ? node->node.import_node.path : NULL; +} + +char const * +minissd_get_data_name(AstNode const *node) +{ + return (node && node->type == NODE_DATA) ? node->node.data_node.name : NULL; +} + +char const * +minissd_get_enum_name(AstNode const *node) { - return node ? node->type : -1; + return (node && node->type == NODE_ENUM) ? node->node.enum_node.name : NULL; } -const char *minissd_get_import_path(const AstNode *node) +char const * +minissd_get_handler_name(Handler const *handler) { - return (node && node->type == NODE_IMPORT) ? node->node.importNode.path : NULL; + return (handler) ? handler->name : NULL; } -const char *minissd_get_data_name(const AstNode *node) +char const * +minissd_get_handler_return_type(Handler const *handler) { - return (node && node->type == NODE_DATA) ? node->node.dataNode.name : NULL; + return (handler) ? handler->opt_return_type : NULL; } -const char *minissd_get_enum_name(const AstNode *node) +char const * +minissd_get_event_name(Event const *event) { - return (node && node->type == NODE_ENUM) ? node->node.enumNode.name : NULL; + return (event) ? event->name : NULL; } // Attribute accessors -Attribute *minissd_get_attributes(const AstNode *node) +Attribute const * +minissd_get_attributes(AstNode const *node) { - return node ? node->ll_attributes : NULL; + return node ? node->opt_ll_attributes : NULL; } -const char *minissd_get_attribute_name(const Attribute *attr) +char const * +minissd_get_attribute_name(Attribute const *attr) { return attr ? attr->name : NULL; } -Argument *minissd_get_attribute_arguments(const Attribute *attr) +AttributeParameter const * +minissd_get_attribute_parameters(Attribute const *attr) { - return attr ? attr->ll_arguments : NULL; + return attr ? attr->opt_ll_arguments : NULL; } // Property accessors -Property *minissd_get_properties(const AstNode *node) +Property const * +minissd_get_properties(AstNode const *node) { - return (node && node->type == NODE_DATA) ? node->node.dataNode.ll_properties : NULL; + return (node && node->type == NODE_DATA) ? node->node.data_node.ll_properties : NULL; } -const char *minissd_get_property_name(const Property *prop) +char const * +minissd_get_property_name(Property const *prop) { return prop ? prop->name : NULL; } -Attribute *minissd_get_property_attributes(const Property *prop) +Attribute const * +minissd_get_property_attributes(Property const *prop) { return prop ? prop->attributes : NULL; } -const char *minissd_get_property_type(const Property *prop) +char const * +minissd_get_property_type(Property const *prop) { return prop ? prop->type : NULL; } +Dependency const * +minissd_get_dependencies(AstNode const *node) +{ + return (node && node->type == NODE_SERVICE) ? node->node.service_node.opt_ll_dependencies : NULL; +} + +Handler const * +minissd_get_handlers(AstNode const *node) +{ + return (node && node->type == NODE_SERVICE) ? node->node.service_node.opt_ll_handlers : NULL; +} + +Event const * +minissd_get_events(AstNode const *node) +{ + return (node && node->type == NODE_SERVICE) ? node->node.service_node.opt_ll_events : NULL; +} + // Enum Value accessors -EnumVariant *minissd_get_enum_variants(const AstNode *node) +EnumVariant const * +minissd_get_enum_variants(AstNode const *node) { - return (node && node->type == NODE_ENUM) ? node->node.enumNode.ll_variants : NULL; + return (node && node->type == NODE_ENUM) ? node->node.enum_node.ll_variants : NULL; } -const char *minissd_get_enum_variant_name(const EnumVariant *value) +char const * +minissd_get_enum_variant_name(EnumVariant const *value) { return value ? value->name : NULL; } -Attribute *minissd_get_enum_variant_attributes(const EnumVariant *value) +Attribute const * +minissd_get_enum_variant_attributes(EnumVariant const *value) { return value ? value->attributes : NULL; } -int minissd_get_enum_variant(const EnumVariant *value, bool *has_value) +int minissd_get_enum_variant_value(EnumVariant const *value, bool *has_value) { - if (!value || !value->value) + if (!value || !value->opt_value) { if (has_value) + { *has_value = false; + } return 0; } if (has_value) + { *has_value = true; - return *(value->value); + } + return *(value->opt_value); +} + +Argument const * +minissd_get_handler_arguments(Handler const *handler) +{ + return (handler) ? handler->opt_ll_arguments : NULL; +} + +Argument const * +minissd_get_event_arguments(Event const *event) +{ + return (event) ? event->opt_ll_arguments : NULL; +} + +char const * +minissd_get_argument_name(Argument const *arg) +{ + return arg ? arg->name : NULL; +} +Attribute const * +minissd_get_argument_attributes(Argument const *arg) +{ + return arg ? arg->attributes : NULL; +} +char const * +minissd_get_argument_type(Argument const *arg) +{ + return arg ? arg->type : NULL; } // Traversal functions -AstNode *minissd_get_next_node(const AstNode *node) +AstNode const * +minissd_get_next_node(AstNode const *node) { return node ? node->next : NULL; } -Property *minissd_get_next_property(const Property *prop) +Property const * +minissd_get_next_property(Property const *prop) { return prop ? prop->next : NULL; } -EnumVariant *minissd_get_next_enum_value(const EnumVariant *value) +EnumVariant const * +minissd_get_next_enum_variant(EnumVariant const *value) { return value ? value->next : NULL; } -Attribute *minissd_get_next_attribute(const Attribute *attr) +Attribute const * +minissd_get_next_attribute(Attribute const *attr) { return attr ? attr->next : NULL; } -Argument *minissd_get_next_argument(const Argument *arg) +AttributeParameter const * +minissd_get_next_attribute_parameter(AttributeParameter const *arg) { return arg ? arg->next : NULL; } + +char const * +minissd_get_attribute_parameter_name(AttributeParameter const *arg) +{ + return arg ? arg->key : NULL; +} + +char const * +minissd_get_attribute_parameter_value(AttributeParameter const *arg) +{ + return arg ? arg->opt_value : NULL; +} + +Dependency const * +minissd_get_next_dependency(Dependency const *dep) +{ + return dep ? dep->next : NULL; +} + +Handler const * +minissd_get_next_handler(Handler const *handler) +{ + return handler ? handler->next : NULL; +} + +Event const * +minissd_get_next_event(Event const *event) +{ + return event ? event->next : NULL; +} + +Argument const * +minissd_get_next_argument(Argument const *arg) +{ + return arg ? arg->next : NULL; +} + +char const * +minissd_get_dependency_path(Dependency const *dep) +{ + return dep ? dep->path : NULL; +} + +char const * +minissd_get_service_name(AstNode const *node) +{ + return (node && node->type == NODE_SERVICE) ? node->node.service_node.name : NULL; +} \ No newline at end of file diff --git a/test.ssd b/test.ssd index 01a0951..0daf4fd 100644 --- a/test.ssd +++ b/test.ssd @@ -25,4 +25,17 @@ data MyData { # [ column ( name = "field2" , type="int" )] field2: int, +}; + +#[a(b="c")] + +service MyService { + + #[d(e="f")] + depends a::b::c ; + #[g(h="i")] + fn asdf ( blah : int ) -> out ; + + #[asdlkj(oieruw="doisf")] + event blah ( a : string ) ; }; \ No newline at end of file diff --git a/tests/src/test_parser.cpp b/tests/src/test_parser.cpp index 19bb23f..d9525d1 100644 --- a/tests/src/test_parser.cpp +++ b/tests/src/test_parser.cpp @@ -15,21 +15,111 @@ class ParserTest : public ::testing::Test } }; -// Test for successful parsing of valid input TEST_F(ParserTest, ValidInput_Data) +{ + const char *source_code = "data Person { name: string, };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_DATA); + ASSERT_STREQ(minissd_get_data_name(ast), "Person"); + + Property const *prop = minissd_get_properties(ast); + ASSERT_NE(prop, nullptr); + ASSERT_STREQ(minissd_get_property_name(prop), "name"); + ASSERT_STREQ(minissd_get_property_type(prop), "string"); + + prop = minissd_get_next_property(prop); + ASSERT_EQ(prop, nullptr); +} + +TEST_F(ParserTest, ValidInput_DataNoTrailingComma) +{ + const char *source_code = "data Person { name: string };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_DATA); + ASSERT_STREQ(minissd_get_data_name(ast), "Person"); + + Property const *prop = minissd_get_properties(ast); + ASSERT_NE(prop, nullptr); + ASSERT_STREQ(minissd_get_property_name(prop), "name"); + ASSERT_STREQ(minissd_get_property_type(prop), "string"); + + prop = minissd_get_next_property(prop); + ASSERT_EQ(prop, nullptr); +} + +TEST_F(ParserTest, ValidInput_DataWithSpaceAfter) +{ + const char *source_code = "data Person { name: string, } ;"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_DATA); + ASSERT_STREQ(minissd_get_data_name(ast), "Person"); + + Property const *prop = minissd_get_properties(ast); + ASSERT_NE(prop, nullptr); + ASSERT_STREQ(minissd_get_property_name(prop), "name"); + ASSERT_STREQ(minissd_get_property_type(prop), "string"); + + prop = minissd_get_next_property(prop); + ASSERT_EQ(prop, nullptr); +} + +TEST_F(ParserTest, ValidInput_DataMultipleProperties) +{ + const char *source_code = "data Person { name: string , age: int, };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_DATA); + ASSERT_STREQ(minissd_get_data_name(ast), "Person"); + + Property const *prop = minissd_get_properties(ast); + ASSERT_NE(prop, nullptr); + ASSERT_STREQ(minissd_get_property_name(prop), "name"); + ASSERT_STREQ(minissd_get_property_type(prop), "string"); + + prop = minissd_get_next_property(prop); + ASSERT_NE(prop, nullptr); + ASSERT_STREQ(minissd_get_property_name(prop), "age"); + ASSERT_STREQ(minissd_get_property_type(prop), "int"); +} + +TEST_F(ParserTest, ValidInput_DataMultiplePropertiesWithoutTrailingComma) { const char *source_code = "data Person { name: string, age: int };"; parser = minissd_create_parser(source_code); ast = minissd_parse(parser); - ASSERT_NE(ast, nullptr); // Ensure AST is not NULL - ASSERT_EQ(minissd_get_node_type(ast), NODE_DATA); // Check if node type is 'data' - ASSERT_STREQ(minissd_get_data_name(ast), "Person"); // Check data node name + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_DATA); + ASSERT_STREQ(minissd_get_data_name(ast), "Person"); - // Check properties - Property *prop = minissd_get_properties(ast); - ASSERT_NE(prop, nullptr); // Ensure there are properties + Property const *prop = minissd_get_properties(ast); + ASSERT_NE(prop, nullptr); ASSERT_STREQ(minissd_get_property_name(prop), "name"); ASSERT_STREQ(minissd_get_property_type(prop), "string"); @@ -39,108 +129,870 @@ TEST_F(ParserTest, ValidInput_Data) ASSERT_STREQ(minissd_get_property_type(prop), "int"); } -// Test for successful parsing of enum nodes +TEST_F(ParserTest, ValidInput_DataWithAttribute) +{ + const char *source_code = "data Person { #[test] name: string, };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_DATA); + + ASSERT_STREQ(minissd_get_data_name(ast), "Person"); + + Property const *prop = minissd_get_properties(ast); + ASSERT_NE(prop, nullptr); + ASSERT_STREQ(minissd_get_property_name(prop), "name"); + ASSERT_STREQ(minissd_get_property_type(prop), "string"); + + Attribute const *attr = minissd_get_property_attributes(prop); + ASSERT_NE(attr, nullptr); + ASSERT_STREQ(minissd_get_attribute_name(attr), "test"); + ASSERT_EQ(minissd_get_attribute_parameters(attr), nullptr); + + prop = minissd_get_next_property(prop); + ASSERT_EQ(prop, nullptr); +} + +TEST_F(ParserTest, InvalidInput_DataWithoutProperties) +{ + const char *source_code = "data Person { };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_EQ(ast, nullptr); + ASSERT_STREQ(parser->error, "Error: Expected property at line 1, column 17"); +} + +TEST_F(ParserTest, InvalidInput_DataWithoutName) +{ + const char *source_code = "data { name: string };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_EQ(ast, nullptr); + ASSERT_STREQ(parser->error, "Error: Expected data name at line 1, column 8"); +} + +TEST_F(ParserTest, InvalidInput_DataWithoutSemicolon) +{ + const char *source_code = "data Person { name: string }"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_EQ(ast, nullptr); + ASSERT_STREQ(parser->error, "Error: Expected ';' after data declaration at line 1, column 29"); +} + +TEST_F(ParserTest, ValidInput_ServiceOneHandler) +{ + const char *source_code = "service MyService { fn some_function(); };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_SERVICE); + ASSERT_STREQ(minissd_get_service_name(ast), "MyService"); + + Handler const *handlers = minissd_get_handlers(ast); + ASSERT_NE(handlers, nullptr); + ASSERT_STREQ(minissd_get_handler_name(handlers), "some_function"); + ASSERT_EQ(minissd_get_handler_arguments(handlers), nullptr); + + handlers = minissd_get_next_handler(handlers); + ASSERT_EQ(handlers, nullptr); +} + +TEST_F(ParserTest, ValidInput_ServiceOneHandlerAndArguments) +{ + const char *source_code = "service MyService { fn some_function(a: int, b: string); };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_SERVICE); + ASSERT_STREQ(minissd_get_service_name(ast), "MyService"); + + Handler const *handlers = minissd_get_handlers(ast); + ASSERT_NE(handlers, nullptr); + ASSERT_STREQ(minissd_get_handler_name(handlers), "some_function"); + Argument const *args = minissd_get_handler_arguments(handlers); + ASSERT_NE(args, nullptr); + ASSERT_STREQ(minissd_get_argument_name(args), "a"); + ASSERT_STREQ(minissd_get_argument_type(args), "int"); + args = minissd_get_next_argument(args); + ASSERT_NE(args, nullptr); + ASSERT_STREQ(minissd_get_argument_name(args), "b"); + ASSERT_STREQ(minissd_get_argument_type(args), "string"); + args = minissd_get_next_argument(args); + ASSERT_EQ(args, nullptr); + + handlers = minissd_get_next_handler(handlers); + ASSERT_EQ(handlers, nullptr); +} + +TEST_F(ParserTest, ValidInput_ServiceOneHandlerArgumentsAndReturnValue) +{ + const char *source_code = "service MyService { fn some_function(a: int, b: string) -> int ; };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_SERVICE); + ASSERT_STREQ(minissd_get_service_name(ast), "MyService"); + + Handler const *handlers = minissd_get_handlers(ast); + ASSERT_NE(handlers, nullptr); + ASSERT_STREQ(minissd_get_handler_name(handlers), "some_function"); + Argument const *args = minissd_get_handler_arguments(handlers); + ASSERT_NE(args, nullptr); + ASSERT_STREQ(minissd_get_argument_name(args), "a"); + ASSERT_STREQ(minissd_get_argument_type(args), "int"); + args = minissd_get_next_argument(args); + ASSERT_NE(args, nullptr); + ASSERT_STREQ(minissd_get_argument_name(args), "b"); + ASSERT_STREQ(minissd_get_argument_type(args), "string"); + args = minissd_get_next_argument(args); + ASSERT_EQ(args, nullptr); + + ASSERT_STREQ(minissd_get_handler_return_type(handlers), "int"); + + handlers = minissd_get_next_handler(handlers); + ASSERT_EQ(handlers, nullptr); +} + +TEST_F(ParserTest, ValidInput_ServiceOneEventWithArguments) +{ + const char *source_code = "service MyService { event some_event(a: int, b: string) ; };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_SERVICE); + ASSERT_STREQ(minissd_get_service_name(ast), "MyService"); + + Event const *events = minissd_get_events(ast); + ASSERT_NE(events, nullptr); + ASSERT_STREQ(minissd_get_event_name(events), "some_event"); + Argument const *args = minissd_get_event_arguments(events); + ASSERT_NE(args, nullptr); + ASSERT_STREQ(minissd_get_argument_name(args), "a"); + ASSERT_STREQ(minissd_get_argument_type(args), "int"); + args = minissd_get_next_argument(args); + ASSERT_NE(args, nullptr); + ASSERT_STREQ(minissd_get_argument_name(args), "b"); + ASSERT_STREQ(minissd_get_argument_type(args), "string"); + args = minissd_get_next_argument(args); + ASSERT_EQ(args, nullptr); + + events = minissd_get_next_event(events); + ASSERT_EQ(events, nullptr); +} + +TEST_F(ParserTest, InvalidInput_ServiceOneEventWithArgumentsAndReturnType) +{ + const char *source_code = "service MyService { event some_event(a: int, b: string) -> int ; };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_EQ(ast, nullptr); + ASSERT_STREQ(parser->error, "Error: Expected ';' after service component at line 1, column 58"); +} + +TEST_F(ParserTest, ValidInput_ServiceOneEvent) +{ + const char *source_code = "service MyService { event some_event(); };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_SERVICE); + ASSERT_STREQ(minissd_get_service_name(ast), "MyService"); + + Event const *events = minissd_get_events(ast); + ASSERT_NE(events, nullptr); + ASSERT_STREQ(minissd_get_event_name(events), "some_event"); + ASSERT_EQ(minissd_get_event_arguments(events), nullptr); + + events = minissd_get_next_event(events); + ASSERT_EQ(events, nullptr); +} + +TEST_F(ParserTest, ValidInput_ServiceOneEventWithSpaceAfter) +{ + const char *source_code = "service MyService { event some_event(); } ;"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_SERVICE); + ASSERT_STREQ(minissd_get_service_name(ast), "MyService"); + + Event const *events = minissd_get_events(ast); + ASSERT_NE(events, nullptr); + ASSERT_STREQ(minissd_get_event_name(events), "some_event"); + ASSERT_EQ(minissd_get_event_arguments(events), nullptr); + + events = minissd_get_next_event(events); + ASSERT_EQ(events, nullptr); +} + +TEST_F(ParserTest, ValidInput_ServiceOneHandlerWithSpaceAfter) +{ + const char *source_code = "service MyService { fn some_function(); } ;"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_SERVICE); + ASSERT_STREQ(minissd_get_service_name(ast), "MyService"); + + Handler const *handlers = minissd_get_handlers(ast); + ASSERT_NE(handlers, nullptr); + ASSERT_STREQ(minissd_get_handler_name(handlers), "some_function"); + ASSERT_EQ(minissd_get_handler_arguments(handlers), nullptr); + + handlers = minissd_get_next_handler(handlers); + ASSERT_EQ(handlers, nullptr); +} + +TEST_F(ParserTest, ValidInput_ServiceOneEventOneHandler) +{ + const char *source_code = "service MyService { fn some_function(); event some_event(); };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_SERVICE); + ASSERT_STREQ(minissd_get_service_name(ast), "MyService"); + + Event const *events = minissd_get_events(ast); + ASSERT_NE(events, nullptr); + ASSERT_STREQ(minissd_get_event_name(events), "some_event"); + ASSERT_EQ(minissd_get_event_arguments(events), nullptr); + + ASSERT_EQ(minissd_get_next_event(events), nullptr); + + Handler const *handlers = minissd_get_handlers(ast); + ASSERT_NE(handlers, nullptr); + ASSERT_STREQ(minissd_get_handler_name(handlers), "some_function"); + ASSERT_EQ(minissd_get_handler_arguments(handlers), nullptr); + + ASSERT_EQ(minissd_get_next_handler(handlers), nullptr); +} + +TEST_F(ParserTest, ValidInput_ServiceOneHandlerOneDependency) +{ + const char *source_code = "service MyService { fn some_function(); depends some::other::service; };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_SERVICE); + ASSERT_STREQ(minissd_get_service_name(ast), "MyService"); + + Handler const *handlers = minissd_get_handlers(ast); + ASSERT_NE(handlers, nullptr); + ASSERT_STREQ(minissd_get_handler_name(handlers), "some_function"); + ASSERT_EQ(minissd_get_handler_arguments(handlers), nullptr); + + ASSERT_EQ(minissd_get_next_handler(handlers), nullptr); + + Dependency const *dependencies = minissd_get_dependencies(ast); + ASSERT_NE(dependencies, nullptr); + ASSERT_STREQ(minissd_get_dependency_path(dependencies), "some::other::service"); + + ASSERT_EQ(minissd_get_next_dependency(dependencies), nullptr); +} + +TEST_F(ParserTest, ValidInput_ServiceOneEventOneDependency) +{ + const char *source_code = "service MyService { event some_event(); depends some::other::service; };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_SERVICE); + ASSERT_STREQ(minissd_get_service_name(ast), "MyService"); + + Event const *events = minissd_get_events(ast); + ASSERT_NE(events, nullptr); + ASSERT_STREQ(minissd_get_event_name(events), "some_event"); + ASSERT_EQ(minissd_get_event_arguments(events), nullptr); + + ASSERT_EQ(minissd_get_next_event(events), nullptr); + + Dependency const *dependencies = minissd_get_dependencies(ast); + ASSERT_NE(dependencies, nullptr); + ASSERT_STREQ(minissd_get_dependency_path(dependencies), "some::other::service"); + + ASSERT_EQ(minissd_get_next_dependency(dependencies), nullptr); +} + +TEST_F(ParserTest, ValidInput_ServiceMultipleDependenciesEventsHandlers) +{ + const char *source_code = "service MyService { depends a::b::c ; depends d::e::f ; fn a(); fn b(); event c(); event d(); };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_SERVICE); + ASSERT_STREQ(minissd_get_service_name(ast), "MyService"); + + Handler const *handlers = minissd_get_handlers(ast); + ASSERT_NE(handlers, nullptr); + ASSERT_STREQ(minissd_get_handler_name(handlers), "a"); + ASSERT_EQ(minissd_get_handler_arguments(handlers), nullptr); + + handlers = minissd_get_next_handler(handlers); + ASSERT_NE(handlers, nullptr); + ASSERT_STREQ(minissd_get_handler_name(handlers), "b"); + ASSERT_EQ(minissd_get_handler_arguments(handlers), nullptr); + + ASSERT_EQ(minissd_get_next_handler(handlers), nullptr); + + Event const *events = minissd_get_events(ast); + ASSERT_NE(events, nullptr); + ASSERT_STREQ(minissd_get_event_name(events), "c"); + ASSERT_EQ(minissd_get_event_arguments(events), nullptr); + + events = minissd_get_next_event(events); + ASSERT_NE(events, nullptr); + ASSERT_STREQ(minissd_get_event_name(events), "d"); + ASSERT_EQ(minissd_get_event_arguments(events), nullptr); + + ASSERT_EQ(minissd_get_next_event(events), nullptr); + + Dependency const *dependencies = minissd_get_dependencies(ast); + ASSERT_NE(dependencies, nullptr); + ASSERT_STREQ(minissd_get_dependency_path(dependencies), "a::b::c"); + + dependencies = minissd_get_next_dependency(dependencies); + ASSERT_NE(dependencies, nullptr); + ASSERT_STREQ(minissd_get_dependency_path(dependencies), "d::e::f"); + + ASSERT_EQ(minissd_get_next_dependency(dependencies), nullptr); +} + +TEST_F(ParserTest, InvalidInput_ServiceNoHandlerAndNoEvent) +{ + const char *source_code = "service MyService {};"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_EQ(ast, nullptr); + ASSERT_STREQ(parser->error, "Error: Service must have at least one handler or event at line 1, column 22"); +} + +TEST_F(ParserTest, InvalidInput_ServiceWithoutName) +{ + const char *source_code = "service { fn some_function(); };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_EQ(ast, nullptr); + ASSERT_STREQ(parser->error, "Error: Expected service name at line 1, column 11"); +} + +TEST_F(ParserTest, InvalidInput_ServiceWithoutSemicolon) +{ + const char *source_code = "service MyService { fn some_function(); }"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_EQ(ast, nullptr); + ASSERT_STREQ(parser->error, "Error: Expected ';' after service declaration at line 1, column 42"); +} + +TEST_F(ParserTest, ValidInput_Import) +{ + const char *source_code = "import my::module;"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_IMPORT); + ASSERT_STREQ(minissd_get_import_path(ast), "my::module"); +} + +TEST_F(ParserTest, ValidInput_ImportWithSpaceAfter) +{ + const char *source_code = "import my::module ;"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_IMPORT); + ASSERT_STREQ(minissd_get_import_path(ast), "my::module"); +} + +TEST_F(ParserTest, ValidInput_ImportWithAttribute) +{ + const char *source_code = "#[test] import my::module;"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_IMPORT); + ASSERT_STREQ(minissd_get_import_path(ast), "my::module"); + Attribute const *attr = minissd_get_attributes(ast); + ASSERT_NE(attr, nullptr); + ASSERT_STREQ(minissd_get_attribute_name(attr), "test"); + ASSERT_EQ(minissd_get_attribute_parameters(attr), nullptr); + ASSERT_EQ(minissd_get_next_attribute(attr), nullptr); +} + +TEST_F(ParserTest, ValidInput_ImportWithAttributes1) +{ + const char *source_code = "#[test, blah] import my::module;"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_IMPORT); + ASSERT_STREQ(minissd_get_import_path(ast), "my::module"); + Attribute const *attr = minissd_get_attributes(ast); + ASSERT_NE(attr, nullptr); + ASSERT_STREQ(minissd_get_attribute_name(attr), "test"); + ASSERT_EQ(minissd_get_attribute_parameters(attr), nullptr); + attr = minissd_get_next_attribute(attr); + ASSERT_NE(attr, nullptr); + ASSERT_STREQ(minissd_get_attribute_name(attr), "blah"); + ASSERT_EQ(minissd_get_attribute_parameters(attr), nullptr); + ASSERT_EQ(minissd_get_next_attribute(attr), nullptr); +} + +TEST_F(ParserTest, ValidInput_ImportWithAttributes2) +{ + const char *source_code = "#[test] #[blah] import my::module;"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_IMPORT); + ASSERT_STREQ(minissd_get_import_path(ast), "my::module"); + Attribute const *attr = minissd_get_attributes(ast); + ASSERT_NE(attr, nullptr); + ASSERT_STREQ(minissd_get_attribute_name(attr), "test"); + ASSERT_EQ(minissd_get_attribute_parameters(attr), nullptr); + attr = minissd_get_next_attribute(attr); + ASSERT_NE(attr, nullptr); + ASSERT_STREQ(minissd_get_attribute_name(attr), "blah"); + ASSERT_EQ(minissd_get_attribute_parameters(attr), nullptr); + ASSERT_EQ(minissd_get_next_attribute(attr), nullptr); +} + +TEST_F(ParserTest, ValidInput_ImportWithAttributeParameter) +{ + const char *source_code = "#[test(a)] import my::module;"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_IMPORT); + ASSERT_STREQ(minissd_get_import_path(ast), "my::module"); + Attribute const *attr = minissd_get_attributes(ast); + ASSERT_NE(attr, nullptr); + ASSERT_STREQ(minissd_get_attribute_name(attr), "test"); + AttributeParameter const *param = minissd_get_attribute_parameters(attr); + ASSERT_NE(param, nullptr); + ASSERT_STREQ(minissd_get_attribute_parameter_name(param), "a"); + ASSERT_EQ(minissd_get_attribute_parameter_value(param), nullptr); + ASSERT_EQ(minissd_get_next_attribute(attr), nullptr); +} + +TEST_F(ParserTest, ValidInput_ImportWithAttributeParameters) +{ + const char *source_code = "#[test(a, b)] import my::module;"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_IMPORT); + ASSERT_STREQ(minissd_get_import_path(ast), "my::module"); + Attribute const *attr = minissd_get_attributes(ast); + ASSERT_NE(attr, nullptr); + ASSERT_STREQ(minissd_get_attribute_name(attr), "test"); + AttributeParameter const *param = minissd_get_attribute_parameters(attr); + ASSERT_NE(param, nullptr); + ASSERT_STREQ(minissd_get_attribute_parameter_name(param), "a"); + ASSERT_EQ(minissd_get_attribute_parameter_value(param), nullptr); + ASSERT_EQ(minissd_get_next_attribute(attr), nullptr); + param = minissd_get_next_attribute_parameter(param); + ASSERT_NE(param, nullptr); + ASSERT_STREQ(minissd_get_attribute_parameter_name(param), "b"); + ASSERT_EQ(minissd_get_attribute_parameter_value(param), nullptr); + ASSERT_EQ(minissd_get_next_attribute(attr), nullptr); +} + +TEST_F(ParserTest, ValidInput_ImportWithAttributeParameterAndValue) +{ + const char *source_code = "#[test(a = \"asd\")] import my::module;"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_IMPORT); + ASSERT_STREQ(minissd_get_import_path(ast), "my::module"); + Attribute const *attr = minissd_get_attributes(ast); + ASSERT_NE(attr, nullptr); + ASSERT_STREQ(minissd_get_attribute_name(attr), "test"); + AttributeParameter const *param = minissd_get_attribute_parameters(attr); + ASSERT_NE(param, nullptr); + ASSERT_STREQ(minissd_get_attribute_parameter_name(param), "a"); + ASSERT_STREQ(minissd_get_attribute_parameter_value(param), "asd"); + param = minissd_get_next_attribute_parameter(param); + ASSERT_EQ(param, nullptr); + ASSERT_EQ(minissd_get_next_attribute(attr), nullptr); +} + +TEST_F(ParserTest, ValidInput_ImportWithAttributeMultipleParametersAndValue) +{ + const char *source_code = "#[test(a = \"asd\", b = \"dsa\")] import my::module;"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_IMPORT); + ASSERT_STREQ(minissd_get_import_path(ast), "my::module"); + Attribute const *attr = minissd_get_attributes(ast); + ASSERT_NE(attr, nullptr); + ASSERT_STREQ(minissd_get_attribute_name(attr), "test"); + AttributeParameter const *param = minissd_get_attribute_parameters(attr); + ASSERT_NE(param, nullptr); + ASSERT_STREQ(minissd_get_attribute_parameter_name(param), "a"); + ASSERT_STREQ(minissd_get_attribute_parameter_value(param), "asd"); + param = minissd_get_next_attribute_parameter(param); + ASSERT_NE(param, nullptr); + ASSERT_STREQ(minissd_get_attribute_parameter_name(param), "b"); + ASSERT_STREQ(minissd_get_attribute_parameter_value(param), "dsa"); + param = minissd_get_next_attribute_parameter(param); + ASSERT_EQ(param, nullptr); + ASSERT_EQ(minissd_get_next_attribute(attr), nullptr); +} + +TEST_F(ParserTest, InvalidInput_ImportWithoutPath) +{ + const char *source_code = "import ;"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_EQ(ast, nullptr); + ASSERT_STREQ(parser->error, "Error: Expected import path at line 1, column 9"); +} + +TEST_F(ParserTest, InvalidInput_ImportWithoutSemicolon) +{ + const char *source_code = "import my::module"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_EQ(ast, nullptr); + ASSERT_STREQ(parser->error, "Error: Expected ';' after import declaration at line 1, column 18"); +} + +TEST_F(ParserTest, InvalidInput_ImportWithSpaceInPath) +{ + const char *source_code = "import my:: module;"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_EQ(ast, nullptr); + ASSERT_STREQ(parser->error, "Error: Expected ';' after import declaration at line 1, column 14"); +} + TEST_F(ParserTest, ValidInput_Enum) { - const char *source_code = "enum Color { Red = 1, Green, Blue };"; + const char *source_code = "enum Color { Red, };"; parser = minissd_create_parser(source_code); ast = minissd_parse(parser); - ASSERT_NE(ast, nullptr); // Ensure AST is not NULL - ASSERT_EQ(minissd_get_node_type(ast), NODE_ENUM); // Check if node type is 'enum' - ASSERT_STREQ(minissd_get_enum_name(ast), "Color"); // Check enum node name + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_ENUM); + ASSERT_STREQ(minissd_get_enum_name(ast), "Color"); - // Check enum variants - EnumVariant *variant = minissd_get_enum_variants(ast); - ASSERT_NE(variant, nullptr); // Ensure there are enum variants + EnumVariant const *variant = minissd_get_enum_variants(ast); + ASSERT_NE(variant, nullptr); ASSERT_STREQ(minissd_get_enum_variant_name(variant), "Red"); - ASSERT_EQ(minissd_get_enum_variant(variant, nullptr), 1); // Check variant for 'Red' + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 0); + + variant = minissd_get_next_enum_variant(variant); + ASSERT_EQ(variant, nullptr); +} + +TEST_F(ParserTest, ValidInput_EnumNoTrailingComma) +{ + const char *source_code = "enum Color { Red };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); - variant = minissd_get_next_enum_value(variant); + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_ENUM); + ASSERT_STREQ(minissd_get_enum_name(ast), "Color"); + + EnumVariant const *variant = minissd_get_enum_variants(ast); ASSERT_NE(variant, nullptr); - ASSERT_STREQ(minissd_get_enum_variant_name(variant), "Green"); - ASSERT_EQ(minissd_get_enum_variant(variant, nullptr), 0); // Default variant for 'Green' + ASSERT_STREQ(minissd_get_enum_variant_name(variant), "Red"); + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 0); + + variant = minissd_get_next_enum_variant(variant); + ASSERT_EQ(variant, nullptr); +} - variant = minissd_get_next_enum_value(variant); +TEST_F(ParserTest, ValidInput_EnumWithValue) +{ + const char *source_code = "enum Color { Red = 1, };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_ENUM); + ASSERT_STREQ(minissd_get_enum_name(ast), "Color"); + + EnumVariant const *variant = minissd_get_enum_variants(ast); ASSERT_NE(variant, nullptr); - ASSERT_STREQ(minissd_get_enum_variant_name(variant), "Blue"); - ASSERT_EQ(minissd_get_enum_variant(variant, nullptr), 0); // Default variant for 'Blue' + ASSERT_STREQ(minissd_get_enum_variant_name(variant), "Red"); + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 1); + + variant = minissd_get_next_enum_variant(variant); + ASSERT_EQ(variant, nullptr); } -// Test for successful parsing of import nodes -TEST_F(ParserTest, ValidInput_Import) +TEST_F(ParserTest, ValidInput_EnumWithValueNoTrailingComma) { - const char *source_code = "import my::module;"; + const char *source_code = "enum Color { Red = 1 };"; parser = minissd_create_parser(source_code); ast = minissd_parse(parser); - ASSERT_NE(ast, nullptr); // Ensure AST is not NULL - ASSERT_EQ(minissd_get_node_type(ast), NODE_IMPORT); // Check if node type is 'import' - ASSERT_STREQ(minissd_get_import_path(ast), "my::module"); // Check path + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_ENUM); + ASSERT_STREQ(minissd_get_enum_name(ast), "Color"); + + EnumVariant const *variant = minissd_get_enum_variants(ast); + ASSERT_NE(variant, nullptr); + ASSERT_STREQ(minissd_get_enum_variant_name(variant), "Red"); + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 1); + + variant = minissd_get_next_enum_variant(variant); + ASSERT_EQ(variant, nullptr); } -// Test for valid input: No enum values -TEST_F(ParserTest, ValidInput_MissingEnumValues) +TEST_F(ParserTest, ValidInput_EnumWithValues) { - const char *source_code = "enum Color { Red, Green };"; + const char *source_code = "enum Color { Red = 1, Green = 2, };"; parser = minissd_create_parser(source_code); ast = minissd_parse(parser); ASSERT_NE(ast, nullptr); - ASSERT_EQ(minissd_get_node_type(ast), NODE_ENUM); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_ENUM); ASSERT_STREQ(minissd_get_enum_name(ast), "Color"); - EnumVariant *variant = minissd_get_enum_variants(ast); + EnumVariant const *variant = minissd_get_enum_variants(ast); ASSERT_NE(variant, nullptr); ASSERT_STREQ(minissd_get_enum_variant_name(variant), "Red"); - ASSERT_EQ(minissd_get_enum_variant(variant, nullptr), 0); // Default variant for 'Red' + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 1); - variant = minissd_get_next_enum_value(variant); + variant = minissd_get_next_enum_variant(variant); ASSERT_NE(variant, nullptr); ASSERT_STREQ(minissd_get_enum_variant_name(variant), "Green"); - ASSERT_EQ(minissd_get_enum_variant(variant, nullptr), 0); // Default variant for 'Green' + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 2); + + variant = minissd_get_next_enum_variant(variant); + ASSERT_EQ(variant, nullptr); } -// Test for invalid input: Missing type in data node -TEST_F(ParserTest, InvalidInput_MissingType) +TEST_F(ParserTest, ValidInput_EnumWithValuesNoTrailingComma) { - const char *source_code = "data Person { name, age: int };"; // Missing type for 'name' + const char *source_code = "enum Color { Red = 1, Green = 2 };"; parser = minissd_create_parser(source_code); ast = minissd_parse(parser); - ASSERT_EQ(ast, nullptr); // Parsing should fail - ASSERT_STREQ(parser->error, "Error: Expected ':' after property name at line 1, column 20"); + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_ENUM); + ASSERT_STREQ(minissd_get_enum_name(ast), "Color"); + + EnumVariant const *variant = minissd_get_enum_variants(ast); + ASSERT_NE(variant, nullptr); + ASSERT_STREQ(minissd_get_enum_variant_name(variant), "Red"); + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 1); + + variant = minissd_get_next_enum_variant(variant); + ASSERT_NE(variant, nullptr); + ASSERT_STREQ(minissd_get_enum_variant_name(variant), "Green"); + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 2); + + variant = minissd_get_next_enum_variant(variant); + ASSERT_EQ(variant, nullptr); } -// Test for invalid input: Missing braces in data node -TEST_F(ParserTest, InvalidInput_MissingBraces) +TEST_F(ParserTest, ValidInput_EnumWithSpaceAfter) { - const char *source_code = "data Person name: string, age: int"; // Missing closing brace + const char *source_code = "enum Color { Red } ;"; parser = minissd_create_parser(source_code); ast = minissd_parse(parser); - ASSERT_EQ(ast, nullptr); // Parsing should fail - ASSERT_STREQ(parser->error, "Error: Expected '{' after data name at line 1, column 14"); + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_ENUM); + ASSERT_STREQ(minissd_get_enum_name(ast), "Color"); + + EnumVariant const *variant = minissd_get_enum_variants(ast); + ASSERT_NE(variant, nullptr); + ASSERT_STREQ(minissd_get_enum_variant_name(variant), "Red"); + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 0); + + variant = minissd_get_next_enum_variant(variant); + ASSERT_EQ(variant, nullptr); } -// Test for invalid input: No enum variants TEST_F(ParserTest, InvalidInput_NoEnumVariants) { - const char *source_code = "enum Color {};"; // Missing closing brace + const char *source_code = "enum Color {};"; parser = minissd_create_parser(source_code); ast = minissd_parse(parser); - ASSERT_EQ(ast, nullptr); // Parsing should fail + ASSERT_EQ(ast, nullptr); ASSERT_STREQ(parser->error, "Error: Enum must have at least one variant at line 1, column 15"); } -// Test for empty input (edge case) +TEST_F(ParserTest, InvalidInput_NoEnumName) +{ + const char *source_code = "enum { Red };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_EQ(ast, nullptr); + ASSERT_STREQ(parser->error, "Error: Expected enum name at line 1, column 8"); +} + +TEST_F(ParserTest, InvalidInput_EnumWithoutSemicolon) +{ + const char *source_code = "enum Color { Red }"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_EQ(ast, nullptr); + ASSERT_STREQ(parser->error, "Error: Expected ';' after enum declaration at line 1, column 19"); +} + +TEST_F(ParserTest, InvalidInput_MissingType) +{ + const char *source_code = "data Person { name, age: int };"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_EQ(ast, nullptr); + ASSERT_STREQ(parser->error, "Error: Expected ':' after property name at line 1, column 20"); +} + +TEST_F(ParserTest, InvalidInput_MissingBraces) +{ + const char *source_code = "data Person name: string, age: int"; + + parser = minissd_create_parser(source_code); + ast = minissd_parse(parser); + + ASSERT_EQ(ast, nullptr); + ASSERT_STREQ(parser->error, "Error: Expected '{' after data name at line 1, column 14"); +} + TEST_F(ParserTest, EmptyInput) { const char *source_code = ""; @@ -148,23 +1000,21 @@ TEST_F(ParserTest, EmptyInput) parser = minissd_create_parser(source_code); ast = minissd_parse(parser); - ASSERT_EQ(ast, nullptr); // No AST should be generated + ASSERT_EQ(ast, nullptr); ASSERT_STREQ(parser->error, "Error: Expected at least one node at line 1, column 1"); } -// Test for edge case: Invalid character TEST_F(ParserTest, InvalidCharacter) { - const char *source_code = "data Person { name: string, age: int }; @"; // Invalid character '@' + const char *source_code = "data Person { name: string, age: int }; @"; parser = minissd_create_parser(source_code); ast = minissd_parse(parser); - ASSERT_EQ(ast, nullptr); // Parsing should fail due to invalid character + ASSERT_EQ(ast, nullptr); ASSERT_STREQ(parser->error, "Error: Expected identifier at line 1, column 42"); } -// Test for correctly parsing attributes in data node TEST_F(ParserTest, ValidInput_WithAttributes) { const char *source_code = "data Person { #[attr1(name=\"value1\")] name: string, age: int };"; @@ -172,25 +1022,25 @@ TEST_F(ParserTest, ValidInput_WithAttributes) parser = minissd_create_parser(source_code); ast = minissd_parse(parser); - ASSERT_NE(ast, nullptr); // Ensure AST is not NULL - ASSERT_EQ(minissd_get_node_type(ast), NODE_DATA); // Check if node type is 'data' - ASSERT_STREQ(minissd_get_data_name(ast), "Person"); // Check data node name + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_DATA); + ASSERT_STREQ(minissd_get_data_name(ast), "Person"); - Property *props = minissd_get_properties(ast); - ASSERT_NE(props, nullptr); // Ensure there are properties + Property const *props = minissd_get_properties(ast); + ASSERT_NE(props, nullptr); - // Check for attributes - Attribute *attr = minissd_get_property_attributes(props); - ASSERT_NE(attr, nullptr); // Ensure there are attributes + Attribute const *attr = minissd_get_property_attributes(props); + ASSERT_NE(attr, nullptr); ASSERT_STREQ(minissd_get_attribute_name(attr), "attr1"); - Argument *arg = minissd_get_attribute_arguments(attr); - ASSERT_NE(arg, nullptr); // Ensure there are arguments - ASSERT_STREQ(arg->key, "name"); - ASSERT_STREQ(arg->value, "value1"); + AttributeParameter const *param = minissd_get_attribute_parameters(attr); + ASSERT_NE(param, nullptr); + ASSERT_STREQ(param->key, "name"); + ASSERT_STREQ(param->opt_value, "value1"); } -// Test for multiple attributes in the same node TEST_F(ParserTest, ValidInput_MultipleAttributes) { const char *source_code = "data Person { #[attr1] #[attr2(name=\"value1\")] name: string, age: int };"; @@ -198,27 +1048,27 @@ TEST_F(ParserTest, ValidInput_MultipleAttributes) parser = minissd_create_parser(source_code); ast = minissd_parse(parser); - ASSERT_NE(ast, nullptr); // Ensure AST is not NULL - ASSERT_EQ(minissd_get_node_type(ast), NODE_DATA); // Check if node type is 'data' - ASSERT_STREQ(minissd_get_data_name(ast), "Person"); // Check data node name + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_DATA); + ASSERT_STREQ(minissd_get_data_name(ast), "Person"); - Property *props = minissd_get_properties(ast); - ASSERT_NE(props, nullptr); // Ensure there are properties + Property const *props = minissd_get_properties(ast); + ASSERT_NE(props, nullptr); - // Check for first attribute - Attribute *attr = minissd_get_property_attributes(props); - ASSERT_NE(attr, nullptr); // Ensure there are attributes + Attribute const *attr = minissd_get_property_attributes(props); + ASSERT_NE(attr, nullptr); ASSERT_STREQ(minissd_get_attribute_name(attr), "attr1"); - // Check for second attribute attr = minissd_get_next_attribute(attr); - ASSERT_NE(attr, nullptr); // Ensure there is another attribute + ASSERT_NE(attr, nullptr); ASSERT_STREQ(minissd_get_attribute_name(attr), "attr2"); - Argument *arg = minissd_get_attribute_arguments(attr); - ASSERT_NE(arg, nullptr); // Ensure there are arguments - ASSERT_STREQ(arg->key, "name"); - ASSERT_STREQ(arg->value, "value1"); + AttributeParameter const *param = minissd_get_attribute_parameters(attr); + ASSERT_NE(param, nullptr); + ASSERT_STREQ(param->key, "name"); + ASSERT_STREQ(param->opt_value, "value1"); } TEST_F(ParserTest, ValidInput_MultipleAttributes2) @@ -228,25 +1078,25 @@ TEST_F(ParserTest, ValidInput_MultipleAttributes2) parser = minissd_create_parser(source_code); ast = minissd_parse(parser); - ASSERT_NE(ast, nullptr); // Ensure AST is not NULL - ASSERT_EQ(minissd_get_node_type(ast), NODE_DATA); // Check if node type is 'data' - ASSERT_STREQ(minissd_get_data_name(ast), "Person"); // Check data node name + ASSERT_NE(ast, nullptr); + NodeType const *node_type = minissd_get_node_type(ast); + ASSERT_NE(node_type, nullptr); + ASSERT_EQ(*node_type, NODE_DATA); + ASSERT_STREQ(minissd_get_data_name(ast), "Person"); - Property *props = minissd_get_properties(ast); - ASSERT_NE(props, nullptr); // Ensure there are properties + Property const *props = minissd_get_properties(ast); + ASSERT_NE(props, nullptr); - // Check for first attribute - Attribute *attr = minissd_get_property_attributes(props); - ASSERT_NE(attr, nullptr); // Ensure there are attributes + Attribute const *attr = minissd_get_property_attributes(props); + ASSERT_NE(attr, nullptr); ASSERT_STREQ(minissd_get_attribute_name(attr), "attr1"); - // Check for second attribute attr = minissd_get_next_attribute(attr); - ASSERT_NE(attr, nullptr); // Ensure there is another attribute + ASSERT_NE(attr, nullptr); ASSERT_STREQ(minissd_get_attribute_name(attr), "attr2"); - Argument *arg = minissd_get_attribute_arguments(attr); - ASSERT_NE(arg, nullptr); // Ensure there are arguments - ASSERT_STREQ(arg->key, "name"); - ASSERT_STREQ(arg->value, "value1"); -} + AttributeParameter const *param = minissd_get_attribute_parameters(attr); + ASSERT_NE(param, nullptr); + ASSERT_STREQ(param->key, "name"); + ASSERT_STREQ(param->opt_value, "value1"); +} \ No newline at end of file