From 745649df1c4e5a5d27eafba0c610423476c16b3a Mon Sep 17 00:00:00 2001 From: Matteo Flebus Date: Mon, 12 Jan 2026 21:31:15 +0100 Subject: [PATCH 01/12] feat(lexer): finished --- src/lexer/lexer.c | 75 +++++++++++++++++++++++++++++++++++------------ src/lexer/lexer.h | 1 + 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c index def814c..5d4ba94 100644 --- a/src/lexer/lexer.c +++ b/src/lexer/lexer.c @@ -1,15 +1,53 @@ #include "lexer.h" +#include #include #include #include #include #include "io_backend/io_backend.h" +#include "utils/string_utils/string_utils.h" static char *end_last_token; static ssize_t remaining_chars; +/* @brief: saves state for the next call the the lexer. + * + */ +static void save_state(char *stream, ssize_t i) +{ + remaining_chars -= i; + end_last_token = stream + i; + return; +} + +/* @return: true if a special character from the grammar was found, + * false otherwise. + * + */ +static bool is_special_char(char c) +{ + return c == '\'' || c == '\n' || c == ';'; +} + +/* @return: true if a keyword from the grammar was found, false otherwise. + * + */ +static bool is_keyword(char *stream, ssize_t i) +{ + if (i == 2) + { + return strcmp(stream, "if") == 0 || strcmp(stream, "fi") == 0; + } + if (i == 4) + { + return strcmp(stream, "then") || strcmp(stream, "else") + || strcmp(stream, "elif"); + } + return false; +} + char *new_token(char *begin, ssize_t size) { char *res = calloc(size + 1, sizeof(char)); @@ -32,6 +70,10 @@ char *stream_init(void) stream = end_last_token; } + char *trimed_stream = trim_blanks_left(stream); + remaining_chars -= trimed_stream - stream; + stream = trimed_stream; + return stream; } @@ -39,34 +81,29 @@ char *get_token(void) { char *stream = stream_init(); - bool inquotes = false; ssize_t i = 0; while (i < remaining_chars) { - switch (stream[i]) + if (is_special_char(stream[i])) { - case '\'': - inquotes = !inquotes; + if (i == 0) // where we create spe_char token + i++; break; - - case ' ' | '\n' | '\t': - if (inquotes) - break; - else - { - // token creation - // skip blank char - // exit from loop - char *token = new_token(stream, i); - return token; - } - default: + } + if (isblank(stream[i])) + { + break; + } + else if (is_keyword(stream, i)) + { + i++; break; } i++; } - remaining_chars -= i; - return NULL; + save_state(stream, i); + + return new_token(stream, i); } diff --git a/src/lexer/lexer.h b/src/lexer/lexer.h index 406ca9c..7e7ca10 100644 --- a/src/lexer/lexer.h +++ b/src/lexer/lexer.h @@ -30,6 +30,7 @@ char *new_token(char *begin, ssize_t size); * If it is, it calls stream_read() from IO_backend, * and sets [remaing_chars]. * If not, it starts from the end of the last token. + * Also trims left blanks before returning. * * @return: char* stream from which we tokenise. */ From b9eb7b90cbb2bb316c023e1ec6faa24274cf12ca Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Mon, 12 Jan 2026 17:29:00 +0100 Subject: [PATCH 02/12] fix(autotools): missing Makefile in configure.ac --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index f8638ca..14a03fd 100644 --- a/configure.ac +++ b/configure.ac @@ -24,6 +24,7 @@ AC_PROG_CC # List Makefiles in subdirectories AC_CONFIG_FILES([ + Makefile src/Makefile src/parser/Makefile src/lexer/Makefile From 8b75d73d56059f1ce305f68216b9b6db0b933506 Mon Sep 17 00:00:00 2001 From: "william.valenduc" Date: Mon, 12 Jan 2026 17:41:24 +0000 Subject: [PATCH 03/12] refactor(string_utils)!: rename skip_blanks to trim_blank_left and update signature --- src/utils/string_utils/string_utils.c | 20 ++++------ src/utils/string_utils/string_utils.h | 9 +++-- tests/unit/utils/utils_tests.c | 56 +++++++++++++++------------ 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/src/utils/string_utils/string_utils.c b/src/utils/string_utils/string_utils.c index 6762b07..8a8176a 100644 --- a/src/utils/string_utils/string_utils.c +++ b/src/utils/string_utils/string_utils.c @@ -3,17 +3,13 @@ #include #include -ssize_t skip_blanks(char **str) +char *trim_blank_left(char *str) { - if (str == NULL || *str == NULL) - { - return 0; - } - ssize_t skipped = 0; - while (*str[skipped] != '\0' && !isblank(str[skipped])) - { - skipped++; - } - *str += skipped; - return skipped; + if (str == NULL) + return NULL; + + while (*str != '\0' && isblank(*str)) + str++; + + return str; } diff --git a/src/utils/string_utils/string_utils.h b/src/utils/string_utils/string_utils.h index 5e6df94..496c1d5 100644 --- a/src/utils/string_utils/string_utils.h +++ b/src/utils/string_utils/string_utils.h @@ -4,9 +4,12 @@ #include /* - * @brief: skips blank characters at the beginning of [str]. - * @return: number of characters skipped. + * @brief trims leading blank characters (space and tab) from the input string. + * @param str input string to be trimmed. + * @return pointer to the first non-blank character in the string. If the + * string consists entirely of blank characters, returns a pointer to the null + * terminator at the end of the string. */ -ssize_t skip_blanks(char **str); +char *trim_blank_left(char *str); #endif /* STRING_UTILS_H */ diff --git a/tests/unit/utils/utils_tests.c b/tests/unit/utils/utils_tests.c index 1076aab..b7c326f 100644 --- a/tests/unit/utils/utils_tests.c +++ b/tests/unit/utils/utils_tests.c @@ -5,7 +5,7 @@ #include #include -#include "utils/string_utils/string_utils.h" +#include "../../../src/utils/string_utils/string_utils.h" TestSuite(string_utils); @@ -14,10 +14,11 @@ Test(string_utils, skipblank_basic) char input[] = " Hello World"; char expected_str[] = "Hello World"; - ssize_t actual = skip_blanks(input); + char *trimmed = trim_blank_left(input); + ssize_t offset = trimmed - input; ssize_t expected = 2; - cr_expect(eq(str, input, expected_str)); - cr_expect(actual == expected); + cr_expect(eq(str, trimmed, expected_str)); + cr_expect(offset == expected); } Test(string_utils, skipblank_noblank) @@ -25,10 +26,11 @@ Test(string_utils, skipblank_noblank) char input[] = "Hello World"; char expected_str[] = "Hello World"; - ssize_t actual = skip_blanks(input); + char *trimmed = trim_blank_left(input); + ssize_t offset = trimmed - input; ssize_t expected = 0; - cr_expect(eq(str, input, expected_str)); - cr_expect(actual == expected); + cr_expect(eq(str, trimmed, expected_str)); + cr_expect(offset == expected); } Test(string_utils, skipblank_tab) @@ -36,10 +38,11 @@ Test(string_utils, skipblank_tab) char input[] = "\tHello World"; char expected_str[] = "Hello World"; - ssize_t actual = skip_blanks(input); + char *trimmed = trim_blank_left(input); + ssize_t offset = trimmed - input; ssize_t expected = 1; - cr_expect(eq(str, input, expected_str)); - cr_expect(actual == expected); + cr_expect(eq(str, trimmed, expected_str)); + cr_expect(offset == expected); } Test(string_utils, skipblank_space_tab) @@ -47,10 +50,11 @@ Test(string_utils, skipblank_space_tab) char input[] = " \tHello World"; char expected_str[] = "Hello World"; - ssize_t actual = skip_blanks(input); + char *trimmed = trim_blank_left(input); + ssize_t offset = trimmed - input; ssize_t expected = 2; - cr_expect(eq(str, input, expected_str)); - cr_expect(actual == expected); + cr_expect(eq(str, trimmed, expected_str)); + cr_expect(offset == expected); } Test(string_utils, skipblank_2tab_1space) @@ -58,10 +62,11 @@ Test(string_utils, skipblank_2tab_1space) char input[] = "\t \tHello World"; char expected_str[] = "Hello World"; - ssize_t actual = skip_blanks(input); + char *trimmed = trim_blank_left(input); + ssize_t offset = trimmed - input; ssize_t expected = 3; - cr_expect(eq(str, input, expected_str)); - cr_expect(actual == expected); + cr_expect(eq(str, trimmed, expected_str)); + cr_expect(offset == expected); } Test(string_utils, skipblank_a_lot) @@ -69,10 +74,11 @@ Test(string_utils, skipblank_a_lot) char input[] = "\t \t \tHello World"; char expected_str[] = "Hello World"; - ssize_t actual = skip_blanks(input); + char *trimmed = trim_blank_left(input); + ssize_t offset = trimmed - input; ssize_t expected = 8; - cr_expect(eq(str, input, expected_str)); - cr_expect(actual == expected); + cr_expect(eq(str, trimmed, expected_str)); + cr_expect(offset == expected); } Test(string_utils, skipblank_newline) @@ -80,18 +86,20 @@ Test(string_utils, skipblank_newline) char input[] = "\nHello World"; char expected_str[] = "\nHello World"; - ssize_t actual = skip_blanks(input); + char *trimmed = trim_blank_left(input); + ssize_t offset = trimmed - input; ssize_t expected = 0; - cr_expect(eq(str, input, expected_str)); - cr_expect(actual == expected); + cr_expect(eq(str, trimmed, expected_str)); + cr_expect(offset == expected); } Test(string_utils, skipblank_nul) { char *input = NULL; - ssize_t actual = skip_blanks(input); + char *trimmed = trim_blank_left(input); + ssize_t offset = trimmed - input; ssize_t expected = 0; cr_expect(input == NULL); - cr_expect(actual == expected); + cr_expect(offset == expected); } From 7b7467104287d9bc3665e650d5d21637c67f41d9 Mon Sep 17 00:00:00 2001 From: "william.valenduc" Date: Mon, 12 Jan 2026 18:42:02 +0000 Subject: [PATCH 04/12] feat(args): args_handler --- src/utils/args/args.c | 64 +++++++++++++++++++++++++++++++++++++++++++ src/utils/args/args.h | 41 +++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/utils/args/args.c create mode 100644 src/utils/args/args.h diff --git a/src/utils/args/args.c b/src/utils/args/args.c new file mode 100644 index 0000000..40b5516 --- /dev/null +++ b/src/utils/args/args.c @@ -0,0 +1,64 @@ +#include "./args.h" + +#include +#include +#include + +int args_handler(int argc, char **argv, struct args_options *options) +{ + options->type = INPUT_UNDEFINED; + options->input_source = NULL; + options->pretty_print = false; + options->verbose = false; + + for (int i = 1; i < argc; i++) + { + if (strcmp(argv[i], "--pretty-print") == 0) + { + options->pretty_print = true; + } + else if (strcmp(argv[i], "--verbose") == 0) + { + options->verbose = true; + } + else if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) + { + if (options->type != INPUT_UNDEFINED) + return 1; // Error: multiple input types specified + + options->type = INPUT_CMD; + options->input_source = argv[i + 1]; + i++; + } + else if (argv[i][0] == '-') + { + return 1; // Error: unknown option + } + else + { + if (options->type != INPUT_UNDEFINED) + return 1; // Error: multiple input types specified + + options->type = INPUT_FILE; + options->input_source = argv[i]; + } + } + + if (options->type == INPUT_UNDEFINED) + options->type = INPUT_STDIN; + + return 0; +} + +void args_print(struct args_options *options) +{ + printf("Input type: %s\n", + options->type == INPUT_CMD ? "COMMAND" + : options->type == INPUT_FILE ? "FILE" + : options->type == INPUT_STDIN ? "STDIN" + : "UNDEFINED"); + printf("Input source: %s\n", + options->input_source ? options->input_source : "NULL"); + printf("Pretty print: %s\n", options->pretty_print ? "true" : "false"); + printf("Verbose: %s\n", options->verbose ? "true" : "false"); +} diff --git a/src/utils/args/args.h b/src/utils/args/args.h new file mode 100644 index 0000000..ddb6d8b --- /dev/null +++ b/src/utils/args/args.h @@ -0,0 +1,41 @@ +#ifndef ARGS_H +#define ARGS_H + +#include + +enum input_type +{ + INPUT_UNDEFINED, + INPUT_FILE, + INPUT_CMD, + INPUT_STDIN +}; + +struct args_options +{ + /** Source of the input, filename or command string depending on type, NULL + * if INPUT_STDIN */ + const char *input_source; + /** Type of the input source */ + enum input_type type; + /** Enable or disable pretty printing of outputs */ + bool pretty_print; + /** Enable or disable verbose mode */ + bool verbose; +}; + +/** + * Handles command-line arguments and populates the args_options structure. + * @param argc The argument count. + * @param argv The argument vector. + * @param options Pointer to args_options structure to be populated. + * @return 0 on success, non-zero on failure. + */ +int args_handler(int argc, char **argv, struct args_options *options); + +/** Prints the parsed arguments for debugging purposes. + * @param options Pointer to args_options structure containing parsed options. + */ +void args_print(struct args_options *options); + +#endif /* ARGS_H */ From 111f2cf5c23f2df4095c1e0f914519ffbb88770f Mon Sep 17 00:00:00 2001 From: "william.valenduc" Date: Mon, 12 Jan 2026 19:30:11 +0000 Subject: [PATCH 05/12] feat(args): args_handler error handling and print_usage --- src/main.c | 15 ++++- src/utils/args/args.c | 33 ++++++++-- src/utils/args/args.h | 7 +++ tests/unit/utils/args.c | 134 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 tests/unit/utils/args.c diff --git a/src/main.c b/src/main.c index f20378e..6ced238 100644 --- a/src/main.c +++ b/src/main.c @@ -1,8 +1,19 @@ // all includes +#include + +#include "utils/args/args.h" + int main(int argc, char **argv) { - (void)argc; - (void)argv; + struct args_options options; + int r = args_handler(argc, argv, &options); + if (r != 0) + { + print_usage(stderr, argv[0]); + return 2; + } + + args_print(&options); return 0; } diff --git a/src/utils/args/args.c b/src/utils/args/args.c index 40b5516..a2dabd8 100644 --- a/src/utils/args/args.c +++ b/src/utils/args/args.c @@ -21,10 +21,19 @@ int args_handler(int argc, char **argv, struct args_options *options) { options->verbose = true; } - else if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) + else if (strcmp(argv[i], "-c") == 0) { if (options->type != INPUT_UNDEFINED) - return 1; // Error: multiple input types specified + { + fprintf(stderr, "Multiple input sources specified: %s\n", + argv[i + 1]); + return 1; + } + else if (i + 1 >= argc) + { + fprintf(stderr, "No command provided after -c\n"); + return 1; + } options->type = INPUT_CMD; options->input_source = argv[i + 1]; @@ -32,12 +41,17 @@ int args_handler(int argc, char **argv, struct args_options *options) } else if (argv[i][0] == '-') { - return 1; // Error: unknown option + fprintf(stderr, "Unknown option: %s\n", argv[i]); + return 1; } else { if (options->type != INPUT_UNDEFINED) - return 1; // Error: multiple input types specified + { + fprintf(stderr, "Multiple input sources specified: %s\n", + argv[i]); + return 1; + } options->type = INPUT_FILE; options->input_source = argv[i]; @@ -62,3 +76,14 @@ void args_print(struct args_options *options) printf("Pretty print: %s\n", options->pretty_print ? "true" : "false"); printf("Verbose: %s\n", options->verbose ? "true" : "false"); } + +void print_usage(FILE *std, const char *program_name) +{ + fprintf(std, "Usage: %s [OPTIONS] [SCRIPT] [ARGUMENTS...]\n", program_name); + fprintf(std, "Options:\n"); + fprintf(std, " -c [SCRIPT] Execute the given command string.\n"); + fprintf(std, " --pretty-print Enable pretty printing of outputs.\n"); + fprintf(std, " --verbose Enable verbose mode.\n"); + fprintf(std, + "If no SCRIPT is provided, input is read from standard input.\n"); +} diff --git a/src/utils/args/args.h b/src/utils/args/args.h index ddb6d8b..d19859b 100644 --- a/src/utils/args/args.h +++ b/src/utils/args/args.h @@ -2,6 +2,7 @@ #define ARGS_H #include +#include enum input_type { @@ -38,4 +39,10 @@ int args_handler(int argc, char **argv, struct args_options *options); */ void args_print(struct args_options *options); +/** Prints the usage information for the program. + * @param std The output stream to print to (e.g., stdout or stderr). + * @param program_name The name of the program. + */ +void print_usage(FILE *std, const char *program_name); + #endif /* ARGS_H */ diff --git a/tests/unit/utils/args.c b/tests/unit/utils/args.c new file mode 100644 index 0000000..04dfe26 --- /dev/null +++ b/tests/unit/utils/args.c @@ -0,0 +1,134 @@ +#include "../../../src/utils/args/args.h" + +#include +#include +#include + +TestSuite(utils_args); + +Test(utils_args, basic_command) +{ + int argc = 3; + struct args_options options; + char *input[] = { "program", "-c", "echo Hello, World!" }; + + int r = args_handler(argc, input, &options); + + cr_expect(r == 0); + cr_expect(options.pretty_print == false); + cr_expect(options.verbose == false); + cr_expect(options.type == INPUT_CMD); + cr_expect(eq(options.input_source, "echo Hello, World!")); +} + +Test(utils_args, basic_command_with_flags) +{ + int argc = 5; + struct args_options options; + char *input[] = { "program", "--pretty-print", "-c", "echo Hello, World!", + "--verbose" }; + + int r = args_handler(argc, input, &options); + + cr_expect(r == 0); + cr_expect(options.pretty_print == true); + cr_expect(options.verbose == true); + cr_expect(options.type == INPUT_CMD); + cr_expect(eq(options.input_source, "echo Hello, World!")); +} + +Test(utils_args, basic_file_input) +{ + int argc = 2; + struct args_options options; + char *input[] = { "program", "input.txt" }; + + int r = args_handler(argc, input, &options); + + cr_expect(r == 0); + cr_expect(options.pretty_print == false); + cr_expect(options.verbose == false); + cr_expect(options.type == INPUT_FILE); + cr_expect(eq(options.input_source, "input.txt")); +} + +Test(utils_args, basic_file_input_with_flags) +{ + int argc = 4; + struct args_options options; + char *input[] = { "program", "--verbose", "input.txt", "--pretty-print" }; + + int r = args_handler(argc, input, &options); + + cr_expect(r == 0); + cr_expect(options.pretty_print == true); + cr_expect(options.verbose == true); + cr_expect(options.type == INPUT_FILE); + cr_expect(eq(options.input_source, "input.txt")); +} + +Test(utils_args, basic_stdin_input) +{ + int argc = 1; + struct args_options options; + char *input[] = { "program" }; + + int r = args_handler(argc, input, &options); + + cr_expect(r == 0); + cr_expect(options.pretty_print == false); + cr_expect(options.verbose == false); + cr_expect(options.type == INPUT_STDIN); + cr_expect(options.input_source == NULL); +} + +Test(utils_args, pretty_print_and_verbose_flags) +{ + int argc = 3; + struct args_options options; + char *input[] = { "program", "--pretty-print", "--verbose" }; + + int r = args_handler(argc, input, &options); + + cr_expect(r == 0); + cr_expect(options.pretty_print == true); + cr_expect(options.verbose == true); + cr_expect(options.type == INPUT_STDIN); + cr_expect(options.input_source == NULL); +} + +Test(utils_args, missing_command_after_c, .init = cr_redirect_stderr) +{ + int argc = 2; + struct args_options options; + char *input[] = { "program", "-c" }; + + int r = args_handler(argc, input, &options); + + cr_expect(r != 0); + cr_assert_stderr_eq_str("No command provided after -c\n"); +} + +Test(utils_args, unknown_option, .init = cr_redirect_stderr) +{ + int argc = 2; + struct args_options options; + char *input[] = { "program", "--unknown" }; + + int r = args_handler(argc, input, &options); + + cr_expect(r != 0); + cr_assert_stderr_eq_str("Unknown option: --unknown\n"); +} + +Test(utils_args, conflicting_input_types, .init = cr_redirect_stderr) +{ + int argc = 4; + struct args_options options; + char *input[] = { "program", "-c", "echo Hello", "input.txt" }; + + int r = args_handler(argc, input, &options); + + cr_expect(r != 0); + cr_assert_stderr_eq_str("Multiple input sources specified: input.txt\n"); +} From 8a727ebd98d3cb09d06236dbe107700c2eaa56cb Mon Sep 17 00:00:00 2001 From: Matteo Flebus Date: Mon, 12 Jan 2026 21:41:13 +0100 Subject: [PATCH 06/12] fix(lexer): typo in function call --- src/lexer/lexer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c index 5d4ba94..0cc237c 100644 --- a/src/lexer/lexer.c +++ b/src/lexer/lexer.c @@ -70,7 +70,7 @@ char *stream_init(void) stream = end_last_token; } - char *trimed_stream = trim_blanks_left(stream); + char *trimed_stream = trim_blank_left(stream); remaining_chars -= trimed_stream - stream; stream = trimed_stream; From e000d2cf338c3ca619a5446e2480254bdde96022 Mon Sep 17 00:00:00 2001 From: Matteo Flebus Date: Mon, 12 Jan 2026 21:46:33 +0100 Subject: [PATCH 07/12] fix(utils): include args module in build system --- src/utils/Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am index b876698..600ee40 100644 --- a/src/utils/Makefile.am +++ b/src/utils/Makefile.am @@ -3,7 +3,8 @@ lib_LIBRARIES = libutils.a libutils_a_SOURCES = \ string_utils/string_utils.c \ ast/ast.c \ - lists/lists.c + lists/lists.c \ + args/args.c libutils_a_CPPFLAGS = -I$(top_srcdir)/src From 937e935a6e3722001d94039e6d965352a4f0ed3b Mon Sep 17 00:00:00 2001 From: Matteo Flebus Date: Mon, 12 Jan 2026 21:48:11 +0100 Subject: [PATCH 08/12] style(all): clang format --- src/io_backend/io_backend.h | 12 +++++++----- src/parser/parser.h | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/io_backend/io_backend.h b/src/io_backend/io_backend.h index 34d85f3..fab5cce 100644 --- a/src/io_backend/io_backend.h +++ b/src/io_backend/io_backend.h @@ -3,7 +3,8 @@ #include -enum iob_mode { +enum iob_mode +{ IOB_MODE_NULL = 0, IOB_MODE_STDIN, IOB_MODE_SCRIPT, @@ -16,14 +17,15 @@ enum iob_mode { * the script name when mode is set to IOB_SCRIPT, * the command to execute when mode is set to IOB_CMD, */ -struct iob_context { +struct iob_context +{ enum iob_mode mode; - char* args; + char *args; }; /* * @brief Initializes the IO Backend module - * + * * @param context contains the input mode and the args * @return 0 on success, the corresponding error code otherwise */ @@ -41,6 +43,6 @@ void iob_close(); * * */ -ssize_t stream_read(char** stream); +ssize_t stream_read(char **stream); #endif /* ! IO_BACKEND_H */ diff --git a/src/parser/parser.h b/src/parser/parser.h index 8dccb31..d10cae8 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -10,7 +10,7 @@ * * @warning NOT IMPLEMENTED */ -struct ast* get_ast(); +struct ast *get_ast(); /* @brief Builds the AST representation of the given command string. * @@ -19,6 +19,6 @@ struct ast* get_ast(); * * @warning NOT IMPLEMENTED */ -struct ast* get_ast_str(char* command); +struct ast *get_ast_str(char *command); #endif /* ! PARSER_H */ From a11a8ab63556400abcc4ec43c5a5176aa54f0acd Mon Sep 17 00:00:00 2001 From: Matteo Flebus Date: Mon, 12 Jan 2026 21:51:11 +0100 Subject: [PATCH 09/12] fix(autotools): removed useless Makefile.am in utils/ast --- src/utils/ast/Makefile.am | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 src/utils/ast/Makefile.am diff --git a/src/utils/ast/Makefile.am b/src/utils/ast/Makefile.am deleted file mode 100644 index fa41e3d..0000000 --- a/src/utils/ast/Makefile.am +++ /dev/null @@ -1,9 +0,0 @@ -lib_LIBRARIES = libast.a - -libast_a_SOURCES = \ - ast.c \ - ast.h - -libast_a_CPPFLAGS = -I$(top_srcdir)/src - -noinst_LIBRARIES = libast.a From fe649af2dd205bcb725b9a02919a4cbea983427f Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Thu, 8 Jan 2026 15:02:33 +0100 Subject: [PATCH 10/12] doc: finished --- src/io_backend/io_backend.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/io_backend/io_backend.h b/src/io_backend/io_backend.h index fab5cce..437c339 100644 --- a/src/io_backend/io_backend.h +++ b/src/io_backend/io_backend.h @@ -31,17 +31,14 @@ struct iob_context */ int iob_init(struct iob_context *context); -/* TODO - * - * - * +/* @brief Closes the opened buffers and exits the modules gracefully */ -void iob_close(); +void iob_close(void); -/*i TODO - * - * +/* @brief reads at most one line of the input and stores it into *stream * + * @param stream is a pointer that will be set to a string to parse + * @return the number of read characters if positive, the error code otherwise */ ssize_t stream_read(char **stream); From a653c02c9fefb24f277a19d84d96cac1d785cb6c Mon Sep 17 00:00:00 2001 From: Matteo Flebus Date: Tue, 13 Jan 2026 17:07:23 +0100 Subject: [PATCH 11/12] fix(iobackend): rebasing lexer on dev --- src/io_backend/io_backend.c | 81 +++++++++++++++++++++++++++++++++---- src/io_backend/io_backend.h | 27 ++++++++++--- 2 files changed, 95 insertions(+), 13 deletions(-) diff --git a/src/io_backend/io_backend.c b/src/io_backend/io_backend.c index 176585f..7e7c1c3 100644 --- a/src/io_backend/io_backend.c +++ b/src/io_backend/io_backend.c @@ -1,35 +1,102 @@ #include "io_backend.h" #include +#include +#include + +// === Static variables static struct iob_context context; -static FILE *input; +static FILE *input = NULL; +static char *stream_buf = NULL; +static size_t stream_buf_size = 0; +static enum iob_state state = IOB_STATE_NOT_INITIALIZED; + +// === Functions int iob_init(struct iob_context *ctx) { + if (state != IOB_STATE_NOT_INITIALIZED) + return IOB_ERROR_MODULE_NOT_INITIALIZED; + context = *ctx; switch (context.mode) { case IOB_MODE_STDIN: input = stdin; + state = IOB_STATE_READY; return 0; case IOB_MODE_SCRIPT: if (context.args == NULL) - return -2; + return IOB_ERROR_BAD_ARG; input = fopen(context.args, "r"); if (input == NULL) - return -4; + return IOB_ERROR_CANNOT_OPEN_FILE; + state = IOB_STATE_READY; return 0; case IOB_MODE_CMD: if (context.args != NULL) - return -2; - else - return 0; + return IOB_ERROR_BAD_ARG; + state = IOB_STATE_READY; + return 0; default: - return -1; + return IOB_ERROR_BAD_ARG; + } +} + +void iob_close(void) +{ + fclose(input); + if ((context.mode == IOB_MODE_STDIN || context.mode == IOB_MODE_SCRIPT) + && stream_buf != NULL) + { + free(stream_buf); + stream_buf_size = 0; + } + state = IOB_STATE_NOT_INITIALIZED; +} + +ssize_t stream_read(char **stream) +{ + // Check args + if (stream == NULL) + return IOB_ERROR_BAD_ARG; + + // Check env + if (state == IOB_STATE_NOT_INITIALIZED) + return IOB_ERROR_MODULE_NOT_INITIALIZED; + if (state == IOB_STATE_FINISHED) + return 0; + if (state == IOB_STATE_ERROR) + return IOB_ERROR_GENERIC; + + // Use input + if (context.mode == IOB_MODE_STDIN || context.mode == IOB_MODE_SCRIPT) + { + ssize_t nread = getline(&stream_buf, &stream_buf_size, input); + if (nread == -1) + { + state = IOB_STATE_FINISHED; + return 0; + } + else if (nread < 0) + state = IOB_STATE_ERROR; + + return nread; + } + // Use args + else if (context.mode == IOB_MODE_CMD) + { + *stream = context.args; + return strlen(context.args); + } + else + { + *stream = NULL; + return IOB_ERROR_GENERIC; } } diff --git a/src/io_backend/io_backend.h b/src/io_backend/io_backend.h index 437c339..5aef0f8 100644 --- a/src/io_backend/io_backend.h +++ b/src/io_backend/io_backend.h @@ -3,19 +3,32 @@ #include -enum iob_mode -{ +// Error codes +#define IOB_ERROR_GENERIC -1 +#define IOB_ERROR_BAD_ARG -2 +#define IOB_ERROR_MODULE_NOT_INITIALIZED -3 +#define IOB_ERROR_MODULE_ALREADY_INITALIZED -4 +#define IOB_ERROR_CANNOT_OPEN_FILE -5 + +enum iob_mode { IOB_MODE_NULL = 0, IOB_MODE_STDIN, IOB_MODE_SCRIPT, IOB_MODE_CMD }; +enum iob_state { + IOB_STATE_NOT_INITIALIZED, + IOB_STATE_READY, + IOB_STATE_FINISHED, + IOB_STATE_ERROR +}; + /* @struct iob_context * @var mode * @var args contains - * the script name when mode is set to IOB_SCRIPT, - * the command to execute when mode is set to IOB_CMD, + * the script name when mode is set to IOB_MODE_SCRIPT, + * the command to execute when mode is set to IOB_MODE_CMD */ struct iob_context { @@ -31,14 +44,16 @@ struct iob_context */ int iob_init(struct iob_context *context); -/* @brief Closes the opened buffers and exits the modules gracefully +/* @brief Closes the opened buffers and the module gracefully */ void iob_close(void); /* @brief reads at most one line of the input and stores it into *stream * * @param stream is a pointer that will be set to a string to parse - * @return the number of read characters if positive, the error code otherwise + * @return the number of read characters if positive, + * zero if finished (reached EOF), + * the error code otherwise */ ssize_t stream_read(char **stream); From e5925d69ecb220095a9827e2cf25faf32c3369f5 Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Thu, 8 Jan 2026 16:59:37 +0100 Subject: [PATCH 12/12] test: iob_init and iob_close tests (+ small typo fix in src) --- src/io_backend/io_backend.c | 2 +- src/io_backend/io_backend.h | 2 +- tests/unit/io_backend/io_backend.c | 122 +++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 tests/unit/io_backend/io_backend.c diff --git a/src/io_backend/io_backend.c b/src/io_backend/io_backend.c index 7e7c1c3..97fe974 100644 --- a/src/io_backend/io_backend.c +++ b/src/io_backend/io_backend.c @@ -17,7 +17,7 @@ static enum iob_state state = IOB_STATE_NOT_INITIALIZED; int iob_init(struct iob_context *ctx) { if (state != IOB_STATE_NOT_INITIALIZED) - return IOB_ERROR_MODULE_NOT_INITIALIZED; + return IOB_ERROR_MODULE_ALREADY_INITIALIZED; context = *ctx; diff --git a/src/io_backend/io_backend.h b/src/io_backend/io_backend.h index 5aef0f8..3282016 100644 --- a/src/io_backend/io_backend.h +++ b/src/io_backend/io_backend.h @@ -7,7 +7,7 @@ #define IOB_ERROR_GENERIC -1 #define IOB_ERROR_BAD_ARG -2 #define IOB_ERROR_MODULE_NOT_INITIALIZED -3 -#define IOB_ERROR_MODULE_ALREADY_INITALIZED -4 +#define IOB_ERROR_MODULE_ALREADY_INITIALIZED -4 #define IOB_ERROR_CANNOT_OPEN_FILE -5 enum iob_mode { diff --git a/tests/unit/io_backend/io_backend.c b/tests/unit/io_backend/io_backend.c new file mode 100644 index 0000000..8640cdb --- /dev/null +++ b/tests/unit/io_backend/io_backend.c @@ -0,0 +1,122 @@ +#include +#include + +#include + +#include + +TestSuite(IO_Backend); + +// IOB Init + +Test(IO_Backend, init_null) +{ + struct iob_context ctx = { + .iob_mode = IOB_MODE_NULL; + .args = NULL; + }; + int actual = iob_init(ctx); + int expected = IOB_ERROR_BAD_ARG; + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +} + +Test(IO_Backend, init_stdin) +{ + struct iob_context ctx = { + .iob_mode = IOB_MODE_STDIN; + .args = NULL; + }; + int actual = iob_init(ctx); + int expected = 0; + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); + iob_close(); +} + +// WARNING: this one could fail because of iob_close in the previous test +// Same applies for other tests +Test(IO_Backend, init_script) +{ + char* script_name = "script.tmp" + struct iob_context ctx = { + .iob_mode = IOB_MODE_SCRIPT; + .args = script_name; + }; + // Create file + FILE* f = fopen(script_name, "w"); + fclose(f); + + int actual = iob_init(ctx); + int expected = 0; + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); + iob_close(); + remove(script_name); +} + +Test(IO_Backend, init_script_not_a_file) +{ + char* script_name = "not_a_file.tmp" + struct iob_context ctx = { + .iob_mode = IOB_MODE_SCRIPT; + .args = script_name; + }; + int actual = iob_init(ctx); + int expected = IOB_ERROR_CANNOT_OPEN_FILE; + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +} + +Test(IO_Backend, init_script_null) +{ + struct iob_context ctx = { + .iob_mode = IOB_MODE_SCRIPT; + .args = NULL; + }; + int actual = iob_init(ctx); + int expected = IOB_ERROR_CANNOT_OPEN_FILE; + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +} + +Test(IO_Backend, init_cmd) +{ + char* cmd = "iamacommand --yesido" + struct iob_context ctx = { + .iob_mode = IOB_MODE_CMD; + .args = cmd; + }; + int actual = iob_init(ctx); + int expected = 0; + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); + iob_close(); +} + +Test(IO_Backend, init_cmd_null) +{ + struct iob_context ctx = { + .iob_mode = IOB_MODE_CMD; + .args = NULL; + }; + int actual = iob_init(ctx); + int expected = IOB_ERROR_BAD_ARG; + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +} + +Test(IO_Backend, init_already_init) +{ + char* cmd = "iamacommand --yesido" + struct iob_context ctx = { + .iob_mode = IOB_MODE_CMD; + .args = cmd; + }; + iob_init(ctx); + int actual = iob_init(ctx); + int expected = IOB_ERROR_ALREADY_INITIALIZED; + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); + iob_close(); +} + +Test(IO_Backend, close_not_init) +{ + iob_close(); // Shouldn't do anything +} + +// IOB Stream +// TODO