Skip to content

Commit 6ffae75

Browse files
authored
Merge branch 'main' into complete-and-easy
2 parents 08f75c3 + c736b7e commit 6ffae75

File tree

11 files changed

+233
-33
lines changed

11 files changed

+233
-33
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ jobs:
1616
strategy:
1717
fail-fast: true
1818
matrix:
19-
# Pinning to Ubuntu 20.04 because building on newer Ubuntu versions causes linux-gnu
19+
# Pinning to Ubuntu 24.04 because building on newer Ubuntu versions causes linux-gnu
2020
# builds to link against a too-new-for-many-Linux-installs glibc version. Consider
21-
# revisiting this when 20.04 is closer to EOL (April 2025)
22-
platform: [macos-latest, ubuntu-20.04]
21+
# revisiting this when 24.04 is closer to EOL
22+
platform: [macos-latest, ubuntu-24.04]
2323
feature: [default]
2424
include:
2525
- feature: default
@@ -49,7 +49,7 @@ jobs:
4949
strategy:
5050
fail-fast: true
5151
matrix:
52-
platform: [macos-latest, ubuntu-20.04]
52+
platform: [macos-latest, ubuntu-24.04]
5353
feature: [default]
5454
include:
5555
- feature: default

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, TypeDecl, TypeDeclId, 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

@@ -92,6 +93,7 @@ impl Compiler {
9293
ast_nodes: vec![],
9394
node_types: vec![],
9495
blocks: vec![],
96+
pipelines: vec![],
9597
source: vec![],
9698
file_offsets: vec![],
9799

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 {
@@ -196,6 +238,7 @@ pub enum AstNode {
196238
field: NodeId,
197239
},
198240
Block(BlockId),
241+
Pipeline(PipelineId),
199242
If {
200243
condition: NodeId,
201244
then_block: NodeId,
@@ -261,17 +304,57 @@ impl Parser {
261304
self.compiler
262305
}
263306

264-
pub fn expression_or_assignment(&mut self) -> NodeId {
307+
pub fn expression(&mut self) -> NodeId {
265308
let _span = span!();
266-
self.math_expression(true)
309+
self.math_expression(false).get_node_id()
267310
}
268311

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

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

@@ -281,9 +364,9 @@ impl Parser {
281364

282365
// Check for special forms
283366
if self.is_keyword(b"if") {
284-
return self.if_expression();
367+
return AssignmentOrExpression::Expression(self.if_expression());
285368
} else if self.is_keyword(b"match") {
286-
return self.match_expression();
369+
return AssignmentOrExpression::Expression(self.match_expression());
287370
}
288371
// TODO
289372
// } else if self.is_keyword(b"where") {
@@ -298,18 +381,18 @@ impl Parser {
298381
}
299382
let op = self.operator();
300383

301-
let rhs = self.expression();
384+
let rhs = self.pipeline_or_expression();
302385
let span_end = self.get_span_end(rhs);
303386

304-
return self.create_node(
387+
return AssignmentOrExpression::Assignment(self.create_node(
305388
AstNode::BinaryOp {
306389
lhs: leftmost,
307390
op,
308391
rhs,
309392
},
310393
span_start,
311394
span_end,
312-
);
395+
));
313396
}
314397

315398
while self.has_tokens() {
@@ -380,7 +463,7 @@ impl Parser {
380463
);
381464
}
382465

383-
leftmost
466+
AssignmentOrExpression::Expression(leftmost)
384467
}
385468

386469
pub fn simple_expression(&mut self, bareword_context: BarewordContext) -> NodeId {
@@ -1153,7 +1236,7 @@ impl Parser {
11531236

11541237
self.equals();
11551238

1156-
let initializer = self.expression();
1239+
let initializer = self.pipeline_or_expression();
11571240

11581241
let span_end = self.get_span_end(initializer);
11591242

@@ -1190,7 +1273,7 @@ impl Parser {
11901273

11911274
self.equals();
11921275

1193-
let initializer = self.expression();
1276+
let initializer = self.pipeline_or_expression();
11941277

11951278
let span_end = self.get_span_end(initializer);
11961279

@@ -1259,19 +1342,19 @@ impl Parser {
12591342
code_body.push(self.alias_statement());
12601343
} else {
12611344
let exp_span_start = self.position();
1262-
let expression = self.expression_or_assignment();
1263-
let exp_span_end = self.get_span_end(expression);
1345+
let pipeline = self.pipeline_or_expression_or_assignment();
1346+
let exp_span_end = self.get_span_end(pipeline);
12641347

12651348
if self.is_semicolon() {
12661349
// This is a statement, not an expression
12671350
self.tokens.advance();
12681351
code_body.push(self.create_node(
1269-
AstNode::Statement(expression),
1352+
AstNode::Statement(pipeline),
12701353
exp_span_start,
12711354
exp_span_end,
12721355
))
12731356
} else {
1274-
code_body.push(expression);
1357+
code_body.push(pipeline);
12751358
}
12761359
}
12771360
}

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

@@ -427,13 +427,22 @@ impl<'a> Resolver<'a> {
427427
self.resolve_node(in_ty);
428428
self.resolve_node(out_ty);
429429
}
430+
AstNode::Pipeline(pipeline_id) => self.resolve_pipeline(pipeline_id),
430431
AstNode::Param { .. } => (/* seems unused for now */),
431432
AstNode::NamedValue { .. } => (/* seems unused for now */),
432433
// All remaining matches do not contain NodeId => there is nothing to resolve
433434
_ => (),
434435
}
435436
}
436437

438+
pub fn resolve_pipeline(&mut self, pipeline_id: PipelineId) {
439+
let pipeline = &self.compiler.pipelines[pipeline_id.0];
440+
441+
for exp in pipeline.get_expressions() {
442+
self.resolve_node(*exp)
443+
}
444+
}
445+
437446
pub fn resolve_variable(&mut self, unbound_node_id: NodeId) {
438447
let var_name = trim_var_name(self.compiler.get_span_contents(unbound_node_id));
439448

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)