Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ pub enum Token {
GreaterThanEqual,
#[token(">")]
GreaterThan,
#[token("++=")]
PlusPlusEquals,
#[token("++")]
PlusPlus,
#[token("+=")]
Expand Down
47 changes: 34 additions & 13 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,29 +74,34 @@ pub enum AstNode {
Null,

// Operators
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rearranged the operators list by precedence (highest to lowest)

Pow,
Multiply,
Divide,
FloorDiv,
Modulo,
Plus,
Minus,
Equal,
NotEqual,
LessThan,
GreaterThan,
LessThanOrEqual,
GreaterThanOrEqual,
Plus,
RegexMatch,
NotRegexMatch,
In,
Append,
Minus,
Multiply,
Divide,
// Modulo,
And,
Xor,
Or,
Pow,

// Assignments
Assignment,
AddAssignment,
SubtractAssignment,
MultiplyAssignment,
DivideAssignment,
// TODO: append assignment ++=
AppendAssignment,

// Statements
Let {
Expand Down Expand Up @@ -198,22 +203,27 @@ impl AstNode {
pub fn precedence(&self) -> usize {
match self {
AstNode::Pow => 100,
AstNode::Multiply | AstNode::Divide => 95,
//AstNode::Modulo => 95,
AstNode::Multiply | AstNode::Divide | AstNode::FloorDiv | AstNode::Modulo => 95,
AstNode::Plus | AstNode::Minus => 90,
AstNode::LessThan
| AstNode::LessThanOrEqual
| AstNode::GreaterThan
| AstNode::GreaterThanOrEqual
| AstNode::Equal
| AstNode::NotEqual => 80,
| AstNode::NotEqual
| AstNode::RegexMatch
| AstNode::NotRegexMatch
| AstNode::In
| AstNode::Append => 80,
AstNode::And => 50,
AstNode::Xor => 45,
AstNode::Or => 40,
AstNode::Assignment
| AstNode::AddAssignment
| AstNode::SubtractAssignment
| AstNode::MultiplyAssignment
| AstNode::DivideAssignment => ASSIGNMENT_PRECEDENCE,
| AstNode::DivideAssignment
| AstNode::AppendAssignment => ASSIGNMENT_PRECEDENCE,
_ => 0,
}
}
Expand Down Expand Up @@ -671,20 +681,27 @@ impl Parser {
Token::Dash => self.advance_node(AstNode::Minus, span),
Token::Asterisk => self.advance_node(AstNode::Multiply, span),
Token::ForwardSlash => self.advance_node(AstNode::Divide, span),
Token::ForwardSlashForwardSlash => self.advance_node(AstNode::FloorDiv, span),
Token::LessThan => self.advance_node(AstNode::LessThan, span),
Token::LessThanEqual => self.advance_node(AstNode::LessThanOrEqual, span),
Token::GreaterThan => self.advance_node(AstNode::GreaterThan, span),
Token::GreaterThanEqual => self.advance_node(AstNode::GreaterThanOrEqual, span),
Token::EqualsEquals => self.advance_node(AstNode::Equal, span),
Token::ExclamationEquals => self.advance_node(AstNode::NotEqual, span),
Token::EqualsTilde => self.advance_node(AstNode::RegexMatch, span),
Token::ExclamationTilde => self.advance_node(AstNode::NotRegexMatch, span),
Token::AsteriskAsterisk => self.advance_node(AstNode::Pow, span),
Token::Equals => self.advance_node(AstNode::Assignment, span),
Token::PlusEquals => self.advance_node(AstNode::AddAssignment, span),
Token::DashEquals => self.advance_node(AstNode::SubtractAssignment, span),
Token::AsteriskEquals => self.advance_node(AstNode::MultiplyAssignment, span),
Token::ForwardSlashEquals => self.advance_node(AstNode::DivideAssignment, span),
Token::PlusPlusEquals => self.advance_node(AstNode::AppendAssignment, span),
Token::Bareword => match self.compiler.get_span_contents_manual(span.start, span.end) {
b"mod" => self.advance_node(AstNode::Modulo, span),
b"in" => self.advance_node(AstNode::In, span),
b"and" => self.advance_node(AstNode::And, span),
b"xor" => self.advance_node(AstNode::Xor, span),
b"or" => self.advance_node(AstNode::Or, span),
op => self.error(format!(
"Unknown operator: '{}'",
Expand Down Expand Up @@ -1271,21 +1288,25 @@ impl Parser {
| Token::Dash
| Token::Asterisk
| Token::ForwardSlash
| Token::ForwardSlashForwardSlash
| Token::LessThan
| Token::LessThanEqual
| Token::GreaterThan
| Token::GreaterThanEqual
| Token::EqualsEquals
| Token::ExclamationEquals
| Token::EqualsTilde
| Token::ExclamationTilde
| Token::AsteriskAsterisk
| Token::Equals
| Token::PlusEquals
| Token::DashEquals
| Token::AsteriskEquals
| Token::ForwardSlashEquals => true,
| Token::ForwardSlashEquals
| Token::PlusPlusEquals => true,
Token::Bareword => {
let op = self.compiler.get_span_contents_manual(span.start, span.end);
op == b"and" || op == b"or"
op == b"mod" || op == b"in" || op == b"and" || op == b"xor" || op == b"or"
}
_ => false,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
source: src/test.rs
expression: evaluate_example(path)
input_file: tests/binary_ops_exact.nu
snapshot_kind: text
---
==== COMPILER ====
0: Int (0 to 1) "1"
Expand All @@ -26,9 +25,19 @@ snapshot_kind: text
18: And (44 to 47)
19: False (48 to 53)
20: BinaryOp { lhs: NodeId(17), op: NodeId(18), rhs: NodeId(19) } (39 to 53)
21: Block(BlockId(0)) (0 to 54)
21: String (54 to 59) ""foo""
22: RegexMatch (60 to 62)
23: String (63 to 68) "".*o""
24: BinaryOp { lhs: NodeId(21), op: NodeId(22), rhs: NodeId(23) } (54 to 68)
25: Int (69 to 70) "1"
26: In (71 to 73)
27: Int (75 to 76) "1"
28: Int (78 to 79) "2"
29: List([NodeId(27), NodeId(28)]) (74 to 79)
30: BinaryOp { lhs: NodeId(25), op: NodeId(26), rhs: NodeId(29) } (69 to 79)
31: Block(BlockId(0)) (0 to 81)
==== SCOPE ====
0: Frame Scope, node_id: NodeId(21) (empty)
0: Frame Scope, node_id: NodeId(31) (empty)
==== TYPES ====
0: int
1: forbidden
Expand All @@ -51,4 +60,14 @@ snapshot_kind: text
18: forbidden
19: bool
20: bool
21: bool
21: string
22: forbidden
23: string
24: bool
25: int
26: forbidden
27: int
28: int
29: list<int>
30: bool
31: bool
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
source: src/test.rs
expression: evaluate_example(path)
input_file: tests/binary_ops_mismatch.nu
snapshot_kind: text
---
==== COMPILER ====
0: String (0 to 3) ""a""
Expand All @@ -17,9 +16,13 @@ snapshot_kind: text
9: And (26 to 29)
10: String (30 to 33) ""a""
11: BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(10) } (21 to 33)
12: Block(BlockId(0)) (0 to 34)
12: True (34 to 38)
13: NotRegexMatch (39 to 41)
14: String (42 to 48) ""true""
15: BinaryOp { lhs: NodeId(12), op: NodeId(13), rhs: NodeId(14) } (34 to 48)
16: Block(BlockId(0)) (0 to 49)
==== SCOPE ====
0: Frame Scope, node_id: NodeId(12) (empty)
0: Frame Scope, node_id: NodeId(16) (empty)
==== TYPES ====
0: string
1: error
Expand All @@ -33,8 +36,13 @@ snapshot_kind: text
9: error
10: string
11: error
12: error
12: bool
13: error
14: string
15: error
16: error
==== TYPE ERRORS ====
Error (NodeId 1): type mismatch: unsupported addition between string and float
Error (NodeId 5): type mismatch: unsupported append between string and float
Error (NodeId 9): type mismatch: unsupported logical operation between bool and string
Error (NodeId 13): type mismatch: unsupported string operation between bool and string
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
source: src/test.rs
expression: evaluate_example(path)
input_file: tests/binary_ops_subtypes.nu
snapshot_kind: text
---
==== COMPILER ====
0: Int (0 to 1) "1"
Expand Down Expand Up @@ -48,9 +47,19 @@ snapshot_kind: text
40: List([NodeId(39)]) (87 to 91)
41: List([NodeId(40)]) (86 to 92)
42: BinaryOp { lhs: NodeId(37), op: NodeId(38), rhs: NodeId(41) } (73 to 92)
43: Block(BlockId(0)) (0 to 93)
43: Int (94 to 95) "1"
44: In (96 to 98)
45: Float (100 to 103) "1.0"
46: Int (105 to 106) "1"
47: List([NodeId(45), NodeId(46)]) (99 to 106)
48: BinaryOp { lhs: NodeId(43), op: NodeId(44), rhs: NodeId(47) } (94 to 106)
49: Float (108 to 111) "2.3"
50: Modulo (112 to 115)
51: Int (116 to 117) "1"
52: BinaryOp { lhs: NodeId(49), op: NodeId(50), rhs: NodeId(51) } (108 to 117)
53: Block(BlockId(0)) (0 to 118)
==== SCOPE ====
0: Frame Scope, node_id: NodeId(43) (empty)
0: Frame Scope, node_id: NodeId(53) (empty)
==== TYPES ====
0: int
1: forbidden
Expand Down Expand Up @@ -95,4 +104,14 @@ snapshot_kind: text
40: list<float>
41: list<list<float>>
42: list<list<number>>
43: list<list<number>>
43: int
44: forbidden
45: float
46: int
47: list<number>
48: bool
49: float
50: forbidden
51: int
52: float
53: float
41 changes: 38 additions & 3 deletions src/typechecker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,12 @@ impl<'a> Typechecker<'a> {
Some(Type::Bool)
}
}
AstNode::Minus | AstNode::Multiply | AstNode::Divide | AstNode::Pow => {
AstNode::Minus
| AstNode::Multiply
| AstNode::Divide
| AstNode::FloorDiv
| AstNode::Modulo
| AstNode::Pow => {
let type_id = check_numeric_op(lhs_type, rhs_type);

if type_id == Type::Unknown {
Expand All @@ -525,7 +530,36 @@ impl<'a> Typechecker<'a> {
Some(type_id)
}
}
AstNode::And | AstNode::Or => match (lhs_type, rhs_type) {
AstNode::RegexMatch | AstNode::NotRegexMatch => match (lhs_type, rhs_type) {
(Type::String | Type::Any, Type::String | Type::Any) => Some(Type::Bool),
_ => {
self.binary_op_err("string operation", lhs, op, rhs);
None
}
},
AstNode::In => match rhs_type {
Type::String => match lhs_type {
Type::String | Type::Any => Some(Type::Bool),
_ => {
self.binary_op_err("string operation", lhs, op, rhs);
None
}
},
Type::List(elem_ty) => {
if is_type_compatible(lhs_type, self.types[elem_ty.0]) {
Some(Type::Bool)
} else {
self.binary_op_err("list operation", lhs, op, rhs);
None
}
}
Type::Any => Some(Type::Bool),
_ => {
self.binary_op_err("list/string operation", lhs, op, rhs);
None
}
},
AstNode::And | AstNode::Xor | AstNode::Or => match (lhs_type, rhs_type) {
(Type::Bool, Type::Bool) => Some(Type::Bool),
_ => {
self.binary_op_err("logical operation", lhs, op, rhs);
Expand Down Expand Up @@ -576,7 +610,8 @@ impl<'a> Typechecker<'a> {
| AstNode::AddAssignment
| AstNode::SubtractAssignment
| AstNode::MultiplyAssignment
| AstNode::DivideAssignment => Some(Type::None),
| AstNode::DivideAssignment
| AstNode::AppendAssignment => Some(Type::None),
_ => panic!("internal error: unsupported node passed as binary op: {op:?}"),
};

Expand Down
2 changes: 2 additions & 0 deletions tests/binary_ops_exact.nu
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
1 + 1
1.0 + 1.0
true and false
"foo" =~ ".*o"
1 in [1, 2]
1 change: 1 addition & 0 deletions tests/binary_ops_mismatch.nu
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"a" + 1.0
"a" ++ 1.0
true and "a"
true !~ "true"
4 changes: 3 additions & 1 deletion tests/binary_ops_subtypes.nu
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@
[1] ++ 1.0
[1.0 1] ++ "a"
[[1] [2]] ++ [[3]]
[[1] [2]] ++ [[3.0]]
[[1] [2]] ++ [[3.0]]
1 in [1.0, 1]
2.3 mod 1