feat: while and for loops support for parser, plus new ASTs, new tokens and fixes inside parser

This commit is contained in:
Gu://em_ 2026-01-30 19:48:31 +01:00
parent f8b91d4da3
commit 423793903d
9 changed files with 341 additions and 35 deletions

View file

@ -72,6 +72,8 @@ enum token_type
TOKEN_FOR, TOKEN_FOR,
TOKEN_WHILE, TOKEN_WHILE,
TOKEN_UNTIL, TOKEN_UNTIL,
TOKEN_DO,
TOKEN_DONE,
TOKEN_CASE TOKEN_CASE
}; };

View file

@ -81,3 +81,145 @@ struct ast *parse_prefix(struct lexer_context *ctx)
return NULL; return NULL;
} }
} }
// TODO NOT IMPLEMENTED
struct ast *parse_funcdec(struct lexer_context *ctx)
{
(void)ctx;
perror("Error: usage of a not implemented function (parse_funcdec)");
return NULL;
}
struct ast *parse_for(struct lexer_context *ctx)
{
(void)ctx;
perror("Error: usage of a not implemented function (parse_for)");
return NULL;
}
struct ast *parse_while(struct lexer_context *ctx)
{
struct token *token = PEEK_TOKEN();
// 'while'
if (token->type != TOKEN_WHILE)
{
perror(
"Internal error: expected a TOKEN_WHILE but got a different type");
return NULL;
}
POP_TOKEN();
// condition
struct ast *condition = parse_compound_list(ctx);
if (condition == NULL)
return NULL;
token = PEEK_TOKEN();
// 'do'
if (token->type != TOKEN_DO)
{
ast_free(&condition);
perror("Syntax error: expected the 'do' keyowrd but got a different "
"token");
return NULL;
}
POP_TOKEN();
token = PEEK_TOKEN();
// body
struct ast *body = parse_compound_list(ctx);
if (body == NULL)
{
ast_free(&condition);
return NULL;
}
token = PEEK_TOKEN();
// 'done'
if (token->type != TOKEN_DONE)
{
ast_free(&condition);
perror("Syntax error: expected the 'done' keyowrd but got a different "
"token");
return NULL;
}
POP_TOKEN();
struct ast *result = ast_create_loop(condition, body);
if (result == NULL)
{
ast_free(&condition);
ast_free(&body);
perror("Internal error: could not create ast node (is your memory full "
"?)");
return NULL;
}
return result;
}
struct ast *parse_until(struct lexer_context *ctx)
{
struct token *token = PEEK_TOKEN();
// 'while'
if (token->type != TOKEN_UNTIL)
{
perror(
"Internal error: expected a TOKEN_WHILE but got a different type");
return NULL;
}
POP_TOKEN();
// condition
struct ast *condition = parse_compound_list(ctx);
if (condition == NULL)
return NULL;
condition =
ast_create_neg(true, condition); // TODO check result (beware to not
// exceed function lines limit)
token = PEEK_TOKEN();
// 'do'
if (token->type != TOKEN_DO)
{
ast_free(&condition);
perror("Syntax error: expected the 'do' keyowrd but got a different "
"token");
return NULL;
}
POP_TOKEN();
token = PEEK_TOKEN();
// body
struct ast *body = parse_compound_list(ctx);
if (body == NULL)
{
ast_free(&condition);
return NULL;
}
token = PEEK_TOKEN();
// 'done'
if (token->type != TOKEN_DONE)
{
ast_free(&condition);
perror("Syntax error: expected the 'done' keyowrd but got a different "
"token");
return NULL;
}
POP_TOKEN();
struct ast *result = ast_create_loop(condition, body);
if (result == NULL)
{
ast_free(&condition);
ast_free(&body);
perror("Internal error: could not create ast node (is your memory full "
"?)");
return NULL;
}
return result;
}

View file

@ -24,4 +24,46 @@ struct ast *parse_redirection(struct lexer_context *ctx);
*/ */
struct ast *parse_prefix(struct lexer_context *ctx); struct ast *parse_prefix(struct lexer_context *ctx);
/*
* @brief parses a funcdec rule
* @warning NOT IMPLEMENTED
*
* @code funcdec = WORD '(' ')' {'\n'} shell_command ;
*
* @first WORD
*/
struct ast *parse_funcdec(struct lexer_context *ctx);
/*
* @brief parses a for rule
* @warning NOT IMPLEMENTED
*
* @code rule_for = 'for' WORD
* ( [';'] | [ {'\n'} 'in' { WORD } ( ';' | '\n' ) ] )
* {'\n'} 'do' compound_list 'done' ;
*
* @first TOKEN_FOR
*/
struct ast *parse_for(struct lexer_context *ctx);
/*
* @brief parses a while rule
* @warning NOT IMPLEMENTED
*
* @code rule_while = 'while' compound_list 'do' compound_list 'done' ;
*
* @first TOKEN_WHILE
*/
struct ast *parse_while(struct lexer_context *ctx);
/*
* @brief parses an until rule
* @warning NOT IMPLEMENTED
*
* @code rule_until = 'until' compound_list 'do' compound_list 'done' ;
*
* @first TOKEN_UNTIL
*/
struct ast *parse_until(struct lexer_context *ctx);
#endif /* ! GRAMMAR_ADVANCED_H */ #endif /* ! GRAMMAR_ADVANCED_H */

View file

@ -26,6 +26,29 @@ static enum ast_and_or_type and_or_tok_to_ast(enum token_type tok_type)
} }
} }
/* @brief: frees command_elements and redirections lists (helper func)
* @return: NULL
*/
static void *err_simple_command(struct list *command_elements,
struct list *redirections)
{
list_deep_destroy(command_elements);
list_deep_destroy(redirections);
return NULL;
}
/* @brief: frees all the arguments. (helper func)
* @return: NULL.
*/
static void *err_if_rule(struct ast **cond, struct ast **then_clause,
struct ast **else_clause)
{
ast_free(cond);
ast_free(then_clause);
ast_free(else_clause);
return NULL;
}
// === Functions // === Functions
struct ast *parse_list(struct lexer_context *ctx) struct ast *parse_list(struct lexer_context *ctx)
@ -73,16 +96,11 @@ struct ast *parse_and_or(struct lexer_context *ctx)
while (token->type == TOKEN_AND || token->type == TOKEN_OR) while (token->type == TOKEN_AND || token->type == TOKEN_OR)
{ {
// Set left part // Build AST (left part)
enum ast_and_or_type type = and_or_tok_to_ast(token->type);
struct ast *left = result; struct ast *left = result;
// eat and_or token POP_TOKEN();
token = POP_TOKEN();
// Set type
enum ast_and_or_type type = and_or_tok_to_ast(token->type);
token = PEEK_TOKEN(); token = PEEK_TOKEN();
// Skip newlines // Skip newlines
@ -94,6 +112,12 @@ struct ast *parse_and_or(struct lexer_context *ctx)
// Right part // Right part
struct ast *right = parse_pipeline(ctx); struct ast *right = parse_pipeline(ctx);
if (right == NULL)
{
ast_free(&left);
return NULL;
}
token = PEEK_TOKEN();
result = ast_create_and_or(left, right, type); result = ast_create_and_or(left, right, type);
if (result == NULL) if (result == NULL)
@ -120,30 +144,33 @@ struct ast *parse_pipeline(struct lexer_context *ctx)
token = PEEK_TOKEN(); token = PEEK_TOKEN();
} }
// command rule
struct ast *left = parse_command(ctx); struct ast *left = parse_command(ctx);
token = PEEK_TOKEN();
if (negation) if (negation)
{ {
left = ast_create_neg(negation, left); left = ast_create_neg(negation, left);
} }
token = PEEK_TOKEN(); // Pipes
while (token->type == TOKEN_PIPE) while (token->type == TOKEN_PIPE)
{ {
POP_TOKEN(); POP_TOKEN();
token = PEEK_TOKEN();
// skip newlines // skip newlines
token = PEEK_TOKEN();
while (token->type == TOKEN_NEWLINE) while (token->type == TOKEN_NEWLINE)
{ {
POP_TOKEN(); POP_TOKEN();
token = PEEK_TOKEN(); token = PEEK_TOKEN();
} }
// command rule
struct ast *right = parse_command(ctx); struct ast *right = parse_command(ctx);
token = PEEK_TOKEN();
// Create AST // Create AST
left = ast_create_pipe(left, right); left = ast_create_pipe(left, right);
token = PEEK_TOKEN();
} }
return left; return left;
@ -162,6 +189,11 @@ struct ast *parse_command(struct lexer_context *ctx)
{ {
result = parse_shell_command(ctx); result = parse_shell_command(ctx);
} }
// WARNING funcdec seems to require a LL(2) parser
else if (is_first(*token, RULE_FUNCDEC))
{
result = parse_funcdec(ctx);
}
else else
{ {
perror("Syntax error: unexpected token"); perror("Syntax error: unexpected token");
@ -171,17 +203,6 @@ struct ast *parse_command(struct lexer_context *ctx)
return result; return result;
} }
/* @brief: frees command_elements and redirections lists (helper func)
* @return: NULL
*/
static void *err_simple_command(struct list *command_elements,
struct list *redirections)
{
list_deep_destroy(command_elements);
list_deep_destroy(redirections);
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;
@ -299,19 +320,41 @@ struct ast *parse_element(struct lexer_context *ctx)
struct ast *parse_shell_command(struct lexer_context *ctx) struct ast *parse_shell_command(struct lexer_context *ctx)
{ {
return parse_if_rule(ctx); struct token *token = PEEK_TOKEN();
} struct ast *result = NULL;
/* @brief: frees all the arguments. (helper func) // Grouping
* @return: NULL. // '(' or '{'
*/ if (token->type == TOKEN_LEFT_BRACKET || token->type == TOKEN_LEFT_PAREN)
static void *err_if_rule(struct ast **cond, struct ast **then_clause, {
struct ast **else_clause) POP_TOKEN();
{ result = parse_compound_list(ctx);
ast_free(cond); if (result == NULL)
ast_free(then_clause); return NULL;
ast_free(else_clause);
return NULL; // ')' or '}'
token = PEEK_TOKEN();
if (token->type == TOKEN_LEFT_BRACKET
|| token->type == TOKEN_LEFT_PAREN)
{
ast_free(&result);
perror("Syntax error: bracket/parenthesis mismatch");
return NULL;
}
POP_TOKEN();
return result;
}
else if (is_first(*token, RULE_IF))
{
return parse_if_rule(ctx);
}
// TODO loops and case
else
{
perror("Syntax error: unexpected token in parse_shell_command");
return NULL;
}
} }
struct ast *parse_if_rule(struct lexer_context *ctx) struct ast *parse_if_rule(struct lexer_context *ctx)

View file

@ -43,6 +43,7 @@ struct ast *parse_pipeline(struct lexer_context *ctx);
* *
* @code command = simple_command * @code command = simple_command
* | shell_command * | shell_command
*
* ; * ;
* @first first(simple_command), first(shell_command) * @first first(simple_command), first(shell_command)
*/ */
@ -72,7 +73,10 @@ struct ast *parse_element(struct lexer_context *ctx);
/* /*
* @brief Only parses if rules for the moment * @brief Only parses if rules for the moment
* *
* @code shell_command = if_rule ; * @code shell_command = '{' compound_list '}'
* | '(' compound_list ')'
* | if_rule
* ;
* *
* @first first(if_rule) * @first first(if_rule)
*/ */

View file

@ -8,6 +8,7 @@
#include "ast_end.h" #include "ast_end.h"
#include "ast_if.h" #include "ast_if.h"
#include "ast_list.h" #include "ast_list.h"
#include "ast_loop.h"
#include "ast_neg.h" #include "ast_neg.h"
#include "ast_pipe.h" #include "ast_pipe.h"
#include "ast_redir.h" #include "ast_redir.h"

View file

@ -16,6 +16,7 @@ enum ast_type
AST_WORD, AST_WORD,
AST_PIPE, AST_PIPE,
AST_NEG, AST_NEG,
AST_LOOP,
AST_ASSIGNMENT AST_ASSIGNMENT
}; };

37
src/utils/ast/ast_loop.c Normal file
View file

@ -0,0 +1,37 @@
#include "ast_loop.h"
#include <stdbool.h>
#include <stdlib.h>
struct ast *ast_create_loop(struct ast *condition, struct ast *body)
{
struct ast_loop *node_data = malloc(sizeof(struct ast_loop));
if (!node_data)
return NULL;
node_data->condition = condition;
node_data->body = body;
return ast_create(AST_LOOP, node_data);
}
struct ast_loop *ast_get_loop(struct ast *node)
{
if (node == NULL || node->type != AST_LOOP)
return NULL;
return (struct ast_loop *)node->data;
}
bool ast_is_loop(struct ast *node)
{
return node != NULL && node->type == AST_LOOP;
}
void ast_free_loop(struct ast_loop *loop_data)
{
if (loop_data == NULL)
return;
ast_free(&loop_data->condition);
ast_free(&loop_data->body);
free(loop_data);
}

34
src/utils/ast/ast_loop.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef AST_LOOP_H
#define AST_LOOP_H
#include "ast_base.h"
struct ast_loop
{
// Repeat body while condition is true
struct ast *condition;
struct ast *body;
};
/**
* Checks if the given AST node is a loop.
*/
bool ast_is_loop(struct ast *node);
/**
* Retrieves the loop data from the given AST node.
* Assumes that the node is of type AST_LOOP.
*/
struct ast_loop *ast_get_loop(struct ast *node);
/**
* Creates a new AST node representing a loop.
*/
struct ast *ast_create_loop(struct ast* condition, struct ast* body);
/*
* @brief: frees the given ast_loop and sets the pointer to NULL.
*/
void ast_free_loop(struct ast_loop *loop_node);
#endif /* ! AST_LOOP_H */