feat: ordering operators
This commit is contained in:
parent
519fa4ee99
commit
c2b95dfee4
4 changed files with 98 additions and 0 deletions
|
@ -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",
|
||||||
|
|
|
@ -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();
|
||||||
|
|
20
src/lexer.rs
20
src/lexer.rs
|
@ -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),
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
|
Loading…
Reference in a new issue