Skip to content

Commit

Permalink
Update prec climber to support unary operators
Browse files Browse the repository at this point in the history
  • Loading branch information
segeljakt committed Dec 2, 2018
1 parent 49847de commit fce43c4
Show file tree
Hide file tree
Showing 7 changed files with 526 additions and 27 deletions.
32 changes: 17 additions & 15 deletions meta/src/parser.rs
Expand Up @@ -11,7 +11,7 @@ use std::char;
use std::iter::Peekable;

use pest::iterators::{Pair, Pairs};
use pest::prec_climber::{Assoc, Operator, PrecClimber};
use pest::pratt_parser::{Assoc, Op, PrattParser};
use pest::{Span, Parser};
use pest::error::{Error, ErrorVariant};

Expand Down Expand Up @@ -177,10 +177,9 @@ pub fn consume_rules(pairs: Pairs<Rule>) -> Result<Vec<AstRule>, Vec<Error<Rule>
fn consume_rules_with_spans<'i>(
pairs: Pairs<'i, Rule>
) -> Result<Vec<ParserRule<'i>>, Vec<Error<Rule>>> {
let climber = PrecClimber::new(vec![
Operator::new(Rule::choice_operator, Assoc::Left),
Operator::new(Rule::sequence_operator, Assoc::Left),
]);
let pratt = PrattParser::new()
.op(Op::infix(Rule::choice_operator, Assoc::Left))
.op(Op::infix(Rule::sequence_operator, Assoc::Left));

pairs
.filter(|pair| pair.as_rule() == Rule::grammar_rule)
Expand All @@ -206,7 +205,7 @@ fn consume_rules_with_spans<'i>(

pairs.next().unwrap(); // opening_brace

let node = consume_expr(pairs.next().unwrap().into_inner().peekable(), &climber)?;
let node = consume_expr(pairs.next().unwrap().into_inner().peekable(), &pratt)?;

Ok(ParserRule {
name,
Expand All @@ -220,17 +219,17 @@ fn consume_rules_with_spans<'i>(

fn consume_expr<'i>(
pairs: Peekable<Pairs<'i, Rule>>,
climber: &PrecClimber<Rule>
pratt: &PrattParser<Rule>
) -> Result<ParserNode<'i>, Vec<Error<Rule>>> {
fn unaries<'i>(
mut pairs: Peekable<Pairs<'i, Rule>>,
climber: &PrecClimber<Rule>
pratt: &PrattParser<Rule>
) -> Result<ParserNode<'i>, Vec<Error<Rule>>> {
let pair = pairs.next().unwrap();

let node = match pair.as_rule() {
Rule::opening_paren => {
let node = unaries(pairs, climber)?;
let node = unaries(pairs, pratt)?;
let end = node.span.end_pos();

ParserNode {
Expand All @@ -239,7 +238,7 @@ fn consume_expr<'i>(
}
}
Rule::positive_predicate_operator => {
let node = unaries(pairs, climber)?;
let node = unaries(pairs, pratt)?;
let end = node.span.end_pos();

ParserNode {
Expand All @@ -248,7 +247,7 @@ fn consume_expr<'i>(
}
}
Rule::negative_predicate_operator => {
let node = unaries(pairs, climber)?;
let node = unaries(pairs, pratt)?;
let end = node.span.end_pos();

ParserNode {
Expand All @@ -258,14 +257,14 @@ fn consume_expr<'i>(
}
other_rule => {
let node = match other_rule {
Rule::expression => consume_expr(pair.into_inner().peekable(), climber)?,
Rule::expression => consume_expr(pair.into_inner().peekable(), pratt)?,
Rule::_push => {
let start = pair.clone().into_span().start_pos();
let mut pairs = pair.into_inner();
pairs.next().unwrap(); // opening_paren
let pair = pairs.next().unwrap();

let node = consume_expr(pair.into_inner().peekable(), climber)?;
let node = consume_expr(pair.into_inner().peekable(), pratt)?;
let end = node.span.end_pos();

ParserNode {
Expand Down Expand Up @@ -525,7 +524,7 @@ fn consume_expr<'i>(
Ok(node)
}

let term = |pair: Pair<'i, Rule>| unaries(pair.into_inner().peekable(), climber);
let term = |pair: Pair<'i, Rule>| unaries(pair.into_inner().peekable(), pratt);
let infix = |lhs: Result<ParserNode<'i>, Vec<Error<Rule>>>,
op: Pair<'i, Rule>,
rhs: Result<ParserNode<'i>, Vec<Error<Rule>>>| match op.as_rule(
Expand Down Expand Up @@ -557,7 +556,10 @@ fn consume_expr<'i>(
_ => unreachable!()
};

climber.climb(pairs, term, infix)
pratt
.map_primary(term)
.map_infix(infix)
.parse(pairs)
}

fn unescape(string: &str) -> Option<String> {
Expand Down
3 changes: 3 additions & 0 deletions pest/Cargo.toml
Expand Up @@ -14,6 +14,9 @@ readme = "_README.md"
[dependencies]
ucd-trie = "0.1.1"

[dev-dependencies]
pest_derive = "2.0"

[badges]
codecov = { repository = "pest-parser/pest" }
maintenance = { status = "actively-developed" }
Expand Down
16 changes: 16 additions & 0 deletions pest/examples/calc.pest
@@ -0,0 +1,16 @@
WHITESPACE = _{ " " | "\t" | NEWLINE }

program = { SOI ~ expr ~ EOI }
expr = { prefix* ~ primary ~ postfix* ~ (infix ~ prefix* ~ primary ~ postfix* )* }
infix = _{ add | sub | mul | div | pow }
add = { "+" } // Addition
sub = { "-" } // Subtraction
mul = { "*" } // Multiplication
div = { "/" } // Division
pow = { "^" } // Exponentiation
prefix = _{ neg }
neg = { "-" } // Negation
postfix = _{ fac }
fac = { "!" } // Factorial
primary = _{ int | "(" ~ expr ~ ")" }
int = @{ (ASCII_NONZERO_DIGIT ~ ASCII_DIGIT+ | ASCII_DIGIT) }
113 changes: 113 additions & 0 deletions pest/examples/calc.rs
@@ -0,0 +1,113 @@
#![allow(bad_style)]

#[macro_use]
extern crate pest_derive;
extern crate pest;

mod parser {
#[derive(Parser)]
#[grammar = "../examples/calc.pest"]
pub struct Parser;
}

use parser::Rule;
use pest::{
iterators::Pairs,
pratt_parser::{Assoc::*, Op, PrattParser},
Parser
};
use std::io::{stdin, stdout, Write};

fn parse_to_str(pairs: Pairs<Rule>, pratt: &PrattParser<Rule>) -> String {
pratt
.map_primary(|primary| match primary.as_rule() {
Rule::int => primary.as_str().to_owned(),
Rule::expr => parse_to_str(primary.into_inner(), pratt),
_ => unreachable!()
})
.map_prefix(|op, rhs| match op.as_rule() {
Rule::neg => format!("(-{})", rhs),
_ => unreachable!()
})
.map_postfix(|lhs, op| match op.as_rule() {
Rule::fac => format!("({}!)", lhs),
_ => unreachable!()
})
.map_infix(|lhs, op, rhs| match op.as_rule() {
Rule::add => format!("({}+{})", lhs, rhs),
Rule::sub => format!("({}-{})", lhs, rhs),
Rule::mul => format!("({}*{})", lhs, rhs),
Rule::div => format!("({}/{})", lhs, rhs),
Rule::pow => format!("({}^{})", lhs, rhs),
_ => unreachable!()
})
.parse(pairs)
}

fn parse_to_i32(pairs: Pairs<Rule>, pratt: &PrattParser<Rule>) -> i128 {
pratt
.map_primary(|primary| match primary.as_rule() {
Rule::int => primary.as_str().parse().unwrap(),
Rule::expr => parse_to_i32(primary.into_inner(), pratt),
_ => unreachable!()
})
.map_prefix(|op, rhs| match op.as_rule() {
Rule::neg => -rhs,
_ => unreachable!()
})
.map_postfix(|lhs, op| match op.as_rule() {
Rule::fac => (1..lhs + 1).product(),
_ => unreachable!()
})
.map_infix(|lhs, op, rhs| match op.as_rule() {
Rule::add => lhs + rhs,
Rule::sub => lhs - rhs,
Rule::mul => lhs * rhs,
Rule::div => lhs / rhs,
Rule::pow => (1..rhs + 1).map(|_| lhs).product(),
_ => unreachable!()
})
.parse(pairs)
}

fn main() {
let pratt = PrattParser::new()
.op(Op::infix(Rule::add, Left) | Op::infix(Rule::sub, Left))
.op(Op::infix(Rule::mul, Left) | Op::infix(Rule::div, Left))
.op(Op::infix(Rule::pow, Right))
.op(Op::postfix(Rule::fac))
.op(Op::prefix(Rule::neg));

let stdin = stdin();
let mut stdout = stdout();

loop {
let source = {
print!("> ");
let _ = stdout.flush();
let mut input = String::new();
let _ = stdin.read_line(&mut input);
input.trim().to_string()
};

let pairs = match parser::Parser::parse(Rule::program, &source) {
Ok(mut parse_tree) => {
parse_tree
.next()
.unwrap()
.into_inner() // inner of program
.next()
.unwrap()
.into_inner() // inner of expr
}
Err(err) => {
println!("Failed parsing input: {:}", err);
continue;
}
};

print!("{} => ", source);
print!("{} => ", parse_to_str(pairs.clone(), &pratt));
println!("{}", parse_to_i32(pairs.clone(), &pratt));
}
}
1 change: 1 addition & 0 deletions pest/src/lib.rs
Expand Up @@ -76,6 +76,7 @@ mod parser;
mod parser_state;
mod position;
pub mod prec_climber;
pub mod pratt_parser;
mod span;
mod stack;
mod token;
Expand Down

0 comments on commit fce43c4

Please sign in to comment.