ruly/
lib.rs

1pub use pmacro_ruly::*;
2#[doc(hidden)]
3pub use regex::Regex;
4#[doc(hidden)]
5pub mod stack;
6
7#[macro_export(local_inner_macros)]
8#[doc(hidden)]
9macro_rules! __set_fun_sub {
10    ( [$t:ty]; $p:ident ) => {
11        __set_fun_sub!([$t, $t];$p)
12    };
13
14    ( [$s:ty, $t:ty]; $p:ident ) => {{
15        let mut st = stack::Stack::new(<$s>::read($p)?);
16
17        while let Ok(a) = <$t>::read($p) {
18            st.push("".to_string(), a);
19        }
20
21        st
22    }};
23
24    ( [$t:ty;($reg:expr)]; $p:ident ) => {
25        __set_fun_sub!([$t, $t; ($reg)];$p)
26    };
27
28    ( [$s:ty, $t:ty; ($reg:expr)]; $p:ident ) => {{
29        let tmp = $p.get_current();
30        let reg = Regex::new($reg).unwrap();
31
32        if let Ok(a) = <$s>::read($p) {
33            let mut st = stack::Stack::new(a);
34
35            while let Some((end, infix)) = $p.find_at_top(reg.clone()) {
36                if ReservedWords.contains(&{ &infix.to_string() }) {
37                    return Err((String::from("no match"), tmp));
38                }
39
40                $p.set_current(end);
41                $p.skip();
42
43                if let Ok(b) = <$t>::read($p) {
44                    st.push(
45                        infix.to_string(),
46                        b,
47                    );
48                } else {
49                    return Err((String::from("no match"), tmp));
50                }
51            }
52
53            st
54        } else {
55            return Err((String::from("no match"), tmp));
56        }
57    }};
58
59    ( $t:ty; $p:ident ) => {{
60        Box::new(<$t>::read($p)?)
61    }};
62
63    ( {  ( $t:expr ), $sort:ty, $c:expr }; $p:ident ) => {{
64        let tmp = $p.get_current();
65        let reg = Regex::new($t).unwrap();
66        let closure = $c;
67
68        match $p.find_at_top(reg) {
69            None => {
70                return Err((String::from("no match"), tmp));
71            }
72
73            Some((end, s)) => {
74                if ReservedWords.contains(&{ &s.to_string() }) {
75                    return Err((String::from("no match"), tmp));
76                }
77
78                $p.set_current(end);
79                $p.skip();
80                (s.to_string(), closure(s))
81            }
82        }
83    }};
84
85    ( {  Reserved ( $t:expr ), $sort:ty, $c:expr }; $p:ident ) => {{
86        let tmp = $p.get_current();
87        let reg = Regex::new($t).unwrap();
88        let closure = $c;
89
90        match $p.find_at_top(reg) {
91            None => {
92                return Err((String::from("no match"), tmp));
93            }
94
95            Some((end, s)) => {
96                if !ReservedWords.contains(&{ &s.to_string() }) {
97                    return Err((String::from("no match"), tmp));
98                }
99
100                $p.set_current(end);
101                $p.skip();
102                (s.to_string(), closure(s))
103            }
104        }
105    }};
106
107    ( { ( $t:expr ) }; $p:ident ) => {{
108        let tmp = $p.get_current();
109        let reg = Regex::new($t).unwrap();
110
111        match $p.find_at_top(reg) {
112            None => {
113                return Err((String::from("no match"), tmp));
114            }
115
116            Some((end, s)) => {
117                if ReservedWords.contains(&{ &s.to_string() }) {
118                    return Err((String::from("no match"), tmp));
119                }
120
121                $p.set_current(end);
122                $p.skip();
123                s.to_string()
124            }
125        }
126    }};
127
128    ( { Reserved ( $t:expr ) }; $p:ident ) => {{
129        let tmp = $p.get_current();
130        let reg = Regex::new($t).unwrap();
131
132        match $p.find_at_top(reg) {
133            None => {
134                return Err((String::from("no match"), tmp));
135            }
136
137            Some((end, s)) => {
138                if !ReservedWords.contains(&{ &s.to_string() }) {
139                    return Err((String::from("no match"), tmp));
140                }
141
142                $p.set_current(end);
143                $p.skip();
144                s.to_string()
145            }
146        }
147    }};
148}
149
150#[macro_export(local_inner_macros)]
151#[doc(hidden)]
152macro_rules! __set_fun {
153    ( $v:ident ; $i:ident ; ( $($sort:tt),* ); $p:ident ) => {
154        Ok( $v::$i( $( __set_fun_sub!($sort;$p) ),* ) )
155    };
156}
157
158// $v => $i $sorts | $i $sorts | ...
159// $sorts = ( $sort, $sort, ... )
160#[macro_export(local_inner_macros)]
161macro_rules! add_rule {
162    ( $v:ident => $( $i:ident $sorts:tt )|+ ) => {
163        create_enum!($v [ $([$i $sorts]),* ] );
164
165        impl<P: Parse> Product<P> for $v {
166            fn read(parser: &mut P) -> Result<Self, (String, usize)>{
167                let start_point = parser.get_current();
168
169                $(
170                let tmp = |p: &mut P| -> Result<Self, (String, usize)> {
171                    __set_fun!($v;$i;$sorts;p)
172                };
173                if let Ok(a) = tmp(parser){
174                    return Ok(a);
175                }
176                parser.set_current(start_point);
177                )*
178
179                Err((String::from("no match"), start_point))
180            }
181        }
182
183        impl std::fmt::Display for $v {
184            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
185                create_match!($v [ $([$i $sorts]),* ] );
186                std::write!(f, "")
187            }
188        }
189    };
190}
191
192#[macro_export(local_inner_macros)]
193macro_rules! reserved_words {
194    ( $( $e:expr ),* ) => {
195        const ReservedWords: [&str; 0 $(  + { $e ; 1 } )* ] = [ $( $e ),* ];
196    };
197}
198
199pub trait Parse: Sized {
200    #[doc(hidden)]
201    fn skip(&mut self);
202
203    #[doc(hidden)]
204    fn get_current(&self) -> usize;
205
206    #[doc(hidden)]
207    fn set_current(&mut self, c: usize);
208
209    #[doc(hidden)]
210    fn find_at_top(&self, reg: Regex) -> Option<(usize, String)>;
211
212    fn new() -> Self;
213
214    fn set_input(&mut self, s: &str);
215
216    fn set_skip_reg(&mut self, reg_str: &str);
217
218    fn run<T: Product<Self>>(&mut self) -> Result<T, (String, usize)>;
219}
220
221pub trait Product<P: Parse>: Sized {
222    fn read(p: &mut P) -> Result<Self, (String, usize)>;
223}
224
225#[derive(Debug)]
226pub struct Ruly {
227    input: String,
228    current: usize,
229    skip_reg: Regex,
230}
231
232impl Parse for Ruly {
233    fn skip(&mut self) {
234        if let Some(mat) = self.skip_reg.find_at(&self.input, self.current) {
235            if self.current == mat.start() {
236                self.current = mat.end();
237            }
238        }
239    }
240
241    fn get_current(&self) -> usize {
242        self.current
243    }
244
245    fn set_current(&mut self, c: usize) {
246        self.current = c;
247    }
248
249    fn find_at_top(&self, reg: Regex) -> Option<(usize, String)> {
250        if let Some(mat) = reg.find_at(&self.input, self.current) {
251            if self.current == mat.start() {
252                return Some((mat.end(), mat.as_str().to_string()));
253            }
254        }
255
256        None
257    }
258
259    fn new() -> Self {
260        Ruly {
261            input: String::new(),
262            current: 0,
263            skip_reg: Regex::new(r"").unwrap(),
264        }
265    }
266
267    fn set_input(&mut self, s: &str) {
268        self.input = s.to_string();
269        self.current = 0;
270    }
271
272    fn set_skip_reg(&mut self, reg_str: &str) {
273        self.skip_reg = Regex::new(reg_str).unwrap();
274    }
275
276    fn run<T: Product<Self>>(&mut self) -> Result<T, (String, usize)> {
277        self.skip();
278        let ret = T::read(self);
279        if self.is_end() {
280            ret
281        } else {
282            Err((self.get_next_chars(), self.get_current()))
283        }
284    }
285}
286
287#[doc(hidden)]
288impl Ruly {
289    fn is_end(&self) -> bool {
290        self.current == self.input.len()
291    }
292
293    fn get_next_chars(&self) -> String {
294        String::from(&self.input[self.current..std::cmp::min(self.input.len(), self.current + 20)])
295    }
296}
297
298#[cfg(test)]
299mod tests {
300    use super::*;
301    #[test]
302    fn test() {
303        reserved_words!();
304
305        add_rule!(
306            Nat => Zero ({("Z")})
307                |   Succ ({("S")},{(r"\(")},Nat,{(r"\)")})
308        );
309
310        add_rule!(
311            Judgement => Plus (Nat, {("plus")}, Nat, {("is")}, Nat)
312                |   Times (Nat, {("times")}, Nat, {("is")}, Nat)
313        );
314
315        println!("reserved words: {:?}", ReservedWords);
316
317        let s = "S(Z) plus S(S(S(Z))) is S(S(S(S(Z))))";
318        let mut ruly = Ruly::new();
319        ruly.set_skip_reg(r"[ \n\r\t]*");
320        ruly.set_input(&s);
321
322        match ruly.run::<Judgement>() {
323            Ok(judgement) => {
324                println!("{:?}", judgement);
325                println!("{}", judgement);
326            }
327
328            err => {
329                println!("{:?}", err);
330            }
331        }
332    }
333}