From c2b95dfee4c07f798926cc641b061c6aeb1b60ee Mon Sep 17 00:00:00 2001 From: Roman Godmaire Date: Sat, 17 Feb 2024 21:54:14 -0500 Subject: [PATCH] feat: ordering operators --- src/evaluator/env.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++ src/evaluator/mod.rs | 13 ++++++++++ src/lexer.rs | 20 +++++++++++++++ src/parser.rs | 5 ++++ 4 files changed, 98 insertions(+) diff --git a/src/evaluator/env.rs b/src/evaluator/env.rs index f41a244..ac27f25 100644 --- a/src/evaluator/env.rs +++ b/src/evaluator/env.rs @@ -155,6 +155,66 @@ pub fn core_environment() -> Rc { Ok(Expression::Boolean(false).into()) }), ), + ( + "<", + Expression::NativeFunc(|args| { + if args.len() != 2 { + Err(Error::MismatchedArgCount(2, args.len()))? + } + + let less_than = match (args[0].borrow(), args[1].borrow()) { + (Expression::Int(lhs), Expression::Int(rhs)) => lhs < rhs, + _ => Err(Error::ExpectedNumber)?, + }; + + Ok(Expression::Boolean(less_than).into()) + }), + ), + ( + ">", + Expression::NativeFunc(|args| { + if args.len() != 2 { + Err(Error::MismatchedArgCount(2, args.len()))? + } + + let greater_than = match (args[0].borrow(), args[1].borrow()) { + (Expression::Int(lhs), Expression::Int(rhs)) => lhs > rhs, + _ => Err(Error::ExpectedNumber)?, + }; + + Ok(Expression::Boolean(greater_than).into()) + }), + ), + ( + "<=", + Expression::NativeFunc(|args| { + if args.len() != 2 { + Err(Error::MismatchedArgCount(2, args.len()))? + } + + let less_than_equal = match (args[0].borrow(), args[1].borrow()) { + (Expression::Int(lhs), Expression::Int(rhs)) => lhs <= rhs, + _ => Err(Error::ExpectedNumber)?, + }; + + Ok(Expression::Boolean(less_than_equal).into()) + }), + ), + ( + ">=", + Expression::NativeFunc(|args| { + if args.len() != 2 { + Err(Error::MismatchedArgCount(2, args.len()))? + } + + let greater_than_equal = match (args[0].borrow(), args[1].borrow()) { + (Expression::Int(lhs), Expression::Int(rhs)) => lhs >= rhs, + _ => Err(Error::ExpectedNumber)?, + }; + + Ok(Expression::Boolean(greater_than_equal).into()) + }), + ), // Branching ( "if", diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index 19b51d8..3c8a72f 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -85,6 +85,8 @@ pub enum Error { ExpectedSymbol, #[error("expected list")] ExpectedList, + #[error("expected number")] + ExpectedNumber, #[error("expected {0} arguments, got {1}")] MismatchedArgCount(usize, usize), } @@ -222,6 +224,17 @@ mod test { #[case("(not true)", "false")] #[case("(not nil)", "false")] #[case("(not 1)", "false")] + // Ordering + #[case("(< 1 2)", "true")] + #[case("(< 2 1)", "false")] + #[case("(> 1 2)", "false")] + #[case("(> 2 1)", "true")] + #[case("(<= 1 2)", "true")] + #[case("(<= 1 1)", "true")] + #[case("(<= 2 1)", "false")] + #[case("(>= 2 1)", "true")] + #[case("(>= 1 1)", "true")] + #[case("(>= 1 2)", "false")] fn test_evaluator(#[case] input: &str, #[case] expected: &str) { let env = core_environment(); let tokens = lexer::read(input).unwrap(); diff --git a/src/lexer.rs b/src/lexer.rs index 01634cf..e9e3651 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -26,6 +26,11 @@ pub enum Token { Asterisk, Slash, + LessThan, + GreaterThan, + LessThanEqual, + GreaterThanEqual, + // Values Keyword(String), Int(i64), @@ -97,6 +102,21 @@ fn next_token(input: &mut Peekable) -> Result> { '*' => Token::Asterisk, '/' => Token::Slash, + '<' => match input.peek() { + Some('=') => { + input.next(); + Token::LessThanEqual + } + _ => Token::LessThan, + }, + '>' => match input.peek() { + Some('=') => { + input.next(); + Token::GreaterThanEqual + } + _ => Token::GreaterThan, + }, + '"' => read_string(input)?, ':' => read_keyword(input), diff --git a/src/parser.rs b/src/parser.rs index 2dbfb1a..13c5252 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -59,6 +59,11 @@ fn next_statement(tokens: &mut Peekable>) -> Result Token::Asterisk => Node::Symbol("*".into()), Token::Slash => Node::Symbol("/".into()), + Token::LessThan => Node::Symbol("<".into()), + Token::GreaterThan => Node::Symbol(">".into()), + Token::LessThanEqual => Node::Symbol("<=".into()), + Token::GreaterThanEqual => Node::Symbol(">=".into()), + Token::Keyword(val) => Node::Keyword(val), Token::Ident(val) => Node::Symbol(val), Token::String(val) => Node::String(val),