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#[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}