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

@ -81,3 +81,145 @@ struct ast *parse_prefix(struct lexer_context *ctx)
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);
/*
* @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 */

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
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)
{
// Set left part
// Build AST (left part)
enum ast_and_or_type type = and_or_tok_to_ast(token->type);
struct ast *left = result;
// eat and_or token
token = POP_TOKEN();
// Set type
enum ast_and_or_type type = and_or_tok_to_ast(token->type);
POP_TOKEN();
token = PEEK_TOKEN();
// Skip newlines
@ -94,6 +112,12 @@ struct ast *parse_and_or(struct lexer_context *ctx)
// Right part
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);
if (result == NULL)
@ -120,30 +144,33 @@ struct ast *parse_pipeline(struct lexer_context *ctx)
token = PEEK_TOKEN();
}
// command rule
struct ast *left = parse_command(ctx);
token = PEEK_TOKEN();
if (negation)
{
left = ast_create_neg(negation, left);
}
token = PEEK_TOKEN();
// Pipes
while (token->type == TOKEN_PIPE)
{
POP_TOKEN();
token = PEEK_TOKEN();
// skip newlines
token = PEEK_TOKEN();
while (token->type == TOKEN_NEWLINE)
{
POP_TOKEN();
token = PEEK_TOKEN();
}
// command rule
struct ast *right = parse_command(ctx);
token = PEEK_TOKEN();
// Create AST
left = ast_create_pipe(left, right);
token = PEEK_TOKEN();
}
return left;
@ -162,6 +189,11 @@ struct ast *parse_command(struct lexer_context *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
{
perror("Syntax error: unexpected token");
@ -171,17 +203,6 @@ struct ast *parse_command(struct lexer_context *ctx)
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 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)
{
return parse_if_rule(ctx);
}
struct token *token = PEEK_TOKEN();
struct ast *result = 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;
// Grouping
// '(' or '{'
if (token->type == TOKEN_LEFT_BRACKET || token->type == TOKEN_LEFT_PAREN)
{
POP_TOKEN();
result = parse_compound_list(ctx);
if (result == NULL)
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)

View file

@ -43,6 +43,7 @@ struct ast *parse_pipeline(struct lexer_context *ctx);
*
* @code command = simple_command
* | 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
*
* @code shell_command = if_rule ;
* @code shell_command = '{' compound_list '}'
* | '(' compound_list ')'
* | if_rule
* ;
*
* @first first(if_rule)
*/