blob: 5cac18fc0a6b49c92037272661137fbb6263fbab [file] [log] [blame] [edit]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! A simple parser for calculator expressions.
//!
//! This parser is intentionally not as fully featured as it could be, electing
//! to not support more complex expressions to aid in readability. It's just
//! provided to make it a bit easier to add expressions via
//! [input.txt](input.txt).
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::space0;
use nom::combinator::map;
use nom::number::complete::double;
use nom::sequence::delimited;
use nom::{IResult, Parser};
#[derive(Debug, PartialEq)]
pub(crate) enum Operator {
Add,
Subtract,
Multiply,
Divide,
Pow,
}
/// This enum is intentionally overly simplified to make the example easier to
/// understand.
#[derive(Debug, PartialEq)]
pub(crate) enum Expression {
Leaf(f64, Operator, f64),
}
/// Parses an Operator
fn operator(s: &str) -> IResult<&str, Operator> {
alt((
map(tag("+"), |_: &str| Operator::Add),
map(tag("-"), |_: &str| Operator::Subtract),
map(tag("*"), |_: &str| Operator::Multiply),
map(tag("/"), |_: &str| Operator::Divide),
map(tag("^"), |_: &str| Operator::Pow),
))
.parse(s)
}
/// Parses a float64. Allows one or more spaces before and after the number.
fn number(s: &str) -> IResult<&str, f64> {
map(delimited(space0, double, space0), |n| n).parse(s)
}
/// Parses an Expression::Leaf.
fn leaf(s: &str) -> IResult<&str, Expression> {
map((number, operator, number), |(left, op, right)| Expression::Leaf(left, op, right)).parse(s)
}
/// Parses an Expression.
fn expression(s: &str) -> IResult<&str, Expression> {
// Uses leaf, leaf as the tuple to make it easier to support more complex
// Expression enum variants in the future.
alt((leaf, leaf)).parse(s)
}
/// Given a string, parses an Expression. Panics if the parse is unsuccessful.
pub(crate) fn parse(s: &str) -> Expression {
let (_, exp) = expression(s).expect("parse error");
exp
}
#[cfg(test)]
mod tests {
use crate::parse::*;
#[test]
fn parse_operators() {
let input = "+";
let (_, actual) = operator(input).expect("Parse error");
assert_eq!(actual, Operator::Add);
let input = "-";
let (_, actual) = operator(input).expect("Parse error");
assert_eq!(actual, Operator::Subtract);
let input = "*";
let (_, actual) = operator(input).expect("Parse error");
assert_eq!(actual, Operator::Multiply);
let input = "/";
let (_, actual) = operator(input).expect("Parse error");
assert_eq!(actual, Operator::Divide);
let input = "^";
let (_, actual) = operator(input).expect("Parse error");
assert_eq!(actual, Operator::Pow);
}
#[test]
fn parse_expression() {
let input = "1 + 2";
let (_, actual) = expression(input).expect("Parse error");
assert_eq!(actual, Expression::Leaf(1.0, Operator::Add, 2.0));
}
#[test]
fn parse_expression_with_negative_numbers() {
let input = "-1 + -2";
let (_, actual) = expression(input).expect("Parse error");
assert_eq!(actual, Expression::Leaf(-1.0, Operator::Add, -2.0));
}
#[test]
fn parse_expression_with_subtraction() {
let input = "-1.234 - -2.345";
let (_, actual) = expression(input).expect("Parse error");
assert_eq!(actual, Expression::Leaf(-1.234, Operator::Subtract, -2.345));
}
#[test]
fn parse_expression_with_multiplication() {
let input = "1.5 * 2.0";
let (_, actual) = expression(input).expect("Parse error");
assert_eq!(actual, Expression::Leaf(1.5, Operator::Multiply, 2.0));
}
#[test]
fn parse_expression_with_division() {
let input = "1.5 / 3.0";
let (_, actual) = expression(input).expect("Parse error");
assert_eq!(actual, Expression::Leaf(1.5, Operator::Divide, 3.0));
}
#[test]
fn parse_expression_with_pow() {
let input = "2.0 ^ 4.5";
let (_, actual) = expression(input).expect("Parse error");
assert_eq!(actual, Expression::Leaf(2.0, Operator::Pow, 4.5));
}
}