2026-01-23 17:01:26 +01:00
|
|
|
// === Includes
|
|
|
|
|
#include "grammar.h"
|
|
|
|
|
|
2026-01-24 13:06:39 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
2026-01-27 00:30:19 +01:00
|
|
|
#include "grammar_basic.h"
|
2026-01-23 17:01:26 +01:00
|
|
|
|
|
|
|
|
// === Static variables
|
|
|
|
|
|
2026-01-24 13:06:39 +01:00
|
|
|
// rule-indexed array containing firsts
|
|
|
|
|
static struct firsts_list *firsts_map = NULL;
|
2026-01-23 17:01:26 +01:00
|
|
|
|
|
|
|
|
// === Static functions
|
2026-01-24 13:06:39 +01:00
|
|
|
|
|
|
|
|
/* @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
|
|
|
|
|
*/
|
2026-01-24 16:13:16 +01:00
|
|
|
static bool add_first(enum rule rule, enum token_type token)
|
2026-01-24 13:06:39 +01:00
|
|
|
{
|
|
|
|
|
struct firsts_list *item = &firsts_map[rule];
|
|
|
|
|
if (item->tokens != NULL)
|
|
|
|
|
{
|
|
|
|
|
// Check for duplicates
|
|
|
|
|
for (size_t i = 0; i < item->list_length; i++)
|
|
|
|
|
{
|
2026-01-24 16:13:16 +01:00
|
|
|
if (item->tokens[i] == token)
|
2026-01-24 13:06:39 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Append
|
|
|
|
|
item->list_length++;
|
2026-01-27 00:30:19 +01:00
|
|
|
item->tokens = realloc(item->tokens,
|
|
|
|
|
(item->list_length) * sizeof(enum token_type));
|
2026-01-24 13:06:39 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Create entry
|
2026-01-24 16:13:16 +01:00
|
|
|
item->list_length = 1;
|
|
|
|
|
item->tokens = calloc(1, sizeof(enum token_type));
|
2026-01-24 13:06:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for alloc error
|
|
|
|
|
if (item->tokens == NULL)
|
|
|
|
|
{
|
|
|
|
|
item->list_length = 0;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fill
|
|
|
|
|
item->tokens[item->list_length - 1] = token;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-29 19:47:59 +01:00
|
|
|
/* @brief Add a list of tokens to a rule's firsts (in firsts_map)
|
|
|
|
|
*
|
|
|
|
|
* @arg rule the rule to which add a first
|
|
|
|
|
* @arg tokens_list the list of tokens to add to the rule's firsts
|
|
|
|
|
* @return true on success, false on error
|
|
|
|
|
*/
|
|
|
|
|
static bool add_firsts(enum rule rule, struct firsts_list *tokens_list)
|
|
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < tokens_list->list_length; i++)
|
|
|
|
|
{
|
|
|
|
|
bool res = add_first(rule, tokens_list->tokens[i]);
|
|
|
|
|
if (!res)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-24 13:06:39 +01:00
|
|
|
/* @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)
|
|
|
|
|
{
|
2026-01-27 16:05:11 +01:00
|
|
|
perror("Internal error: couldn't create the firsts_map (is your memory "
|
2026-01-27 19:56:33 +01:00
|
|
|
"full ?)");
|
2026-01-24 13:06:39 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
2026-01-23 17:01:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// === Functions
|
|
|
|
|
|
2026-01-30 18:26:56 +01:00
|
|
|
int grammar_init(void)
|
2026-01-23 17:01:26 +01:00
|
|
|
{
|
2026-01-24 13:06:39 +01:00
|
|
|
// Initialize the firsts map
|
|
|
|
|
bool success = init_firsts_map();
|
|
|
|
|
if (success != true)
|
|
|
|
|
return false;
|
2026-01-23 17:01:26 +01:00
|
|
|
|
2026-01-24 13:06:39 +01:00
|
|
|
// Populate the firsts map
|
2026-01-29 19:47:59 +01:00
|
|
|
// TODO CHECK ORDER
|
2026-01-24 16:13:16 +01:00
|
|
|
|
2026-01-29 19:47:59 +01:00
|
|
|
// If
|
|
|
|
|
add_first(RULE_IF, TOKEN_IF);
|
2026-01-24 16:13:16 +01:00
|
|
|
|
2026-01-29 19:47:59 +01:00
|
|
|
// Else clause
|
|
|
|
|
add_first(RULE_ELSE_CLAUSE, TOKEN_ELSE);
|
|
|
|
|
add_first(RULE_ELSE_CLAUSE, TOKEN_ELIF);
|
2026-01-24 16:13:16 +01:00
|
|
|
|
2026-01-29 19:47:59 +01:00
|
|
|
// For
|
|
|
|
|
add_first(RULE_FOR, TOKEN_FOR);
|
2026-01-24 16:13:16 +01:00
|
|
|
|
2026-01-29 19:47:59 +01:00
|
|
|
// While
|
|
|
|
|
add_first(RULE_WHILE, TOKEN_WHILE);
|
2026-01-24 16:13:16 +01:00
|
|
|
|
2026-01-29 19:47:59 +01:00
|
|
|
// Until
|
|
|
|
|
add_first(RULE_WHILE, TOKEN_UNTIL);
|
2026-01-24 16:13:16 +01:00
|
|
|
|
2026-01-29 19:47:59 +01:00
|
|
|
// Case
|
|
|
|
|
add_first(RULE_CASE, TOKEN_CASE);
|
2026-01-24 16:13:16 +01:00
|
|
|
|
2026-01-29 19:47:59 +01:00
|
|
|
// Case item
|
|
|
|
|
add_first(RULE_CASE_ITEM, TOKEN_LEFT_PAREN);
|
|
|
|
|
add_first(RULE_CASE_ITEM, TOKEN_WORD);
|
2026-01-24 16:13:16 +01:00
|
|
|
|
2026-01-29 19:47:59 +01:00
|
|
|
// Case clause
|
|
|
|
|
add_firsts(RULE_CASE_CLAUSE, first(RULE_CASE_ITEM));
|
2026-01-24 16:13:16 +01:00
|
|
|
|
2026-01-29 19:47:59 +01:00
|
|
|
// Redirection
|
|
|
|
|
add_first(RULE_REDIRECTION, TOKEN_IONUMBER);
|
|
|
|
|
add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT);
|
|
|
|
|
add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT);
|
|
|
|
|
add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT_RIGHT);
|
|
|
|
|
add_first(RULE_REDIRECTION, TOKEN_REDIR_DOUBLE_RIGHT);
|
|
|
|
|
add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT_AMP);
|
|
|
|
|
add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT_AMP);
|
|
|
|
|
add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT_PIPE);
|
|
|
|
|
// %RIP Matteo 30/01/2026
|
2026-01-24 16:13:16 +01:00
|
|
|
|
2026-01-29 19:47:59 +01:00
|
|
|
// Element
|
2026-01-24 16:13:16 +01:00
|
|
|
add_first(RULE_ELEMENT, TOKEN_WORD);
|
2026-01-30 16:51:10 +01:00
|
|
|
add_first(RULE_ELEMENT, TOKEN_ASSIGNMENT_WORD);
|
2026-01-29 19:47:59 +01:00
|
|
|
add_firsts(RULE_ELEMENT, first(RULE_REDIRECTION));
|
|
|
|
|
|
|
|
|
|
// Prefix
|
|
|
|
|
add_first(RULE_PREFIX, TOKEN_ASSIGNMENT_WORD);
|
|
|
|
|
add_firsts(RULE_PREFIX, first(RULE_REDIRECTION));
|
|
|
|
|
|
|
|
|
|
// Shell command
|
|
|
|
|
add_firsts(RULE_SHELL_COMMAND, first(RULE_IF));
|
|
|
|
|
|
|
|
|
|
// Simple command
|
|
|
|
|
add_firsts(RULE_SIMPLE_COMMAND, first(RULE_PREFIX));
|
2026-01-29 20:29:02 +01:00
|
|
|
add_first(RULE_SIMPLE_COMMAND, TOKEN_WORD);
|
2026-01-24 16:13:16 +01:00
|
|
|
|
2026-01-29 19:47:59 +01:00
|
|
|
// Funcdec
|
|
|
|
|
add_first(RULE_FUNCDEC, TOKEN_WORD);
|
2026-01-24 16:13:16 +01:00
|
|
|
|
2026-01-29 19:47:59 +01:00
|
|
|
// Command
|
|
|
|
|
add_firsts(RULE_COMMAND, first(RULE_SIMPLE_COMMAND));
|
|
|
|
|
add_firsts(RULE_COMMAND, first(RULE_SHELL_COMMAND));
|
|
|
|
|
add_firsts(RULE_COMMAND, first(RULE_FUNCDEC));
|
|
|
|
|
|
|
|
|
|
// Pipeline
|
|
|
|
|
add_first(RULE_PIPELINE, TOKEN_WORD);
|
|
|
|
|
add_firsts(RULE_PIPELINE, first(RULE_COMMAND));
|
|
|
|
|
|
|
|
|
|
// And Or
|
|
|
|
|
add_firsts(RULE_AND_OR, first(RULE_PIPELINE));
|
|
|
|
|
|
|
|
|
|
// Compound list
|
|
|
|
|
add_first(RULE_COMPOUND_LIST, TOKEN_NEWLINE);
|
|
|
|
|
add_firsts(RULE_COMPOUND_LIST, first(RULE_AND_OR));
|
|
|
|
|
|
|
|
|
|
// List
|
|
|
|
|
add_firsts(RULE_LIST, first(RULE_AND_OR));
|
|
|
|
|
|
|
|
|
|
// Input
|
|
|
|
|
add_first(RULE_INPUT, TOKEN_NEWLINE);
|
|
|
|
|
add_first(RULE_INPUT, TOKEN_EOF);
|
|
|
|
|
add_firsts(RULE_INPUT, first(RULE_LIST));
|
2026-01-23 17:01:26 +01:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void grammar_close(void)
|
|
|
|
|
{
|
2026-01-24 13:06:39 +01:00
|
|
|
// 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;
|
2026-01-23 17:01:26 +01:00
|
|
|
}
|
|
|
|
|
|
2026-01-24 16:13:16 +01:00
|
|
|
struct firsts_list *first(enum rule rule)
|
|
|
|
|
{
|
|
|
|
|
if (firsts_map == NULL || firsts_map[rule].tokens == NULL)
|
|
|
|
|
{
|
2026-01-27 16:05:11 +01:00
|
|
|
perror("Internal error: attempted to get the firsts of a rule without "
|
2026-01-27 19:56:33 +01:00
|
|
|
"properly initializing the firsts map");
|
2026-01-24 16:13:16 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-23 17:01:26 +01:00
|
|
|
struct ast *parse_input(struct lexer_context *ctx)
|
|
|
|
|
{
|
2026-01-24 16:48:21 +01:00
|
|
|
struct token *token = PEEK_TOKEN();
|
|
|
|
|
|
|
|
|
|
if (token->type == TOKEN_EOF)
|
2026-01-27 18:00:59 +01:00
|
|
|
{
|
|
|
|
|
POP_TOKEN();
|
|
|
|
|
return ast_create_end();
|
|
|
|
|
}
|
2026-01-24 16:48:21 +01:00
|
|
|
|
|
|
|
|
if (token->type == TOKEN_NEWLINE)
|
|
|
|
|
{
|
|
|
|
|
POP_TOKEN();
|
|
|
|
|
return ast_create_list(NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ast *ast = parse_list(ctx);
|
|
|
|
|
token = PEEK_TOKEN();
|
2026-01-30 12:21:29 +01:00
|
|
|
|
|
|
|
|
if (ast == NULL)
|
2026-01-24 16:48:21 +01:00
|
|
|
{
|
2026-01-30 12:21:29 +01:00
|
|
|
if (token != NULL && token->type == TOKEN_EOF)
|
2026-01-26 19:00:20 +01:00
|
|
|
{
|
2026-01-24 16:48:21 +01:00
|
|
|
POP_TOKEN();
|
2026-01-26 19:00:20 +01:00
|
|
|
}
|
2026-01-30 12:21:29 +01:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (token->type == TOKEN_NEWLINE || token->type == TOKEN_EOF)
|
|
|
|
|
{
|
|
|
|
|
POP_TOKEN();
|
2026-01-24 16:48:21 +01:00
|
|
|
return ast;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-27 16:05:11 +01:00
|
|
|
perror("Syntax error: expected newline or EOF after list");
|
2026-01-24 16:48:21 +01:00
|
|
|
ast_free(&ast);
|
|
|
|
|
return NULL;
|
2026-01-23 17:01:26 +01:00
|
|
|
}
|