summaryrefslogtreecommitdiffstats
path: root/vendor/pest/tests/calculator.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/pest/tests/calculator.rs')
-rw-r--r--vendor/pest/tests/calculator.rs201
1 files changed, 201 insertions, 0 deletions
diff --git a/vendor/pest/tests/calculator.rs b/vendor/pest/tests/calculator.rs
new file mode 100644
index 000000000..b78ee0d27
--- /dev/null
+++ b/vendor/pest/tests/calculator.rs
@@ -0,0 +1,201 @@
+// pest. The Elegant Parser
+// Copyright (c) 2018 DragoČ™ Tiselice
+//
+// Licensed under the Apache License, Version 2.0
+// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
+// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. All files in the project carrying such notice may not be copied,
+// modified, or distributed except according to those terms.
+
+#[macro_use]
+extern crate pest;
+
+use pest::error::Error;
+use pest::iterators::{Pair, Pairs};
+use pest::prec_climber::{Assoc, Operator, PrecClimber};
+use pest::{state, ParseResult, Parser, ParserState};
+
+#[allow(dead_code, non_camel_case_types)]
+#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+enum Rule {
+ expression,
+ primary,
+ number,
+ plus,
+ minus,
+ times,
+ divide,
+ modulus,
+ power,
+}
+
+struct CalculatorParser;
+
+impl Parser<Rule> for CalculatorParser {
+ fn parse(rule: Rule, input: &str) -> Result<Pairs<Rule>, Error<Rule>> {
+ fn expression(state: Box<ParserState<Rule>>) -> ParseResult<Box<ParserState<Rule>>> {
+ state.rule(Rule::expression, |s| {
+ s.sequence(|s| {
+ primary(s).and_then(|s| {
+ s.repeat(|s| {
+ s.sequence(|s| {
+ plus(s)
+ .or_else(|s| minus(s))
+ .or_else(|s| times(s))
+ .or_else(|s| divide(s))
+ .or_else(|s| modulus(s))
+ .or_else(|s| power(s))
+ .and_then(|s| primary(s))
+ })
+ })
+ })
+ })
+ })
+ }
+
+ fn primary(state: Box<ParserState<Rule>>) -> ParseResult<Box<ParserState<Rule>>> {
+ state
+ .sequence(|s| {
+ s.match_string("(")
+ .and_then(|s| expression(s))
+ .and_then(|s| s.match_string(")"))
+ })
+ .or_else(|s| number(s))
+ }
+
+ fn number(state: Box<ParserState<Rule>>) -> ParseResult<Box<ParserState<Rule>>> {
+ state.rule(Rule::number, |s| {
+ s.sequence(|s| {
+ s.optional(|s| s.match_string("-")).and_then(|s| {
+ s.match_string("0").or_else(|s| {
+ s.sequence(|s| {
+ s.match_range('1'..'9')
+ .and_then(|s| s.repeat(|s| s.match_range('0'..'9')))
+ })
+ })
+ })
+ })
+ })
+ }
+
+ fn plus(state: Box<ParserState<Rule>>) -> ParseResult<Box<ParserState<Rule>>> {
+ state.rule(Rule::plus, |s| s.match_string("+"))
+ }
+
+ fn minus(state: Box<ParserState<Rule>>) -> ParseResult<Box<ParserState<Rule>>> {
+ state.rule(Rule::minus, |s| s.match_string("-"))
+ }
+
+ fn times(state: Box<ParserState<Rule>>) -> ParseResult<Box<ParserState<Rule>>> {
+ state.rule(Rule::times, |s| s.match_string("*"))
+ }
+
+ fn divide(state: Box<ParserState<Rule>>) -> ParseResult<Box<ParserState<Rule>>> {
+ state.rule(Rule::divide, |s| s.match_string("/"))
+ }
+
+ fn modulus(state: Box<ParserState<Rule>>) -> ParseResult<Box<ParserState<Rule>>> {
+ state.rule(Rule::modulus, |s| s.match_string("%"))
+ }
+
+ fn power(state: Box<ParserState<Rule>>) -> ParseResult<Box<ParserState<Rule>>> {
+ state.rule(Rule::power, |s| s.match_string("^"))
+ }
+
+ state(input, |state| match rule {
+ Rule::expression => expression(state),
+ _ => unreachable!(),
+ })
+ }
+}
+
+fn consume<'i>(pair: Pair<'i, Rule>, climber: &PrecClimber<Rule>) -> i32 {
+ let primary = |pair| consume(pair, climber);
+ let infix = |lhs: i32, op: Pair<Rule>, rhs: i32| match op.as_rule() {
+ Rule::plus => lhs + rhs,
+ Rule::minus => lhs - rhs,
+ Rule::times => lhs * rhs,
+ Rule::divide => lhs / rhs,
+ Rule::modulus => lhs % rhs,
+ Rule::power => lhs.pow(rhs as u32),
+ _ => unreachable!(),
+ };
+
+ match pair.as_rule() {
+ Rule::expression => climber.climb(pair.into_inner(), primary, infix),
+ Rule::number => pair.as_str().parse().unwrap(),
+ _ => unreachable!(),
+ }
+}
+
+#[test]
+fn number() {
+ parses_to! {
+ parser: CalculatorParser,
+ input: "-12",
+ rule: Rule::expression,
+ tokens: [
+ expression(0, 3, [
+ number(0, 3)
+ ])
+ ]
+ };
+}
+
+#[test]
+fn parens() {
+ parses_to! {
+ parser: CalculatorParser,
+ input: "((-12))",
+ rule: Rule::expression,
+ tokens: [
+ expression(0, 7, [
+ expression(1, 6, [
+ expression(2, 5, [
+ number(2, 5)
+ ])
+ ])
+ ])
+ ]
+ };
+}
+
+#[test]
+fn expression() {
+ parses_to! {
+ parser: CalculatorParser,
+ input: "-12+3*(4-9)^7^2",
+ rule: Rule::expression,
+ tokens: [
+ expression(0, 15, [
+ number(0, 3),
+ plus(3, 4),
+ number(4, 5),
+ times(5, 6),
+ expression(7, 10, [
+ number(7, 8),
+ minus(8, 9),
+ number(9, 10)
+ ]),
+ power(11, 12),
+ number(12, 13),
+ power(13, 14),
+ number(14, 15)
+ ])
+ ]
+ };
+}
+
+#[test]
+fn prec_climb() {
+ let climber = PrecClimber::new(vec![
+ Operator::new(Rule::plus, Assoc::Left) | Operator::new(Rule::minus, Assoc::Left),
+ Operator::new(Rule::times, Assoc::Left)
+ | Operator::new(Rule::divide, Assoc::Left)
+ | Operator::new(Rule::modulus, Assoc::Left),
+ Operator::new(Rule::power, Assoc::Right),
+ ]);
+
+ let pairs = CalculatorParser::parse(Rule::expression, "-12+3*(4-9)^3^2/9%7381");
+ assert_eq!(-1_525, consume(pairs.unwrap().next().unwrap(), &climber));
+}