Commit 656c4f9d authored by Daan Sprenkels's avatar Daan Sprenkels
Browse files

combinators: add `seq` and `many` combinator

parent 6c01b4ee
Pipeline #951 failed with stage
......@@ -6,21 +6,21 @@ pub struct Op2(pub Box<Ast>, pub Box<Ast>);
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Ast {
LiteralInt(i64),
Add(Op2),
Sub(Op2),
Mul(Op2),
Div(Op2),
Mod(Op2),
Eq(Op2),
Lt(Op2),
Gt(Op2),
Le(Op2),
Ge(Op2),
Ne(Op2),
And(Op2),
Or(Op2),
Colon(Op2),
Seq(Vec<Ast>)
Add(Option<Op2>),
Sub(Option<Op2>),
Mul(Option<Op2>),
Div(Option<Op2>),
Mod(Option<Op2>),
Eq(Option<Op2>),
Lt(Option<Op2>),
Gt(Option<Op2>),
Le(Option<Op2>),
Ge(Option<Op2>),
Ne(Option<Op2>),
And(Option<Op2>),
Or(Option<Op2>),
Colon(Option<Op2>),
LiteralIntList(Vec<Ast>)
}
pub type Parser = Box<Fn(&[Token]) -> Vec<(Ast, &[Token])>>;
......@@ -45,6 +45,11 @@ pub fn satisfy(pred: Box<Fn(&Token) -> bool>, p: Parser) -> Parser {
)
}
pub fn is(input: &Token, p: Parser) -> Parser {
let input_token = input.clone();
satisfy(Box::new(move |x| x == &input_token), p)
}
pub fn take1(p: Parser) -> Parser {
Box::new(move |input|
match input.split_first() {
......@@ -67,17 +72,62 @@ pub fn or2(p1: Parser, p2: Parser) -> Parser {
or(vec!(p1, p2))
}
pub fn seq2(p1: Parser, p2: Parser) -> Parser {
// TODO: make this suitbale for chains of any length
pub fn seq(ps: Vec<Parser>, reduce_func: Box<Fn(Vec<Ast>) -> Ast>) -> Parser {
Box::new(move |input| {
let result1 = p1(input);
let mut output = Vec::new();
for (parse1, remaining1) in result1 {
let result2 = p2(remaining1);
for (parse2, remaining2) in result2 {
output.push((Ast::Seq(vec!(parse1.clone(), parse2.clone())), remaining2));
assert!(ps.len() >= 1);
let mut outputs: Vec<(Vec<Ast>, &[Token])> = vec!((Vec::new(), input));
// initialize the rest of the list
for p in ps.iter() {
let mut new_outputs: Vec<(Vec<Ast>, &[Token])> = Vec::new();
for (parses, remaining) in outputs {
for (new_parse, new_remaining) in p(remaining) {
let mut new_parses = parses.clone();
new_parses.push(new_parse);
new_outputs.push((new_parses, new_remaining));
}
}
outputs = new_outputs;
}
// reduce the collected tokens into one token
outputs.into_iter().map(|x| {
let (parse, remaining) = x;
(reduce_func(parse), remaining)
}).collect()
})
}
pub fn seq2(p1: Parser, p2: Parser, reduce_func: Box<Fn(Vec<Ast>) -> Ast>) -> Parser {
seq(vec!(p1, p2), reduce_func)
}
pub fn many(p: Parser, reduce_func: Box<Fn(Vec<Ast>) -> Ast>) -> Parser {
Box::new(move |input| {
let mut outputs: Vec<(Vec<Ast>, &[Token])> = vec!((Vec::new(), input));
let mut last_added_outputs = outputs.clone();
// initialize the rest of the list
loop {
let mut new_outputs: Vec<(Vec<Ast>, &[Token])> = Vec::new();
for (parses, remaining) in last_added_outputs {
for (new_parse, new_remaining) in p(remaining) {
let mut new_parses = parses.clone();
new_parses.push(new_parse);
new_outputs.push((new_parses, new_remaining));
}
}
if new_outputs.is_empty() {
break;
}
last_added_outputs = new_outputs.clone();
outputs.append(&mut new_outputs);
}
output
// reduce the collected tokens into one token
outputs.into_iter().map(|x| {
let (parse, remaining) = x;
(reduce_func(parse), remaining)
}).collect()
})
}
use combinators::*;
use scanner::Token;
use std::rc::Rc;
#[test]
fn test_fail() {
......@@ -43,22 +42,58 @@ fn p_literalint() -> Parser {
)
}
fn p_op2() -> Parser {
seq(vec!(
p_literalint(),
or(vec!(
is(&Token::Plus, token(Ast::Add(None))),
is(&Token::Minus, token(Ast::Sub(None)))
)),
p_literalint()
), Box::new(move |vec| {
let children = Some(Op2(Box::new(vec[0].clone()), Box::new(vec[2].clone())));
match vec[1] {
Ast::Add(_) => Ast::Add(children),
Ast::Sub(_) => Ast::Sub(children),
_ => unreachable!()
}
}))
}
#[test]
fn test_op2() {
let i_plus = &vec!(Token::Plus)[..];
let i_minus = &vec!(Token::Minus)[..];
let p_plus = token(Ast::LiteralInt(3));
let p_satisfy = satisfy(
Box::new(move |x| x == &Token::Plus),
token(Ast::LiteralInt(3))
// test for plus
assert_eq!(
p_op2()(&[Token::LiteralInt(3), Token::Plus, Token::LiteralInt(5)][..]),
vec!((
Ast::Add(Some(Op2(
Box::new(Ast::LiteralInt(3)), Box::new(Ast::LiteralInt(5))
))), &[][..]
))
);
// assert_eq!(
// p_plus_or_plus()(&[Token::Plus][..]),
// vec!((Ast::Op2(Op2::Add), &[Token::Plus][..]), (Ast::Op2(Op2::Add), &[][..]))
// );
// assert_eq!(
// p_plus_and_then_or_before_minus()(&[Token::Minus, Token::Plus][..]),
// vec!((Ast::Seq(vec!(Ast::Op2(Op2::Sub), Ast::Op2(Op2::Add))), &[][..]))
// );
// test for minus
assert_eq!(
p_op2()(&[Token::LiteralInt(3), Token::Minus, Token::LiteralInt(5)][..]),
vec!((
Ast::Sub(Some(Op2(
Box::new(Ast::LiteralInt(3)), Box::new(Ast::LiteralInt(5))
))), &[][..]
))
);
}
fn p_many_threes() -> Parser {
let p_three = is(&Token::LiteralInt(3), token(Ast::LiteralInt(3)));
many(p_three, Box::new(move |x| Ast::LiteralIntList(x)))
}
#[test]
fn test_many_threes() {
let lexes = vec!(Token::LiteralInt(3), Token::LiteralInt(3));
let parses = p_many_threes()(&lexes[..]);
let nought = (Ast::LiteralIntList(vec!()), &[Token::LiteralInt(3), Token::LiteralInt(3)][..]);
let one = (Ast::LiteralIntList(vec!(Ast::LiteralInt(3))), &[Token::LiteralInt(3)][..]);
let two = (Ast::LiteralIntList(vec!(Ast::LiteralInt(3), Ast::LiteralInt(3))), &[][..]);
assert_eq!(parses, vec!(nought, one, two));
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment