Skip to content

Commit

Permalink
feat: compound and comparison operators for int and float
Browse files Browse the repository at this point in the history
  • Loading branch information
fcoury committed Jul 5, 2024
1 parent 84c1d33 commit 483313c
Show file tree
Hide file tree
Showing 5 changed files with 247 additions and 1 deletion.
24 changes: 24 additions & 0 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,31 @@ impl Interpreter {
Operator::Divide => Ok(Value::Int(left / right)),
Operator::Equals => Ok(Value::Bool(left == right)),
Operator::Modulo => Ok(Value::Int(left % right)),
Operator::LessThan => Ok(Value::Bool(left < right)),
Operator::LessThanEquals => Ok(Value::Bool(left <= right)),
Operator::GreaterThan => Ok(Value::Bool(left > right)),
Operator::GreaterThanEquals => Ok(Value::Bool(left >= right)),
},
(Value::Float(left), Value::Float(right)) => match op {
Operator::Plus => Ok(Value::Float(left + right)),
Operator::Minus => Ok(Value::Float(left - right)),
Operator::Multiply => Ok(Value::Float(left * right)),
Operator::Divide => Ok(Value::Float(left / right)),
Operator::Equals => Ok(Value::Bool(left == right)),
Operator::Modulo => Ok(Value::Float(left % right)),
Operator::LessThan => Ok(Value::Bool(left < right)),
Operator::LessThanEquals => Ok(Value::Bool(left <= right)),
Operator::GreaterThan => Ok(Value::Bool(left > right)),
Operator::GreaterThanEquals => Ok(Value::Bool(left >= right)),
},
(Value::Int(_), Value::Float(_)) => Err(Error::new_runtime(
"Can't compare integer with float".to_string(),
span,
)),
(Value::Float(_), Value::Int(_)) => Err(Error::new_runtime(
"Can't compare float with integer".to_string(),
span,
)),
_ => Err(Error::new_runtime(
"Binary operation on non-numeric values".to_string(),
span,
Expand Down
83 changes: 82 additions & 1 deletion src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub enum TokenKind {
If,
Else,
Match,
While,
For,
In,
Break,
Expand Down Expand Up @@ -62,6 +63,10 @@ pub enum TokenKind {
StarEquals,
SlashEquals,
PercentEquals,
LessThan,
LessThanEquals,
GreaterThan,
GreaterThanEquals,
Colon,
Dot,
DblDot,
Expand Down Expand Up @@ -162,6 +167,22 @@ impl Lexer {
Some('}') => self.create_token(TokenKind::RBrace),
Some('[') => self.create_token(TokenKind::LSquare),
Some(']') => self.create_token(TokenKind::RSquare),
Some('>') => {
if self.peek_char() == Some('=') {
self.read_char();
self.create_token(TokenKind::GreaterThanEquals)
} else {
self.create_token(TokenKind::GreaterThan)
}
}
Some('<') => {
if self.peek_char() == Some('=') {
self.read_char();
self.create_token(TokenKind::LessThanEquals)
} else {
self.create_token(TokenKind::LessThan)
}
}
Some(',') => self.create_token(TokenKind::Comma),
Some('+') => {
if self.peek_char() == Some('=') {
Expand Down Expand Up @@ -214,7 +235,7 @@ impl Lexer {
self.read_char();
self.create_token(TokenKind::SlashEquals)
} else {
return self.create_token(TokenKind::Slash);
self.create_token(TokenKind::Slash)
}
}
Some('%') => {
Expand Down Expand Up @@ -293,6 +314,7 @@ impl Lexer {
"if" => TokenKind::If,
"else" => TokenKind::Else,
"match" => TokenKind::Match,
"while" => TokenKind::While,
"for" => TokenKind::For,
"in" => TokenKind::In,
"break" => TokenKind::Break,
Expand Down Expand Up @@ -1079,4 +1101,63 @@ mod tests {
assert_eq!(kind, expected);
}
}

#[test]
fn test_comparison_operators() {
let code = "x > 10; x < 10; x >= 10; x <= 10;";
let mut lexer = Lexer::new(code);
let expected_tokens = vec![
TokenKind::Identifier("x".to_string()),
TokenKind::GreaterThan,
TokenKind::Int(10),
TokenKind::Semicolon,
TokenKind::Identifier("x".to_string()),
TokenKind::LessThan,
TokenKind::Int(10),
TokenKind::Semicolon,
TokenKind::Identifier("x".to_string()),
TokenKind::GreaterThanEquals,
TokenKind::Int(10),
TokenKind::Semicolon,
TokenKind::Identifier("x".to_string()),
TokenKind::LessThanEquals,
TokenKind::Int(10),
TokenKind::Semicolon,
TokenKind::Eof,
];

for expected in expected_tokens {
let kind = lexer.next_token().kind;
assert_eq!(kind, expected);
}
}

// #[test]
// fn test_lex_while() {
// let code = r#"
// while x < 10 {
// x += 1;
// }
// "#;
//
// let mut lexer = Lexer::new(code);
// let expected_tokens = vec![
// TokenKind::While,
// TokenKind::Identifier("x".to_string()),
// TokenKind::DblLess,
// TokenKind::Int(10),
// TokenKind::LBrace,
// TokenKind::Identifier("x".to_string()),
// TokenKind::PlusEquals,
// TokenKind::Int(1),
// TokenKind::Semicolon,
// TokenKind::RBrace,
// TokenKind::Eof,
// ];
//
// for expected in expected_tokens {
// let kind = lexer.next_token().kind;
// assert_eq!(kind, expected);
// }
// }
}
118 changes: 118 additions & 0 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ pub enum Operator {
Divide,
Equals,
Modulo,
LessThan,
GreaterThan,
LessThanEquals,
GreaterThanEquals,
}

pub struct Parser {
Expand Down Expand Up @@ -1127,6 +1131,11 @@ impl Parser {
TokenKind::Asterisk => Some(Operator::Multiply),
TokenKind::Slash => Some(Operator::Divide),
TokenKind::DblEquals => return Some(Operator::Equals),
TokenKind::LessThan => return Some(Operator::LessThan),
TokenKind::GreaterThan => return Some(Operator::GreaterThan),
TokenKind::LessThanEquals => return Some(Operator::LessThanEquals),
TokenKind::GreaterThanEquals => return Some(Operator::GreaterThanEquals),
TokenKind::Percent => Some(Operator::Modulo),
_ => None,
}
}
Expand Down Expand Up @@ -2011,4 +2020,113 @@ mod tests {
))
);
}

#[test]
fn test_parse_operators() {
let code = r#"
let x = 1 + 2;
let y = 3 - 4;
let z = 5 * 6;
let w = 7 / 8;
let a = 9 % 10;
"#;

let ast = parse(code);

assert_eq!(
ast[0],
Stmt::Let(
"x".to_string(),
Expr::BinaryOp(
Box::new(Expr::Int(1, Span::new(21, 22))),
Operator::Plus,
Box::new(Expr::Int(2, Span::new(25, 26))),
Span::new(21, 24),
),
Span::new(13, 27),
)
);

assert_eq!(
ast[1],
Stmt::Let(
"y".to_string(),
Expr::BinaryOp(
Box::new(Expr::Int(3, Span::new(48, 49))),
Operator::Minus,
Box::new(Expr::Int(4, Span::new(52, 53))),
Span::new(48, 51),
),
Span::new(40, 54),
)
);

assert_eq!(
ast[2],
Stmt::Let(
"z".to_string(),
Expr::BinaryOp(
Box::new(Expr::Int(5, Span::new(75, 76))),
Operator::Multiply,
Box::new(Expr::Int(6, Span::new(79, 80))),
Span::new(75, 78),
),
Span::new(67, 81),
)
);

assert_eq!(
ast[3],
Stmt::Let(
"w".to_string(),
Expr::BinaryOp(
Box::new(Expr::Int(7, Span::new(102, 103))),
Operator::Divide,
Box::new(Expr::Int(8, Span::new(106, 107))),
Span::new(102, 105),
),
Span::new(94, 108),
)
);

assert_eq!(
ast[4],
Stmt::Let(
"a".to_string(),
Expr::BinaryOp(
Box::new(Expr::Int(9, Span::new(129, 130))),
Operator::Modulo,
Box::new(Expr::Int(10, Span::new(133, 135))),
Span::new(129, 134),
),
Span::new(121, 136),
)
);
}

#[test]
fn test_parse_compound_operators() {
let code = r#"
x += 2;
y -= 4;
z *= 6;
w /= 8;
a %= 10;
"#;

let ast = parse(code);

assert_eq!(
ast[0],
Stmt::Expression(Expr::CompoundAssign(
Box::new(Expr::Identifier(
"x".to_string(),
Span { start: 13, end: 14 }
)),
Operator::Plus,
Box::new(Expr::Int(2, Span { start: 18, end: 19 })),
Span { start: 13, end: 19 }
))
);
}
}
18 changes: 18 additions & 0 deletions tests/scripts/opers.hk
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,21 @@ a += 1;

a %= 2;
println(a);

let b = 0.1;
b += 0.8;
println(a);

b -= 0.3;
println(b);

b *= 0.2;
println(b);

b /= 0.2;
println(b);

b += 0.1;

b %= 0.2;
println(b);
5 changes: 5 additions & 0 deletions tests/scripts/opers.hk.out
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@
6
0
1
1
0.6000000000000001
0.12000000000000002
0.6000000000000001
0.10000000000000003

0 comments on commit 483313c

Please sign in to comment.