use std::io;
use yapcol::error::Error;
use yapcol::input::Input;
use yapcol::{Parser, attempt, between, chain_left, chain_right, is, option, satisfy};
mod expression;
use expression::{Expression, Operator, evaluate};
#[derive(Debug, PartialEq, Clone)]
enum Token {
Number(i32),
Operator(Operator),
OpenParenthesis,
CloseParenthesis,
}
fn tokenize(input: String) -> Vec<Token> {
let mut tokens = Vec::new();
let input = input.chars().collect::<Vec<char>>();
let mut i = 0;
while i < input.len() {
let token = match input[i] {
'(' => Token::OpenParenthesis,
')' => Token::CloseParenthesis,
'+' => Token::Operator(Operator::Addition),
'-' => Token::Operator(Operator::Subtraction),
'*' => Token::Operator(Operator::Multiplication),
'/' => Token::Operator(Operator::Division),
'^' => Token::Operator(Operator::Exponentiation),
c if c.is_numeric() => {
let number: String = input
.iter()
.skip(i)
.take_while(|&c| c.is_numeric())
.collect();
i += number.len() - 1;
let number = number.parse().unwrap();
Token::Number(number)
}
c => panic!("Unexpected character: {c}"),
};
i += 1;
tokens.push(token);
}
tokens
}
trait TokenExpressionParser<I>: Parser<I, Expression>
where
I: Iterator<Item = Token>,
{
}
impl<I, T> TokenExpressionParser<I> for T
where
I: Iterator<Item = Token>,
T: Fn(&mut Input<I>) -> Result<Expression, Error>,
{
}
fn parse_number<I>() -> impl TokenExpressionParser<I>
where
I: Iterator<Item = Token>,
{
let f = |token: &Token| match token {
Token::Number(number) => Ok(Expression::Number(*number)),
_ => Err(Error::UnexpectedToken),
};
satisfy(f)
}
fn build_operation(op: Operator) -> impl Fn(Expression, Expression) -> Expression {
move |o1, o2| Expression::Operation(Box::new(o1), op.clone(), Box::new(o2))
}
fn parse_operations<I>(
operator1: Operator,
operator2: Operator,
) -> impl Parser<I, Box<dyn Fn(Expression, Expression) -> Expression>>
where
I: Iterator<Item = Token>,
{
move |input| {
let parse_multiplication = is(Token::Operator(operator1.clone()));
let parse_division = is(Token::Operator(operator2.clone()));
let parse_attempt_multiplication = attempt(&parse_multiplication);
let operator = option(&parse_attempt_multiplication, &parse_division)(input)?;
match operator {
Token::Operator(op) => Ok(Box::new(build_operation(op))),
_ => Err(Error::UnexpectedToken),
}
}
}
fn parse_expression<I>() -> impl TokenExpressionParser<I>
where
I: Iterator<Item = Token>,
{
|input| {
let parse_operator = parse_operations(Operator::Addition, Operator::Subtraction);
chain_left(&parse_factor(), &parse_operator)(input)
}
}
fn parse_factor<I>() -> impl TokenExpressionParser<I>
where
I: Iterator<Item = Token>,
{
|input| {
let parse_operator = parse_operations(Operator::Multiplication, Operator::Division);
chain_left(&parse_exponentiation(), &parse_operator)(input)
}
}
fn parse_exponentiation<I>() -> impl TokenExpressionParser<I>
where
I: Iterator<Item = Token>,
{
|input| {
let parse_operator =
|input: &mut Input<I>| match is(Token::Operator(Operator::Exponentiation))(input) {
Ok(_) => Ok(build_operation(Operator::Exponentiation)),
Err(_) => Err(Error::UnexpectedToken),
};
chain_right(&parse_bottom(), &parse_operator)(input)
}
}
fn parse_bottom<I>() -> impl TokenExpressionParser<I>
where
I: Iterator<Item = Token>,
{
|input| {
let parse_number = parse_number();
let parse_open = is(Token::OpenParenthesis);
let parse_expression = parse_expression();
let parse_close = is(Token::CloseParenthesis);
let parse_parenthesis = between(&parse_open, &parse_expression, &parse_close);
let parse_parenthesis = attempt(&parse_parenthesis);
option(&parse_parenthesis, &parse_number)(input)
}
}
fn main() {
let stdin = io::stdin();
let input = &mut String::new();
loop {
println!("Enter expression, or 'q' to quit:");
input.clear();
match stdin.read_line(input) {
Ok(_) if input.len() == 2 && input.starts_with('q') => break,
Ok(_) => {
input.retain(|c| c != '\n');
let tokens = tokenize(input.clone());
let mut input = Input::new(tokens);
match parse_expression()(&mut input) {
Ok(e) => println!("Success: {:?}", evaluate(e)),
Err(e) => println!("Failed to parse expression: {:?}", e),
}
}
Err(_) => println!("Failed to read input."),
}
}
}
#[cfg(test)]
mod tokenize_tests {
use super::*;
#[test]
fn addition() {
let input = String::from("+");
let tokens = tokenize(input);
assert_eq!(tokens, vec![Token::Operator(Operator::Addition),])
}
#[test]
fn subtraction() {
let input = String::from("-");
let tokens = tokenize(input);
assert_eq!(tokens, vec![Token::Operator(Operator::Subtraction),])
}
#[test]
fn multiplication() {
let input = String::from("*");
let tokens = tokenize(input);
assert_eq!(tokens, vec![Token::Operator(Operator::Multiplication),])
}
#[test]
fn division() {
let input = String::from("/");
let tokens = tokenize(input);
assert_eq!(tokens, vec![Token::Operator(Operator::Division),])
}
#[test]
fn number_single() {
let input = String::from("1");
let tokens = tokenize(input);
assert_eq!(tokens, vec![Token::Number(1)])
}
#[test]
fn number_multiple() {
let input = String::from("167253571");
let tokens = tokenize(input);
assert_eq!(tokens, vec![Token::Number(167253571)])
}
#[test]
fn addition_operation() {
let input = String::from("15+3");
let tokens = tokenize(input);
assert_eq!(
tokens,
vec![
Token::Number(15),
Token::Operator(Operator::Addition),
Token::Number(3),
]
);
}
}
#[cfg(test)]
mod evaluation_tests {
use super::*;
use yapcol::end_of_input;
fn parse_and_evaluate(input: &str) -> i32 {
let tokens = tokenize(String::from(input));
let mut input = Input::new(tokens);
let output = parse_expression()(&mut input);
assert!(end_of_input()(&mut input).is_ok());
evaluate(output.unwrap())
}
#[test]
fn single_number() {
assert_eq!(parse_and_evaluate("42"), 42);
}
#[test]
fn addition() {
assert_eq!(parse_and_evaluate("10+5"), 15);
}
#[test]
fn subtraction() {
assert_eq!(parse_and_evaluate("10-3"), 7);
}
#[test]
fn multiplication() {
assert_eq!(parse_and_evaluate("4*5"), 20);
}
#[test]
fn division() {
assert_eq!(parse_and_evaluate("20/4"), 5);
}
#[test]
fn addition_and_multiplication_precedence() {
assert_eq!(parse_and_evaluate("2+3*4"), 14);
}
#[test]
fn subtraction_and_division_precedence() {
assert_eq!(parse_and_evaluate("20-10/2"), 15);
}
#[test]
fn two_additions() {
assert_eq!(parse_and_evaluate("1+2+3"), 6);
}
#[test]
fn simple_exponentiation() {
assert_eq!(parse_and_evaluate("2^3"), 8);
}
#[test]
fn double_exponentiation() {
assert_eq!(parse_and_evaluate("4^2^3"), 65_536);
}
#[test]
fn mixed_operations_no_exponentiation() {
assert_eq!(parse_and_evaluate("10+2*3-1"), 15);
}
#[test]
fn mixed_operations_exponentiation() {
assert_eq!(parse_and_evaluate("28/4+2*3-2^3"), 5);
}
#[test]
fn mixed_operations_exponentiation_parenthesis() {
assert_eq!(parse_and_evaluate("28/4+2*(3-2)^3"), 9);
}
}