1use log::trace;
2use nu_protocol::{ParseError, Span, SyntaxShape, Type, ast::*, engine::StateWorkingSet};
3use std::sync::Arc;
4
5use crate::{
6 Token,
7 lite_parser::{LiteCommand, LitePipeline, LiteRedirection, LiteRedirectionTarget, lite_parse},
8 parse_keywords::parse_def_predecl,
9 parser::{
10 parse_builtin_commands, parse_expression, parse_value, wrap_element_with_collect,
11 wrap_expr_with_collect,
12 },
13 type_check,
14};
15
16fn parse_redirection_target(
17 working_set: &mut StateWorkingSet,
18 target: &LiteRedirectionTarget,
19) -> RedirectionTarget {
20 match target {
21 LiteRedirectionTarget::File {
22 connector,
23 file,
24 append,
25 } => RedirectionTarget::File {
26 expr: parse_value(working_set, *file, &SyntaxShape::Any, None),
27 append: *append,
28 span: *connector,
29 },
30 LiteRedirectionTarget::Pipe { connector } => RedirectionTarget::Pipe { span: *connector },
31 }
32}
33
34pub(crate) fn parse_redirection(
35 working_set: &mut StateWorkingSet,
36 target: &LiteRedirection,
37) -> PipelineRedirection {
38 match target {
39 LiteRedirection::Single { source, target } => PipelineRedirection::Single {
40 source: *source,
41 target: parse_redirection_target(working_set, target),
42 },
43 LiteRedirection::Separate { out, err } => PipelineRedirection::Separate {
44 out: parse_redirection_target(working_set, out),
45 err: parse_redirection_target(working_set, err),
46 },
47 }
48}
49
50pub(crate) fn parse_pipeline_element(
51 working_set: &mut StateWorkingSet,
52 command: &LiteCommand,
53 input_type: &Type,
54) -> PipelineElement {
55 trace!("parsing: pipeline element");
56
57 let expr = parse_expression(working_set, &command.parts, Some(input_type));
58
59 let redirection = command
60 .redirection
61 .as_ref()
62 .map(|r| parse_redirection(working_set, r));
63
64 PipelineElement {
65 pipe: command.pipe,
66 expr,
67 redirection,
68 }
69}
70
71pub(crate) fn redirecting_builtin_error(
72 name: &'static str,
73 redirection: &LiteRedirection,
74) -> ParseError {
75 match redirection {
76 LiteRedirection::Single { target, .. } => {
77 ParseError::RedirectingBuiltinCommand(name, target.connector(), None)
78 }
79 LiteRedirection::Separate { out, err } => ParseError::RedirectingBuiltinCommand(
80 name,
81 out.connector().min(err.connector()),
82 Some(out.connector().max(err.connector())),
83 ),
84 }
85}
86
87pub fn parse_pipeline(
88 working_set: &mut StateWorkingSet,
89 pipeline: &LitePipeline,
90 input_type: Option<&Type>,
91) -> Pipeline {
92 match pipeline.commands.as_slice() {
93 [] => unreachable!("at this point the pipeline must have at least one element"),
94 [single] => parse_builtin_commands(working_set, single, input_type),
95 [first, rest @ ..] => {
96 let mut current_pipeline_type = input_type.cloned().unwrap_or(Type::Any);
97
98 let mut elements = Vec::new();
99 elements.push({
100 let element = parse_pipeline_element(working_set, first, ¤t_pipeline_type);
101 current_pipeline_type = element.expr.ty.clone();
103
104 element
105 });
106
107 let rest_elements = rest.iter().map(|element| {
109 let input_clone = current_pipeline_type.clone();
110 let element = parse_pipeline_element(working_set, element, ¤t_pipeline_type);
111 current_pipeline_type = element.expr.ty.clone();
113
114 if element.has_in_variable(working_set) {
116 wrap_element_with_collect(working_set, element, Some(&input_clone))
117 } else {
118 element
119 }
120 });
121
122 elements.extend(rest_elements);
123
124 Pipeline { elements }
125 }
126 }
127}
128
129pub fn parse_block(
130 working_set: &mut StateWorkingSet,
131 tokens: &[Token],
132 span: Span,
133 scoped: bool,
134 is_subexpression: bool,
135 input_type: Option<&Type>,
136) -> Block {
137 let (lite_block, err) = lite_parse(tokens, working_set);
138 if let Some(err) = err {
139 working_set.error(err);
140 }
141
142 trace!("parsing block: {lite_block:?}");
143
144 if scoped {
145 working_set.enter_scope();
146 }
147
148 for pipeline in &lite_block.block {
151 if let [lite_command] = pipeline.commands.as_slice() {
152 parse_def_predecl(working_set, lite_command.command_parts())
153 }
154 }
155
156 let mut block = Block::new_with_capacity(lite_block.block.len());
157 block.span = Some(span);
158
159 if let [first, rest @ ..] = lite_block.block.as_slice() {
160 let pipeline = parse_pipeline(working_set, first, input_type);
162 block.pipelines.push(pipeline);
163
164 for lite_pipeline in rest {
165 let pipeline = parse_pipeline(working_set, lite_pipeline, None);
166 block.pipelines.push(pipeline);
167 }
168 }
169
170 if !is_subexpression
173 && block
174 .pipelines
175 .iter()
176 .flat_map(|pipeline| pipeline.elements.first())
177 .any(|element| element.has_in_variable(working_set))
178 {
179 let inner_block = std::mem::take(&mut block);
181 block.span = inner_block.span;
182 let ty = inner_block.output_type();
183 let block_id = working_set.add_block(Arc::new(inner_block));
184
185 let subexpression = Expression::new(working_set, Expr::Subexpression(block_id), span, ty);
187 let collect = wrap_expr_with_collect(working_set, subexpression, input_type);
188
189 block.pipelines.push(Pipeline {
190 elements: vec![PipelineElement {
191 pipe: None,
192 expr: collect,
193 redirection: None,
194 }],
195 });
196 }
197
198 if scoped {
199 working_set.exit_scope();
200 }
201
202 let errors = type_check::check_block_input_output(working_set, &block);
203 working_set.parse_errors.extend(errors);
204
205 block
206}