brainfuck/
parser.rs

1pub use nom::types::CompleteByteSlice;
2
3use crate::ast::{Block, Node};
4use nom::{alt, call, complete, do_parse, eof, is_not, many0, many_till, map, preceded, tag};
5
6const ALLOWED: &'static str = "<>+-.,[]";
7
8pub fn bf_skip_unknown<T>(i: T) -> nom::IResult<T, T, u32>
9    where T:            nom::InputTake+nom::InputTakeAtPosition+Copy,
10          &'static str: nom::FindToken<<T as nom::InputTakeAtPosition>::Item> {
11    is_not!(i, ALLOWED).or_else(|e| match e {
12        nom::Err::Incomplete(size) => Err(nom::Err::Incomplete(size)),
13        _                          => Ok((i, i.take(0))),
14    })
15}
16
17pub fn bf_skip_unknown_no_incomplete<T>(i: T) -> nom::IResult<T, T, u32>
18    where T:            nom::InputTake+nom::InputTakeAtPosition+Copy,
19          &'static str: nom::FindToken<<T as nom::InputTakeAtPosition>::Item> {
20    bf_skip_unknown(i).or(Ok((i, i.take(0))))
21}
22
23macro_rules! bf_tag (
24    ($i: expr, $tag: expr) => ({
25        do_parse!($i,
26                 bf_skip_unknown               >>
27            res: tag!($tag)                    >>
28                 bf_skip_unknown_no_incomplete >>
29            (res)
30        )
31    });
32);
33
34macro_rules! bf_named {
35    (pub $name:ident<$o:ty>, $submac:ident!( $($args:tt)* )) => (
36        fn $name<T>( i: T ) -> nom::IResult<T, $o, u32>
37            where T:            nom::InputTake+nom::InputTakeAtPosition+nom::AtEof+nom::Compare<&'static str>+Copy+PartialEq,
38                  &'static str: nom::FindToken<<T as nom::InputTakeAtPosition>::Item> {
39            $submac!(i, $($args)*)
40        }
41    );
42}
43
44bf_named!(pub lshift<Node>,     do_parse!(bf_tag!("<") >> (Node::LShift)));
45bf_named!(pub rshift<Node>,     do_parse!(bf_tag!(">") >> (Node::RShift)));
46bf_named!(pub plus<Node>,       do_parse!(bf_tag!("+") >> (Node::Inc)));
47bf_named!(pub minus<Node>,      do_parse!(bf_tag!("-") >> (Node::Dec)));
48bf_named!(pub dot<Node>,        do_parse!(bf_tag!(".") >> (Node::PutCh)));
49bf_named!(pub comma<Node>,      do_parse!(bf_tag!(",") >> (Node::GetCh)));
50bf_named!(pub parse_loop<Node>, preceded!(bf_tag!("["), map!(many_till!(call!(node), bf_tag!("]")), |(nodes, _)| Node::Loop(From::from(nodes)))));
51bf_named!(pub node<Node>,       alt!(lshift | rshift | plus | minus | dot | comma | parse_loop));
52
53pub fn parse<T>(i: T) -> Result<Block, nom::Err<T, u32>>
54    where T:            nom::InputTake+nom::InputTakeAtPosition+nom::InputLength+nom::AtEof+nom::Compare<&'static str>+Copy+PartialEq,
55          &'static str: nom::FindToken<<T as nom::InputTakeAtPosition>::Item> {
56    do_parse!(i,
57        res: map!(many0!(complete!(node)), From::from) >>
58             eof!()                                    >>
59        (res)
60    ).map(|(_, block)| block)
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    use crate::ast::{Block, Node};
68    use nom::{self, Needed};
69
70    const EMPTY: &'static [u8] = b"";
71
72    #[test]
73    fn test_lshift() {
74        assert_eq!(lshift(&b"<"[..]),   Ok((EMPTY, Node::LShift)));
75        assert_eq!(lshift(&b"a"[..]),   Err(nom::Err::Incomplete(Needed::Size(1))));
76        assert_eq!(lshift(&b"a<b"[..]), Ok((&b"b"[..], Node::LShift)));
77    }
78
79    #[test]
80    fn test_rshift() {
81        assert_eq!(rshift(&b">"[..]),   Ok((EMPTY, Node::RShift)));
82        assert_eq!(rshift(&b"a"[..]),   Err(nom::Err::Incomplete(Needed::Size(1))));
83        assert_eq!(rshift(&b"a>b"[..]), Ok((&b"b"[..], Node::RShift)));
84    }
85
86    #[test]
87    fn test_plus() {
88        assert_eq!(plus(&b"+"[..]),   Ok((EMPTY, Node::Inc)));
89        assert_eq!(plus(&b"a"[..]),   Err(nom::Err::Incomplete(Needed::Size(1))));
90        assert_eq!(plus(&b"a+b"[..]), Ok((&b"b"[..], Node::Inc)));
91    }
92
93    #[test]
94    fn test_minus() {
95        assert_eq!(minus(&b"-"[..]),   Ok((EMPTY, Node::Dec)));
96        assert_eq!(minus(&b"a"[..]),   Err(nom::Err::Incomplete(Needed::Size(1))));
97        assert_eq!(minus(&b"a-b"[..]), Ok((&b"b"[..], Node::Dec)));
98    }
99
100    #[test]
101    fn test_dot() {
102        assert_eq!(dot(&b"."[..]),   Ok((EMPTY, Node::PutCh)));
103        assert_eq!(dot(&b"a"[..]),   Err(nom::Err::Incomplete(Needed::Size(1))));
104        assert_eq!(dot(&b"a.b"[..]), Ok((&b"b"[..], Node::PutCh)));
105    }
106
107    #[test]
108    fn test_comma() {
109        assert_eq!(comma(&b","[..]),   Ok((EMPTY, Node::GetCh)));
110        assert_eq!(comma(&b"a"[..]),   Err(nom::Err::Incomplete(Needed::Size(1))));
111        assert_eq!(comma(&b"a,b"[..]), Ok((&b"b"[..], Node::GetCh)));
112    }
113
114    #[test]
115    fn test_parse_loop() {
116        let nodes1 = vec![Node::RShift, Node::Inc, Node::LShift, Node::PutCh];
117        let nodes2 = vec![Node::RShift, Node::Inc, Node::LShift, Node::PutCh];
118        assert_eq!(parse_loop(&b"[>+<.]"[..]),            Ok((EMPTY, Node::Loop(From::from(nodes1)))));
119        assert_eq!(parse_loop(&b"a"[..]),                 Err(nom::Err::Incomplete(Needed::Size(1))));
120        assert_eq!(parse_loop(&b"a[ b>  +e<//.'r]@"[..]), Ok((&b"@"[..], Node::Loop(From::from(nodes2)))));
121    }
122
123    #[test]
124    fn test_parse_nested_loop() {
125        let iinodes = vec![Node::Inc,    Node::LShift];
126        let inodes  = vec![Node::RShift, Node::Loop(From::from(iinodes)), Node::Dec];
127        let nodes   = vec![Node::GetCh,  Node::Loop(From::from(inodes)),  Node::PutCh];
128        assert_eq!(parse_loop(&b"[,[>[+<]-].]"[..]), Ok((EMPTY, Node::Loop(From::from(nodes)))));
129    }
130
131    #[test]
132    fn test_node() {
133        let nodes = vec![Node::RShift, Node::Inc, Node::LShift, Node::PutCh];
134        assert_eq!(node(&b"<"[..]),      Ok((EMPTY, Node::LShift)));
135        assert_eq!(node(&b">"[..]),      Ok((EMPTY, Node::RShift)));
136        assert_eq!(node(&b"+"[..]),      Ok((EMPTY, Node::Inc)));
137        assert_eq!(node(&b"-"[..]),      Ok((EMPTY, Node::Dec)));
138        assert_eq!(node(&b"."[..]),      Ok((EMPTY, Node::PutCh)));
139        assert_eq!(node(&b","[..]),      Ok((EMPTY, Node::GetCh)));
140        assert_eq!(node(&b"[>+<.]"[..]), Ok((EMPTY, Node::Loop(From::from(nodes)))));
141        assert_eq!(node(&b"a"[..]),      Err(nom::Err::Incomplete(Needed::Size(1))));
142    }
143
144    #[test]
145    fn test_parse() {
146        let mut block = Block::new();
147        block.push(Node::Loop(From::from(vec![Node::LShift, Node::RShift])));
148        block.push(Node::PutCh);
149        assert_eq!(parse(CompleteByteSlice(&b"abc[<>]."[..])), Ok(block));
150        assert_eq!(parse(CompleteByteSlice(EMPTY)),            Ok(Block::new()));
151    }
152}