From 915aa2e7bf8224c967eef7a3bc583c09757c8cd7 Mon Sep 17 00:00:00 2001 From: WindSoilder Date: Thu, 3 Apr 2025 17:52:05 +0800 Subject: [PATCH 01/12] WIP --- src/parser.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/parser.rs b/src/parser.rs index 58d8f8e..d9c9501 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -152,6 +152,7 @@ pub enum AstNode { field: NodeId, }, Block(BlockId), + Pipeline(Vec), If { condition: NodeId, then_block: NodeId, @@ -292,6 +293,16 @@ impl Parser { self.math_expression(false) } + pub fn pipeline(&mut self) -> NodeId { + let span_start = self.position(); + let expressions = vec![self.expression()]; + while self.is_pipe() { + self.pipe(); + expressions.push(self.expression()); + } + let span_end = self.position(); + } + pub fn math_expression(&mut self, allow_assignment: bool) -> NodeId { let mut expr_stack = Vec::<(NodeId, NodeId)>::new(); From 0bb23fb51c6c2cd7c2352ce990afaeb1371496d7 Mon Sep 17 00:00:00 2001 From: WindSoilder Date: Tue, 8 Apr 2025 17:08:57 +0800 Subject: [PATCH 02/12] implement a new pipeline type --- src/parser.rs | 64 ++++++++++++++++++++++++++++++++++++--------------- tests/let_.nu | 6 ++++- tests/mut_.nu | 6 ++++- 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index fc7b85e..cd00ccc 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -54,6 +54,19 @@ pub enum BarewordContext { Call, } +enum MathExpressionNode { + Assignment(NodeId), + Expression(NodeId), +} + +impl MathExpressionNode { + fn get_node_id(&self) -> NodeId { + match self { + MathExpressionNode::Assignment(i) | MathExpressionNode::Expression(i) => *i, + } + } +} + // TODO: All nodes with Vec<...> should be moved to their own ID (like BlockId) to allow Copy trait #[derive(Debug, PartialEq, Clone)] pub enum AstNode { @@ -261,27 +274,42 @@ impl Parser { self.compiler } - pub fn expression_or_assignment(&mut self) -> NodeId { + pub fn expression(&mut self) -> NodeId { let _span = span!(); - self.math_expression(true) + self.math_expression(false).get_node_id() } - pub fn expression(&mut self) -> NodeId { + pub fn pipeline_or_assignment(&mut self) -> NodeId { + // get the first expression let _span = span!(); - self.math_expression(false) + let span_start = self.position(); + let first = self.math_expression(true); + let first_id = first.get_node_id(); + if let MathExpressionNode::Assignment(_) = &first { + return first_id; + } + + let mut expressions = vec![first_id]; + while self.is_pipe() { + self.pipe(); + expressions.push(self.expression()); + } + let span_end = self.position(); + self.create_node(AstNode::Pipeline(expressions), span_start, span_end) } pub fn pipeline(&mut self) -> NodeId { let span_start = self.position(); - let expressions = vec![self.expression()]; + let mut expressions = vec![self.expression()]; while self.is_pipe() { self.pipe(); expressions.push(self.expression()); } let span_end = self.position(); + self.create_node(AstNode::Pipeline(expressions), span_start, span_end) } - pub fn math_expression(&mut self, allow_assignment: bool) -> NodeId { + fn math_expression(&mut self, allow_assignment: bool) -> MathExpressionNode { let _span = span!(); let mut expr_stack = Vec::<(NodeId, NodeId)>::new(); @@ -291,9 +319,9 @@ impl Parser { // Check for special forms if self.is_keyword(b"if") { - return self.if_expression(); + return MathExpressionNode::Expression(self.if_expression()); } else if self.is_keyword(b"match") { - return self.match_expression(); + return MathExpressionNode::Expression(self.match_expression()); } // TODO // } else if self.is_keyword(b"where") { @@ -308,10 +336,10 @@ impl Parser { } let op = self.operator(); - let rhs = self.expression(); + let rhs = self.pipeline(); let span_end = self.get_span_end(rhs); - return self.create_node( + return MathExpressionNode::Assignment(self.create_node( AstNode::BinaryOp { lhs: leftmost, op, @@ -319,7 +347,7 @@ impl Parser { }, span_start, span_end, - ); + )); } while self.has_tokens() { @@ -390,7 +418,7 @@ impl Parser { ); } - leftmost + MathExpressionNode::Expression(leftmost) } pub fn simple_expression(&mut self, bareword_context: BarewordContext) -> NodeId { @@ -1130,7 +1158,7 @@ impl Parser { self.equals(); - let initializer = self.expression(); + let initializer = self.pipeline(); let span_end = self.get_span_end(initializer); @@ -1167,7 +1195,7 @@ impl Parser { self.equals(); - let initializer = self.expression(); + let initializer = self.pipeline(); let span_end = self.get_span_end(initializer); @@ -1236,19 +1264,19 @@ impl Parser { code_body.push(self.alias_statement()); } else { let exp_span_start = self.position(); - let expression = self.expression_or_assignment(); - let exp_span_end = self.get_span_end(expression); + let pipeline = self.pipeline_or_assignment(); + let exp_span_end = self.get_span_end(pipeline); if self.is_semicolon() { // This is a statement, not an expression self.tokens.advance(); code_body.push(self.create_node( - AstNode::Statement(expression), + AstNode::Statement(pipeline), exp_span_start, exp_span_end, )) } else { - code_body.push(expression); + code_body.push(pipeline); } } } diff --git a/tests/let_.nu b/tests/let_.nu index 0984227..0187a94 100644 --- a/tests/let_.nu +++ b/tests/let_.nu @@ -1,3 +1,7 @@ let x = 123 -$x \ No newline at end of file +$x + +let x = 123 | 456 + +$x diff --git a/tests/mut_.nu b/tests/mut_.nu index fc3a260..000ce73 100644 --- a/tests/mut_.nu +++ b/tests/mut_.nu @@ -1,3 +1,7 @@ mut x: int = 123 -$x = 3 + 456 \ No newline at end of file +$x = 3 + 456 + +mut y: int = 123 | 344 + +$y = 3 + 1 From d0996e22d6fb631ebf08f61fbb7ee62add3b8c20 Mon Sep 17 00:00:00 2001 From: WindSoilder Date: Wed, 9 Apr 2025 11:12:50 +0800 Subject: [PATCH 03/12] first impl pipeline --- src/ir_generator.rs | 8 ++++ src/parser.rs | 28 +++++++++++++- src/resolver.rs | 5 +++ ..._nu_parser__test__node_output@let_.nu.snap | 18 +++++++-- ..._nu_parser__test__node_output@mut_.nu.snap | 37 +++++++++++++++++-- src/typechecker.rs | 13 +++++++ 6 files changed, 100 insertions(+), 9 deletions(-) diff --git a/src/ir_generator.rs b/src/ir_generator.rs index 74a61f4..4c867b9 100644 --- a/src/ir_generator.rs +++ b/src/ir_generator.rs @@ -97,6 +97,14 @@ impl<'a> IrGenerator<'a> { fn generate_node(&mut self, node_id: NodeId) -> Option { let ast_node = &self.compiler.ast_nodes[node_id.0]; match ast_node { + AstNode::Pipeline(expressions) => { + if expressions.len() == 1 { + self.generate_node(expressions[0]) + } else { + self.error(format!("node {:?} not suported yet", ast_node), node_id); + None + } + } AstNode::Int => { let next_reg = self.next_register(); let val = self.compiler.node_as_i64(node_id); diff --git a/src/parser.rs b/src/parser.rs index cd00ccc..02e63cc 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -26,6 +26,21 @@ impl Block { } } +#[derive(Debug, Clone)] +pub struct Pipeline { + pub nodes: Vec, +} + +impl Pipeline { + pub fn new(nodes: Vec) -> Self { + debug_assert!( + nodes.len() > 1, + "a pipeline must contain at least 2 nodes, or else it's actually an expression" + ); + Self { nodes } + } +} + #[derive(Debug, Clone, PartialEq)] pub enum BlockContext { /// This block is a whole block of code not wrapped in curlies (e.g., a file) @@ -208,7 +223,7 @@ pub enum AstNode { field: NodeId, }, Block(BlockId), - Pipeline(Vec), + Pipeline(Pipeline), If { condition: NodeId, then_block: NodeId, @@ -288,6 +303,10 @@ impl Parser { if let MathExpressionNode::Assignment(_) = &first { return first_id; } + // pipeline with one element is an expression actually + if !self.is_pipe() { + return first_id; + } let mut expressions = vec![first_id]; while self.is_pipe() { @@ -300,7 +319,12 @@ impl Parser { pub fn pipeline(&mut self) -> NodeId { let span_start = self.position(); - let mut expressions = vec![self.expression()]; + let first_id = self.expression(); + // pipeline with one element is an expression actually. + if !self.is_pipe() { + return first_id; + } + let mut expressions = vec![first_id]; while self.is_pipe() { self.pipe(); expressions.push(self.expression()); diff --git a/src/resolver.rs b/src/resolver.rs index bbdb7ca..02693b9 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -338,6 +338,11 @@ impl<'a> Resolver<'a> { } } AstNode::Statement(node) => self.resolve_node(node), + AstNode::Pipeline(ref expressions) => { + for exp in expressions { + self.resolve_node(*exp) + } + } AstNode::Param { .. } => (/* seems unused for now */), AstNode::Type { .. } => ( /* probably doesn't make sense to resolve? */ ), AstNode::NamedValue { .. } => (/* seems unused for now */), diff --git a/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap b/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap index c151562..4e4d792 100644 --- a/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap @@ -9,16 +9,28 @@ snapshot_kind: text 1: Int (8 to 11) "123" 2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: false } (0 to 11) 3: Variable (13 to 15) "$x" -4: Block(BlockId(0)) (0 to 15) +4: Variable (21 to 22) "x" +5: Int (25 to 28) "123" +6: Int (31 to 34) "456" +7: Pipeline([NodeId(5), NodeId(6)]) (25 to 34) +8: Let { variable_name: NodeId(4), ty: None, initializer: NodeId(7), is_mutable: false } (17 to 34) +9: Variable (36 to 38) "$x" +10: Block(BlockId(0)) (0 to 39) ==== SCOPE ==== -0: Frame Scope, node_id: NodeId(4) - variables: [ x: NodeId(0) ] +0: Frame Scope, node_id: NodeId(10) + variables: [ x: NodeId(4) ] ==== TYPES ==== 0: int 1: int 2: () 3: int 4: int +5: int +6: int +7: int +8: () +9: int +10: int ==== IR ==== register_count: 0 file_count: 0 diff --git a/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap b/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap index a366991..3af9cd4 100644 --- a/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap @@ -2,6 +2,7 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/mut_.nu +snapshot_kind: text --- ==== COMPILER ==== 0: Variable (4 to 5) "x" @@ -16,10 +17,24 @@ input_file: tests/mut_.nu 9: Int (27 to 30) "456" 10: BinaryOp { lhs: NodeId(7), op: NodeId(8), rhs: NodeId(9) } (23 to 30) 11: BinaryOp { lhs: NodeId(5), op: NodeId(6), rhs: NodeId(10) } (18 to 30) -12: Block(BlockId(0)) (0 to 30) +12: Variable (36 to 37) "y" +13: Name (39 to 42) "int" +14: Type { name: NodeId(13), args: None, optional: false } (39 to 42) +15: Int (45 to 48) "123" +16: Int (51 to 54) "344" +17: Pipeline([NodeId(15), NodeId(16)]) (45 to 54) +18: Let { variable_name: NodeId(12), ty: Some(NodeId(14)), initializer: NodeId(17), is_mutable: true } (32 to 54) +19: Variable (56 to 58) "$y" +20: Assignment (59 to 60) +21: Int (61 to 62) "3" +22: Plus (63 to 64) +23: Int (65 to 66) "1" +24: BinaryOp { lhs: NodeId(21), op: NodeId(22), rhs: NodeId(23) } (61 to 66) +25: BinaryOp { lhs: NodeId(19), op: NodeId(20), rhs: NodeId(24) } (56 to 66) +26: Block(BlockId(0)) (0 to 68) ==== SCOPE ==== -0: Frame Scope, node_id: NodeId(12) - variables: [ x: NodeId(0) ] +0: Frame Scope, node_id: NodeId(26) + variables: [ x: NodeId(0), y: NodeId(12) ] ==== TYPES ==== 0: int 1: unknown @@ -33,7 +48,21 @@ input_file: tests/mut_.nu 9: int 10: int 11: () -12: () +12: int +13: unknown +14: int +15: int +16: int +17: int +18: () +19: int +20: forbidden +21: int +22: forbidden +23: int +24: int +25: () +26: () ==== IR ==== register_count: 0 file_count: 0 diff --git a/src/typechecker.rs b/src/typechecker.rs index 71077e8..f9c6a37 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -338,6 +338,19 @@ impl<'a> Typechecker<'a> { self.set_node_type_id(node_id, block_type); } + AstNode::Pipeline(ref elements) => { + for inner in elements { + self.typecheck_node(*inner) + } + + // pipeline type is the type of the last statement, since blocks + // by themselves aren't supposed to be typed + let pipeline_type = elements + .last() + .map_or(NONE_TYPE, |node_id| self.type_id_of(*node_id)); + + self.set_node_type_id(node_id, pipeline_type); + } AstNode::Closure { params, block } => { // TODO: input/output types if let Some(params_node_id) = params { From 9e9d93d16c83a641dbfd24aed48ee5636a16d8b2 Mon Sep 17 00:00:00 2001 From: WindSoilder Date: Wed, 9 Apr 2025 11:22:02 +0800 Subject: [PATCH 04/12] fix --- src/ir_generator.rs | 8 ----- src/parser.rs | 31 +++++++++++++------ src/resolver.rs | 4 +-- ..._nu_parser__test__node_output@let_.nu.snap | 2 +- ..._nu_parser__test__node_output@mut_.nu.snap | 2 +- ...parser__test__node_output@pipeline.nu.snap | 25 +++++++++++++++ src/typechecker.rs | 9 +++--- tests/pipeline.nu | 1 + 8 files changed, 57 insertions(+), 25 deletions(-) create mode 100644 src/snapshots/new_nu_parser__test__node_output@pipeline.nu.snap create mode 100644 tests/pipeline.nu diff --git a/src/ir_generator.rs b/src/ir_generator.rs index 4c867b9..74a61f4 100644 --- a/src/ir_generator.rs +++ b/src/ir_generator.rs @@ -97,14 +97,6 @@ impl<'a> IrGenerator<'a> { fn generate_node(&mut self, node_id: NodeId) -> Option { let ast_node = &self.compiler.ast_nodes[node_id.0]; match ast_node { - AstNode::Pipeline(expressions) => { - if expressions.len() == 1 { - self.generate_node(expressions[0]) - } else { - self.error(format!("node {:?} not suported yet", ast_node), node_id); - None - } - } AstNode::Int => { let next_reg = self.next_register(); let val = self.compiler.node_as_i64(node_id); diff --git a/src/parser.rs b/src/parser.rs index 02e63cc..263b378 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -26,7 +26,7 @@ impl Block { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Pipeline { pub nodes: Vec, } @@ -39,6 +39,10 @@ impl Pipeline { ); Self { nodes } } + + pub fn get_expressions(&self) -> &Vec { + &self.nodes + } } #[derive(Debug, Clone, PartialEq)] @@ -294,7 +298,7 @@ impl Parser { self.math_expression(false).get_node_id() } - pub fn pipeline_or_assignment(&mut self) -> NodeId { + pub fn pipeline_or_expression_or_assignment(&mut self) -> NodeId { // get the first expression let _span = span!(); let span_start = self.position(); @@ -314,10 +318,15 @@ impl Parser { expressions.push(self.expression()); } let span_end = self.position(); - self.create_node(AstNode::Pipeline(expressions), span_start, span_end) + self.create_node( + AstNode::Pipeline(Pipeline::new(expressions)), + span_start, + span_end, + ) } - pub fn pipeline(&mut self) -> NodeId { + pub fn pipeline_or_expression(&mut self) -> NodeId { + let _span = span!(); let span_start = self.position(); let first_id = self.expression(); // pipeline with one element is an expression actually. @@ -330,7 +339,11 @@ impl Parser { expressions.push(self.expression()); } let span_end = self.position(); - self.create_node(AstNode::Pipeline(expressions), span_start, span_end) + self.create_node( + AstNode::Pipeline(Pipeline::new(expressions)), + span_start, + span_end, + ) } fn math_expression(&mut self, allow_assignment: bool) -> MathExpressionNode { @@ -360,7 +373,7 @@ impl Parser { } let op = self.operator(); - let rhs = self.pipeline(); + let rhs = self.pipeline_or_expression(); let span_end = self.get_span_end(rhs); return MathExpressionNode::Assignment(self.create_node( @@ -1182,7 +1195,7 @@ impl Parser { self.equals(); - let initializer = self.pipeline(); + let initializer = self.pipeline_or_expression(); let span_end = self.get_span_end(initializer); @@ -1219,7 +1232,7 @@ impl Parser { self.equals(); - let initializer = self.pipeline(); + let initializer = self.pipeline_or_expression(); let span_end = self.get_span_end(initializer); @@ -1288,7 +1301,7 @@ impl Parser { code_body.push(self.alias_statement()); } else { let exp_span_start = self.position(); - let pipeline = self.pipeline_or_assignment(); + let pipeline = self.pipeline_or_expression_or_assignment(); let exp_span_end = self.get_span_end(pipeline); if self.is_semicolon() { diff --git a/src/resolver.rs b/src/resolver.rs index 02693b9..13e09de 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -338,8 +338,8 @@ impl<'a> Resolver<'a> { } } AstNode::Statement(node) => self.resolve_node(node), - AstNode::Pipeline(ref expressions) => { - for exp in expressions { + AstNode::Pipeline(ref pipeline) => { + for exp in pipeline.get_expressions() { self.resolve_node(*exp) } } diff --git a/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap b/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap index 4e4d792..7200281 100644 --- a/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap @@ -12,7 +12,7 @@ snapshot_kind: text 4: Variable (21 to 22) "x" 5: Int (25 to 28) "123" 6: Int (31 to 34) "456" -7: Pipeline([NodeId(5), NodeId(6)]) (25 to 34) +7: Pipeline(Pipeline { nodes: [NodeId(5), NodeId(6)] }) (25 to 34) 8: Let { variable_name: NodeId(4), ty: None, initializer: NodeId(7), is_mutable: false } (17 to 34) 9: Variable (36 to 38) "$x" 10: Block(BlockId(0)) (0 to 39) diff --git a/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap b/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap index 3af9cd4..c56c92f 100644 --- a/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap @@ -22,7 +22,7 @@ snapshot_kind: text 14: Type { name: NodeId(13), args: None, optional: false } (39 to 42) 15: Int (45 to 48) "123" 16: Int (51 to 54) "344" -17: Pipeline([NodeId(15), NodeId(16)]) (45 to 54) +17: Pipeline(Pipeline { nodes: [NodeId(15), NodeId(16)] }) (45 to 54) 18: Let { variable_name: NodeId(12), ty: Some(NodeId(14)), initializer: NodeId(17), is_mutable: true } (32 to 54) 19: Variable (56 to 58) "$y" 20: Assignment (59 to 60) diff --git a/src/snapshots/new_nu_parser__test__node_output@pipeline.nu.snap b/src/snapshots/new_nu_parser__test__node_output@pipeline.nu.snap new file mode 100644 index 0000000..5e4db79 --- /dev/null +++ b/src/snapshots/new_nu_parser__test__node_output@pipeline.nu.snap @@ -0,0 +1,25 @@ +--- +source: src/test.rs +expression: evaluate_example(path) +input_file: tests/pipeline.nu +snapshot_kind: text +--- +==== COMPILER ==== +0: Int (0 to 1) "1" +1: Int (4 to 5) "3" +2: Int (8 to 9) "5" +3: Pipeline(Pipeline { nodes: [NodeId(0), NodeId(1), NodeId(2)] }) (0 to 9) +4: Block(BlockId(0)) (0 to 10) +==== SCOPE ==== +0: Frame Scope, node_id: NodeId(4) (empty) +==== TYPES ==== +0: int +1: int +2: int +3: int +4: int +==== IR ==== +register_count: 0 +file_count: 0 +==== IR ERRORS ==== +Error (NodeId 3): node Pipeline(Pipeline { nodes: [NodeId(0), NodeId(1), NodeId(2)] }) not suported yet diff --git a/src/typechecker.rs b/src/typechecker.rs index f9c6a37..45125e5 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -338,14 +338,15 @@ impl<'a> Typechecker<'a> { self.set_node_type_id(node_id, block_type); } - AstNode::Pipeline(ref elements) => { - for inner in elements { + AstNode::Pipeline(ref pipeline) => { + let expressions = pipeline.get_expressions(); + for inner in expressions { self.typecheck_node(*inner) } - // pipeline type is the type of the last statement, since blocks + // pipeline type is the type of the last expression, since blocks // by themselves aren't supposed to be typed - let pipeline_type = elements + let pipeline_type = expressions .last() .map_or(NONE_TYPE, |node_id| self.type_id_of(*node_id)); diff --git a/tests/pipeline.nu b/tests/pipeline.nu new file mode 100644 index 0000000..0baafe3 --- /dev/null +++ b/tests/pipeline.nu @@ -0,0 +1 @@ +1 | 3 | 5 From 7dce4a16b658e0808ffc90e95bb97c921df56623 Mon Sep 17 00:00:00 2001 From: WindSoilder Date: Wed, 9 Apr 2025 12:56:19 +0800 Subject: [PATCH 05/12] add support for pipeline with newline --- src/parser.rs | 42 +++++++++---------- ...parser__test__node_output@pipeline.nu.snap | 12 +++++- tests/pipeline.nu | 5 +++ 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 263b378..d65076e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -298,6 +298,23 @@ impl Parser { self.math_expression(false).get_node_id() } + fn pipeline(&mut self, first_element: NodeId, span_start: usize) -> NodeId { + let mut expressions = vec![first_element]; + while self.is_pipe() { + self.pipe(); + // maybe a new time + if self.is_newline() { + self.tokens.advance() + } + expressions.push(self.expression()); + } + let span_end = self.position(); + self.create_node( + AstNode::Pipeline(Pipeline::new(expressions)), + span_start, + span_end, + ) + } pub fn pipeline_or_expression_or_assignment(&mut self) -> NodeId { // get the first expression let _span = span!(); @@ -311,18 +328,7 @@ impl Parser { if !self.is_pipe() { return first_id; } - - let mut expressions = vec![first_id]; - while self.is_pipe() { - self.pipe(); - expressions.push(self.expression()); - } - let span_end = self.position(); - self.create_node( - AstNode::Pipeline(Pipeline::new(expressions)), - span_start, - span_end, - ) + self.pipeline(first_id, span_start) } pub fn pipeline_or_expression(&mut self) -> NodeId { @@ -333,17 +339,7 @@ impl Parser { if !self.is_pipe() { return first_id; } - let mut expressions = vec![first_id]; - while self.is_pipe() { - self.pipe(); - expressions.push(self.expression()); - } - let span_end = self.position(); - self.create_node( - AstNode::Pipeline(Pipeline::new(expressions)), - span_start, - span_end, - ) + self.pipeline(first_id, span_start) } fn math_expression(&mut self, allow_assignment: bool) -> MathExpressionNode { diff --git a/src/snapshots/new_nu_parser__test__node_output@pipeline.nu.snap b/src/snapshots/new_nu_parser__test__node_output@pipeline.nu.snap index 5e4db79..bc81e0a 100644 --- a/src/snapshots/new_nu_parser__test__node_output@pipeline.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@pipeline.nu.snap @@ -9,15 +9,23 @@ snapshot_kind: text 1: Int (4 to 5) "3" 2: Int (8 to 9) "5" 3: Pipeline(Pipeline { nodes: [NodeId(0), NodeId(1), NodeId(2)] }) (0 to 9) -4: Block(BlockId(0)) (0 to 10) +4: Int (42 to 43) "1" +5: Int (46 to 47) "2" +6: Int (50 to 51) "3" +7: Pipeline(Pipeline { nodes: [NodeId(4), NodeId(5), NodeId(6)] }) (42 to 51) +8: Block(BlockId(0)) (0 to 52) ==== SCOPE ==== -0: Frame Scope, node_id: NodeId(4) (empty) +0: Frame Scope, node_id: NodeId(8) (empty) ==== TYPES ==== 0: int 1: int 2: int 3: int 4: int +5: int +6: int +7: int +8: int ==== IR ==== register_count: 0 file_count: 0 diff --git a/tests/pipeline.nu b/tests/pipeline.nu index 0baafe3..120eb46 100644 --- a/tests/pipeline.nu +++ b/tests/pipeline.nu @@ -1 +1,6 @@ 1 | 3 | 5 + +# test pipeline with multiline +1 | +2 | +3 From 23b031c6a65d74a0f701bff72eb69835ae22d751 Mon Sep 17 00:00:00 2001 From: WindSoilder Date: Wed, 9 Apr 2025 12:59:22 +0800 Subject: [PATCH 06/12] add comment --- src/parser.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/parser.rs b/src/parser.rs index d65076e..f27c7d9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -26,6 +26,13 @@ impl Block { } } +// Pipeline just contains a list of expressions +// +// It's not allowed if there is only one element in pipeline, in that +// case, it's just an expression. +// +// Making such restriction can reduce indirect access on expression, which +// can improve performance in parse time. #[derive(Debug, Clone, PartialEq)] pub struct Pipeline { pub nodes: Vec, From ccebd5c82f5e32a6bbdd963f96ea956437ce8e89 Mon Sep 17 00:00:00 2001 From: WindSoilder Date: Wed, 16 Apr 2025 07:11:48 +0800 Subject: [PATCH 07/12] rename --- src/parser.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index f27c7d9..befc82d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -80,15 +80,15 @@ pub enum BarewordContext { Call, } -enum MathExpressionNode { +enum AssignmentOrExpression { Assignment(NodeId), Expression(NodeId), } -impl MathExpressionNode { +impl AssignmentOrExpression { fn get_node_id(&self) -> NodeId { match self { - MathExpressionNode::Assignment(i) | MathExpressionNode::Expression(i) => *i, + AssignmentOrExpression::Assignment(i) | AssignmentOrExpression::Expression(i) => *i, } } } @@ -328,7 +328,7 @@ impl Parser { let span_start = self.position(); let first = self.math_expression(true); let first_id = first.get_node_id(); - if let MathExpressionNode::Assignment(_) = &first { + if let AssignmentOrExpression::Assignment(_) = &first { return first_id; } // pipeline with one element is an expression actually @@ -349,7 +349,7 @@ impl Parser { self.pipeline(first_id, span_start) } - fn math_expression(&mut self, allow_assignment: bool) -> MathExpressionNode { + fn math_expression(&mut self, allow_assignment: bool) -> AssignmentOrExpression { let _span = span!(); let mut expr_stack = Vec::<(NodeId, NodeId)>::new(); @@ -359,9 +359,9 @@ impl Parser { // Check for special forms if self.is_keyword(b"if") { - return MathExpressionNode::Expression(self.if_expression()); + return AssignmentOrExpression::Expression(self.if_expression()); } else if self.is_keyword(b"match") { - return MathExpressionNode::Expression(self.match_expression()); + return AssignmentOrExpression::Expression(self.match_expression()); } // TODO // } else if self.is_keyword(b"where") { @@ -379,7 +379,7 @@ impl Parser { let rhs = self.pipeline_or_expression(); let span_end = self.get_span_end(rhs); - return MathExpressionNode::Assignment(self.create_node( + return AssignmentOrExpression::Assignment(self.create_node( AstNode::BinaryOp { lhs: leftmost, op, @@ -458,7 +458,7 @@ impl Parser { ); } - MathExpressionNode::Expression(leftmost) + AssignmentOrExpression::Expression(leftmost) } pub fn simple_expression(&mut self, bareword_context: BarewordContext) -> NodeId { From c9a2cf269616ac8bd2756b95b414acfadb94f442 Mon Sep 17 00:00:00 2001 From: WindSoilder Date: Wed, 16 Apr 2025 07:31:21 +0800 Subject: [PATCH 08/12] put pipeline into compiler --- src/compiler.rs | 6 ++++-- src/parser.rs | 8 ++++++-- src/resolver.rs | 16 ++++++++++------ ...new_nu_parser__test__node_output@let_.nu.snap | 3 ++- ...new_nu_parser__test__node_output@mut_.nu.snap | 3 ++- ...nu_parser__test__node_output@pipeline.nu.snap | 7 ++++--- src/typechecker.rs | 3 ++- 7 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index 98b6c3e..45f42a2 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,5 +1,5 @@ use crate::errors::SourceError; -use crate::parser::{AstNode, Block, NodeId}; +use crate::parser::{AstNode, Block, NodeId, Pipeline}; use crate::protocol::Command; use crate::resolver::{DeclId, Frame, NameBindings, ScopeId, VarId, Variable}; use crate::typechecker::{TypeId, Types}; @@ -44,7 +44,8 @@ pub struct Compiler { pub ast_nodes: Vec, pub node_types: Vec, // node_lifetimes: Vec, - pub blocks: Vec, // Blocks, indexed by BlockId + pub blocks: Vec, // Blocks, indexed by BlockId + pub pipelines: Vec, // Pipelines, indexed by PipelineId pub source: Vec, pub file_offsets: Vec<(String, usize, usize)>, // fname, start, end @@ -87,6 +88,7 @@ impl Compiler { ast_nodes: vec![], node_types: vec![], blocks: vec![], + pipelines: vec![], source: vec![], file_offsets: vec![], diff --git a/src/parser.rs b/src/parser.rs index befc82d..8349171 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -15,6 +15,9 @@ pub struct NodeId(pub usize); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct BlockId(pub usize); +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct PipelineId(pub usize); + #[derive(Debug, Clone)] pub struct Block { pub nodes: Vec, @@ -234,7 +237,7 @@ pub enum AstNode { field: NodeId, }, Block(BlockId), - Pipeline(Pipeline), + Pipeline(PipelineId), If { condition: NodeId, then_block: NodeId, @@ -315,9 +318,10 @@ impl Parser { } expressions.push(self.expression()); } + self.compiler.pipelines.push(Pipeline::new(expressions)); let span_end = self.position(); self.create_node( - AstNode::Pipeline(Pipeline::new(expressions)), + AstNode::Pipeline(PipelineId(self.compiler.pipelines.len() - 1)), span_start, span_end, ) diff --git a/src/resolver.rs b/src/resolver.rs index 13e09de..d0f5319 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -2,7 +2,7 @@ use crate::protocol::{Command, Declaration}; use crate::{ compiler::Compiler, errors::{Severity, SourceError}, - parser::{AstNode, BlockId, NodeId}, + parser::{AstNode, BlockId, NodeId, PipelineId}, }; use std::collections::HashMap; @@ -338,11 +338,7 @@ impl<'a> Resolver<'a> { } } AstNode::Statement(node) => self.resolve_node(node), - AstNode::Pipeline(ref pipeline) => { - for exp in pipeline.get_expressions() { - self.resolve_node(*exp) - } - } + AstNode::Pipeline(pipeline_id) => self.resolve_pipeline(pipeline_id), AstNode::Param { .. } => (/* seems unused for now */), AstNode::Type { .. } => ( /* probably doesn't make sense to resolve? */ ), AstNode::NamedValue { .. } => (/* seems unused for now */), @@ -351,6 +347,14 @@ impl<'a> Resolver<'a> { } } + pub fn resolve_pipeline(&mut self, pipeline_id: PipelineId) { + let pipeline = &self.compiler.pipelines[pipeline_id.0]; + + for exp in pipeline.get_expressions() { + self.resolve_node(*exp) + } + } + pub fn resolve_variable(&mut self, unbound_node_id: NodeId) { let var_name = trim_var_name(self.compiler.get_span_contents(unbound_node_id)); diff --git a/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap b/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap index 7200281..5ace565 100644 --- a/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap @@ -12,7 +12,7 @@ snapshot_kind: text 4: Variable (21 to 22) "x" 5: Int (25 to 28) "123" 6: Int (31 to 34) "456" -7: Pipeline(Pipeline { nodes: [NodeId(5), NodeId(6)] }) (25 to 34) +7: Pipeline(PipelineId(0)) (25 to 34) 8: Let { variable_name: NodeId(4), ty: None, initializer: NodeId(7), is_mutable: false } (17 to 34) 9: Variable (36 to 38) "$x" 10: Block(BlockId(0)) (0 to 39) @@ -36,3 +36,4 @@ register_count: 0 file_count: 0 ==== IR ERRORS ==== Error (NodeId 2): node Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: false } not suported yet + diff --git a/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap b/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap index c56c92f..208c8dd 100644 --- a/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap @@ -22,7 +22,7 @@ snapshot_kind: text 14: Type { name: NodeId(13), args: None, optional: false } (39 to 42) 15: Int (45 to 48) "123" 16: Int (51 to 54) "344" -17: Pipeline(Pipeline { nodes: [NodeId(15), NodeId(16)] }) (45 to 54) +17: Pipeline(PipelineId(0)) (45 to 54) 18: Let { variable_name: NodeId(12), ty: Some(NodeId(14)), initializer: NodeId(17), is_mutable: true } (32 to 54) 19: Variable (56 to 58) "$y" 20: Assignment (59 to 60) @@ -68,3 +68,4 @@ register_count: 0 file_count: 0 ==== IR ERRORS ==== Error (NodeId 4): node Let { variable_name: NodeId(0), ty: Some(NodeId(2)), initializer: NodeId(3), is_mutable: true } not suported yet + diff --git a/src/snapshots/new_nu_parser__test__node_output@pipeline.nu.snap b/src/snapshots/new_nu_parser__test__node_output@pipeline.nu.snap index bc81e0a..c45a045 100644 --- a/src/snapshots/new_nu_parser__test__node_output@pipeline.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@pipeline.nu.snap @@ -8,11 +8,11 @@ snapshot_kind: text 0: Int (0 to 1) "1" 1: Int (4 to 5) "3" 2: Int (8 to 9) "5" -3: Pipeline(Pipeline { nodes: [NodeId(0), NodeId(1), NodeId(2)] }) (0 to 9) +3: Pipeline(PipelineId(0)) (0 to 9) 4: Int (42 to 43) "1" 5: Int (46 to 47) "2" 6: Int (50 to 51) "3" -7: Pipeline(Pipeline { nodes: [NodeId(4), NodeId(5), NodeId(6)] }) (42 to 51) +7: Pipeline(PipelineId(1)) (42 to 51) 8: Block(BlockId(0)) (0 to 52) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(8) (empty) @@ -30,4 +30,5 @@ snapshot_kind: text register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 3): node Pipeline(Pipeline { nodes: [NodeId(0), NodeId(1), NodeId(2)] }) not suported yet +Error (NodeId 3): node Pipeline(PipelineId(0)) not suported yet + diff --git a/src/typechecker.rs b/src/typechecker.rs index 45125e5..5f52cec 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -338,7 +338,8 @@ impl<'a> Typechecker<'a> { self.set_node_type_id(node_id, block_type); } - AstNode::Pipeline(ref pipeline) => { + AstNode::Pipeline(pipeline_id) => { + let pipeline = &self.compiler.pipelines[pipeline_id.0]; let expressions = pipeline.get_expressions(); for inner in expressions { self.typecheck_node(*inner) From 372842ad1590892417f1a6543d7534a48b0932df Mon Sep 17 00:00:00 2001 From: WindSoilder Date: Wed, 16 Apr 2025 07:38:01 +0800 Subject: [PATCH 09/12] use ubuntu 22.04 --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c00a096..7a8e2d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,10 +16,10 @@ jobs: strategy: fail-fast: true matrix: - # Pinning to Ubuntu 20.04 because building on newer Ubuntu versions causes linux-gnu + # Pinning to Ubuntu 22.04 because building on newer Ubuntu versions causes linux-gnu # builds to link against a too-new-for-many-Linux-installs glibc version. Consider - # revisiting this when 20.04 is closer to EOL (April 2025) - platform: [macos-latest, ubuntu-20.04] + # revisiting this when 22.04 is closer to EOL (June 2027) + platform: [macos-latest, ubuntu-22.04] feature: [default] include: - feature: default From baece09c730af4c2f6ee830a6936049bd5295990 Mon Sep 17 00:00:00 2001 From: WindSoilder Date: Wed, 16 Apr 2025 07:38:46 +0800 Subject: [PATCH 10/12] use ubuntu 22.04 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a8e2d6..5693aac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: strategy: fail-fast: true matrix: - platform: [macos-latest, ubuntu-20.04] + platform: [macos-latest, ubuntu-22.04] feature: [default] include: - feature: default From c9353c35949a7e2600db1b2031e2610f0d61a860 Mon Sep 17 00:00:00 2001 From: WindSoilder Date: Fri, 18 Apr 2025 06:53:45 +0800 Subject: [PATCH 11/12] Revert "use ubuntu 22.04" This reverts commit baece09c730af4c2f6ee830a6936049bd5295990. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5693aac..7a8e2d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: strategy: fail-fast: true matrix: - platform: [macos-latest, ubuntu-22.04] + platform: [macos-latest, ubuntu-20.04] feature: [default] include: - feature: default From 51002f0afed621d7df9701936862942ed52c2f98 Mon Sep 17 00:00:00 2001 From: WindSoilder Date: Fri, 18 Apr 2025 06:53:46 +0800 Subject: [PATCH 12/12] Revert "use ubuntu 22.04" This reverts commit 372842ad1590892417f1a6543d7534a48b0932df. --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a8e2d6..c00a096 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,10 +16,10 @@ jobs: strategy: fail-fast: true matrix: - # Pinning to Ubuntu 22.04 because building on newer Ubuntu versions causes linux-gnu + # Pinning to Ubuntu 20.04 because building on newer Ubuntu versions causes linux-gnu # builds to link against a too-new-for-many-Linux-installs glibc version. Consider - # revisiting this when 22.04 is closer to EOL (June 2027) - platform: [macos-latest, ubuntu-22.04] + # revisiting this when 20.04 is closer to EOL (April 2025) + platform: [macos-latest, ubuntu-20.04] feature: [default] include: - feature: default