aboutsummaryrefslogtreecommitdiff
path: root/src/parser/parser.c
blob: 4fde122843cd46e36f589d47e5a57bf0ce2e9561 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/* ************************************************************************** */
/*                                                                            */
/*                                                        :::      ::::::::   */
/*   parser.c                                           :+:      :+:    :+:   */
/*                                                    +:+ +:+         +:+     */
/*   By: nahaddac <nahaddac@student.42.fr>          +#+  +:+       +#+        */
/*                                                +#+#+#+#+#+   +#+           */
/*   Created: 2020/06/17 18:09:04 by nahaddac          #+#    #+#             */
/*   Updated: 2020/10/10 18:38:37 by charles          ###   ########.fr       */
/*                                                                            */
/* ************************************************************************** */

/*
** \file   parse.c
** \brief  Parser
*/

#include "minishell.h"
#include "parser.h"

static char		*g_sep_str_lookup[] = {
	[TAG_END] = ";",
	[TAG_OR] = "||",
	[TAG_REDIR_IN] = "<",
	[TAG_REDIR_APPEND] = ">>",
	[TAG_PARENT_CLOSE] = ")",
	[TAG_AND] = "&&",
	[TAG_PIPE] = "|",
	[TAG_REDIR_OUT] = ">",
	[TAG_PARENT_OPEN] = "(",
};

t_parsed		*destroy_ret(t_parsed *destroyed, t_parsed *ret)
{
	parsed_destroy(destroyed);
	return (ret);
}

/*
** \brief        Parse a expression pipeline
** \param input  Input tokens
** \return       Parsed containning a pipeline of expression
**               Parsed error if a pipe is missing
**               or the expression parser return an error
*/

t_parsed		*parse_pipeline(t_tok_lst *input)
{
	t_parsed	*expr;
	t_parsed	*tail;
	t_ast		*expr_ast;
	t_ast		*pipeline_ast;

	expr = parse_expr(input);
	if (parsed_check(expr) || expr->rest == NULL || expr->rest->tag != TAG_PIPE)
		return (expr);
	tok_lst_pop_front(&expr->rest, free);
	if (expr->rest == NULL)
		return (destroy_ret(expr, parsed_expected()));
	if (parsed_check(tail = parse_pipeline(expr->rest)))
		return (destroy_ret(expr, tail));
	expr_ast = expr->ast;
	free(expr);
	if (tail->ast->tag == AST_CMD || tail->ast->tag == AST_PARENT)
	{
		pipeline_ast = ast_new(AST_PIPELINE);
		exit_if((pipeline_ast->pipeline = ft_lstnew(tail->ast)) == NULL);
	}
	else
		pipeline_ast = tail->ast;
	exit_if(ft_lstpush_front_node(&pipeline_ast->pipeline, expr_ast) == NULL);
	tail->ast = pipeline_ast;
	return (tail);
}

/*
** \brief          Build a operation AST node from left, right components
**                 and a separator tag
** \param left     Left hand side of the operation
** \param right    Right hand side of the operation
** \param sep_tag  Separator tag of the operation
** \return         Parsed containning an operation AST
*/

static t_parsed	*st_parse_op_build(
	t_parsed *left, t_parsed *right, enum e_tok sep_tag)
{
	t_ast	*ast;

	exit_if((ast = ast_new(AST_OP)) == NULL);
	ast->op.left = left->ast;
	ast->op.right = right->ast;
	ast->op.sep = sep_tag;
	left->rest = right->rest;
	left->ast = ast;
	free(right);
	return (left);
}

/*
** \brief        Parse an operation (&&, ||, ;)
** \param input  Input tokens
** \return       Parsed containning an operation AST
**               Parsed error if separator isn't valid
**               or left/right parser failed
*/

t_parsed		*parse_op(t_tok_lst *input)
{
	t_parsed	*left;
	t_parsed	*right;
	enum e_tok	sep_tag;

	if (input == NULL)
		return (NULL);
	if (parsed_check(left = parse_pipeline(input)))
		return (left);
	if ((input = left->rest) == NULL || input->tag & TAG_PARENT_CLOSE)
		return (left);
	if (!((sep_tag = input->tag) & TAG_IS_SEP))
	{
		tok_lst_destroy(&left->rest, free);
		parsed_destroy(left);
		return (parsed_unexpected(g_sep_str_lookup[sep_tag]));
	}
	tok_lst_pop_front(&input, free);
	if (input == NULL && sep_tag == TAG_END)
		return ((left->rest = NULL) ? left : left);
	if (input == NULL)
		return (destroy_ret(left, parsed_expected()));
	right = parse_op(input);
	if (parsed_check(right))
		return (destroy_ret(left, right));
	return (st_parse_op_build(left, right, sep_tag));
}

/*
** \brief        Parse the lexer output into an AST
** \param input  Lexer output, a list of tokens
** \return       Parsed containning the AST to evaluate
**               Parsed error if one of the parser failed
*/

t_parsed		*parse(t_tok_lst *input)
{
	t_parsed	*parsed;
	t_parsed	*ret;

	if (input == NULL)
		return (NULL);
	parsed = parse_op(input);
	if (parsed_check(parsed))
		return (parsed);
	if (parsed->rest != NULL)
	{
		ret = parsed_unexpected(parsed->rest->content);
		tok_lst_destroy(&parsed->rest, free);
		ast_destroy(parsed->ast);
		free(parsed);
		return (ret);
	}
	return (parsed);
}