feat: ordering operators

This commit is contained in:
Roman Godmaire 2024-02-17 21:54:14 -05:00
parent 519fa4ee99
commit c2b95dfee4
4 changed files with 98 additions and 0 deletions

View file

@ -155,6 +155,66 @@ pub fn core_environment() -> Rc<Environment> {
Ok(Expression::Boolean(false).into()) 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 // Branching
( (
"if", "if",

View file

@ -85,6 +85,8 @@ pub enum Error {
ExpectedSymbol, ExpectedSymbol,
#[error("expected list")] #[error("expected list")]
ExpectedList, ExpectedList,
#[error("expected number")]
ExpectedNumber,
#[error("expected {0} arguments, got {1}")] #[error("expected {0} arguments, got {1}")]
MismatchedArgCount(usize, usize), MismatchedArgCount(usize, usize),
} }
@ -222,6 +224,17 @@ mod test {
#[case("(not true)", "false")] #[case("(not true)", "false")]
#[case("(not nil)", "false")] #[case("(not nil)", "false")]
#[case("(not 1)", "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) { fn test_evaluator(#[case] input: &str, #[case] expected: &str) {
let env = core_environment(); let env = core_environment();
let tokens = lexer::read(input).unwrap(); let tokens = lexer::read(input).unwrap();

View file

@ -26,6 +26,11 @@ pub enum Token {
Asterisk, Asterisk,
Slash, Slash,
LessThan,
GreaterThan,
LessThanEqual,
GreaterThanEqual,
// Values // Values
Keyword(String), Keyword(String),
Int(i64), Int(i64),
@ -97,6 +102,21 @@ fn next_token(input: &mut Peekable<Chars>) -> Result<Option<Token>> {
'*' => Token::Asterisk, '*' => Token::Asterisk,
'/' => Token::Slash, '/' => 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_string(input)?,
':' => read_keyword(input), ':' => read_keyword(input),

View file

@ -59,6 +59,11 @@ fn next_statement(tokens: &mut Peekable<IntoIter<Token>>) -> Result<Option<Node>
Token::Asterisk => Node::Symbol("*".into()), Token::Asterisk => Node::Symbol("*".into()),
Token::Slash => 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::Keyword(val) => Node::Keyword(val),
Token::Ident(val) => Node::Symbol(val), Token::Ident(val) => Node::Symbol(val),
Token::String(val) => Node::String(val), Token::String(val) => Node::String(val),