merge new parser into dev
This commit is contained in:
commit
d707c6180b
16 changed files with 725 additions and 228 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
206
src/parser/grammar.c
Normal 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
93
src/parser/grammar.h
Normal 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 */
|
||||
45
src/parser/grammar_advanced.c
Normal file
45
src/parser/grammar_advanced.c
Normal 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);
|
||||
}
|
||||
27
src/parser/grammar_advanced.h
Normal file
27
src/parser/grammar_advanced.h
Normal 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 */
|
||||
|
|
@ -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,11 +314,14 @@ 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
112
src/parser/grammar_basic.h
Normal 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 */
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
45
src/utils/ast/ast_word.c
Normal 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
35
src/utils/ast/ast_word.h
Normal 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 */
|
||||
Loading…
Add table
Add a link
Reference in a new issue