merge new parser into dev

This commit is contained in:
Matteo Flebus 2026-01-26 18:40:10 +01:00
commit d707c6180b
16 changed files with 725 additions and 228 deletions

View file

@ -31,6 +31,9 @@ static int main_loop(struct lexer_context *ctx, struct args_options *options,
struct hash_map *vars)
{
int return_code = SUCCESS;
// init parser
int parser_init();
// Retrieve and build first AST
struct ast *command_ast = get_ast(ctx);
@ -61,6 +64,7 @@ static int main_loop(struct lexer_context *ctx, struct args_options *options,
return err_input(&vars);
ast_free(&command_ast);
parser_close();
return return_code;
}

View file

@ -2,7 +2,9 @@ lib_LIBRARIES = libparser.a
libparser_a_SOURCES = \
parser.c \
parsing_utils.c
grammar.c \
grammar_basic.c \
grammar_advanced.c
libparser_a_CPPFLAGS = -I$(top_srcdir)/src

206
src/parser/grammar.c Normal file
View file

@ -0,0 +1,206 @@
// === Includes
#include "grammar.h"
#include <stdio.h>
#include <stdlib.h>
#include "grammar_basic.h"
// === Static variables
// rule-indexed array containing firsts
static struct firsts_list *firsts_map = NULL;
// === Static functions
/* @brief Add a token to a rule's firsts (in firsts_map)
*
* @arg rule the rule to which add a first
* @arg token the token to add to the rule's firsts
* @return true on success, false on error
*/
static bool add_first(enum rule rule, enum token_type token)
{
struct firsts_list *item = &firsts_map[rule];
if (item->tokens != NULL)
{
// Check for duplicates
for (size_t i = 0; i < item->list_length; i++)
{
if (item->tokens[i] == token)
return true;
}
// Append
item->list_length++;
item->tokens = realloc(
item->tokens, (item->list_length) * sizeof(enum token_type));
}
else
{
// Create entry
item->list_length = 1;
item->tokens = calloc(1, sizeof(enum token_type));
}
// Check for alloc error
if (item->tokens == NULL)
{
item->list_length = 0;
return false;
}
// Fill
item->list_length++;
item->tokens[item->list_length - 1] = token;
return true;
}
/* @brief initializes the firsts_map static variable (does not populate it)
* @return true on success, false on error
*/
static bool init_firsts_map(void)
{
firsts_map = calloc(NUMBER_OF_RULES, sizeof(struct firsts_list));
if (firsts_map == NULL)
{
puts("Internal error: couldn't create the firsts_map (is your memory "
"full ?)");
return false;
}
return true;
}
/* @brief: add all the redirection token_types to the first of [rule].
* this also contains IONUMBER
*/
static void add_first_redir(enum rule rule)
{
add_first(rule, TOKEN_IONUMBER);
add_first(rule, TOKEN_REDIR_LEFT);
add_first(rule, TOKEN_REDIR_RIGHT);
add_first(rule, TOKEN_REDIR_LEFT_RIGHT);
add_first(rule, TOKEN_REDIR_DOUBLE_RIGHT);
add_first(rule, TOKEN_REDIR_LEFT_AMP);
add_first(rule, TOKEN_REDIR_RIGHT_PIPE);
}
// === Functions
bool grammar_init(void)
{
// Initialize the firsts map
bool success = init_firsts_map();
if (success != true)
return false;
// Populate the firsts map
add_first(RULE_INPUT, TOKEN_WORD);
add_first(RULE_INPUT, TOKEN_IF);
add_first(RULE_INPUT, TOKEN_NEWLINE);
add_first(RULE_INPUT, TOKEN_EOF);
add_first(RULE_LIST, TOKEN_WORD);
add_first(RULE_LIST, TOKEN_IF);
add_first(RULE_AND_OR, TOKEN_WORD);
add_first(RULE_AND_OR, TOKEN_IF);
add_first(RULE_PIPELINE, TOKEN_WORD);
add_first(RULE_PIPELINE, TOKEN_IF);
add_first(RULE_COMMAND, TOKEN_WORD);
add_first(RULE_COMMAND, TOKEN_IF);
add_first(RULE_SIMPLE_COMMAND, TOKEN_WORD);
add_first(RULE_SHELL_COMMAND, TOKEN_IF);
add_first(RULE_IF, TOKEN_IF);
add_first(RULE_COMPOUND_LIST, TOKEN_NEWLINE);
add_first(RULE_COMPOUND_LIST, TOKEN_WORD);
add_first(RULE_COMPOUND_LIST, TOKEN_IF);
add_first(RULE_ELSE_CLAUSE, TOKEN_ELSE);
add_first(RULE_ELSE_CLAUSE, TOKEN_ELIF);
add_first(RULE_ELEMENT, TOKEN_WORD);
add_first_redir(RULE_ELEMENT);
add_first_redir(RULE_REDIRECTION);
add_first_redir(RULE_PREFIX);
return true;
}
void grammar_close(void)
{
// Deep free firsts map
for (int i = 0; i < NUMBER_OF_RULES; i++)
{
if (firsts_map[i].tokens != NULL)
{
free(firsts_map[i].tokens);
}
}
free(firsts_map);
firsts_map = NULL;
}
struct firsts_list *first(enum rule rule)
{
if (firsts_map == NULL || firsts_map[rule].tokens == NULL)
{
puts("Internal error: attempted to get the firsts of a rule without "
"properly initializing the firsts map");
return NULL;
}
return &firsts_map[rule];
}
bool is_first(struct token token, enum rule rule)
{
struct firsts_list *firsts = &firsts_map[rule];
for (size_t i = 0; i < firsts->list_length; i++)
{
if (firsts->tokens[i] == token.type)
return true;
}
return false;
}
struct ast *parse_input(struct lexer_context *ctx)
{
struct token *token = PEEK_TOKEN();
if (token->type == TOKEN_EOF)
return ast_create_list(NULL);
if (token->type == TOKEN_NEWLINE)
{
POP_TOKEN();
return ast_create_list(NULL);
}
struct ast *ast = parse_list(ctx);
if (ast == NULL)
return NULL;
token = PEEK_TOKEN();
if (token->type == TOKEN_NEWLINE || token->type == TOKEN_EOF)
{
if (token->type == TOKEN_NEWLINE)
POP_TOKEN();
return ast;
}
puts("Syntax error: expected newline or EOF after list");
ast_free(&ast);
return NULL;
}

93
src/parser/grammar.h Normal file
View file

@ -0,0 +1,93 @@
#ifndef GRAMMAR_H
#define GRAMMAR_H
#include <stdbool.h>
#include "../lexer/lexer.h"
// === Macros
#define PEEK_TOKEN() \
peek_token(ctx); \
if (token == NULL) \
{ \
puts("Internal error: cannot get the following token"); \
return NULL; \
}
#define POP_TOKEN() \
pop_token(ctx); \
if (token == NULL) \
{ \
puts("Internal error: cannot get the following token"); \
return NULL; \
}
// === Structures
enum rule {
RULE_NULL = 0,
RULE_INPUT,
RULE_LIST,
RULE_AND_OR,
RULE_PIPELINE,
RULE_COMMAND,
RULE_SIMPLE_COMMAND,
RULE_SHELL_COMMAND,
RULE_IF,
RULE_COMPOUND_LIST,
RULE_ELSE_CLAUSE,
RULE_ELEMENT,
RULE_REDIRECTION,
RULE_PREFIX,
NUMBER_OF_RULES
};
struct firsts_list {
enum token_type* tokens; // Heap allocated array
size_t list_length;
};
// === Functions
/*
* @brief Initializes the grammar submodule
* @return PARSER_INIT_SUCCESS on success PARSER_INIT_ERROR on error
* @warning Do not use outside the parser
*/
bool grammar_init(void);
/*
* @brief Closes the grammar submodule
* @warning Do not use outside the parser
*/
void grammar_close(void);
/*
* @brief get the first accepted tokens of a rule
*
* @arg r the rule
* @return the accepted tokens as a firsts_list struct
*/
struct firsts_list *first(enum rule r);
/*
* @brief tells is token belong to the firsts of a specific rule
*
* @arg token
* @arg rule
* @return true if token belongs to rule's firsts, false otherwise
*/
bool is_first(struct token token, enum rule rule);
/* @brief Acts as the entry point of the parser, calls parse_list
*
* @code input = list '\n'
* | list EOF
* | '\n'
* | EOF
* ;
*/
struct ast *parse_input(struct lexer_context *ctx);
#endif /* ! GRAMMAR_H */

View file

@ -0,0 +1,45 @@
#include "grammar_advanced.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "grammar_basic.h"
struct ast *parse_redirection(struct lexer_context *ctx)
{
struct token *token = PEEK_TOKEN();
int io_number = -1;
if (token->type == TOKEN_IONUMBER)
{
io_number = atoi(token->data);
POP_TOKEN();
token = PEEK_TOKEN();
}
if (token->type != TOKEN_REDIRECTION)
{
puts("Syntax error: expected a redirection token but got something "
"else");
return NULL;
}
char *redir_op = strdup(token->data);
POP_TOKEN();
token = PEEK_TOKEN();
if (token->type != TOKEN_WORD)
{
puts("Syntax error: expected a word after redirection");
free(redir_op);
return NULL;
}
char *target = strdup(token->data);
POP_TOKEN();
return ast_create_redir(io_number, redir_op, target);
}
struct ast *parse_prefix(struct lexer_context *ctx)
{
return parse_redirection(ctx);
}

View file

@ -0,0 +1,27 @@
#ifndef GRAMMAR_ADVANCED_H
#define GRAMMAR_ADVANCED_H
#include "grammar.h"
// === Functions
/*
* @brief parses a redirection rule
*
* @code redirection = [IONUMBER] ( '>' | '<' | '>>' | '>&' | '<&' | '>|' | '<>' ) WORD ;
*
* @first TOKEN_IONUMBER, TOKEN_REDIRECTION
*/
struct ast *parse_redirection(struct lexer_context *ctx);
/*
* @brief parses a prefix rule
*
* @code prefix = redirection ;
*
* @first first(redirection)
*/
struct ast *parse_prefix(struct lexer_context *ctx);
#endif /* ! GRAMMAR_ADVANCED_H */

View file

@ -1,19 +1,15 @@
#define _POSIX_C_SOURCE 200809L
#include "grammar_basic.h"
// === Includes
#include "parsing_utils.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "../lexer/lexer.h"
#include "../utils/ast/ast.h"
#include "../utils/lists/lists.h"
#include "grammar.h"
#include "grammar_advanced.h"
// === Static functions
enum ast_and_or_type and_or_tok_to_ast(enum token_type tok_type)
static enum ast_and_or_type and_or_tok_to_ast(enum token_type tok_type)
{
switch (tok_type)
{
@ -27,53 +23,8 @@ enum ast_and_or_type and_or_tok_to_ast(enum token_type tok_type)
}
}
/* Returns true if c is a command terminator, false otherwise
static bool isterminator(struct token *token)
{
if (token == NULL)
return false;
switch (token->type)
{
case TOKEN_NEWLINE:
case TOKEN_SEMICOLON:
case TOKEN_EOF:
return true;
default:
return false;
}
}
*/
/* @brief: returns true if token is an end of list indicator.
* @warning: not used
*/
/*
static bool is_end_of_list(struct token *token)
{
if (token == NULL)
return false;
switch (token->type)
{
case TOKEN_NEWLINE:
case TOKEN_EOF:
return true;
default:
return false;
}
}
*/
// === Functions
struct ast *parse_input(struct lexer_context *ctx)
{
return parse_list(ctx);
}
struct ast *parse_list(struct lexer_context *ctx)
{
struct list *result_list = NULL;
@ -92,20 +43,20 @@ struct ast *parse_list(struct lexer_context *ctx)
while (token->type == TOKEN_SEMICOLON)
{
token = POP_TOKEN();
// if (!isterminator(token)) // Follow(list)
// {
current_node = parse_and_or(ctx);
if (current_node == NULL)
{
// TODO free list
// There must be a function for that
return NULL;
}
result_list = list_append(result_list, current_node);
// }
token = PEEK_TOKEN();
if (is_first(*token, RULE_AND_OR))
{
current_node = parse_and_or(ctx);
if (current_node == NULL)
{
struct ast *tmp = ast_create_list(result_list);
ast_free(&tmp);
return NULL;
}
result_list = list_append(result_list, current_node);
token = PEEK_TOKEN();
}
}
// result_list = list_append(result_list, current_node);
return ast_create_list(result_list);
}
@ -113,33 +64,41 @@ struct ast *parse_list(struct lexer_context *ctx)
struct ast *parse_and_or(struct lexer_context *ctx)
{
struct ast *result = parse_pipeline(ctx);
if (result == NULL)
return NULL;
struct token *token = PEEK_TOKEN();
if (token->type == TOKEN_AND || token->type == TOKEN_OR)
while (token->type == TOKEN_AND || token->type == TOKEN_OR)
{
// set left part
// Set left part
struct ast *left = result;
// eat and_or token
token = POP_TOKEN();
// set type
// Set type
enum ast_and_or_type type = and_or_tok_to_ast(token->type);
token = PEEK_TOKEN();
// skip newlines
// Skip newlines
while (token->type == TOKEN_NEWLINE)
{
token = POP_TOKEN();
token = PEEK_TOKEN();
}
// right part
// Right part
struct ast *right = parse_pipeline(ctx);
result = ast_create_and_or(left, right, type);
if (result == NULL)
{
ast_free(&left);
ast_free(&right);
return NULL;
}
}
return result;
@ -164,53 +123,92 @@ struct ast *parse_command(struct lexer_context *ctx)
}
else
{
return ast_create_void(); // TODO not sure what to do
puts("Syntax error: expected command");
return NULL;
}
}
struct ast *parse_simple_command(struct lexer_context *ctx)
{
struct list *command_elements = NULL;
struct token *token = PEEK_TOKEN();
// WORD
struct token *token = POP_TOKEN();
if (token->type != TOKEN_WORD)
{
puts("Expected a command but got a different token type");
return NULL;
}
char *command = strdup(token->data);
command_elements = list_append(command_elements, command);
while (token->type == TOKEN_WORD)
token = PEEK_TOKEN();
// Eventual elements
while (is_first(*token, RULE_ELEMENT))
{
token = pop_token(ctx);
if (token == NULL)
// Get element
struct ast *element = parse_element(ctx);
if (element == NULL)
{
// TODO free
list_deep_destroy(command_elements);
return NULL;
}
char *word = strdup(token->data);
if (word == NULL)
// Get element type
if (ast_is_word(element))
{
// TODO free
puts("Internal error: Couldn't copy token content (is memory full "
"?)");
struct ast_word *element_word = ast_get_word(element);
command_elements =
list_append(command_elements, element_word->word);
}
else if (ast_is_redir(element))
{
// TODO
puts("NOT IMPLEMENTED");
return NULL;
}
command_elements = list_append(command_elements, word);
token = peek_token(ctx);
if (token == NULL)
else
{
// TODO free
puts("Internal error: unexpected return value from parse_element "
"in parse_simple_command");
list_deep_destroy(command_elements);
return NULL;
}
// Forward
token = PEEK_TOKEN();
}
// Result
struct ast *result = ast_create_command(command_elements);
if (result == NULL)
{
// TODO free
list_deep_destroy(command_elements);
return NULL;
}
return result;
}
struct ast *parse_element(struct lexer_context *ctx)
{
struct token *token = PEEK_TOKEN();
if (token->type == TOKEN_WORD)
{
token = POP_TOKEN();
return ast_create_word(token->data);
}
else if (token->type == TOKEN_IONUMBER || token->type == TOKEN_REDIRECTION)
{
return parse_redirection(ctx);
}
else
{
puts("Syntax error: unexpected token at parse_element");
return NULL;
}
}
struct ast *parse_shell_command(struct lexer_context *ctx)
{
return parse_if_rule(ctx);
@ -257,6 +255,7 @@ struct ast *parse_if_rule(struct lexer_context *ctx)
return NULL;
}
// Fi keyword
token = POP_TOKEN();
if (token->type != TOKEN_FI)
{
@ -267,6 +266,7 @@ struct ast *parse_if_rule(struct lexer_context *ctx)
return NULL;
}
// Result
struct ast *result =
ast_create_if(condition_content, then_content, else_content);
if (result == NULL)
@ -294,7 +294,7 @@ struct ast *parse_compound_list(struct lexer_context *ctx)
token = PEEK_TOKEN();
}
// and_or
// And/or
current_cmd = parse_and_or(ctx);
if (current_cmd == NULL)
return NULL;
@ -314,12 +314,15 @@ struct ast *parse_compound_list(struct lexer_context *ctx)
token = PEEK_TOKEN();
}
// and_or
current_cmd = parse_and_or(ctx);
if (current_cmd == NULL)
return NULL;
result_list = list_append(result_list, current_cmd);
// And/or
if (is_first(*token, RULE_AND_OR))
{
current_cmd = parse_and_or(ctx);
if (current_cmd == NULL)
return NULL;
result_list = list_append(result_list, current_cmd);
}
token = PEEK_TOKEN();
}
@ -352,7 +355,7 @@ struct ast *parse_else_clause(struct lexer_context *ctx)
token = POP_TOKEN();
struct ast *condition = parse_compound_list(ctx);
// 'then'
// Then keyword
token = POP_TOKEN();
if (token->type != TOKEN_THEN)
{

112
src/parser/grammar_basic.h Normal file
View file

@ -0,0 +1,112 @@
#ifndef GRAMMAR_BASIC_H
#define GRAMMAR_BASIC_H
#include "../lexer/lexer.h"
#include "../utils/ast/ast.h"
// === Functions
/*
* @brief parses a list of [and_or] rules separated by semicolons and that
* ends by a newline
*
* @code list = and_or { ';' and_or } [ ';' ] ;
*
* @first first(and_or)
*/
struct ast *parse_list(struct lexer_context *ctx);
/*
* @brief Only parses a pipeline rule for the moment
*
* @code and_or = pipeline { ( '&&' | '||' ) {'\n'} pipeline } ;
*
* @first first(pipeline)
*/
struct ast *parse_and_or(struct lexer_context *ctx);
/*
* @brief Only parses a command rule for the moment
*
* @code pipeline = command ;
*
* @first first(command)
*/
struct ast *parse_pipeline(struct lexer_context *ctx);
/*
* @brief Parses a simple command rule or a shell command rule depending on
* the first token.
* @note
* TOKEN_WORD => simple_command
* TOKEN_IF => shell_command
*
* @code command = simple_command
* | shell_command
* ;
* @first first(simple_command), first(shell_command)
*/
struct ast *parse_command(struct lexer_context *ctx);
/*
* @brief Parses a simple list of words (command and arguments)
* ending by a separator
*
* @code simple_command = WORD { element } ;
*
* @first WORD
*/
struct ast *parse_simple_command(struct lexer_context *ctx);
/*
* @brief Parses an element rule
*
* @code element = WORD
* | redirection
* ;
*
* @first WORD, first(redirection)
*/
struct ast *parse_element(struct lexer_context *ctx);
/*
* @brief Only parses if rules for the moment
*
* @code shell_command = if_rule ;
*
* @first first(if_rule)
*/
struct ast *parse_shell_command(struct lexer_context *ctx);
/*
* @brief Parses a if rule (condition, then-clause, elif-clause, else-clause)
*
* @code if_rule = 'if' compound_list 'then' compound_list [else_clause] 'fi' ;
*
* @first TOKEN_IF
*/
struct ast *parse_if_rule(struct lexer_context *ctx);
/*
* @brief parses commands inside if/else clauses and returns the corresponding
* AST list
*
* @code compound_list = {'\n'} and_or { ( ';' | '\n' ) {'\n'} and_or } [';']
* {'\n'} ;
*
* @first TOKEN_NEWLINE, first(and_or)
*/
struct ast *parse_compound_list(struct lexer_context *ctx);
/*
* @brief
*
* @code else_clause = 'else' compound_list
* | 'elif' compound_list 'then' compound_list [else_clause]
* ;
*
* @first TOKEN_ELSE, TOKEN_ELIF
*/
struct ast *parse_else_clause(struct lexer_context *ctx);
#endif /* ! GRAMMAR_BASIC_H */

View file

@ -1,49 +1,52 @@
#include "parser.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../lexer/lexer.h"
#include "../parser/parsing_utils.h"
#include "../utils/lists/lists.h"
#include "grammar.h"
// === Static functions
// ...
// === Static variables
static enum parser_state state = PARSER_STATE_NOT_INITIALIZED;
// === Functions
bool parser_init(void)
{
if (state == PARSER_STATE_READY)
{
puts("Internal error: tried to initialize the parser module twice.");
return NULL;
}
int success = grammar_init();
if (success == false)
return false;
state = PARSER_STATE_READY;
return true;
}
struct ast *get_ast(struct lexer_context *ctx)
{
struct token *token = PEEK_TOKEN();
struct ast *res;
if (token->type == TOKEN_EOF)
if (ctx == NULL)
{
token = pop_token(ctx);
return ast_create_end();
puts("Internal error: called parser with no lexer context (NULL "
"pointer). Aborting.");
return NULL;
}
else if (token->type == TOKEN_NEWLINE)
if (state == PARSER_STATE_NOT_INITIALIZED)
{
token = pop_token(ctx);
return ast_create_void();
puts("Internal error: attempted to call parser without initializing "
"it. Aborting.");
return NULL;
}
else // TOKEN WORD
if (state == PARSER_STATE_CLOSED)
{
res = parse_list(ctx);
puts("Internal error: attempted to call parser after closing it. "
"Aborting.");
return NULL;
}
/*
if (token == NULL)
{
puts("Internal error: cannot get the following token");
puts("Hint: EOF might be missing");
return NULL;
}
*/
return res;
return parse_input(ctx);
}
// TODO
@ -51,4 +54,4 @@ struct ast *get_ast_str(char *command)
{
(void)command;
return NULL;
}
}

View file

@ -1,9 +1,29 @@
#ifndef PARSER_H
#define PARSER_H
#include <stdbool.h>
#include "../lexer/lexer.h"
#include "../utils/ast/ast.h"
enum parser_state
{
PARSER_STATE_NOT_INITIALIZED = 0,
PARSER_STATE_READY,
PARSER_STATE_CLOSED
};
/* @brief Initializes the parser module
* @warning parser needs to be closed after use with parser_close()
*
* @return Returns false on error and true on success
*/
bool parser_init(void);
/* @brief Closes the parser module after use
*/
void parser_close(void);
/* @brief Builds the AST representation of the next command to execute.
*
* @return Returns the AST representation of the next command to execute.

View file

@ -1,100 +0,0 @@
#ifndef PARSING_UTILS_H
#define PARSING_UTILS_H
#include "../lexer/lexer.h"
// === Macros
#define PEEK_TOKEN() \
peek_token(ctx); \
if (token == NULL) \
{ \
puts("Internal error: cannot get the following token"); \
return NULL; \
}
#define POP_TOKEN() \
pop_token(ctx); \
if (token == NULL) \
{ \
puts("Internal error: cannot get the following token"); \
return NULL; \
}
/* @brief Acts as the entry point of the parser, calls parse_list
*
* @code input = list '\n'
* | list EOF
* | '\n'
* | EOF
* ;
*/
struct ast *parse_input(struct lexer_context *ctx);
/* @brief: parses a list of [and_or] rules separated by semicolons and that
* ends by a newline
*
* @code list = and_or { ';' and_or } [ ';' ] ;
*/
struct ast *parse_list(struct lexer_context *ctx);
/* @brief Only parses a pipeline rule for the moment
*
* @code and_or = pipeline ;
*/
struct ast *parse_and_or(struct lexer_context *ctx);
/* @brief Only parses a command rule for the moment
*
* @code pipeline = command ;
*/
struct ast *parse_pipeline(struct lexer_context *ctx);
/* @brief Parses a simple command rule or a shell command rule depending on
* the first token.
* @note
* TOKEN_WORD => simple_command
* TOKEN_IF => shell_command
*
* @code command = simple_command
* | shell_command
* ;
*/
struct ast *parse_command(struct lexer_context *ctx);
/* @brief Parses a simple list of words (command and arguments)
* ending by a separator
*
* @code simple_command = WORD { element } ;
*/
struct ast *parse_simple_command(struct lexer_context *ctx);
/* @brief Only parses if rules for the moment
*
* @code shell_command = if_rule ;
*/
struct ast *parse_shell_command(struct lexer_context *ctx);
/* @brief Parses a if rule (condition, then-clause, elif-clause, else-clause)
*
* @code if_rule = 'if' compound_list 'then' compound_list [else_clause] 'fi' ;
*/
struct ast *parse_if_rule(struct lexer_context *ctx);
/* @brief parses commands inside if/else clauses and returns the corresponding
* AST list
*
* @code compound_list = {'\n'} and_or { ( ';' | '\n' ) {'\n'} and_or } [';']
* {'\n'} ;
*/
struct ast *parse_compound_list(struct lexer_context *ctx);
/* @brief
*
* @code else_clause = 'else' compound_list
* | 'elif' compound_list 'then' compound_list [else_clause]
* ;
*/
struct ast *parse_else_clause(struct lexer_context *ctx);
#endif /* ! PARSING_UTILS_H */

View file

@ -10,7 +10,7 @@
void ast_free(struct ast **node)
{
if (*node == NULL)
if (node == NULL || *node == NULL)
return;
// ast void does not need to be freed.
if (ast_is_if(*node))

View file

@ -9,6 +9,7 @@
#include "ast_list.h"
#include "ast_redir.h"
#include "ast_void.h"
#include "ast_word.h"
/**
* Prints the Graphviz DOT representation of the given AST to stdout.

View file

@ -11,7 +11,8 @@ enum ast_type
AST_AND_OR,
AST_REDIR,
AST_VOID,
AST_CMD
AST_CMD,
AST_WORD
};
struct ast

45
src/utils/ast/ast_word.c Normal file
View file

@ -0,0 +1,45 @@
#include "ast_word.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
struct ast *ast_create_word(char *word)
{
struct ast_word *ast_node = malloc(sizeof(struct ast_word));
if (ast_node == NULL)
return NULL;
ast_node->type = AST_WORD;
ast_node->word = strdup(word);
struct ast *res = ast_create(AST_WORD, ast_node);
if (res == NULL)
{
free(ast_node);
return NULL;
}
return res;
}
struct ast_word *ast_get_word(struct ast *node)
{
if (node == NULL || node->type != AST_WORD)
return NULL;
return node->data;
}
bool ast_is_word(struct ast *node)
{
return node && node->type == AST_WORD;
}
void ast_free_word(struct ast_word *ast_node)
{
if (ast_node == NULL)
return;
free(ast_node->word);
free(ast_node);
}

35
src/utils/ast/ast_word.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef AST_WORD_H
#define AST_WORD_H
#include <stdbool.h>
#include "ast_base.h"
struct ast_word
{
enum ast_type type;
char *word;
};
/**
* Checks if the given AST node is a command.
*/
bool ast_is_word(struct ast *node);
/**
* Retrieves the command data from the given AST node.
* Assumes that the node is of type AST_CMD.
*/
struct ast_word *ast_get_word(struct ast *node);
/**
* Creates a new AST node representing a command.
*/
struct ast *ast_create_word(char* word);
/*
* @brief: frees the given ast_command and sets the pointer to NULL.
*/
void ast_free_word(struct ast_word *ast_node);
#endif /* ! AST_WORD_H */