feat(parser): redirections

This commit is contained in:
Matteo Flebus 2026-01-27 19:56:33 +01:00
parent 04ff7376eb
commit 8a5c589742
17 changed files with 78 additions and 108 deletions

View file

@ -25,7 +25,8 @@
// === Structures // === Structures
enum rule { enum rule
{
RULE_NULL = 0, RULE_NULL = 0,
RULE_INPUT, RULE_INPUT,
RULE_LIST, RULE_LIST,
@ -43,8 +44,9 @@ enum rule {
NUMBER_OF_RULES NUMBER_OF_RULES
}; };
struct firsts_list { struct firsts_list
enum token_type* tokens; // Heap allocated array {
enum token_type *tokens; // Heap allocated array
size_t list_length; size_t list_length;
}; };

View file

@ -1,3 +1,5 @@
#define _POSIX_C_SOURCE 200809L
#include "grammar_advanced.h" #include "grammar_advanced.h"
#include <stdio.h> #include <stdio.h>
@ -8,7 +10,7 @@
static enum ast_redir_type redir_tok_to_ast_type(enum token_type tok_type) static enum ast_redir_type redir_tok_to_ast_type(enum token_type tok_type)
{ {
switch(tok_type) switch (tok_type)
{ {
case TOKEN_REDIR_LEFT: case TOKEN_REDIR_LEFT:
return AST_REDIR_TYPE_LESS; return AST_REDIR_TYPE_LESS;
@ -22,9 +24,6 @@ static enum ast_redir_type redir_tok_to_ast_type(enum token_type tok_type)
struct ast *parse_redirection(struct lexer_context *ctx) struct ast *parse_redirection(struct lexer_context *ctx)
{ {
(void)ctx;
return NULL;
/*
struct token *token = PEEK_TOKEN(); struct token *token = PEEK_TOKEN();
int io_number = -1; int io_number = -1;
if (token->type == TOKEN_IONUMBER) if (token->type == TOKEN_IONUMBER)
@ -40,7 +39,6 @@ struct ast *parse_redirection(struct lexer_context *ctx)
"else"); "else");
return NULL; return NULL;
} }
// char *redir_op = strdup(token->data);
enum ast_redir_type redir_type = redir_tok_to_ast_type(token->type); enum ast_redir_type redir_type = redir_tok_to_ast_type(token->type);
POP_TOKEN(); POP_TOKEN();
@ -49,14 +47,12 @@ struct ast *parse_redirection(struct lexer_context *ctx)
if (token->type != TOKEN_WORD) if (token->type != TOKEN_WORD)
{ {
perror("Syntax error: expected a word after redirection"); perror("Syntax error: expected a word after redirection");
// free(redir_op);
return NULL; return NULL;
} }
char *target = strdup(token->data); char *target = strdup(token->data);
POP_TOKEN(); POP_TOKEN();
return ast_create_redir(io_number, redir_type, target); return ast_create_redir(target, io_number, redir_type);
*/
} }
struct ast *parse_prefix(struct lexer_context *ctx) struct ast *parse_prefix(struct lexer_context *ctx)

View file

@ -8,7 +8,8 @@
/* /*
* @brief parses a redirection rule * @brief parses a redirection rule
* *
* @code redirection = [IONUMBER] ( '>' | '<' | '>>' | '>&' | '<&' | '>|' | '<>' ) WORD ; * @code redirection = [IONUMBER] ( '>' | '<' | '>>' | '>&' | '<&' | '>|' | '<>'
* ) WORD ;
* *
* @first TOKEN_IONUMBER, TOKEN_REDIRECTION * @first TOKEN_IONUMBER, TOKEN_REDIRECTION
*/ */
@ -23,5 +24,4 @@ struct ast *parse_redirection(struct lexer_context *ctx);
*/ */
struct ast *parse_prefix(struct lexer_context *ctx); struct ast *parse_prefix(struct lexer_context *ctx);
#endif /* ! GRAMMAR_ADVANCED_H */ #endif /* ! GRAMMAR_ADVANCED_H */

View file

@ -1,13 +1,14 @@
#include <stdbool.h>
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include "grammar_basic.h"
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "../utils/lists/lists.h" #include "../utils/lists/lists.h"
#include "grammar.h" #include "grammar.h"
#include "grammar_advanced.h" #include "grammar_advanced.h"
#include "grammar_basic.h"
// === Static functions // === Static functions
@ -398,7 +399,8 @@ struct ast *parse_else_clause(struct lexer_context *ctx)
token = POP_TOKEN(); token = POP_TOKEN();
if (token->type != TOKEN_THEN) if (token->type != TOKEN_THEN)
{ {
perror("Expected the 'then' keyword but got a different token type"); perror(
"Expected the 'then' keyword but got a different token type");
return NULL; return NULL;
} }

View file

@ -7,11 +7,11 @@
#include "ast_end.h" #include "ast_end.h"
#include "ast_if.h" #include "ast_if.h"
#include "ast_list.h" #include "ast_list.h"
#include "ast_neg.h"
#include "ast_pipe.h"
#include "ast_redir.h" #include "ast_redir.h"
#include "ast_void.h" #include "ast_void.h"
#include "ast_word.h" #include "ast_word.h"
#include "ast_pipe.h"
#include "ast_neg.h"
/** /**
* Prints the Graphviz DOT representation of the given AST to stdout. * Prints the Graphviz DOT representation of the given AST to stdout.

View file

@ -1,8 +1,8 @@
#ifndef AST_BASE_H #ifndef AST_BASE_H
#define AST_BASE_H #define AST_BASE_H
#include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h>
enum ast_type enum ast_type
{ {

View file

@ -6,12 +6,12 @@
struct ast_neg struct ast_neg
{ {
bool negation; // True negates the child's output bool negation; // True negates the child's output
struct ast* child; struct ast *child;
}; };
bool ast_is_neg(struct ast *node); bool ast_is_neg(struct ast *node);
struct ast_neg *ast_get_neg(struct ast *node); struct ast_neg *ast_get_neg(struct ast *node);
struct ast *ast_create_neg(bool negation, struct ast* child); struct ast *ast_create_neg(bool negation, struct ast *child);
void ast_free_neg(struct ast_neg* node); void ast_free_neg(struct ast_neg *node);
#endif /* ! AST_NEG_H */ #endif /* ! AST_NEG_H */

View file

@ -5,14 +5,14 @@
struct ast_pipe struct ast_pipe
{ {
struct ast* left; struct ast *left;
struct ast* right; struct ast *right;
// Output of left will be redirected to right stdin // Output of left will be redirected to right stdin
}; };
bool ast_is_pipe(struct ast *node); bool ast_is_pipe(struct ast *node);
struct ast_pipe *ast_get_pipe(struct ast *node); struct ast_pipe *ast_get_pipe(struct ast *node);
struct ast *ast_create_pipe(struct ast* left, struct ast* right); struct ast *ast_create_pipe(struct ast *left, struct ast *right);
void ast_free_pipe(struct ast_pipe* node); void ast_free_pipe(struct ast_pipe *node);
#endif /* ! AST_PIPE_H */ #endif /* ! AST_PIPE_H */

View file

@ -14,13 +14,12 @@ struct ast_redir *ast_get_redir(struct ast *node)
return NULL; return NULL;
} }
struct ast *ast_create_redir(struct ast *child, char *filename, int io_number, struct ast *ast_create_redir(char *filename, int io_number,
enum ast_redir_type type) enum ast_redir_type type)
{ {
struct ast_redir *redir = malloc(sizeof(struct ast_redir)); struct ast_redir *redir = malloc(sizeof(struct ast_redir));
if (!redir) if (!redir)
return NULL; return NULL;
redir->child = child;
redir->filename = redir->filename =
filename; // Takes ownership? Usually yes in simple ASTs, or dup. Let's filename; // Takes ownership? Usually yes in simple ASTs, or dup. Let's
// assume pointer copy for now, but user must manage memory. // assume pointer copy for now, but user must manage memory.
@ -34,7 +33,6 @@ void ast_free_redir(struct ast_redir *redir)
{ {
if (!redir) if (!redir)
return; return;
ast_free(&redir->child);
free(redir->filename); free(redir->filename);
free(redir); free(redir);
} }

View file

@ -17,7 +17,6 @@ enum ast_redir_type
struct ast_redir struct ast_redir
{ {
struct ast *child;
char *filename; char *filename;
int io_number; // The FD being redirected (default -1 if not specified, int io_number; // The FD being redirected (default -1 if not specified,
// implies 0 or 1 based on type) // implies 0 or 1 based on type)
@ -26,7 +25,7 @@ struct ast_redir
bool ast_is_redir(struct ast *node); bool ast_is_redir(struct ast *node);
struct ast_redir *ast_get_redir(struct ast *node); struct ast_redir *ast_get_redir(struct ast *node);
struct ast *ast_create_redir(struct ast *child, char *filename, int io_number, struct ast *ast_create_redir(char *filename, int io_number,
enum ast_redir_type type); enum ast_redir_type type);
void ast_free_redir(struct ast_redir *redir); void ast_free_redir(struct ast_redir *redir);

View file

@ -1,8 +1,8 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include "ast_word.h" #include "ast_word.h"
#include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -17,6 +17,7 @@ struct ast *ast_create_word(char *word)
struct ast *res = ast_create(AST_WORD, ast_node); struct ast *res = ast_create(AST_WORD, ast_node);
if (res == NULL) if (res == NULL)
{ {
free(ast_node->word);
free(ast_node); free(ast_node);
return NULL; return NULL;
} }

View file

@ -23,7 +23,7 @@ struct ast_word *ast_get_word(struct ast *node);
/** /**
* Creates a new AST node representing a command. * Creates a new AST node representing a command.
*/ */
struct ast *ast_create_word(char* word); struct ast *ast_create_word(char *word);
/* /*
* @brief: frees the given ast_command and sets the pointer to NULL. * @brief: frees the given ast_command and sets the pointer to NULL.

View file

@ -9,27 +9,19 @@ TestSuite(IO_Backend);
Test(IO_Backend, init_null) Test(IO_Backend, init_null)
{ {
struct iob_context ctx = struct iob_context ctx = { .mode = IOB_MODE_NULL, .args = NULL };
{
.mode = IOB_MODE_NULL,
.args = NULL
};
int actual = iob_init(&ctx); int actual = iob_init(&ctx);
int expected = IOB_ERROR_BAD_ARG; int expected = IOB_ERROR_BAD_ARG;
cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual);
} }
Test(IO_Backend, init_stdin) Test(IO_Backend, init_stdin)
{ {
struct iob_context ctx = struct iob_context ctx = { .mode = IOB_MODE_STDIN, .args = NULL };
{
.mode = IOB_MODE_STDIN,
.args = NULL
};
int actual = iob_init(&ctx); int actual = iob_init(&ctx);
int expected = 0; int expected = 0;
cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual);
iob_close(); iob_close();
} }
// WARNING: this one could fail because of iob_close in the previous test // WARNING: this one could fail because of iob_close in the previous test
@ -37,82 +29,62 @@ iob_close();
Test(IO_Backend, init_script) Test(IO_Backend, init_script)
{ {
char *script_name = "script.tmp"; char *script_name = "script.tmp";
struct iob_context ctx = { struct iob_context ctx = { .mode = IOB_MODE_SCRIPT, .args = script_name };
.mode = IOB_MODE_SCRIPT,
.args = script_name
};
// Create file // Create file
FILE *f = fopen(script_name, "w"); FILE *f = fopen(script_name, "w");
fclose(f); fclose(f);
int actual = iob_init(&ctx); int actual = iob_init(&ctx);
int expected = 0; int expected = 0;
cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual);
iob_close(); iob_close();
remove(script_name); remove(script_name);
} }
Test(IO_Backend, init_script_not_a_file) Test(IO_Backend, init_script_not_a_file)
{ {
char *script_name = "not_a_file.tmp"; char *script_name = "not_a_file.tmp";
struct iob_context ctx = { struct iob_context ctx = { .mode = IOB_MODE_SCRIPT, .args = script_name };
.mode = IOB_MODE_SCRIPT,
.args = script_name
};
int actual = iob_init(&ctx); int actual = iob_init(&ctx);
int expected = IOB_ERROR_CANNOT_OPEN_FILE; int expected = IOB_ERROR_CANNOT_OPEN_FILE;
cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual);
} }
Test(IO_Backend, init_script_null) Test(IO_Backend, init_script_null)
{ {
struct iob_context ctx = struct iob_context ctx = { .mode = IOB_MODE_SCRIPT, .args = NULL };
{
.mode = IOB_MODE_SCRIPT,
.args = NULL
};
int actual = iob_init(&ctx); int actual = iob_init(&ctx);
int expected = IOB_ERROR_CANNOT_OPEN_FILE; int expected = IOB_ERROR_CANNOT_OPEN_FILE;
cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual);
} }
Test(IO_Backend, init_cmd) Test(IO_Backend, init_cmd)
{ {
char *cmd = "iamacommand --yesido"; char *cmd = "iamacommand --yesido";
struct iob_context ctx = { struct iob_context ctx = { .mode = IOB_MODE_CMD, .args = cmd };
.mode = IOB_MODE_CMD,
.args = cmd
};
int actual = iob_init(&ctx); int actual = iob_init(&ctx);
int expected = 0; int expected = 0;
cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual);
iob_close(); iob_close();
} }
Test(IO_Backend, init_cmd_null) Test(IO_Backend, init_cmd_null)
{ {
struct iob_context ctx = struct iob_context ctx = { .mode = IOB_MODE_CMD, .args = NULL };
{
.mode = IOB_MODE_CMD,
.args = NULL
};
int actual = iob_init(&ctx); int actual = iob_init(&ctx);
int expected = IOB_ERROR_BAD_ARG; int expected = IOB_ERROR_BAD_ARG;
cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual);
} }
Test(IO_Backend, init_already_init) Test(IO_Backend, init_already_init)
{ {
char *cmd = "iamacommand --yesido"; char *cmd = "iamacommand --yesido";
struct iob_context ctx = { struct iob_context ctx = { .mode = IOB_MODE_CMD, .args = cmd };
.mode = IOB_MODE_CMD,
.args = cmd
};
iob_init(&ctx); iob_init(&ctx);
int actual = iob_init(&ctx); int actual = iob_init(&ctx);
int expected = IOB_ERROR_MODULE_ALREADY_INITIALIZED; int expected = IOB_ERROR_MODULE_ALREADY_INITIALIZED;
cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual);
iob_close(); iob_close();
} }
Test(IO_Backend, close_not_init) Test(IO_Backend, close_not_init)