Skip to content

Commit c736b7e

Browse files
authored
Implement pipeline (#55)
Closes: #37 This pr introduces a new `AstNode::Pipeline` variant. When parsing block, it may run into `pipeline_or_expression_or_assignment` which may return `Pipeline/Expression/Assignment`. And it enables `let` and `mut` using pipelines, so the following is possible: ```nushell let x = 3 | 4 mut y = 5 | 6 $y = 7 | 8 | 9 ``` ## Changes in detail - In `math_expression`, change rhs from `expression` to `pipeline_or_expression`, so `$y = 7 | 8 | 9` is available, it also returns `MathExpressionNode`, so it can be used easier in `pipeline_or_expression_or_assignment`. - In `let_statement`, `mut_statement`, change initializer to `pipeline_or_expression`, so `let x = 7 | 8`, `mut y = 5 | 6` is available - The core logic lays inside `pipeline_or_expression`, in detail, if it get the first expression, and it failed to read a `pipe`, then it returns an Expression node, or else it will consume pipes and return an `Pipeline` node.
1 parent cfaedd6 commit c736b7e

File tree

10 files changed

+229
-29
lines changed

10 files changed

+229
-29
lines changed

src/compiler.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::errors::SourceError;
2-
use crate::parser::{AstNode, Block, NodeId};
2+
use crate::parser::{AstNode, Block, NodeId, Pipeline};
33
use crate::protocol::Command;
44
use crate::resolver::{DeclId, Frame, NameBindings, ScopeId, VarId, Variable};
55
use crate::typechecker::{TypeId, Types};
@@ -44,7 +44,8 @@ pub struct Compiler {
4444
pub ast_nodes: Vec<AstNode>,
4545
pub node_types: Vec<TypeId>,
4646
// node_lifetimes: Vec<AllocationLifetime>,
47-
pub blocks: Vec<Block>, // Blocks, indexed by BlockId
47+
pub blocks: Vec<Block>, // Blocks, indexed by BlockId
48+
pub pipelines: Vec<Pipeline>, // Pipelines, indexed by PipelineId
4849
pub source: Vec<u8>,
4950
pub file_offsets: Vec<(String, usize, usize)>, // fname, start, end
5051

@@ -87,6 +88,7 @@ impl Compiler {
8788
ast_nodes: vec![],
8889
node_types: vec![],
8990
blocks: vec![],
91+
pipelines: vec![],
9092
source: vec![],
9193
file_offsets: vec![],
9294

src/parser.rs

Lines changed: 100 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ pub struct NodeId(pub usize);
1515
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1616
pub struct BlockId(pub usize);
1717

18+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19+
pub struct PipelineId(pub usize);
20+
1821
#[derive(Debug, Clone)]
1922
pub struct Block {
2023
pub nodes: Vec<NodeId>,
@@ -26,6 +29,32 @@ impl Block {
2629
}
2730
}
2831

32+
// Pipeline just contains a list of expressions
33+
//
34+
// It's not allowed if there is only one element in pipeline, in that
35+
// case, it's just an expression.
36+
//
37+
// Making such restriction can reduce indirect access on expression, which
38+
// can improve performance in parse time.
39+
#[derive(Debug, Clone, PartialEq)]
40+
pub struct Pipeline {
41+
pub nodes: Vec<NodeId>,
42+
}
43+
44+
impl Pipeline {
45+
pub fn new(nodes: Vec<NodeId>) -> Self {
46+
debug_assert!(
47+
nodes.len() > 1,
48+
"a pipeline must contain at least 2 nodes, or else it's actually an expression"
49+
);
50+
Self { nodes }
51+
}
52+
53+
pub fn get_expressions(&self) -> &Vec<NodeId> {
54+
&self.nodes
55+
}
56+
}
57+
2958
#[derive(Debug, Clone, PartialEq)]
3059
pub enum BlockContext {
3160
/// This block is a whole block of code not wrapped in curlies (e.g., a file)
@@ -54,6 +83,19 @@ pub enum BarewordContext {
5483
Call,
5584
}
5685

86+
enum AssignmentOrExpression {
87+
Assignment(NodeId),
88+
Expression(NodeId),
89+
}
90+
91+
impl AssignmentOrExpression {
92+
fn get_node_id(&self) -> NodeId {
93+
match self {
94+
AssignmentOrExpression::Assignment(i) | AssignmentOrExpression::Expression(i) => *i,
95+
}
96+
}
97+
}
98+
5799
// TODO: All nodes with Vec<...> should be moved to their own ID (like BlockId) to allow Copy trait
58100
#[derive(Debug, PartialEq, Clone)]
59101
pub enum AstNode {
@@ -195,6 +237,7 @@ pub enum AstNode {
195237
field: NodeId,
196238
},
197239
Block(BlockId),
240+
Pipeline(PipelineId),
198241
If {
199242
condition: NodeId,
200243
then_block: NodeId,
@@ -260,17 +303,57 @@ impl Parser {
260303
self.compiler
261304
}
262305

263-
pub fn expression_or_assignment(&mut self) -> NodeId {
306+
pub fn expression(&mut self) -> NodeId {
264307
let _span = span!();
265-
self.math_expression(true)
308+
self.math_expression(false).get_node_id()
266309
}
267310

268-
pub fn expression(&mut self) -> NodeId {
311+
fn pipeline(&mut self, first_element: NodeId, span_start: usize) -> NodeId {
312+
let mut expressions = vec![first_element];
313+
while self.is_pipe() {
314+
self.pipe();
315+
// maybe a new time
316+
if self.is_newline() {
317+
self.tokens.advance()
318+
}
319+
expressions.push(self.expression());
320+
}
321+
self.compiler.pipelines.push(Pipeline::new(expressions));
322+
let span_end = self.position();
323+
self.create_node(
324+
AstNode::Pipeline(PipelineId(self.compiler.pipelines.len() - 1)),
325+
span_start,
326+
span_end,
327+
)
328+
}
329+
pub fn pipeline_or_expression_or_assignment(&mut self) -> NodeId {
330+
// get the first expression
269331
let _span = span!();
270-
self.math_expression(false)
332+
let span_start = self.position();
333+
let first = self.math_expression(true);
334+
let first_id = first.get_node_id();
335+
if let AssignmentOrExpression::Assignment(_) = &first {
336+
return first_id;
337+
}
338+
// pipeline with one element is an expression actually
339+
if !self.is_pipe() {
340+
return first_id;
341+
}
342+
self.pipeline(first_id, span_start)
271343
}
272344

273-
pub fn math_expression(&mut self, allow_assignment: bool) -> NodeId {
345+
pub fn pipeline_or_expression(&mut self) -> NodeId {
346+
let _span = span!();
347+
let span_start = self.position();
348+
let first_id = self.expression();
349+
// pipeline with one element is an expression actually.
350+
if !self.is_pipe() {
351+
return first_id;
352+
}
353+
self.pipeline(first_id, span_start)
354+
}
355+
356+
fn math_expression(&mut self, allow_assignment: bool) -> AssignmentOrExpression {
274357
let _span = span!();
275358
let mut expr_stack = Vec::<(NodeId, NodeId)>::new();
276359

@@ -280,9 +363,9 @@ impl Parser {
280363

281364
// Check for special forms
282365
if self.is_keyword(b"if") {
283-
return self.if_expression();
366+
return AssignmentOrExpression::Expression(self.if_expression());
284367
} else if self.is_keyword(b"match") {
285-
return self.match_expression();
368+
return AssignmentOrExpression::Expression(self.match_expression());
286369
}
287370
// TODO
288371
// } else if self.is_keyword(b"where") {
@@ -297,18 +380,18 @@ impl Parser {
297380
}
298381
let op = self.operator();
299382

300-
let rhs = self.expression();
383+
let rhs = self.pipeline_or_expression();
301384
let span_end = self.get_span_end(rhs);
302385

303-
return self.create_node(
386+
return AssignmentOrExpression::Assignment(self.create_node(
304387
AstNode::BinaryOp {
305388
lhs: leftmost,
306389
op,
307390
rhs,
308391
},
309392
span_start,
310393
span_end,
311-
);
394+
));
312395
}
313396

314397
while self.has_tokens() {
@@ -379,7 +462,7 @@ impl Parser {
379462
);
380463
}
381464

382-
leftmost
465+
AssignmentOrExpression::Expression(leftmost)
383466
}
384467

385468
pub fn simple_expression(&mut self, bareword_context: BarewordContext) -> NodeId {
@@ -1119,7 +1202,7 @@ impl Parser {
11191202

11201203
self.equals();
11211204

1122-
let initializer = self.expression();
1205+
let initializer = self.pipeline_or_expression();
11231206

11241207
let span_end = self.get_span_end(initializer);
11251208

@@ -1156,7 +1239,7 @@ impl Parser {
11561239

11571240
self.equals();
11581241

1159-
let initializer = self.expression();
1242+
let initializer = self.pipeline_or_expression();
11601243

11611244
let span_end = self.get_span_end(initializer);
11621245

@@ -1225,19 +1308,19 @@ impl Parser {
12251308
code_body.push(self.alias_statement());
12261309
} else {
12271310
let exp_span_start = self.position();
1228-
let expression = self.expression_or_assignment();
1229-
let exp_span_end = self.get_span_end(expression);
1311+
let pipeline = self.pipeline_or_expression_or_assignment();
1312+
let exp_span_end = self.get_span_end(pipeline);
12301313

12311314
if self.is_semicolon() {
12321315
// This is a statement, not an expression
12331316
self.tokens.advance();
12341317
code_body.push(self.create_node(
1235-
AstNode::Statement(expression),
1318+
AstNode::Statement(pipeline),
12361319
exp_span_start,
12371320
exp_span_end,
12381321
))
12391322
} else {
1240-
code_body.push(expression);
1323+
code_body.push(pipeline);
12411324
}
12421325
}
12431326
}

src/resolver.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::protocol::{Command, Declaration};
22
use crate::{
33
compiler::Compiler,
44
errors::{Severity, SourceError},
5-
parser::{AstNode, BlockId, NodeId},
5+
parser::{AstNode, BlockId, NodeId, PipelineId},
66
};
77
use std::collections::HashMap;
88

@@ -338,6 +338,7 @@ impl<'a> Resolver<'a> {
338338
}
339339
}
340340
AstNode::Statement(node) => self.resolve_node(node),
341+
AstNode::Pipeline(pipeline_id) => self.resolve_pipeline(pipeline_id),
341342
AstNode::Param { .. } => (/* seems unused for now */),
342343
AstNode::Type { .. } => ( /* probably doesn't make sense to resolve? */ ),
343344
AstNode::NamedValue { .. } => (/* seems unused for now */),
@@ -346,6 +347,14 @@ impl<'a> Resolver<'a> {
346347
}
347348
}
348349

350+
pub fn resolve_pipeline(&mut self, pipeline_id: PipelineId) {
351+
let pipeline = &self.compiler.pipelines[pipeline_id.0];
352+
353+
for exp in pipeline.get_expressions() {
354+
self.resolve_node(*exp)
355+
}
356+
}
357+
349358
pub fn resolve_variable(&mut self, unbound_node_id: NodeId) {
350359
let var_name = trim_var_name(self.compiler.get_span_contents(unbound_node_id));
351360

src/snapshots/new_nu_parser__test__node_output@let_.nu.snap

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,31 @@ snapshot_kind: text
99
1: Int (8 to 11) "123"
1010
2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: false } (0 to 11)
1111
3: Variable (13 to 15) "$x"
12-
4: Block(BlockId(0)) (0 to 15)
12+
4: Variable (21 to 22) "x"
13+
5: Int (25 to 28) "123"
14+
6: Int (31 to 34) "456"
15+
7: Pipeline(PipelineId(0)) (25 to 34)
16+
8: Let { variable_name: NodeId(4), ty: None, initializer: NodeId(7), is_mutable: false } (17 to 34)
17+
9: Variable (36 to 38) "$x"
18+
10: Block(BlockId(0)) (0 to 39)
1319
==== SCOPE ====
14-
0: Frame Scope, node_id: NodeId(4)
15-
variables: [ x: NodeId(0) ]
20+
0: Frame Scope, node_id: NodeId(10)
21+
variables: [ x: NodeId(4) ]
1622
==== TYPES ====
1723
0: int
1824
1: int
1925
2: ()
2026
3: int
2127
4: int
28+
5: int
29+
6: int
30+
7: int
31+
8: ()
32+
9: int
33+
10: int
2234
==== IR ====
2335
register_count: 0
2436
file_count: 0
2537
==== IR ERRORS ====
2638
Error (NodeId 2): node Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: false } not suported yet
39+

src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
source: src/test.rs
33
expression: evaluate_example(path)
44
input_file: tests/mut_.nu
5+
snapshot_kind: text
56
---
67
==== COMPILER ====
78
0: Variable (4 to 5) "x"
@@ -16,10 +17,24 @@ input_file: tests/mut_.nu
1617
9: Int (27 to 30) "456"
1718
10: BinaryOp { lhs: NodeId(7), op: NodeId(8), rhs: NodeId(9) } (23 to 30)
1819
11: BinaryOp { lhs: NodeId(5), op: NodeId(6), rhs: NodeId(10) } (18 to 30)
19-
12: Block(BlockId(0)) (0 to 30)
20+
12: Variable (36 to 37) "y"
21+
13: Name (39 to 42) "int"
22+
14: Type { name: NodeId(13), args: None, optional: false } (39 to 42)
23+
15: Int (45 to 48) "123"
24+
16: Int (51 to 54) "344"
25+
17: Pipeline(PipelineId(0)) (45 to 54)
26+
18: Let { variable_name: NodeId(12), ty: Some(NodeId(14)), initializer: NodeId(17), is_mutable: true } (32 to 54)
27+
19: Variable (56 to 58) "$y"
28+
20: Assignment (59 to 60)
29+
21: Int (61 to 62) "3"
30+
22: Plus (63 to 64)
31+
23: Int (65 to 66) "1"
32+
24: BinaryOp { lhs: NodeId(21), op: NodeId(22), rhs: NodeId(23) } (61 to 66)
33+
25: BinaryOp { lhs: NodeId(19), op: NodeId(20), rhs: NodeId(24) } (56 to 66)
34+
26: Block(BlockId(0)) (0 to 68)
2035
==== SCOPE ====
21-
0: Frame Scope, node_id: NodeId(12)
22-
variables: [ x: NodeId(0) ]
36+
0: Frame Scope, node_id: NodeId(26)
37+
variables: [ x: NodeId(0), y: NodeId(12) ]
2338
==== TYPES ====
2439
0: int
2540
1: unknown
@@ -33,9 +48,24 @@ input_file: tests/mut_.nu
3348
9: int
3449
10: int
3550
11: ()
36-
12: ()
51+
12: int
52+
13: unknown
53+
14: int
54+
15: int
55+
16: int
56+
17: int
57+
18: ()
58+
19: int
59+
20: forbidden
60+
21: int
61+
22: forbidden
62+
23: int
63+
24: int
64+
25: ()
65+
26: ()
3766
==== IR ====
3867
register_count: 0
3968
file_count: 0
4069
==== IR ERRORS ====
4170
Error (NodeId 4): node Let { variable_name: NodeId(0), ty: Some(NodeId(2)), initializer: NodeId(3), is_mutable: true } not suported yet
71+

0 commit comments

Comments
 (0)