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)
|
struct hash_map *vars)
|
||||||
{
|
{
|
||||||
int return_code = SUCCESS;
|
int return_code = SUCCESS;
|
||||||
|
// init parser
|
||||||
|
int parser_init();
|
||||||
|
|
||||||
// Retrieve and build first AST
|
// Retrieve and build first AST
|
||||||
struct ast *command_ast = get_ast(ctx);
|
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);
|
return err_input(&vars);
|
||||||
|
|
||||||
ast_free(&command_ast);
|
ast_free(&command_ast);
|
||||||
|
parser_close();
|
||||||
|
|
||||||
return return_code;
|
return return_code;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@ lib_LIBRARIES = libparser.a
|
||||||
|
|
||||||
libparser_a_SOURCES = \
|
libparser_a_SOURCES = \
|
||||||
parser.c \
|
parser.c \
|
||||||
parsing_utils.c
|
grammar.c \
|
||||||
|
grammar_basic.c \
|
||||||
|
grammar_advanced.c
|
||||||
|
|
||||||
libparser_a_CPPFLAGS = -I$(top_srcdir)/src
|
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 <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "../lexer/lexer.h"
|
#include "../utils/lists/lists.h"
|
||||||
#include "../utils/ast/ast.h"
|
#include "grammar.h"
|
||||||
|
#include "grammar_advanced.h"
|
||||||
|
|
||||||
// === Static functions
|
// === 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)
|
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
|
// === Functions
|
||||||
|
|
||||||
struct ast *parse_input(struct lexer_context *ctx)
|
|
||||||
{
|
|
||||||
return parse_list(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ast *parse_list(struct lexer_context *ctx)
|
struct ast *parse_list(struct lexer_context *ctx)
|
||||||
{
|
{
|
||||||
struct list *result_list = NULL;
|
struct list *result_list = NULL;
|
||||||
|
|
@ -92,20 +43,20 @@ struct ast *parse_list(struct lexer_context *ctx)
|
||||||
while (token->type == TOKEN_SEMICOLON)
|
while (token->type == TOKEN_SEMICOLON)
|
||||||
{
|
{
|
||||||
token = POP_TOKEN();
|
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();
|
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);
|
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 *parse_and_or(struct lexer_context *ctx)
|
||||||
{
|
{
|
||||||
struct ast *result = parse_pipeline(ctx);
|
struct ast *result = parse_pipeline(ctx);
|
||||||
|
if (result == NULL)
|
||||||
|
return NULL;
|
||||||
struct token *token = PEEK_TOKEN();
|
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;
|
struct ast *left = result;
|
||||||
|
|
||||||
// eat and_or token
|
// eat and_or token
|
||||||
token = POP_TOKEN();
|
token = POP_TOKEN();
|
||||||
|
|
||||||
// set type
|
// Set type
|
||||||
enum ast_and_or_type type = and_or_tok_to_ast(token->type);
|
enum ast_and_or_type type = and_or_tok_to_ast(token->type);
|
||||||
|
|
||||||
token = PEEK_TOKEN();
|
token = PEEK_TOKEN();
|
||||||
|
|
||||||
// skip newlines
|
// Skip newlines
|
||||||
while (token->type == TOKEN_NEWLINE)
|
while (token->type == TOKEN_NEWLINE)
|
||||||
{
|
{
|
||||||
token = POP_TOKEN();
|
token = POP_TOKEN();
|
||||||
token = PEEK_TOKEN();
|
token = PEEK_TOKEN();
|
||||||
}
|
}
|
||||||
|
|
||||||
// right part
|
// Right part
|
||||||
struct ast *right = parse_pipeline(ctx);
|
struct ast *right = parse_pipeline(ctx);
|
||||||
|
|
||||||
result = ast_create_and_or(left, right, type);
|
result = ast_create_and_or(left, right, type);
|
||||||
|
if (result == NULL)
|
||||||
|
{
|
||||||
|
ast_free(&left);
|
||||||
|
ast_free(&right);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -164,53 +123,92 @@ struct ast *parse_command(struct lexer_context *ctx)
|
||||||
}
|
}
|
||||||
else
|
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 ast *parse_simple_command(struct lexer_context *ctx)
|
||||||
{
|
{
|
||||||
struct list *command_elements = NULL;
|
struct list *command_elements = NULL;
|
||||||
struct token *token = PEEK_TOKEN();
|
|
||||||
|
// WORD
|
||||||
|
struct token *token = POP_TOKEN();
|
||||||
if (token->type != TOKEN_WORD)
|
if (token->type != TOKEN_WORD)
|
||||||
{
|
{
|
||||||
puts("Expected a command but got a different token type");
|
puts("Expected a command but got a different token type");
|
||||||
return NULL;
|
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);
|
// Get element
|
||||||
if (token == NULL)
|
struct ast *element = parse_element(ctx);
|
||||||
|
if (element == NULL)
|
||||||
{
|
{
|
||||||
// TODO free
|
list_deep_destroy(command_elements);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
char *word = strdup(token->data);
|
|
||||||
if (word == NULL)
|
// Get element type
|
||||||
|
if (ast_is_word(element))
|
||||||
{
|
{
|
||||||
// TODO free
|
struct ast_word *element_word = ast_get_word(element);
|
||||||
puts("Internal error: Couldn't copy token content (is memory full "
|
command_elements =
|
||||||
"?)");
|
list_append(command_elements, element_word->word);
|
||||||
|
}
|
||||||
|
else if (ast_is_redir(element))
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
puts("NOT IMPLEMENTED");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
command_elements = list_append(command_elements, word);
|
else
|
||||||
token = peek_token(ctx);
|
|
||||||
if (token == NULL)
|
|
||||||
{
|
{
|
||||||
// TODO free
|
puts("Internal error: unexpected return value from parse_element "
|
||||||
|
"in parse_simple_command");
|
||||||
|
list_deep_destroy(command_elements);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Forward
|
||||||
|
token = PEEK_TOKEN();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Result
|
||||||
struct ast *result = ast_create_command(command_elements);
|
struct ast *result = ast_create_command(command_elements);
|
||||||
if (result == NULL)
|
if (result == NULL)
|
||||||
{
|
{
|
||||||
// TODO free
|
list_deep_destroy(command_elements);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
return result;
|
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)
|
struct ast *parse_shell_command(struct lexer_context *ctx)
|
||||||
{
|
{
|
||||||
return parse_if_rule(ctx);
|
return parse_if_rule(ctx);
|
||||||
|
|
@ -257,6 +255,7 @@ struct ast *parse_if_rule(struct lexer_context *ctx)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fi keyword
|
||||||
token = POP_TOKEN();
|
token = POP_TOKEN();
|
||||||
if (token->type != TOKEN_FI)
|
if (token->type != TOKEN_FI)
|
||||||
{
|
{
|
||||||
|
|
@ -267,6 +266,7 @@ struct ast *parse_if_rule(struct lexer_context *ctx)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Result
|
||||||
struct ast *result =
|
struct ast *result =
|
||||||
ast_create_if(condition_content, then_content, else_content);
|
ast_create_if(condition_content, then_content, else_content);
|
||||||
if (result == NULL)
|
if (result == NULL)
|
||||||
|
|
@ -294,7 +294,7 @@ struct ast *parse_compound_list(struct lexer_context *ctx)
|
||||||
token = PEEK_TOKEN();
|
token = PEEK_TOKEN();
|
||||||
}
|
}
|
||||||
|
|
||||||
// and_or
|
// And/or
|
||||||
current_cmd = parse_and_or(ctx);
|
current_cmd = parse_and_or(ctx);
|
||||||
if (current_cmd == NULL)
|
if (current_cmd == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -314,11 +314,14 @@ struct ast *parse_compound_list(struct lexer_context *ctx)
|
||||||
token = PEEK_TOKEN();
|
token = PEEK_TOKEN();
|
||||||
}
|
}
|
||||||
|
|
||||||
// and_or
|
// And/or
|
||||||
current_cmd = parse_and_or(ctx);
|
if (is_first(*token, RULE_AND_OR))
|
||||||
if (current_cmd == NULL)
|
{
|
||||||
return NULL;
|
current_cmd = parse_and_or(ctx);
|
||||||
result_list = list_append(result_list, current_cmd);
|
if (current_cmd == NULL)
|
||||||
|
return NULL;
|
||||||
|
result_list = list_append(result_list, current_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
token = PEEK_TOKEN();
|
token = PEEK_TOKEN();
|
||||||
}
|
}
|
||||||
|
|
@ -352,7 +355,7 @@ struct ast *parse_else_clause(struct lexer_context *ctx)
|
||||||
token = POP_TOKEN();
|
token = POP_TOKEN();
|
||||||
struct ast *condition = parse_compound_list(ctx);
|
struct ast *condition = parse_compound_list(ctx);
|
||||||
|
|
||||||
// 'then'
|
// Then keyword
|
||||||
token = POP_TOKEN();
|
token = POP_TOKEN();
|
||||||
if (token->type != TOKEN_THEN)
|
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 "parser.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "../lexer/lexer.h"
|
#include "grammar.h"
|
||||||
#include "../parser/parsing_utils.h"
|
|
||||||
#include "../utils/lists/lists.h"
|
|
||||||
|
|
||||||
// === Static functions
|
// === Static variables
|
||||||
// ...
|
|
||||||
|
static enum parser_state state = PARSER_STATE_NOT_INITIALIZED;
|
||||||
|
|
||||||
// === Functions
|
// === 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 ast *get_ast(struct lexer_context *ctx)
|
||||||
{
|
{
|
||||||
struct token *token = PEEK_TOKEN();
|
if (ctx == NULL)
|
||||||
struct ast *res;
|
|
||||||
|
|
||||||
if (token->type == TOKEN_EOF)
|
|
||||||
{
|
{
|
||||||
token = pop_token(ctx);
|
puts("Internal error: called parser with no lexer context (NULL "
|
||||||
return ast_create_end();
|
"pointer). Aborting.");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
else if (token->type == TOKEN_NEWLINE)
|
if (state == PARSER_STATE_NOT_INITIALIZED)
|
||||||
{
|
{
|
||||||
token = pop_token(ctx);
|
puts("Internal error: attempted to call parser without initializing "
|
||||||
return ast_create_void();
|
"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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
return parse_input(ctx);
|
||||||
if (token == NULL)
|
|
||||||
{
|
|
||||||
puts("Internal error: cannot get the following token");
|
|
||||||
puts("Hint: EOF might be missing");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,29 @@
|
||||||
#ifndef PARSER_H
|
#ifndef PARSER_H
|
||||||
#define PARSER_H
|
#define PARSER_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "../lexer/lexer.h"
|
#include "../lexer/lexer.h"
|
||||||
#include "../utils/ast/ast.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.
|
/* @brief Builds the AST representation of the next command to execute.
|
||||||
*
|
*
|
||||||
* @return Returns 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)
|
void ast_free(struct ast **node)
|
||||||
{
|
{
|
||||||
if (*node == NULL)
|
if (node == NULL || *node == NULL)
|
||||||
return;
|
return;
|
||||||
// ast void does not need to be freed.
|
// ast void does not need to be freed.
|
||||||
if (ast_is_if(*node))
|
if (ast_is_if(*node))
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include "ast_list.h"
|
#include "ast_list.h"
|
||||||
#include "ast_redir.h"
|
#include "ast_redir.h"
|
||||||
#include "ast_void.h"
|
#include "ast_void.h"
|
||||||
|
#include "ast_word.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints the Graphviz DOT representation of the given AST to stdout.
|
* Prints the Graphviz DOT representation of the given AST to stdout.
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ enum ast_type
|
||||||
AST_AND_OR,
|
AST_AND_OR,
|
||||||
AST_REDIR,
|
AST_REDIR,
|
||||||
AST_VOID,
|
AST_VOID,
|
||||||
AST_CMD
|
AST_CMD,
|
||||||
|
AST_WORD
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ast
|
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