Skip to content

Commit 2984e62

Browse files
authored
Add some operators (#45)
As part of #21, this PR adds binary operators that are either symbols or single barewords. I couldn't figure out an easy way to handle operators that span multiple tokens, such as `starts-with`. I also didn't add `not`. I threw in some code for `++=` but no tests. I assume we'll get to that once we implement typechecking for assignment oeprators, which this PR doesn't do.
1 parent 8255e9e commit 2984e62

9 files changed

+138
-29
lines changed

src/lexer.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@ pub enum Token {
330330
GreaterThanEqual,
331331
#[token(">")]
332332
GreaterThan,
333+
#[token("++=")]
334+
PlusPlusEquals,
333335
#[token("++")]
334336
PlusPlus,
335337
#[token("+=")]

src/parser.rs

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,29 +74,34 @@ pub enum AstNode {
7474
Null,
7575

7676
// Operators
77+
Pow,
78+
Multiply,
79+
Divide,
80+
FloorDiv,
81+
Modulo,
82+
Plus,
83+
Minus,
7784
Equal,
7885
NotEqual,
7986
LessThan,
8087
GreaterThan,
8188
LessThanOrEqual,
8289
GreaterThanOrEqual,
83-
Plus,
90+
RegexMatch,
91+
NotRegexMatch,
92+
In,
8493
Append,
85-
Minus,
86-
Multiply,
87-
Divide,
88-
// Modulo,
8994
And,
95+
Xor,
9096
Or,
91-
Pow,
9297

9398
// Assignments
9499
Assignment,
95100
AddAssignment,
96101
SubtractAssignment,
97102
MultiplyAssignment,
98103
DivideAssignment,
99-
// TODO: append assignment ++=
104+
AppendAssignment,
100105

101106
// Statements
102107
Let {
@@ -198,22 +203,27 @@ impl AstNode {
198203
pub fn precedence(&self) -> usize {
199204
match self {
200205
AstNode::Pow => 100,
201-
AstNode::Multiply | AstNode::Divide => 95,
202-
//AstNode::Modulo => 95,
206+
AstNode::Multiply | AstNode::Divide | AstNode::FloorDiv | AstNode::Modulo => 95,
203207
AstNode::Plus | AstNode::Minus => 90,
204208
AstNode::LessThan
205209
| AstNode::LessThanOrEqual
206210
| AstNode::GreaterThan
207211
| AstNode::GreaterThanOrEqual
208212
| AstNode::Equal
209-
| AstNode::NotEqual => 80,
213+
| AstNode::NotEqual
214+
| AstNode::RegexMatch
215+
| AstNode::NotRegexMatch
216+
| AstNode::In
217+
| AstNode::Append => 80,
210218
AstNode::And => 50,
219+
AstNode::Xor => 45,
211220
AstNode::Or => 40,
212221
AstNode::Assignment
213222
| AstNode::AddAssignment
214223
| AstNode::SubtractAssignment
215224
| AstNode::MultiplyAssignment
216-
| AstNode::DivideAssignment => ASSIGNMENT_PRECEDENCE,
225+
| AstNode::DivideAssignment
226+
| AstNode::AppendAssignment => ASSIGNMENT_PRECEDENCE,
217227
_ => 0,
218228
}
219229
}
@@ -671,20 +681,27 @@ impl Parser {
671681
Token::Dash => self.advance_node(AstNode::Minus, span),
672682
Token::Asterisk => self.advance_node(AstNode::Multiply, span),
673683
Token::ForwardSlash => self.advance_node(AstNode::Divide, span),
684+
Token::ForwardSlashForwardSlash => self.advance_node(AstNode::FloorDiv, span),
674685
Token::LessThan => self.advance_node(AstNode::LessThan, span),
675686
Token::LessThanEqual => self.advance_node(AstNode::LessThanOrEqual, span),
676687
Token::GreaterThan => self.advance_node(AstNode::GreaterThan, span),
677688
Token::GreaterThanEqual => self.advance_node(AstNode::GreaterThanOrEqual, span),
678689
Token::EqualsEquals => self.advance_node(AstNode::Equal, span),
679690
Token::ExclamationEquals => self.advance_node(AstNode::NotEqual, span),
691+
Token::EqualsTilde => self.advance_node(AstNode::RegexMatch, span),
692+
Token::ExclamationTilde => self.advance_node(AstNode::NotRegexMatch, span),
680693
Token::AsteriskAsterisk => self.advance_node(AstNode::Pow, span),
681694
Token::Equals => self.advance_node(AstNode::Assignment, span),
682695
Token::PlusEquals => self.advance_node(AstNode::AddAssignment, span),
683696
Token::DashEquals => self.advance_node(AstNode::SubtractAssignment, span),
684697
Token::AsteriskEquals => self.advance_node(AstNode::MultiplyAssignment, span),
685698
Token::ForwardSlashEquals => self.advance_node(AstNode::DivideAssignment, span),
699+
Token::PlusPlusEquals => self.advance_node(AstNode::AppendAssignment, span),
686700
Token::Bareword => match self.compiler.get_span_contents_manual(span.start, span.end) {
701+
b"mod" => self.advance_node(AstNode::Modulo, span),
702+
b"in" => self.advance_node(AstNode::In, span),
687703
b"and" => self.advance_node(AstNode::And, span),
704+
b"xor" => self.advance_node(AstNode::Xor, span),
688705
b"or" => self.advance_node(AstNode::Or, span),
689706
op => self.error(format!(
690707
"Unknown operator: '{}'",
@@ -1271,21 +1288,25 @@ impl Parser {
12711288
| Token::Dash
12721289
| Token::Asterisk
12731290
| Token::ForwardSlash
1291+
| Token::ForwardSlashForwardSlash
12741292
| Token::LessThan
12751293
| Token::LessThanEqual
12761294
| Token::GreaterThan
12771295
| Token::GreaterThanEqual
12781296
| Token::EqualsEquals
12791297
| Token::ExclamationEquals
1298+
| Token::EqualsTilde
1299+
| Token::ExclamationTilde
12801300
| Token::AsteriskAsterisk
12811301
| Token::Equals
12821302
| Token::PlusEquals
12831303
| Token::DashEquals
12841304
| Token::AsteriskEquals
1285-
| Token::ForwardSlashEquals => true,
1305+
| Token::ForwardSlashEquals
1306+
| Token::PlusPlusEquals => true,
12861307
Token::Bareword => {
12871308
let op = self.compiler.get_span_contents_manual(span.start, span.end);
1288-
op == b"and" || op == b"or"
1309+
op == b"mod" || op == b"in" || op == b"and" || op == b"xor" || op == b"or"
12891310
}
12901311
_ => false,
12911312
}

src/snapshots/new_nu_parser__test__node_output@binary_ops_exact.nu.snap

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
source: src/test.rs
33
expression: evaluate_example(path)
44
input_file: tests/binary_ops_exact.nu
5-
snapshot_kind: text
65
---
76
==== COMPILER ====
87
0: Int (0 to 1) "1"
@@ -26,9 +25,19 @@ snapshot_kind: text
2625
18: And (44 to 47)
2726
19: False (48 to 53)
2827
20: BinaryOp { lhs: NodeId(17), op: NodeId(18), rhs: NodeId(19) } (39 to 53)
29-
21: Block(BlockId(0)) (0 to 54)
28+
21: String (54 to 59) ""foo""
29+
22: RegexMatch (60 to 62)
30+
23: String (63 to 68) "".*o""
31+
24: BinaryOp { lhs: NodeId(21), op: NodeId(22), rhs: NodeId(23) } (54 to 68)
32+
25: Int (69 to 70) "1"
33+
26: In (71 to 73)
34+
27: Int (75 to 76) "1"
35+
28: Int (78 to 79) "2"
36+
29: List([NodeId(27), NodeId(28)]) (74 to 79)
37+
30: BinaryOp { lhs: NodeId(25), op: NodeId(26), rhs: NodeId(29) } (69 to 79)
38+
31: Block(BlockId(0)) (0 to 81)
3039
==== SCOPE ====
31-
0: Frame Scope, node_id: NodeId(21) (empty)
40+
0: Frame Scope, node_id: NodeId(31) (empty)
3241
==== TYPES ====
3342
0: int
3443
1: forbidden
@@ -51,4 +60,14 @@ snapshot_kind: text
5160
18: forbidden
5261
19: bool
5362
20: bool
54-
21: bool
63+
21: string
64+
22: forbidden
65+
23: string
66+
24: bool
67+
25: int
68+
26: forbidden
69+
27: int
70+
28: int
71+
29: list<int>
72+
30: bool
73+
31: bool

src/snapshots/new_nu_parser__test__node_output@binary_ops_mismatch.nu.snap

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
source: src/test.rs
33
expression: evaluate_example(path)
44
input_file: tests/binary_ops_mismatch.nu
5-
snapshot_kind: text
65
---
76
==== COMPILER ====
87
0: String (0 to 3) ""a""
@@ -17,9 +16,13 @@ snapshot_kind: text
1716
9: And (26 to 29)
1817
10: String (30 to 33) ""a""
1918
11: BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(10) } (21 to 33)
20-
12: Block(BlockId(0)) (0 to 34)
19+
12: True (34 to 38)
20+
13: NotRegexMatch (39 to 41)
21+
14: String (42 to 48) ""true""
22+
15: BinaryOp { lhs: NodeId(12), op: NodeId(13), rhs: NodeId(14) } (34 to 48)
23+
16: Block(BlockId(0)) (0 to 49)
2124
==== SCOPE ====
22-
0: Frame Scope, node_id: NodeId(12) (empty)
25+
0: Frame Scope, node_id: NodeId(16) (empty)
2326
==== TYPES ====
2427
0: string
2528
1: error
@@ -33,8 +36,13 @@ snapshot_kind: text
3336
9: error
3437
10: string
3538
11: error
36-
12: error
39+
12: bool
40+
13: error
41+
14: string
42+
15: error
43+
16: error
3744
==== TYPE ERRORS ====
3845
Error (NodeId 1): type mismatch: unsupported addition between string and float
3946
Error (NodeId 5): type mismatch: unsupported append between string and float
4047
Error (NodeId 9): type mismatch: unsupported logical operation between bool and string
48+
Error (NodeId 13): type mismatch: unsupported string operation between bool and string

src/snapshots/new_nu_parser__test__node_output@binary_ops_subtypes.nu.snap

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
source: src/test.rs
33
expression: evaluate_example(path)
44
input_file: tests/binary_ops_subtypes.nu
5-
snapshot_kind: text
65
---
76
==== COMPILER ====
87
0: Int (0 to 1) "1"
@@ -48,9 +47,19 @@ snapshot_kind: text
4847
40: List([NodeId(39)]) (87 to 91)
4948
41: List([NodeId(40)]) (86 to 92)
5049
42: BinaryOp { lhs: NodeId(37), op: NodeId(38), rhs: NodeId(41) } (73 to 92)
51-
43: Block(BlockId(0)) (0 to 93)
50+
43: Int (94 to 95) "1"
51+
44: In (96 to 98)
52+
45: Float (100 to 103) "1.0"
53+
46: Int (105 to 106) "1"
54+
47: List([NodeId(45), NodeId(46)]) (99 to 106)
55+
48: BinaryOp { lhs: NodeId(43), op: NodeId(44), rhs: NodeId(47) } (94 to 106)
56+
49: Float (108 to 111) "2.3"
57+
50: Modulo (112 to 115)
58+
51: Int (116 to 117) "1"
59+
52: BinaryOp { lhs: NodeId(49), op: NodeId(50), rhs: NodeId(51) } (108 to 117)
60+
53: Block(BlockId(0)) (0 to 118)
5261
==== SCOPE ====
53-
0: Frame Scope, node_id: NodeId(43) (empty)
62+
0: Frame Scope, node_id: NodeId(53) (empty)
5463
==== TYPES ====
5564
0: int
5665
1: forbidden
@@ -95,4 +104,14 @@ snapshot_kind: text
95104
40: list<float>
96105
41: list<list<float>>
97106
42: list<list<number>>
98-
43: list<list<number>>
107+
43: int
108+
44: forbidden
109+
45: float
110+
46: int
111+
47: list<number>
112+
48: bool
113+
49: float
114+
50: forbidden
115+
51: int
116+
52: float
117+
53: float

src/typechecker.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,12 @@ impl<'a> Typechecker<'a> {
515515
Some(Type::Bool)
516516
}
517517
}
518-
AstNode::Minus | AstNode::Multiply | AstNode::Divide | AstNode::Pow => {
518+
AstNode::Minus
519+
| AstNode::Multiply
520+
| AstNode::Divide
521+
| AstNode::FloorDiv
522+
| AstNode::Modulo
523+
| AstNode::Pow => {
519524
let type_id = check_numeric_op(lhs_type, rhs_type);
520525

521526
if type_id == Type::Unknown {
@@ -525,7 +530,36 @@ impl<'a> Typechecker<'a> {
525530
Some(type_id)
526531
}
527532
}
528-
AstNode::And | AstNode::Or => match (lhs_type, rhs_type) {
533+
AstNode::RegexMatch | AstNode::NotRegexMatch => match (lhs_type, rhs_type) {
534+
(Type::String | Type::Any, Type::String | Type::Any) => Some(Type::Bool),
535+
_ => {
536+
self.binary_op_err("string operation", lhs, op, rhs);
537+
None
538+
}
539+
},
540+
AstNode::In => match rhs_type {
541+
Type::String => match lhs_type {
542+
Type::String | Type::Any => Some(Type::Bool),
543+
_ => {
544+
self.binary_op_err("string operation", lhs, op, rhs);
545+
None
546+
}
547+
},
548+
Type::List(elem_ty) => {
549+
if is_type_compatible(lhs_type, self.types[elem_ty.0]) {
550+
Some(Type::Bool)
551+
} else {
552+
self.binary_op_err("list operation", lhs, op, rhs);
553+
None
554+
}
555+
}
556+
Type::Any => Some(Type::Bool),
557+
_ => {
558+
self.binary_op_err("list/string operation", lhs, op, rhs);
559+
None
560+
}
561+
},
562+
AstNode::And | AstNode::Xor | AstNode::Or => match (lhs_type, rhs_type) {
529563
(Type::Bool, Type::Bool) => Some(Type::Bool),
530564
_ => {
531565
self.binary_op_err("logical operation", lhs, op, rhs);
@@ -576,7 +610,8 @@ impl<'a> Typechecker<'a> {
576610
| AstNode::AddAssignment
577611
| AstNode::SubtractAssignment
578612
| AstNode::MultiplyAssignment
579-
| AstNode::DivideAssignment => Some(Type::None),
613+
| AstNode::DivideAssignment
614+
| AstNode::AppendAssignment => Some(Type::None),
580615
_ => panic!("internal error: unsupported node passed as binary op: {op:?}"),
581616
};
582617

tests/binary_ops_exact.nu

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@
33
1 + 1
44
1.0 + 1.0
55
true and false
6+
"foo" =~ ".*o"
7+
1 in [1, 2]

tests/binary_ops_mismatch.nu

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
"a" + 1.0
22
"a" ++ 1.0
33
true and "a"
4+
true !~ "true"

tests/binary_ops_subtypes.nu

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@
44
[1] ++ 1.0
55
[1.0 1] ++ "a"
66
[[1] [2]] ++ [[3]]
7-
[[1] [2]] ++ [[3.0]]
7+
[[1] [2]] ++ [[3.0]]
8+
1 in [1.0, 1]
9+
2.3 mod 1

0 commit comments

Comments
 (0)