From e3f23d8325c53837c8b45e7c0f3727d5ec7f98ab Mon Sep 17 00:00:00 2001 From: hardliner66 Date: Sun, 2 Feb 2025 01:04:16 +0100 Subject: [PATCH 01/10] implement full parser --- .vscode/settings.json | 6 +- example/minissd_print.c | 33 +- include/minissd.h | 75 +++- src/minissd.c | 721 ++++++++++++++++++++++++++++++-------- test.ssd | 8 + tests/src/test_parser.cpp | 12 +- 6 files changed, 694 insertions(+), 161 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index c262886..5107356 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,5 +24,9 @@ "clang-tidy.compilerArgs": [ "-Iinclude", "-std=gnu99" - ] + ], + "editor.formatOnType": true, + "[commonlisp]": { + "editor.wordSeparators": "`|;:'\",()" + } } \ No newline at end of file diff --git a/example/minissd_print.c b/example/minissd_print.c index ed5c58f..8131328 100644 --- a/example/minissd_print.c +++ b/example/minissd_print.c @@ -11,12 +11,12 @@ void print_attributes(Attribute *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 (AttributeArgument *arg = minissd_get_attribute_arguments(attr); arg; arg = minissd_get_next_attribute_argument(arg)) { printf(" Argument: %s", arg->key); - if (arg->value) + if (arg->opt_value) { - printf(" = %s", arg->value); + printf(" = %s", arg->opt_value); } printf("\n"); } @@ -103,6 +103,33 @@ int main(int argc, char **argv) } break; + case NODE_SERVICE: + printf("Service\n"); + printf(" Name: %s\n", minissd_get_service_name(node)); + print_attributes(minissd_get_attributes(node)); + + for (Dependency *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 *handler = minissd_get_handlers(node); handler; handler = minissd_get_next_handler(handler)) + { + printf(" Handler: %s\n", handler->name); + print_attributes(handler->opt_ll_attributes); + if (handler->opt_return_type) + { + printf(" Return Type: %s\n", handler->opt_return_type); + } + for (HandlerArgument *arg = minissd_get_handler_arguments(handler); arg; arg = minissd_get_next_handler_argument(arg)) + { + printf(" Argument: %s : %s\n", minissd_get_argument_name(arg), minissd_get_argument_type(arg)); + print_attributes(minissd_get_argument_attributes(arg)); + } + } + break; + default: printf("Unknown Node Type\n"); } diff --git a/include/minissd.h b/include/minissd.h index 735e007..afcd7f6 100644 --- a/include/minissd.h +++ b/include/minissd.h @@ -15,17 +15,17 @@ extern "C" { #endif - typedef struct Argument + typedef struct AttributeArgument { char *key; - char *value; // Nullable - struct Argument *next; - } Argument; + char *opt_value; // Nullable + struct AttributeArgument *next; + } AttributeArgument; typedef struct Attribute { char *name; - Argument *ll_arguments; + AttributeArgument *opt_ll_arguments; struct Attribute *next; } Attribute; @@ -41,10 +41,34 @@ extern "C" { Attribute *attributes; char *name; - int *value; // Nullable + int *opt_value; // Nullable struct EnumVariant *next; } EnumVariant; + typedef struct HandlerArgument + { + Attribute *attributes; + char *name; + char *type; + struct HandlerArgument *next; + } HandlerArgument; + + typedef struct Handler + { + Attribute *opt_ll_attributes; + char *name; + HandlerArgument *opt_ll_arguments; + char *opt_return_type; + struct Handler *next; + } Handler; + + typedef struct Dependency + { + Attribute *opt_ll_attributes; + char *path; + struct Dependency *next; + } Dependency; + typedef struct Import { char *path; @@ -62,22 +86,31 @@ extern "C" EnumVariant *ll_variants; } Enum; + typedef struct Service + { + char *name; + Dependency *opt_ll_dependencies; + Handler *ll_handlers; + } 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 +118,7 @@ extern "C" typedef struct { const char *input; + int input_length; char error[MAX_ERROR_SIZE]; char current; int index; @@ -105,11 +139,12 @@ extern "C" 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); + const char *minissd_get_handler_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); + AttributeArgument *minissd_get_attribute_arguments(const Attribute *attr); // Property and EnumVariant accessors Property *minissd_get_properties(const AstNode *node); @@ -122,12 +157,26 @@ extern "C" Attribute *minissd_get_enum_variant_attributes(const EnumVariant *value); int minissd_get_enum_variant(const EnumVariant *value, bool *has_value); + const char *minissd_get_service_name(const AstNode *node); + HandlerArgument *minissd_get_handler_arguments(const Handler *node); + const char *minissd_get_argument_name(const HandlerArgument *prop); + Attribute *minissd_get_argument_attributes(const HandlerArgument *prop); + const char *minissd_get_argument_type(const HandlerArgument *prop); + + Dependency *minissd_get_dependencies(const AstNode *node); + Handler *minissd_get_handlers(const AstNode *node); + + Dependency *minissd_get_next_dependency(const Dependency *dep); + Handler *minissd_get_next_handler(const Handler *handler); + const char *minissd_get_dependency_path(const Dependency *dep); + // 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); + AttributeArgument *minissd_get_next_attribute_argument(const AttributeArgument *arg); + HandlerArgument *minissd_get_next_handler_argument(const HandlerArgument *arg); #ifdef __cplusplus } diff --git a/src/minissd.c b/src/minissd.c index e7ba542..e8e7a6f 100644 --- a/src/minissd.c +++ b/src/minissd.c @@ -7,11 +7,20 @@ #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; + } size_t len = strlen(s) + 1; char *dup = (char *)malloc(len); @@ -24,29 +33,22 @@ strdup_c99(const char *s) 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_arguments(AttributeArgument *args) { - Argument *current = args; + AttributeArgument *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; + AttributeArgument *next = current->next; free(current); current = next; }; @@ -62,13 +64,34 @@ free_attributes(Attribute *attrs) { free(current_attr->name); } - free_arguments(current_attr->ll_arguments); + free_attribute_arguments(current_attr->opt_ll_arguments); Attribute *next_attr = current_attr->next; free(current_attr); current_attr = next_attr; }; } +static void +free_handler_arguments(HandlerArgument *args) +{ + HandlerArgument *current = args; + while (current) + { + if (current->name) + { + free(current->name); + } + if (current->type) + { + free(current->type); + } + free_attributes(current->attributes); + HandlerArgument *next = current->next; + free(current); + current = next; + }; +} + static void free_properties(Property *prop) { @@ -100,9 +123,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 +134,80 @@ 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_handler_arguments(current->opt_ll_arguments); + Handler *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.enum_node.name); + } + free_enum_variants(current->node.enum_node.ll_variants); + break; + case NODE_SERVICE: + if (current->node.service_node.name) { - free(current->node.enumNode.name); + free(current->node.service_node.name); } - free_enum_variants(current->node.enumNode.ll_variants); + free_dependencies(current->node.service_node.opt_ll_dependencies); + free_handlers(current->node.service_node.ll_handlers); break; default: break; @@ -156,6 +225,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 +265,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 +297,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 +404,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 +416,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 +431,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; + AttributeArgument *arg_head = NULL, *arg_tail = NULL; - eat_whitespace(p); + eat_whitespaces_and_comments(p); while (p->current != ')') { - Argument *arg = (Argument *)calloc(1, sizeof(Argument)); + AttributeArgument *arg = (AttributeArgument *)calloc(1, sizeof(AttributeArgument)); 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_arguments(arg); + free_attribute_arguments(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_arguments(arg); + free_attribute_arguments(arg_head); free_attributes(attr); free_attributes(head); return NULL; @@ -379,7 +480,7 @@ parse_attributes(Parser *p) } arg_tail = arg; - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current != ',') { break; @@ -387,18 +488,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_arguments(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 +512,7 @@ parse_attributes(Parser *p) } tail = attr; - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current != ',') { break; @@ -419,7 +520,7 @@ parse_attributes(Parser *p) advance(p); } - eat_whitespace(p); + eat_whitespaces_and_comments(p); if (p->current != ']') { free_attributes(head); @@ -427,7 +528,7 @@ parse_attributes(Parser *p) return NULL; } advance(p); - eat_whitespace(p); + eat_whitespaces_and_comments(p); } return head; } @@ -444,17 +545,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 +564,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 +589,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 +626,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 +645,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 +655,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 +674,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"); @@ -593,13 +694,249 @@ parse_properties(Parser *p) return head; } +static HandlerArgument * +parse_handler_arguments(Parser *p) +{ + HandlerArgument *head = NULL, *tail = NULL; + while (p->current != ')') + { + HandlerArgument *arg = (HandlerArgument *)calloc(1, sizeof(HandlerArgument)); + 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_handler_arguments(arg); + free_handler_arguments(head); + return NULL; + }; + if (p->current != ':') + { + error(p, "Expected ':' after argument name"); + free_handler_arguments(arg); + free_handler_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_handler_arguments(arg); + free_handler_arguments(head); + return NULL; + }; + + if (!head) + { + head = arg; + } + else + { + tail->next = arg; + } + + if (p->current != ',') + { + break; + } + advance(p); + eat_whitespaces_and_comments(p); + } + return head; +} + +typedef struct ServiceComponents +{ + Handler *ll_handlers; + Dependency *opt_ll_dependencies; +} ServiceComponents; + +static void +free_service_components(ServiceComponents *sc) +{ + free_handlers(sc->ll_handlers); + free_dependencies(sc->opt_ll_dependencies); + free(sc); +} + +static ServiceComponents * +parse_service(Parser *p) +{ + if (p->current != '{') + { + error(p, "Expected '{' after service name"); + return NULL; + } + advance(p); + + ServiceComponents *sc = (ServiceComponents *)calloc(1, sizeof(ServiceComponents)); + Handler *handler_head = NULL, *handler_tail = NULL; + Dependency *dep_head = NULL, *dep_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 'handle' keyword"); + free(ident); + free_dependencies(dep_head); + free_handlers(handler_head); + free_service_components(sc); + 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_service_components(sc); + return NULL; + }; + + if (!dep_head) + { + dep_head = dep; + } + else + { + dep_tail->next = dep; + } + dep_tail = dep; + } + else if (strcmp(ident, "handle") == 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_dependencies(dep_head); + free_service_components(sc); + 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_dependencies(dep_head); + free_service_components(sc); + 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_dependencies(dep_head); + free_service_components(sc); + return NULL; + } + advance(p); + + eat_whitespaces_and_comments(p); + if (p->current == ':') + { + 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_dependencies(dep_head); + free_service_components(sc); + return NULL; + }; + } + + if (!handler_head) + { + handler_head = handler; + } + else + { + handler_tail->next = handler; + } + handler_tail = handler; + } + else + { + error(p, "Expected 'depends' or 'handle' keyword"); + free(ident); + free_dependencies(dep_head); + free_handlers(handler_head); + free_service_components(sc); + return NULL; + } + + if (p->current != ';') + { + error(p, "Expected ';' after service component"); + free(ident); + free_dependencies(dep_head); + free_handlers(handler_head); + free_service_components(sc); + return NULL; + } + advance(p); + + eat_whitespaces_and_comments(p); + } + advance(p); + sc->ll_handlers = handler_head; + sc->opt_ll_dependencies = dep_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 +946,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 +963,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 +983,54 @@ 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->ll_handlers) + { + error(p, "Service must have at least one handler"); + free_service_components(sc); + free(ident); + free_ast(node); + return NULL; + } + node->node.service_node.ll_handlers = sc->ll_handlers; + node->node.service_node.opt_ll_dependencies = sc->opt_ll_dependencies; + free(sc); + } else { error(p, "Unknown node type"); @@ -669,7 +1040,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 +1054,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; @@ -734,29 +1108,9 @@ create_parser(const char *input) 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 +1121,8 @@ void minissd_free_parser(Parser *p) } // Parsing -AstNode *minissd_parse(Parser *p) +AstNode * +minissd_parse(Parser *p) { return parse(p); } @@ -778,114 +1133,204 @@ void minissd_free_ast(AstNode *ast) } // AST Node accessors -NodeType minissd_get_node_type(const AstNode *node) +NodeType +minissd_get_node_type(const AstNode *node) { return node ? node->type : -1; } -const char *minissd_get_import_path(const AstNode *node) +const char * +minissd_get_import_path(const AstNode *node) +{ + return (node && node->type == NODE_IMPORT) ? node->node.import_node.path : NULL; +} + +const char * +minissd_get_data_name(const AstNode *node) { - return (node && node->type == NODE_IMPORT) ? node->node.importNode.path : NULL; + return (node && node->type == NODE_DATA) ? node->node.data_node.name : NULL; } -const char *minissd_get_data_name(const AstNode *node) +const char * +minissd_get_enum_name(const AstNode *node) { - return (node && node->type == NODE_DATA) ? node->node.dataNode.name : NULL; + return (node && node->type == NODE_ENUM) ? node->node.enum_node.name : NULL; } -const char *minissd_get_enum_name(const AstNode *node) +const char * +minissd_get_handler_name(const AstNode *node) { - return (node && node->type == NODE_ENUM) ? node->node.enumNode.name : NULL; + return (node && node->type == NODE_SERVICE) ? node->node.service_node.name : NULL; } // Attribute accessors -Attribute *minissd_get_attributes(const AstNode *node) +Attribute * +minissd_get_attributes(const AstNode *node) { - return node ? node->ll_attributes : NULL; + return node ? node->opt_ll_attributes : NULL; } -const char *minissd_get_attribute_name(const Attribute *attr) +const char * +minissd_get_attribute_name(const Attribute *attr) { return attr ? attr->name : NULL; } -Argument *minissd_get_attribute_arguments(const Attribute *attr) +AttributeArgument * +minissd_get_attribute_arguments(const Attribute *attr) { - return attr ? attr->ll_arguments : NULL; + return attr ? attr->opt_ll_arguments : NULL; } // Property accessors -Property *minissd_get_properties(const AstNode *node) +Property * +minissd_get_properties(const AstNode *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) +const char * +minissd_get_property_name(const Property *prop) { return prop ? prop->name : NULL; } -Attribute *minissd_get_property_attributes(const Property *prop) +Attribute * +minissd_get_property_attributes(const Property *prop) { return prop ? prop->attributes : NULL; } -const char *minissd_get_property_type(const Property *prop) +const char * +minissd_get_property_type(const Property *prop) { return prop ? prop->type : NULL; } +Dependency * +minissd_get_dependencies(const AstNode *node) +{ + return (node && node->type == NODE_SERVICE) ? node->node.service_node.opt_ll_dependencies : NULL; +} + +Handler * +minissd_get_handlers(const AstNode *node) +{ + return (node && node->type == NODE_SERVICE) ? node->node.service_node.ll_handlers : NULL; +} + // Enum Value accessors -EnumVariant *minissd_get_enum_variants(const AstNode *node) +EnumVariant * +minissd_get_enum_variants(const AstNode *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) +const char * +minissd_get_enum_variant_name(const EnumVariant *value) { return value ? value->name : NULL; } -Attribute *minissd_get_enum_variant_attributes(const EnumVariant *value) +Attribute * +minissd_get_enum_variant_attributes(const EnumVariant *value) { return value ? value->attributes : NULL; } int minissd_get_enum_variant(const EnumVariant *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); +} + +HandlerArgument * +minissd_get_handler_arguments(const Handler *handler) +{ + return (handler) ? handler->opt_ll_arguments : NULL; +} +const char * +minissd_get_argument_name(const HandlerArgument *arg) +{ + return arg ? arg->name : NULL; +} +Attribute * +minissd_get_argument_attributes(const HandlerArgument *arg) +{ + return arg ? arg->attributes : NULL; +} +const char * +minissd_get_argument_type(const HandlerArgument *arg) +{ + return arg ? arg->type : NULL; } // Traversal functions -AstNode *minissd_get_next_node(const AstNode *node) +AstNode * +minissd_get_next_node(const AstNode *node) { return node ? node->next : NULL; } -Property *minissd_get_next_property(const Property *prop) +Property * +minissd_get_next_property(const Property *prop) { return prop ? prop->next : NULL; } -EnumVariant *minissd_get_next_enum_value(const EnumVariant *value) +EnumVariant * +minissd_get_next_enum_value(const EnumVariant *value) { return value ? value->next : NULL; } -Attribute *minissd_get_next_attribute(const Attribute *attr) +Attribute * +minissd_get_next_attribute(const Attribute *attr) { return attr ? attr->next : NULL; } -Argument *minissd_get_next_argument(const Argument *arg) +AttributeArgument * +minissd_get_next_attribute_argument(const AttributeArgument *arg) +{ + return arg ? arg->next : NULL; +} + +Dependency * +minissd_get_next_dependency(const Dependency *dep) +{ + return dep ? dep->next : NULL; +} + +Handler * +minissd_get_next_handler(const Handler *handler) +{ + return handler ? handler->next : NULL; +} + +HandlerArgument * +minissd_get_next_handler_argument(const HandlerArgument *arg) { return arg ? arg->next : NULL; } + +const char *minissd_get_dependency_path(const Dependency *dep) +{ + return dep ? dep->path : NULL; +} + +const char *minissd_get_service_name(const AstNode *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..17f6e10 100644 --- a/test.ssd +++ b/test.ssd @@ -25,4 +25,12 @@ data MyData { # [ column ( name = "field2" , type="int" )] field2: int, +}; + +#[a(b="c")] +service MyService { + #[d(e="f")] + depends a::b::c; + #[g(h="i")] + handle asdf(blah: int); }; \ No newline at end of file diff --git a/tests/src/test_parser.cpp b/tests/src/test_parser.cpp index 19bb23f..5ac25f4 100644 --- a/tests/src/test_parser.cpp +++ b/tests/src/test_parser.cpp @@ -184,10 +184,10 @@ TEST_F(ParserTest, ValidInput_WithAttributes) ASSERT_NE(attr, nullptr); // Ensure there are attributes ASSERT_STREQ(minissd_get_attribute_name(attr), "attr1"); - Argument *arg = minissd_get_attribute_arguments(attr); + AttributeArgument *arg = minissd_get_attribute_arguments(attr); ASSERT_NE(arg, nullptr); // Ensure there are arguments ASSERT_STREQ(arg->key, "name"); - ASSERT_STREQ(arg->value, "value1"); + ASSERT_STREQ(arg->opt_value, "value1"); } // Test for multiple attributes in the same node @@ -215,10 +215,10 @@ TEST_F(ParserTest, ValidInput_MultipleAttributes) ASSERT_NE(attr, nullptr); // Ensure there is another attribute ASSERT_STREQ(minissd_get_attribute_name(attr), "attr2"); - Argument *arg = minissd_get_attribute_arguments(attr); + AttributeArgument *arg = minissd_get_attribute_arguments(attr); ASSERT_NE(arg, nullptr); // Ensure there are arguments ASSERT_STREQ(arg->key, "name"); - ASSERT_STREQ(arg->value, "value1"); + ASSERT_STREQ(arg->opt_value, "value1"); } TEST_F(ParserTest, ValidInput_MultipleAttributes2) @@ -245,8 +245,8 @@ TEST_F(ParserTest, ValidInput_MultipleAttributes2) ASSERT_NE(attr, nullptr); // Ensure there is another attribute ASSERT_STREQ(minissd_get_attribute_name(attr), "attr2"); - Argument *arg = minissd_get_attribute_arguments(attr); + AttributeArgument *arg = minissd_get_attribute_arguments(attr); ASSERT_NE(arg, nullptr); // Ensure there are arguments ASSERT_STREQ(arg->key, "name"); - ASSERT_STREQ(arg->value, "value1"); + ASSERT_STREQ(arg->opt_value, "value1"); } From e8ee51547c7a105bc30409bb3c8565da0d629155 Mon Sep 17 00:00:00 2001 From: hardliner66 Date: Sun, 2 Feb 2025 02:10:16 +0100 Subject: [PATCH 02/10] add events --- .vscode/settings.json | 6 +- example/minissd_print.c | 46 ++++-- include/minissd.h | 153 ++++++++++++++----- src/minissd.c | 304 +++++++++++++++++++++++++------------- test.ssd | 9 +- tests/src/test_parser.cpp | 90 ++++++----- 6 files changed, 407 insertions(+), 201 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5107356..c262886 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,9 +24,5 @@ "clang-tidy.compilerArgs": [ "-Iinclude", "-std=gnu99" - ], - "editor.formatOnType": true, - "[commonlisp]": { - "editor.wordSeparators": "`|;:'\",()" - } + ] } \ No newline at end of file diff --git a/example/minissd_print.c b/example/minissd_print.c index 8131328..010d1df 100644 --- a/example/minissd_print.c +++ b/example/minissd_print.c @@ -5,15 +5,15 @@ #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 (AttributeArgument *arg = minissd_get_attribute_arguments(attr); arg; arg = minissd_get_next_attribute_argument(arg)) + for (AttributeParameter const *arg = minissd_get_attribute_parameters(attr); arg; arg = minissd_get_next_attribute_parameter(arg)) { - printf(" Argument: %s", arg->key); + printf(" Parameter: %s", arg->key); if (arg->opt_value) { printf(" = %s", arg->opt_value); @@ -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_value(value)) { bool has_value; int val = minissd_get_enum_variant(value, &has_value); - printf(" Enum Value: %s", minissd_get_enum_variant_name(value)); + printf(" Enum Variant: %s", minissd_get_enum_variant_name(value)); if (has_value) { printf(" = %d", val); @@ -108,30 +116,38 @@ int main(int argc, char **argv) printf(" Name: %s\n", minissd_get_service_name(node)); print_attributes(minissd_get_attributes(node)); - for (Dependency *dep = minissd_get_dependencies(node); dep; dep = minissd_get_next_dependency(dep)) + 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 *handler = minissd_get_handlers(node); handler; handler = minissd_get_next_handler(handler)) + for (Handler const *handler = minissd_get_handlers(node); handler; handler = minissd_get_next_handler(handler)) { printf(" Handler: %s\n", handler->name); - print_attributes(handler->opt_ll_attributes); if (handler->opt_return_type) { printf(" Return Type: %s\n", handler->opt_return_type); } - for (HandlerArgument *arg = minissd_get_handler_arguments(handler); arg; arg = minissd_get_next_handler_argument(arg)) + 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); } - break; - default: - printf("Unknown Node Type\n"); + 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 afcd7f6..01922fe 100644 --- a/include/minissd.h +++ b/include/minissd.h @@ -15,17 +15,17 @@ extern "C" { #endif - typedef struct AttributeArgument + typedef struct AttributeParameter { char *key; char *opt_value; // Nullable - struct AttributeArgument *next; - } AttributeArgument; + struct AttributeParameter *next; + } AttributeParameter; typedef struct Attribute { char *name; - AttributeArgument *opt_ll_arguments; + AttributeParameter *opt_ll_arguments; struct Attribute *next; } Attribute; @@ -45,23 +45,31 @@ extern "C" struct EnumVariant *next; } EnumVariant; - typedef struct HandlerArgument + typedef struct Argument { Attribute *attributes; char *name; char *type; - struct HandlerArgument *next; - } HandlerArgument; + struct Argument *next; + } Argument; typedef struct Handler { Attribute *opt_ll_attributes; char *name; - HandlerArgument *opt_ll_arguments; + 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; @@ -91,6 +99,7 @@ extern "C" char *name; Dependency *opt_ll_dependencies; Handler *ll_handlers; + Event *opt_ll_events; } Service; typedef enum @@ -118,7 +127,7 @@ extern "C" typedef struct { const char *input; - int input_length; + size_t input_length; char error[MAX_ERROR_SIZE]; char current; int index; @@ -135,48 +144,110 @@ extern "C" 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); - const char *minissd_get_handler_name(const AstNode *node); + NodeType const * + minissd_get_node_type(AstNode const *node); + + char const * + minissd_get_import_path(AstNode const *node); + + char const * + minissd_get_data_name(AstNode const *node); + + char const * + minissd_get_enum_name(AstNode const *node); + + char const * + minissd_get_handler_name(AstNode const *node); // Attribute accessors - Attribute *minissd_get_attributes(const AstNode *node); - const char *minissd_get_attribute_name(const Attribute *attr); - AttributeArgument *minissd_get_attribute_arguments(const Attribute *attr); + Attribute const * + minissd_get_attributes(AstNode const *node); + + char const * + minissd_get_attribute_name(Attribute const *attr); + + AttributeParameter const * + minissd_get_attribute_parameters(Attribute const *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); + Property const * + minissd_get_properties(AstNode const *node); + + char const * + minissd_get_property_name(Property const *prop); + + Attribute const * + minissd_get_property_attributes(Property const *prop); + + char const * + minissd_get_property_type(Property const *prop); + + EnumVariant const * + minissd_get_enum_variants(AstNode const *node); + + char const * + minissd_get_enum_variant_name(EnumVariant const *value); - 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); + Attribute const * + minissd_get_enum_variant_attributes(EnumVariant const *value); - const char *minissd_get_service_name(const AstNode *node); - HandlerArgument *minissd_get_handler_arguments(const Handler *node); - const char *minissd_get_argument_name(const HandlerArgument *prop); - Attribute *minissd_get_argument_attributes(const HandlerArgument *prop); - const char *minissd_get_argument_type(const HandlerArgument *prop); + int minissd_get_enum_variant(EnumVariant const *value, bool *has_value); - Dependency *minissd_get_dependencies(const AstNode *node); - Handler *minissd_get_handlers(const AstNode *node); + char const * + minissd_get_service_name(AstNode const *node); - Dependency *minissd_get_next_dependency(const Dependency *dep); - Handler *minissd_get_next_handler(const Handler *handler); - const char *minissd_get_dependency_path(const Dependency *dep); + Argument const * + minissd_get_handler_arguments(Handler const *node); + + Argument const * + minissd_get_event_arguments(Event const *event); + + char const * + minissd_get_argument_name(Argument const *prop); + + Attribute const * + minissd_get_argument_attributes(Argument const *prop); + + char const * + minissd_get_argument_type(Argument const *prop); + + Dependency const * + minissd_get_dependencies(AstNode const *node); + Handler const * + minissd_get_handlers(AstNode const *node); + + Event const * + minissd_get_events(AstNode const *node); + + Dependency const * + minissd_get_next_dependency(Dependency const *dep); + + Handler const * + minissd_get_next_handler(Handler const *handler); + + Event const * + minissd_get_next_event(Event const *event); + + char const *minissd_get_dependency_path(Dependency const *dep); // 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); - AttributeArgument *minissd_get_next_attribute_argument(const AttributeArgument *arg); - HandlerArgument *minissd_get_next_handler_argument(const HandlerArgument *arg); + AstNode const * + minissd_get_next_node(AstNode const *node); + + Property const * + minissd_get_next_property(Property const *prop); + + EnumVariant const * + minissd_get_next_enum_value(EnumVariant const *value); + + Attribute const * + minissd_get_next_attribute(Attribute const *attr); + + AttributeParameter const * + minissd_get_next_attribute_parameter(AttributeParameter const *arg); + + Argument const * + minissd_get_next_argument(Argument const *arg); #ifdef __cplusplus } diff --git a/src/minissd.c b/src/minissd.c index e8e7a6f..5b98e70 100644 --- a/src/minissd.c +++ b/src/minissd.c @@ -35,9 +35,9 @@ strdup_c99(const char *s) // Free functions static void -free_attribute_arguments(AttributeArgument *args) +free_attribute_parameters(AttributeParameter *args) { - AttributeArgument *current = args; + AttributeParameter *current = args; while (current) { if (current->key) @@ -48,7 +48,7 @@ free_attribute_arguments(AttributeArgument *args) { free(current->opt_value); } - AttributeArgument *next = current->next; + AttributeParameter *next = current->next; free(current); current = next; }; @@ -64,7 +64,7 @@ free_attributes(Attribute *attrs) { free(current_attr->name); } - free_attribute_arguments(current_attr->opt_ll_arguments); + free_attribute_parameters(current_attr->opt_ll_arguments); Attribute *next_attr = current_attr->next; free(current_attr); current_attr = next_attr; @@ -72,9 +72,9 @@ free_attributes(Attribute *attrs) } static void -free_handler_arguments(HandlerArgument *args) +free_arguments(Argument *args) { - HandlerArgument *current = args; + Argument *current = args; while (current) { if (current->name) @@ -86,7 +86,7 @@ free_handler_arguments(HandlerArgument *args) free(current->type); } free_attributes(current->attributes); - HandlerArgument *next = current->next; + Argument *next = current->next; free(current); current = next; }; @@ -165,13 +165,31 @@ free_handlers(Handler *handlers) free(current->opt_return_type); } free_attributes(current->opt_ll_attributes); - free_handler_arguments(current->opt_ll_arguments); + 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) { @@ -435,20 +453,20 @@ parse_attributes(Parser *p) if (p->current == '(') { advance(p); - AttributeArgument *arg_head = NULL, *arg_tail = NULL; + AttributeParameter *arg_head = NULL, *arg_tail = NULL; eat_whitespaces_and_comments(p); while (p->current != ')') { - AttributeArgument *arg = (AttributeArgument *)calloc(1, sizeof(AttributeArgument)); + AttributeParameter *arg = (AttributeParameter *)calloc(1, sizeof(AttributeParameter)); assert(arg); eat_whitespaces_and_comments(p); arg->key = parse_identifier(p); if (!arg->key) { - free_attribute_arguments(arg); - free_attribute_arguments(arg_head); + free_attribute_parameters(arg); + free_attribute_parameters(arg_head); free_attributes(attr); free_attributes(head); return NULL; @@ -462,8 +480,8 @@ parse_attributes(Parser *p) arg->opt_value = parse_string(p); if (!arg->opt_value) { - free_attribute_arguments(arg); - free_attribute_arguments(arg_head); + free_attribute_parameters(arg); + free_attribute_parameters(arg_head); free_attributes(attr); free_attributes(head); return NULL; @@ -492,7 +510,7 @@ parse_attributes(Parser *p) if (p->current != ')') { error(p, "Expected ')' after attribute argument"); - free_attribute_arguments(arg_head); + free_attribute_parameters(arg_head); free_attributes(attr); free_attributes(head); @@ -694,13 +712,13 @@ parse_properties(Parser *p) return head; } -static HandlerArgument * +static Argument * parse_handler_arguments(Parser *p) { - HandlerArgument *head = NULL, *tail = NULL; + Argument *head = NULL, *tail = NULL; while (p->current != ')') { - HandlerArgument *arg = (HandlerArgument *)calloc(1, sizeof(HandlerArgument)); + Argument *arg = (Argument *)calloc(1, sizeof(Argument)); assert(arg); eat_whitespaces_and_comments(p); arg->name = parse_identifier(p); @@ -708,15 +726,15 @@ parse_handler_arguments(Parser *p) if (!arg->name) { error(p, "Expected argument name"); - free_handler_arguments(arg); - free_handler_arguments(head); + free_arguments(arg); + free_arguments(head); return NULL; }; if (p->current != ':') { error(p, "Expected ':' after argument name"); - free_handler_arguments(arg); - free_handler_arguments(head); + free_arguments(arg); + free_arguments(head); return NULL; } advance(p); @@ -726,8 +744,8 @@ parse_handler_arguments(Parser *p) if (!arg->type) { error(p, "Expected argument type"); - free_handler_arguments(arg); - free_handler_arguments(head); + free_arguments(arg); + free_arguments(head); return NULL; }; @@ -739,6 +757,7 @@ parse_handler_arguments(Parser *p) { tail->next = arg; } + tail = arg; if (p->current != ',') { @@ -754,6 +773,7 @@ typedef struct ServiceComponents { Handler *ll_handlers; Dependency *opt_ll_dependencies; + Event *opt_ll_events; } ServiceComponents; static void @@ -774,9 +794,9 @@ parse_service(Parser *p) } advance(p); - ServiceComponents *sc = (ServiceComponents *)calloc(1, sizeof(ServiceComponents)); Handler *handler_head = NULL, *handler_tail = NULL; Dependency *dep_head = NULL, *dep_tail = NULL; + Event *event_head = NULL, *event_tail = NULL; while (p->current != '}') { @@ -786,11 +806,11 @@ parse_service(Parser *p) char *ident = parse_identifier(p); if (!ident) { - error(p, "Expected 'depends' or 'handle' keyword"); + error(p, "Expected 'depends' or 'fn' keyword"); free(ident); free_dependencies(dep_head); free_handlers(handler_head); - free_service_components(sc); + free_events(event_head); return NULL; }; @@ -808,7 +828,7 @@ parse_service(Parser *p) free_dependencies(dep); free_dependencies(dep_head); free_handlers(handler_head); - free_service_components(sc); + free_events(event_head); return NULL; }; @@ -822,7 +842,7 @@ parse_service(Parser *p) } dep_tail = dep; } - else if (strcmp(ident, "handle") == 0) + else if (strcmp(ident, "fn") == 0) { Handler *handler = (Handler *)calloc(1, sizeof(Handler)); assert(handler); @@ -838,8 +858,8 @@ parse_service(Parser *p) free(ident); free_handlers(handler); free_handlers(handler_head); + free_events(event_head); free_dependencies(dep_head); - free_service_components(sc); return NULL; }; @@ -850,8 +870,8 @@ parse_service(Parser *p) free(ident); free_handlers(handler); free_handlers(handler_head); + free_events(event_head); free_dependencies(dep_head); - free_service_components(sc); return NULL; } advance(p); @@ -866,15 +886,16 @@ parse_service(Parser *p) free(ident); free_handlers(handler); free_handlers(handler_head); + free_events(event_head); free_dependencies(dep_head); - free_service_components(sc); return NULL; } advance(p); eat_whitespaces_and_comments(p); - if (p->current == ':') + if (p->current == '-' && peek(p) == '>') { + advance(p); advance(p); eat_whitespaces_and_comments(p); handler->opt_return_type = parse_identifier(p); @@ -885,8 +906,8 @@ parse_service(Parser *p) free(ident); free_handlers(handler); free_handlers(handler_head); + free_events(event_head); free_dependencies(dep_head); - free_service_components(sc); return NULL; }; } @@ -901,23 +922,81 @@ parse_service(Parser *p) } 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 'handle' keyword"); + error(p, "Expected 'depends' or 'fn' keyword"); free(ident); free_dependencies(dep_head); free_handlers(handler_head); - free_service_components(sc); return NULL; } + free(ident); + + eat_whitespaces_and_comments(p); if (p->current != ';') { error(p, "Expected ';' after service component"); - free(ident); free_dependencies(dep_head); free_handlers(handler_head); - free_service_components(sc); return NULL; } advance(p); @@ -925,8 +1004,10 @@ parse_service(Parser *p) eat_whitespaces_and_comments(p); } advance(p); + ServiceComponents *sc = (ServiceComponents *)calloc(1, sizeof(ServiceComponents)); sc->ll_handlers = handler_head; sc->opt_ll_dependencies = dep_head; + sc->opt_ll_events = event_head; return sc; } @@ -1029,6 +1110,7 @@ parse_node(Parser *p) } node->node.service_node.ll_handlers = sc->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 @@ -1103,6 +1185,7 @@ 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; @@ -1133,112 +1216,118 @@ 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 : -1; + return node ? &node->type : NULL; } -const char * -minissd_get_import_path(const AstNode *node) +char const * +minissd_get_import_path(AstNode const *node) { return (node && node->type == NODE_IMPORT) ? node->node.import_node.path : NULL; } -const char * -minissd_get_data_name(const AstNode *node) +char const * +minissd_get_data_name(AstNode const *node) { return (node && node->type == NODE_DATA) ? node->node.data_node.name : NULL; } -const char * -minissd_get_enum_name(const AstNode *node) +char const * +minissd_get_enum_name(AstNode const *node) { return (node && node->type == NODE_ENUM) ? node->node.enum_node.name : NULL; } -const char * -minissd_get_handler_name(const AstNode *node) +char const * +minissd_get_handler_name(AstNode const *node) { return (node && node->type == NODE_SERVICE) ? node->node.service_node.name : NULL; } // Attribute accessors -Attribute * -minissd_get_attributes(const AstNode *node) +Attribute const * +minissd_get_attributes(AstNode const *node) { 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; } -AttributeArgument * -minissd_get_attribute_arguments(const Attribute *attr) +AttributeParameter const * +minissd_get_attribute_parameters(Attribute const *attr) { 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.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 * -minissd_get_dependencies(const AstNode *node) +Dependency const * +minissd_get_dependencies(AstNode const *node) { return (node && node->type == NODE_SERVICE) ? node->node.service_node.opt_ll_dependencies : NULL; } -Handler * -minissd_get_handlers(const AstNode *node) +Handler const * +minissd_get_handlers(AstNode const *node) { return (node && node->type == NODE_SERVICE) ? node->node.service_node.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.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(EnumVariant const *value, bool *has_value) { if (!value || !value->opt_value) { @@ -1255,82 +1344,97 @@ int minissd_get_enum_variant(const EnumVariant *value, bool *has_value) return *(value->opt_value); } -HandlerArgument * -minissd_get_handler_arguments(const Handler *handler) +Argument const * +minissd_get_handler_arguments(Handler const *handler) { return (handler) ? handler->opt_ll_arguments : NULL; } -const char * -minissd_get_argument_name(const HandlerArgument *arg) + +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 * -minissd_get_argument_attributes(const HandlerArgument *arg) +Attribute const * +minissd_get_argument_attributes(Argument const *arg) { return arg ? arg->attributes : NULL; } -const char * -minissd_get_argument_type(const HandlerArgument *arg) +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_value(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; } -AttributeArgument * -minissd_get_next_attribute_argument(const AttributeArgument *arg) +AttributeParameter const * +minissd_get_next_attribute_parameter(AttributeParameter const *arg) { return arg ? arg->next : NULL; } -Dependency * -minissd_get_next_dependency(const Dependency *dep) +Dependency const * +minissd_get_next_dependency(Dependency const *dep) { return dep ? dep->next : NULL; } -Handler * -minissd_get_next_handler(const Handler *handler) +Handler const * +minissd_get_next_handler(Handler const *handler) { return handler ? handler->next : NULL; } -HandlerArgument * -minissd_get_next_handler_argument(const HandlerArgument *arg) +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; } -const char *minissd_get_dependency_path(const Dependency *dep) +char const * +minissd_get_dependency_path(Dependency const *dep) { return dep ? dep->path : NULL; } -const char *minissd_get_service_name(const AstNode *node) +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 17f6e10..0daf4fd 100644 --- a/test.ssd +++ b/test.ssd @@ -28,9 +28,14 @@ data MyData { }; #[a(b="c")] + service MyService { + #[d(e="f")] - depends a::b::c; + depends a::b::c ; #[g(h="i")] - handle asdf(blah: int); + 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 5ac25f4..e61d1b9 100644 --- a/tests/src/test_parser.cpp +++ b/tests/src/test_parser.cpp @@ -23,12 +23,14 @@ TEST_F(ParserTest, ValidInput_Data) 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); // Ensure AST is not NULL + NodeType const *node_type = minissd_get_node_type(ast); // Get node type + ASSERT_NE(node_type, nullptr); // Ensure node type is not NULL + ASSERT_EQ(*node_type, NODE_DATA); // Check if node type is 'data' + ASSERT_STREQ(minissd_get_data_name(ast), "Person"); // Check data node name // Check properties - Property *prop = minissd_get_properties(ast); + Property const *prop = minissd_get_properties(ast); ASSERT_NE(prop, nullptr); // Ensure there are properties ASSERT_STREQ(minissd_get_property_name(prop), "name"); ASSERT_STREQ(minissd_get_property_type(prop), "string"); @@ -47,12 +49,14 @@ TEST_F(ParserTest, ValidInput_Enum) 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); // Ensure AST is not NULL + NodeType const *node_type = minissd_get_node_type(ast); // Get node type + ASSERT_NE(node_type, nullptr); // Ensure node type is not NULL + ASSERT_EQ(*node_type, NODE_ENUM); // Check if node type is 'enum' + ASSERT_STREQ(minissd_get_enum_name(ast), "Color"); // Check enum node name // Check enum variants - EnumVariant *variant = minissd_get_enum_variants(ast); + EnumVariant const *variant = minissd_get_enum_variants(ast); ASSERT_NE(variant, nullptr); // Ensure there are enum variants ASSERT_STREQ(minissd_get_enum_variant_name(variant), "Red"); ASSERT_EQ(minissd_get_enum_variant(variant, nullptr), 1); // Check variant for 'Red' @@ -77,7 +81,9 @@ TEST_F(ParserTest, ValidInput_Import) 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' + NodeType const *node_type = minissd_get_node_type(ast); // Get node type + ASSERT_NE(node_type, nullptr); // Ensure node type is not NULL + ASSERT_EQ(*node_type, NODE_IMPORT); // Check if node type is 'import' ASSERT_STREQ(minissd_get_import_path(ast), "my::module"); // Check path } @@ -90,10 +96,12 @@ TEST_F(ParserTest, ValidInput_MissingEnumValues) 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); // Get node type + ASSERT_NE(node_type, nullptr); // Ensure node type is not NULL + ASSERT_EQ(*node_type, NODE_ENUM); // Check if node type is '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' @@ -172,22 +180,24 @@ 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); // Ensure AST is not NULL + NodeType const *node_type = minissd_get_node_type(ast); // Get node type + ASSERT_NE(node_type, nullptr); // Ensure node type is not NULL + ASSERT_EQ(*node_type, NODE_DATA); // Check if node type is 'data' + ASSERT_STREQ(minissd_get_data_name(ast), "Person"); // Check data node name - Property *props = minissd_get_properties(ast); + Property const *props = minissd_get_properties(ast); ASSERT_NE(props, nullptr); // Ensure there are properties // Check for attributes - Attribute *attr = minissd_get_property_attributes(props); + Attribute const *attr = minissd_get_property_attributes(props); ASSERT_NE(attr, nullptr); // Ensure there are attributes ASSERT_STREQ(minissd_get_attribute_name(attr), "attr1"); - AttributeArgument *arg = minissd_get_attribute_arguments(attr); - ASSERT_NE(arg, nullptr); // Ensure there are arguments - ASSERT_STREQ(arg->key, "name"); - ASSERT_STREQ(arg->opt_value, "value1"); + AttributeParameter const *param = minissd_get_attribute_parameters(attr); + ASSERT_NE(param, nullptr); // Ensure there are arguments + ASSERT_STREQ(param->key, "name"); + ASSERT_STREQ(param->opt_value, "value1"); } // Test for multiple attributes in the same node @@ -198,15 +208,17 @@ 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); // Ensure AST is not NULL + NodeType const *node_type = minissd_get_node_type(ast); // Get node type + ASSERT_NE(node_type, nullptr); // Ensure node type is not NULL + ASSERT_EQ(*node_type, NODE_DATA); // Check if node type is 'data' + ASSERT_STREQ(minissd_get_data_name(ast), "Person"); // Check data node name - Property *props = minissd_get_properties(ast); + Property const *props = minissd_get_properties(ast); ASSERT_NE(props, nullptr); // Ensure there are properties // Check for first attribute - Attribute *attr = minissd_get_property_attributes(props); + Attribute const *attr = minissd_get_property_attributes(props); ASSERT_NE(attr, nullptr); // Ensure there are attributes ASSERT_STREQ(minissd_get_attribute_name(attr), "attr1"); @@ -215,10 +227,10 @@ TEST_F(ParserTest, ValidInput_MultipleAttributes) ASSERT_NE(attr, nullptr); // Ensure there is another attribute ASSERT_STREQ(minissd_get_attribute_name(attr), "attr2"); - AttributeArgument *arg = minissd_get_attribute_arguments(attr); - ASSERT_NE(arg, nullptr); // Ensure there are arguments - ASSERT_STREQ(arg->key, "name"); - ASSERT_STREQ(arg->opt_value, "value1"); + AttributeParameter const *param = minissd_get_attribute_parameters(attr); + ASSERT_NE(param, nullptr); // Ensure there are arguments + ASSERT_STREQ(param->key, "name"); + ASSERT_STREQ(param->opt_value, "value1"); } TEST_F(ParserTest, ValidInput_MultipleAttributes2) @@ -228,15 +240,17 @@ 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); // Ensure AST is not NULL + NodeType const *node_type = minissd_get_node_type(ast); // Get node type + ASSERT_NE(node_type, nullptr); // Ensure node type is not NULL + ASSERT_EQ(*node_type, NODE_DATA); // Check if node type is 'data' + ASSERT_STREQ(minissd_get_data_name(ast), "Person"); // Check data node name - Property *props = minissd_get_properties(ast); + Property const *props = minissd_get_properties(ast); ASSERT_NE(props, nullptr); // Ensure there are properties // Check for first attribute - Attribute *attr = minissd_get_property_attributes(props); + Attribute const *attr = minissd_get_property_attributes(props); ASSERT_NE(attr, nullptr); // Ensure there are attributes ASSERT_STREQ(minissd_get_attribute_name(attr), "attr1"); @@ -245,8 +259,8 @@ TEST_F(ParserTest, ValidInput_MultipleAttributes2) ASSERT_NE(attr, nullptr); // Ensure there is another attribute ASSERT_STREQ(minissd_get_attribute_name(attr), "attr2"); - AttributeArgument *arg = minissd_get_attribute_arguments(attr); - ASSERT_NE(arg, nullptr); // Ensure there are arguments - ASSERT_STREQ(arg->key, "name"); - ASSERT_STREQ(arg->opt_value, "value1"); + AttributeParameter const *param = minissd_get_attribute_parameters(attr); + ASSERT_NE(param, nullptr); // Ensure there are arguments + ASSERT_STREQ(param->key, "name"); + ASSERT_STREQ(param->opt_value, "value1"); } From dfb0ff54d2a6eec5f6c7f7fb6742572054a0d5f4 Mon Sep 17 00:00:00 2001 From: hardliner66 Date: Sun, 2 Feb 2025 02:13:16 +0100 Subject: [PATCH 03/10] fix potential leaks --- src/minissd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/minissd.c b/src/minissd.c index 5b98e70..5c01464 100644 --- a/src/minissd.c +++ b/src/minissd.c @@ -984,6 +984,7 @@ parse_service(Parser *p) { error(p, "Expected 'depends' or 'fn' keyword"); free(ident); + free_events(event_head); free_dependencies(dep_head); free_handlers(handler_head); return NULL; @@ -995,6 +996,7 @@ parse_service(Parser *p) if (p->current != ';') { error(p, "Expected ';' after service component"); + free_events(event_head); free_dependencies(dep_head); free_handlers(handler_head); return NULL; From 022bb58b970b95ee5583b8a8c8e2a89e87e5948f Mon Sep 17 00:00:00 2001 From: hardliner66 Date: Sun, 2 Feb 2025 04:06:43 +0100 Subject: [PATCH 04/10] add more tests --- .vscode/settings.json | 1 + example/minissd_print.c | 2 +- include/minissd.h | 18 +- src/minissd.c | 53 +- tests/src/test_parser.cpp | 1022 +++++++++++++++++++++++++++++++++---- 5 files changed, 988 insertions(+), 108 deletions(-) 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/example/minissd_print.c b/example/minissd_print.c index 010d1df..6aa6c4c 100644 --- a/example/minissd_print.c +++ b/example/minissd_print.c @@ -97,7 +97,7 @@ int main(int argc, char **argv) printf(" Name: %s\n", minissd_get_enum_name(node)); print_attributes(minissd_get_attributes(node)); - for (EnumVariant const *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); diff --git a/include/minissd.h b/include/minissd.h index 01922fe..24d9971 100644 --- a/include/minissd.h +++ b/include/minissd.h @@ -98,7 +98,7 @@ extern "C" { char *name; Dependency *opt_ll_dependencies; - Handler *ll_handlers; + Handler *opt_ll_handlers; Event *opt_ll_events; } Service; @@ -157,7 +157,13 @@ extern "C" minissd_get_enum_name(AstNode const *node); char const * - minissd_get_handler_name(AstNode const *node); + minissd_get_handler_name(Handler const *node); + + char const * + minissd_get_handler_return_type(Handler const *handler); + + char const * + minissd_get_event_name(Event const *event); // Attribute accessors Attribute const * @@ -238,7 +244,7 @@ extern "C" minissd_get_next_property(Property const *prop); EnumVariant const * - minissd_get_next_enum_value(EnumVariant const *value); + minissd_get_next_enum_variant(EnumVariant const *value); Attribute const * minissd_get_next_attribute(Attribute const *attr); @@ -246,6 +252,12 @@ extern "C" AttributeParameter const * minissd_get_next_attribute_parameter(AttributeParameter const *arg); + char const * + minissd_get_attribute_parameter_name(AttributeParameter const *arg); + + char const * + minissd_get_attribute_parameter_value(AttributeParameter const *arg); + Argument const * minissd_get_next_argument(Argument const *arg); diff --git a/src/minissd.c b/src/minissd.c index 5c01464..4a68b76 100644 --- a/src/minissd.c +++ b/src/minissd.c @@ -225,7 +225,7 @@ free_ast(AstNode *ast) free(current->node.service_node.name); } free_dependencies(current->node.service_node.opt_ll_dependencies); - free_handlers(current->node.service_node.ll_handlers); + free_handlers(current->node.service_node.opt_ll_handlers); break; default: break; @@ -709,6 +709,13 @@ parse_properties(Parser *p) return NULL; } advance(p); + + if (!head) + { + error(p, "Expected property"); + return NULL; + } + return head; } @@ -771,7 +778,7 @@ parse_handler_arguments(Parser *p) typedef struct ServiceComponents { - Handler *ll_handlers; + Handler *opt_ll_handlers; Dependency *opt_ll_dependencies; Event *opt_ll_events; } ServiceComponents; @@ -779,7 +786,7 @@ typedef struct ServiceComponents static void free_service_components(ServiceComponents *sc) { - free_handlers(sc->ll_handlers); + free_handlers(sc->opt_ll_handlers); free_dependencies(sc->opt_ll_dependencies); free(sc); } @@ -1007,7 +1014,7 @@ parse_service(Parser *p) } advance(p); ServiceComponents *sc = (ServiceComponents *)calloc(1, sizeof(ServiceComponents)); - sc->ll_handlers = handler_head; + sc->opt_ll_handlers = handler_head; sc->opt_ll_dependencies = dep_head; sc->opt_ll_events = event_head; return sc; @@ -1102,15 +1109,15 @@ parse_node(Parser *p) free_ast(node); return NULL; }; - if (!sc->ll_handlers) + if (!sc->opt_ll_handlers && !sc->opt_ll_events) { - error(p, "Service must have at least one handler"); + 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.ll_handlers = sc->ll_handlers; + 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); @@ -1243,9 +1250,21 @@ minissd_get_enum_name(AstNode const *node) } char const * -minissd_get_handler_name(AstNode const *node) +minissd_get_handler_name(Handler const *handler) { - return (node && node->type == NODE_SERVICE) ? node->node.service_node.name : NULL; + return (handler) ? handler->name : NULL; +} + +char const * +minissd_get_handler_return_type(Handler const *handler) +{ + return (handler) ? handler->opt_return_type : NULL; +} + +char const * +minissd_get_event_name(Event const *event) +{ + return (event) ? event->name : NULL; } // Attribute accessors @@ -1301,7 +1320,7 @@ minissd_get_dependencies(AstNode const *node) Handler const * minissd_get_handlers(AstNode const *node) { - return (node && node->type == NODE_SERVICE) ? node->node.service_node.ll_handlers : NULL; + return (node && node->type == NODE_SERVICE) ? node->node.service_node.opt_ll_handlers : NULL; } Event const * @@ -1388,7 +1407,7 @@ minissd_get_next_property(Property const *prop) } EnumVariant const * -minissd_get_next_enum_value(EnumVariant const *value) +minissd_get_next_enum_variant(EnumVariant const *value) { return value ? value->next : NULL; } @@ -1405,6 +1424,18 @@ 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) { diff --git a/tests/src/test_parser.cpp b/tests/src/test_parser.cpp index e61d1b9..cf03347 100644 --- a/tests/src/test_parser.cpp +++ b/tests/src/test_parser.cpp @@ -15,23 +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 - NodeType const *node_type = minissd_get_node_type(ast); // Get node type - ASSERT_NE(node_type, nullptr); // Ensure node type is not NULL - ASSERT_EQ(*node_type, 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 const *prop = minissd_get_properties(ast); - ASSERT_NE(prop, nullptr); // Ensure there are properties + ASSERT_NE(prop, nullptr); ASSERT_STREQ(minissd_get_property_name(prop), "name"); ASSERT_STREQ(minissd_get_property_type(prop), "string"); @@ -41,114 +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 - NodeType const *node_type = minissd_get_node_type(ast); // Get node type - ASSERT_NE(node_type, nullptr); // Ensure node type is not NULL - ASSERT_EQ(*node_type, 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 const *variant = minissd_get_enum_variants(ast); - ASSERT_NE(variant, nullptr); // Ensure there are enum variants + 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(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_EnumNoTrailingComma) +{ + const char *source_code = "enum Color { Red };"; + + 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), "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(variant, nullptr), 0); + + variant = minissd_get_next_enum_variant(variant); + ASSERT_EQ(variant, nullptr); +} + +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"); - variant = minissd_get_next_enum_value(variant); + 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(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 - NodeType const *node_type = minissd_get_node_type(ast); // Get node type - ASSERT_NE(node_type, nullptr); // Ensure node type is not NULL - ASSERT_EQ(*node_type, 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(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); - NodeType const *node_type = minissd_get_node_type(ast); // Get node type - ASSERT_NE(node_type, nullptr); // Ensure node type is not NULL - ASSERT_EQ(*node_type, NODE_ENUM); // Check if node type is '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 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(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(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(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(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(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 = ""; @@ -156,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 };"; @@ -180,27 +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 - NodeType const *node_type = minissd_get_node_type(ast); // Get node type - ASSERT_NE(node_type, nullptr); // Ensure node type is not NULL - ASSERT_EQ(*node_type, 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 const *props = minissd_get_properties(ast); - ASSERT_NE(props, nullptr); // Ensure there are properties + ASSERT_NE(props, nullptr); - // Check for attributes Attribute const *attr = minissd_get_property_attributes(props); - ASSERT_NE(attr, nullptr); // Ensure there are attributes + ASSERT_NE(attr, nullptr); ASSERT_STREQ(minissd_get_attribute_name(attr), "attr1"); AttributeParameter const *param = minissd_get_attribute_parameters(attr); - ASSERT_NE(param, nullptr); // Ensure there are arguments + 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 };"; @@ -208,27 +1048,25 @@ TEST_F(ParserTest, ValidInput_MultipleAttributes) parser = minissd_create_parser(source_code); ast = minissd_parse(parser); - ASSERT_NE(ast, nullptr); // Ensure AST is not NULL - NodeType const *node_type = minissd_get_node_type(ast); // Get node type - ASSERT_NE(node_type, nullptr); // Ensure node type is not NULL - ASSERT_EQ(*node_type, 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 const *props = minissd_get_properties(ast); - ASSERT_NE(props, nullptr); // Ensure there are properties + ASSERT_NE(props, nullptr); - // Check for first attribute Attribute const *attr = minissd_get_property_attributes(props); - ASSERT_NE(attr, nullptr); // Ensure there are attributes + 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"); AttributeParameter const *param = minissd_get_attribute_parameters(attr); - ASSERT_NE(param, nullptr); // Ensure there are arguments + ASSERT_NE(param, nullptr); ASSERT_STREQ(param->key, "name"); ASSERT_STREQ(param->opt_value, "value1"); } @@ -240,27 +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 - NodeType const *node_type = minissd_get_node_type(ast); // Get node type - ASSERT_NE(node_type, nullptr); // Ensure node type is not NULL - ASSERT_EQ(*node_type, 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 const *props = minissd_get_properties(ast); - ASSERT_NE(props, nullptr); // Ensure there are properties + ASSERT_NE(props, nullptr); - // Check for first attribute Attribute const *attr = minissd_get_property_attributes(props); - ASSERT_NE(attr, nullptr); // Ensure there are attributes + 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"); AttributeParameter const *param = minissd_get_attribute_parameters(attr); - ASSERT_NE(param, nullptr); // Ensure there are arguments + ASSERT_NE(param, nullptr); ASSERT_STREQ(param->key, "name"); ASSERT_STREQ(param->opt_value, "value1"); -} +} \ No newline at end of file From e62aa5ee9054bfddb1e205222369ca7a731069dc Mon Sep 17 00:00:00 2001 From: hardliner66 Date: Sun, 2 Feb 2025 04:25:21 +0100 Subject: [PATCH 05/10] re-order functions in header --- example/minissd_print.c | 2 +- include/minissd.h | 125 ++++++++++++++++++++------------------ src/minissd.c | 2 +- tests/src/test_parser.cpp | 18 +++--- 4 files changed, 77 insertions(+), 70 deletions(-) diff --git a/example/minissd_print.c b/example/minissd_print.c index 6aa6c4c..e6c97e1 100644 --- a/example/minissd_print.c +++ b/example/minissd_print.c @@ -100,7 +100,7 @@ int main(int argc, char **argv) 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); + int val = minissd_get_enum_variant_value(value, &has_value); printf(" Enum Variant: %s", minissd_get_enum_variant_name(value)); if (has_value) { diff --git a/include/minissd.h b/include/minissd.h index 24d9971..3e428ee 100644 --- a/include/minissd.h +++ b/include/minissd.h @@ -143,7 +143,7 @@ extern "C" AstNode *minissd_parse(Parser *p); void minissd_free_ast(AstNode *ast); - // AST Node accessors + // AST Node Accessors NodeType const * minissd_get_node_type(AstNode const *node); @@ -156,110 +156,117 @@ extern "C" char const * minissd_get_enum_name(AstNode const *node); + char const * + minissd_get_service_name(AstNode const *node); + + Property const * + minissd_get_properties(AstNode const *node); + + EnumVariant const * + minissd_get_enum_variants(AstNode const *node); + + Dependency const * + minissd_get_dependencies(AstNode const *node); + + Handler const * + minissd_get_handlers(AstNode const *node); + + Event const * + minissd_get_events(AstNode const *node); + + Attribute const * + minissd_get_attributes(AstNode const *node); + + AstNode const * + minissd_get_next_node(AstNode const *node); + + // Handler Accessors char const * minissd_get_handler_name(Handler const *node); char const * minissd_get_handler_return_type(Handler const *handler); + Argument const * + minissd_get_handler_arguments(Handler const *node); + + Handler const * + minissd_get_next_handler(Handler const *handler); + + // Event Accessors char const * minissd_get_event_name(Event const *event); - // Attribute accessors - Attribute const * - minissd_get_attributes(AstNode const *node); + Argument const * + minissd_get_event_arguments(Event const *event); - char const * - minissd_get_attribute_name(Attribute const *attr); + Event const * + minissd_get_next_event(Event const *event); - AttributeParameter const * - minissd_get_attribute_parameters(Attribute const *attr); + // Dependency Accessors + char const * + minissd_get_dependency_path(Dependency const *dep); - // Property and EnumVariant accessors - Property const * - minissd_get_properties(AstNode const *node); + Dependency const * + minissd_get_next_dependency(Dependency const *dep); + // Property Accessors char const * minissd_get_property_name(Property const *prop); - Attribute const * - minissd_get_property_attributes(Property const *prop); - char const * minissd_get_property_type(Property const *prop); - EnumVariant const * - minissd_get_enum_variants(AstNode const *node); + Attribute const * + minissd_get_property_attributes(Property const *prop); + + Property const * + minissd_get_next_property(Property const *prop); + // Enum Variant Accessors char const * minissd_get_enum_variant_name(EnumVariant const *value); + int minissd_get_enum_variant_value(EnumVariant const *value, bool *has_value); + Attribute const * minissd_get_enum_variant_attributes(EnumVariant const *value); - int minissd_get_enum_variant(EnumVariant const *value, bool *has_value); - - char const * - minissd_get_service_name(AstNode const *node); - - Argument const * - minissd_get_handler_arguments(Handler const *node); - - Argument const * - minissd_get_event_arguments(Event const *event); + EnumVariant const * + minissd_get_next_enum_variant(EnumVariant const *value); + // Argument Accessors char const * minissd_get_argument_name(Argument const *prop); - Attribute const * - minissd_get_argument_attributes(Argument const *prop); - char const * minissd_get_argument_type(Argument const *prop); - Dependency const * - minissd_get_dependencies(AstNode const *node); - Handler const * - minissd_get_handlers(AstNode const *node); - - Event const * - minissd_get_events(AstNode const *node); - - Dependency const * - minissd_get_next_dependency(Dependency const *dep); - - Handler const * - minissd_get_next_handler(Handler const *handler); - - Event const * - minissd_get_next_event(Event const *event); - - char const *minissd_get_dependency_path(Dependency const *dep); + Attribute const * + minissd_get_argument_attributes(Argument const *prop); - // Traversal functions - AstNode const * - minissd_get_next_node(AstNode const *node); + Argument const * + minissd_get_next_argument(Argument const *arg); - Property const * - minissd_get_next_property(Property const *prop); + // Attribute Accessors + char const * + minissd_get_attribute_name(Attribute const *attr); - EnumVariant const * - minissd_get_next_enum_variant(EnumVariant const *value); + AttributeParameter const * + minissd_get_attribute_parameters(Attribute const *attr); Attribute const * minissd_get_next_attribute(Attribute const *attr); - AttributeParameter const * - minissd_get_next_attribute_parameter(AttributeParameter const *arg); - + // Attribute Parameter Accessors char const * minissd_get_attribute_parameter_name(AttributeParameter const *arg); char const * minissd_get_attribute_parameter_value(AttributeParameter const *arg); - Argument const * - minissd_get_next_argument(Argument const *arg); + AttributeParameter const * + minissd_get_next_attribute_parameter(AttributeParameter const *arg); #ifdef __cplusplus } diff --git a/src/minissd.c b/src/minissd.c index 4a68b76..15002e6 100644 --- a/src/minissd.c +++ b/src/minissd.c @@ -1348,7 +1348,7 @@ minissd_get_enum_variant_attributes(EnumVariant const *value) return value ? value->attributes : NULL; } -int minissd_get_enum_variant(EnumVariant const *value, bool *has_value) +int minissd_get_enum_variant_value(EnumVariant const *value, bool *has_value) { if (!value || !value->opt_value) { diff --git a/tests/src/test_parser.cpp b/tests/src/test_parser.cpp index cf03347..d9525d1 100644 --- a/tests/src/test_parser.cpp +++ b/tests/src/test_parser.cpp @@ -790,7 +790,7 @@ TEST_F(ParserTest, ValidInput_Enum) 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); + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 0); variant = minissd_get_next_enum_variant(variant); ASSERT_EQ(variant, nullptr); @@ -812,7 +812,7 @@ TEST_F(ParserTest, ValidInput_EnumNoTrailingComma) 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); + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 0); variant = minissd_get_next_enum_variant(variant); ASSERT_EQ(variant, nullptr); @@ -834,7 +834,7 @@ TEST_F(ParserTest, ValidInput_EnumWithValue) 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); + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 1); variant = minissd_get_next_enum_variant(variant); ASSERT_EQ(variant, nullptr); @@ -856,7 +856,7 @@ TEST_F(ParserTest, ValidInput_EnumWithValueNoTrailingComma) 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); + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 1); variant = minissd_get_next_enum_variant(variant); ASSERT_EQ(variant, nullptr); @@ -878,12 +878,12 @@ TEST_F(ParserTest, ValidInput_EnumWithValues) 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); + 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(variant, nullptr), 2); + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 2); variant = minissd_get_next_enum_variant(variant); ASSERT_EQ(variant, nullptr); @@ -905,12 +905,12 @@ TEST_F(ParserTest, ValidInput_EnumWithValuesNoTrailingComma) 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); + 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(variant, nullptr), 2); + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 2); variant = minissd_get_next_enum_variant(variant); ASSERT_EQ(variant, nullptr); @@ -932,7 +932,7 @@ TEST_F(ParserTest, ValidInput_EnumWithSpaceAfter) 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); + ASSERT_EQ(minissd_get_enum_variant_value(variant, nullptr), 0); variant = minissd_get_next_enum_variant(variant); ASSERT_EQ(variant, nullptr); From f561034a6dc34ef6e731084d4dfb5c69f0f4b455 Mon Sep 17 00:00:00 2001 From: hardliner66 Date: Sun, 2 Feb 2025 04:26:35 +0100 Subject: [PATCH 06/10] add stddef header to fix compiler error for gcc --- include/minissd.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/minissd.h b/include/minissd.h index 3e428ee..1bf02c7 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 From 3bf8cb8b9dd8d704b8bb75d1287ccd10e9b2eed4 Mon Sep 17 00:00:00 2001 From: hardliner66 Date: Sun, 2 Feb 2025 04:56:20 +0100 Subject: [PATCH 07/10] try to fix leaks --- src/minissd.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/minissd.c b/src/minissd.c index 15002e6..8fc4eb9 100644 --- a/src/minissd.c +++ b/src/minissd.c @@ -17,18 +17,13 @@ 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; } @@ -788,6 +783,7 @@ 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); } @@ -815,6 +811,7 @@ parse_service(Parser *p) { error(p, "Expected 'depends' or 'fn' keyword"); free(ident); + free_attributes(attributes); free_dependencies(dep_head); free_handlers(handler_head); free_events(event_head); @@ -992,6 +989,7 @@ parse_service(Parser *p) 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; From 2281df2bc5b4ab38408bc11cbbdb6d097f919f12 Mon Sep 17 00:00:00 2001 From: hardliner66 Date: Sun, 2 Feb 2025 05:00:34 +0100 Subject: [PATCH 08/10] update valgrind commandline --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 14bf3b60c004d783b29d16fffd49abea83ac2b6d Mon Sep 17 00:00:00 2001 From: Steve Biedermann Date: Sun, 2 Feb 2025 05:08:13 +0100 Subject: [PATCH 09/10] Fix memory leaks --- src/minissd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/minissd.c b/src/minissd.c index 8fc4eb9..a20c839 100644 --- a/src/minissd.c +++ b/src/minissd.c @@ -221,6 +221,7 @@ free_ast(AstNode *ast) } 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; From 7dafc296c0ffb61df9e22b013eb9a826c509ee49 Mon Sep 17 00:00:00 2001 From: Steve Biedermann Date: Sun, 2 Feb 2025 05:19:03 +0100 Subject: [PATCH 10/10] Add function exports and config for shared libraries --- CMakeLists.txt | 9 ++++- include/minissd.h | 97 +++++++++++++++++++++++++++-------------------- 2 files changed, 64 insertions(+), 42 deletions(-) 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/include/minissd.h b/include/minissd.h index 1bf02c7..cefd4a2 100644 --- a/include/minissd.h +++ b/include/minissd.h @@ -12,6 +12,21 @@ #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" { @@ -137,136 +152,136 @@ 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 const * + MINISSD_API NodeType const * minissd_get_node_type(AstNode const *node); - char const * + MINISSD_API char const * minissd_get_import_path(AstNode const *node); - char const * + MINISSD_API char const * minissd_get_data_name(AstNode const *node); - char const * + MINISSD_API char const * minissd_get_enum_name(AstNode const *node); - char const * + MINISSD_API char const * minissd_get_service_name(AstNode const *node); - Property const * + MINISSD_API Property const * minissd_get_properties(AstNode const *node); - EnumVariant const * + MINISSD_API EnumVariant const * minissd_get_enum_variants(AstNode const *node); - Dependency const * + MINISSD_API Dependency const * minissd_get_dependencies(AstNode const *node); - Handler const * + MINISSD_API Handler const * minissd_get_handlers(AstNode const *node); - Event const * + MINISSD_API Event const * minissd_get_events(AstNode const *node); - Attribute const * + MINISSD_API Attribute const * minissd_get_attributes(AstNode const *node); - AstNode const * + MINISSD_API AstNode const * minissd_get_next_node(AstNode const *node); // Handler Accessors - char const * + MINISSD_API char const * minissd_get_handler_name(Handler const *node); - char const * + MINISSD_API char const * minissd_get_handler_return_type(Handler const *handler); - Argument const * + MINISSD_API Argument const * minissd_get_handler_arguments(Handler const *node); - Handler const * + MINISSD_API Handler const * minissd_get_next_handler(Handler const *handler); // Event Accessors - char const * + MINISSD_API char const * minissd_get_event_name(Event const *event); - Argument const * + MINISSD_API Argument const * minissd_get_event_arguments(Event const *event); - Event const * + MINISSD_API Event const * minissd_get_next_event(Event const *event); // Dependency Accessors - char const * + MINISSD_API char const * minissd_get_dependency_path(Dependency const *dep); - Dependency const * + MINISSD_API Dependency const * minissd_get_next_dependency(Dependency const *dep); // Property Accessors - char const * + MINISSD_API char const * minissd_get_property_name(Property const *prop); - char const * + MINISSD_API char const * minissd_get_property_type(Property const *prop); - Attribute const * + MINISSD_API Attribute const * minissd_get_property_attributes(Property const *prop); - Property const * + MINISSD_API Property const * minissd_get_next_property(Property const *prop); // Enum Variant Accessors - char const * + MINISSD_API char const * minissd_get_enum_variant_name(EnumVariant const *value); - int minissd_get_enum_variant_value(EnumVariant const *value, bool *has_value); + MINISSD_API int minissd_get_enum_variant_value(EnumVariant const *value, bool *has_value); - Attribute const * + MINISSD_API Attribute const * minissd_get_enum_variant_attributes(EnumVariant const *value); - EnumVariant const * + MINISSD_API EnumVariant const * minissd_get_next_enum_variant(EnumVariant const *value); // Argument Accessors - char const * + MINISSD_API char const * minissd_get_argument_name(Argument const *prop); - char const * + MINISSD_API char const * minissd_get_argument_type(Argument const *prop); - Attribute const * + MINISSD_API Attribute const * minissd_get_argument_attributes(Argument const *prop); - Argument const * + MINISSD_API Argument const * minissd_get_next_argument(Argument const *arg); // Attribute Accessors - char const * + MINISSD_API char const * minissd_get_attribute_name(Attribute const *attr); - AttributeParameter const * + MINISSD_API AttributeParameter const * minissd_get_attribute_parameters(Attribute const *attr); - Attribute const * + MINISSD_API Attribute const * minissd_get_next_attribute(Attribute const *attr); // Attribute Parameter Accessors - char const * + MINISSD_API char const * minissd_get_attribute_parameter_name(AttributeParameter const *arg); - char const * + MINISSD_API char const * minissd_get_attribute_parameter_value(AttributeParameter const *arg); - AttributeParameter const * + MINISSD_API AttributeParameter const * minissd_get_next_attribute_parameter(AttributeParameter const *arg); #ifdef __cplusplus