1#![feature(test)]
2
3use std::{
4 cmp::PartialEq,
5 error::Error,
6 fmt::{Debug, Display, Formatter},
7 mem::forget,
8 ptr::null_mut,
9 result::Result,
10 slice,
11 str::FromStr,
12};
13
14impl PartialEq for Wood {
28 fn eq(&self, other: &Self) -> bool {
29 match *self {
30 Leafv(ref sa) => match *other {
31 Leafv(ref so) => sa.v == so.v,
32 _ => false,
33 },
34 Branchv(ref la) => match *other {
35 Branchv(ref lo) => la.v == lo.v,
36 _ => false,
37 },
38 }
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct Branch {
44 pub line: isize,
45 pub column: isize,
46 pub v: Vec<Wood>,
47}
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct Leaf {
50 pub line: isize,
51 pub column: isize,
52 pub v: String,
53}
54#[derive(Debug, Clone, Eq)]
55pub enum Wood {
56 Branchv(Branch),
57 Leafv(Leaf),
58}
59pub use Wood::*;
60
61impl From<String> for Wood {
62 fn from(v: String) -> Wood {
63 Leafv(Leaf {
64 line: -1,
65 column: -1,
66 v,
67 })
68 }
69}
70impl<'a> From<&'a str> for Wood {
71 fn from(v: &'a str) -> Wood {
72 Wood::from(v.to_string())
73 }
74}
75impl From<Vec<Wood>> for Wood {
76 fn from(v: Vec<Wood>) -> Wood {
77 Branchv(Branch {
78 line: -1,
79 column: -1,
80 v,
81 })
82 }
83}
84
85fn tail<I: Iterator>(mut v: I) -> I {
86 v.next();
87 v
88}
89
90fn push_escaped(take: &mut String, give: &str) {
101 for c in give.chars() {
102 match c {
103 '\n' => {
104 take.push('\\');
105 take.push('n');
106 }
107 '\t' => {
108 take.push('\\');
109 take.push('t');
110 }
111 '"' => {
112 take.push('\\');
113 take.push('"');
114 }
115 _ => {
116 take.push(c);
117 }
118 }
119 }
120}
121
122pub enum LB<'a> {
124 L(&'a str),
125 B(&'a [Wood]),
126}
127pub use LB::*;
128
129impl Wood {
130 pub fn leaf(v: String) -> Wood {
131 Wood::Leafv(Leaf {
132 line: -1,
133 column: -1,
134 v: v,
135 })
136 }
137 pub fn branch(v: Vec<Wood>) -> Wood {
138 Wood::Branchv(Branch {
139 line: -1,
140 column: -1,
141 v: v,
142 })
143 }
144 pub fn empty() -> Wood {
145 Wood::branch(Vec::new())
146 }
147 pub fn is_leaf(&self) -> bool {
148 match self {
149 &Leafv(_) => true,
150 _ => false,
151 }
152 }
153 pub fn is_branch(&self) -> bool {
154 match self {
155 &Branchv(_) => true,
156 _ => false,
157 }
158 }
159 pub fn what(&self) -> LB<'_> {
160 match *self {
161 Leafv(ref s) => L(&s.v),
162 Branchv(ref s) => B(&s.v),
163 }
164 }
165 pub fn get_leaf(&self) -> Option<&str> {
166 match *self {
167 Leafv(ref s) => Some(&s.v),
168 Branchv(_) => None,
169 }
170 }
171 pub fn get_branch(&self) -> Option<&[Wood]> {
172 match *self {
173 Leafv(_) => None,
174 Branchv(ref s) => Some(&s.v),
175 }
176 }
177 pub fn line_and_col(&self) -> (isize, isize) {
178 match *self {
179 Wood::Branchv(ref l) => (l.line, l.column),
180 Wood::Leafv(ref a) => (a.line, a.column),
181 }
182 }
183 pub fn line(&self) -> isize {
184 match *self {
185 Leafv(ref s) => s.line,
186 Branchv(ref s) => s.line,
187 }
188 }
189 pub fn col(&self) -> isize {
190 match *self {
191 Leafv(ref s) => s.column,
192 Branchv(ref s) => s.column,
193 }
194 }
195 pub fn initial_str(&self) -> &str {
206 match *self {
207 Branchv(ref v) => {
208 if let Some(ref ss) = v.v.first() {
209 ss.initial_str()
210 } else {
211 ""
212 }
213 }
214 Leafv(ref v) => v.v.as_str(),
215 }
216 }
217 pub fn to_string(&self) -> String {
218 to_woodslist(self)
219 }
220
221 pub fn strip_comments_escape(&mut self, comment_str: &str, comment_escape_str: &str) {
222 match *self {
223 Branchv(ref mut b) => {
224 b.v.retain_mut(|i| {
225 if i.initial_str() == comment_str {
226 false
227 } else {
228 i.strip_comments_escape(comment_str, comment_escape_str);
229 true
230 }
231 });
232 }
233 Leafv(ref mut l) => {
234 if &l.v == comment_escape_str {
235 l.v = comment_str.into();
236 }
237 }
238 }
239 }
240
241 pub fn strip_comments(&mut self, comment_str: &str) {
246 let escstr = format!("\\{}", comment_str);
247 self.strip_comments_escape(comment_str, &escstr);
248 }
249
250 pub fn contents(&self) -> std::slice::Iter<'_, Self> {
252 match *self {
253 Branchv(ref v) => v.v.iter(),
254 Leafv(_) => std::slice::from_ref(self).iter(),
255 }
256 }
257 pub fn head(&self) -> Result<&Wood, Box<WoodError>> {
259 self.contents().next().ok_or_else(|| {
260 Box::new(WoodError::new(
261 self,
262 "there shouldn't be an empty list here".to_string(),
263 ))
264 })
265 }
266 pub fn second(&self) -> Result<&Wood, Box<WoodError>> {
268 self.tail().next().ok_or_else(|| {
269 Box::new(WoodError::new(
270 self,
271 "a second wood was supposed to be present".to_string(),
272 ))
273 })
274 }
275 pub fn tail<'b>(&'b self) -> std::slice::Iter<'b, Self> {
277 match *self {
278 Branchv(ref v) => tail((*v).v.iter()),
279 Leafv(_) => [].iter(),
280 }
281 }
282 pub fn flatter_tail<'a>(
291 &'a self,
292 ) -> std::iter::Chain<slice::Iter<'a, Self>, slice::Iter<'a, Self>> {
293 let mut r = self.contents();
294 if let Some(first) = r.next() {
295 first.tail().chain(r)
296 } else {
297 [].iter().chain([].iter())
298 }
299 }
300 pub fn seek<'a, 'b>(&'a self, key: &'b str) -> Option<&'a Wood> {
303 self.contents().find(|el| el.initial_str() == key)
304 }
305 pub fn seek_val<'a, 'b>(&'a self, key: &'b str) -> Option<&'a Wood> {
306 self.seek(key).and_then(|w| w.tail().next())
307 }
308
309 pub fn find<'a, 'b>(&'a self, key: &'b str) -> Result<&'a Wood, Box<WoodError>> {
311 self.seek(key).ok_or_else(|| {
312 Box::new(WoodError::new(
313 self,
314 format!("could not find child with key \"{}\"", key),
315 ))
316 })
317 }
318 pub fn find_val<'a, 'b>(&'a self, key: &'b str) -> Result<&'a Wood, Box<WoodError>> {
320 self.find(key).and_then(|v| v.second())
321 }
322}
323
324#[macro_export]
326macro_rules! woods {
327 ($($el:expr),* $(,)?)=> {
328 $crate::Branchv($crate::Branch{line:-1, column:-1, v:vec!($($crate::Wood::from($el)),*)})
329 };
330 }
332
333pub trait Wooder<T> {
334 fn woodify(&self, v: &T) -> Wood;
335}
336pub trait Dewooder<T> {
337 fn dewoodify(&self, v: &Wood) -> Result<T, Box<WoodError>>;
338}
339
340#[derive(Debug)]
341pub struct WoodError {
342 pub line: isize,
343 pub column: isize,
344 pub msg: String,
345 pub cause: Option<Box<dyn Error>>,
346}
347impl Display for WoodError {
348 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
349 Debug::fmt(self, f)
350 }
351}
352impl WoodError {
353 pub fn new(source: &Wood, msg: String) -> Self {
354 let (line, column) = source.line_and_col();
355 Self {
356 line,
357 column,
358 msg,
359 cause: None,
360 }
361 }
362 pub fn new_with_cause(source: &Wood, msg: String, cause: Box<dyn Error>) -> Self {
363 let (line, column) = source.line_and_col();
364 WoodError {
365 line,
366 column,
367 msg,
368 cause: Some(cause),
369 }
370 }
371}
372impl Error for WoodError {
373 fn cause(&self) -> Option<&dyn Error> {
374 self.cause.as_ref().map(|e| e.as_ref())
375 }
376}
377
378pub trait Woodable {
379 fn woodify(&self) -> Wood;
380}
381pub trait Dewoodable {
382 fn dewoodify(v: &Wood) -> Result<Self, Box<WoodError>>
383 where
384 Self: Sized;
385}
386
387pub fn woodify<T>(v: &T) -> Wood
388where
389 T: Woodable,
390{
391 v.woodify()
392}
393pub fn dewoodify<T>(v: &Wood) -> Result<T, Box<WoodError>>
394where
395 T: Dewoodable,
396{
397 T::dewoodify(v)
398}
399
400pub fn deserialize<T>(v: &str) -> Result<T, Box<WoodError>>
402where
403 T: Dewoodable,
404{
405 parse_termpose(v).and_then(|w| dewoodify(&w))
406}
407pub fn serialize<T>(v: &T) -> String
409where
410 T: Woodable,
411{
412 woodify(v).to_string()
413}
414
415macro_rules! do_basic_stringifying_woodable_for {
416 ($Type:ident) => {
417 impl Woodable for $Type {
418 fn woodify(&self) -> Wood {
419 self.to_string().into()
420 }
421 }
422 };
423}
424macro_rules! do_basic_destringifying_dewoodable_for {
425 ($Type:ident) => {
426 impl Dewoodable for $Type {
427 fn dewoodify(v: &Wood) -> Result<$Type, Box<WoodError>> {
428 $Type::from_str(v.initial_str()).map_err(|er| {
429 Box::new(WoodError::new_with_cause(
430 v,
431 format!("couldn't parse {}", stringify!($name)),
432 Box::new(er),
433 ))
434 })
435 }
436 }
437 };
438}
439
440do_basic_stringifying_woodable_for!(char);
441do_basic_destringifying_dewoodable_for!(char);
442do_basic_stringifying_woodable_for!(u32);
443do_basic_destringifying_dewoodable_for!(u32);
444do_basic_stringifying_woodable_for!(u64);
445do_basic_destringifying_dewoodable_for!(u64);
446do_basic_stringifying_woodable_for!(u128);
447do_basic_destringifying_dewoodable_for!(u128);
448do_basic_stringifying_woodable_for!(i32);
449do_basic_destringifying_dewoodable_for!(i32);
450do_basic_stringifying_woodable_for!(i64);
451do_basic_destringifying_dewoodable_for!(i64);
452do_basic_stringifying_woodable_for!(i128);
453do_basic_destringifying_dewoodable_for!(i128);
454do_basic_stringifying_woodable_for!(f32);
455do_basic_destringifying_dewoodable_for!(f32);
456do_basic_stringifying_woodable_for!(f64);
457do_basic_destringifying_dewoodable_for!(f64);
458do_basic_stringifying_woodable_for!(isize);
459do_basic_destringifying_dewoodable_for!(isize);
460do_basic_stringifying_woodable_for!(usize);
461do_basic_destringifying_dewoodable_for!(usize);
462
463do_basic_stringifying_woodable_for!(bool);
464impl Dewoodable for bool {
465 fn dewoodify(v: &Wood) -> Result<Self, Box<WoodError>> {
466 match v.initial_str() {
467 "true" | "⊤" | "yes" => Ok(true),
468 "false" | "⟂" | "no" => Ok(false),
469 _ => Err(Box::new(WoodError::new(v, "expected a bool here".into()))),
470 }
471 }
472}
473
474impl Woodable for String {
479 fn woodify(&self) -> Wood {
480 self.as_str().into()
481 }
482}
483impl Dewoodable for String {
484 fn dewoodify(v: &Wood) -> Result<Self, Box<WoodError>> {
485 match *v {
486 Leafv(ref a) => Ok(a.v.clone()),
487 Branchv(_) => Err(Box::new(WoodError::new(
488 v,
489 "sought string, found branch".into(),
490 ))),
491 }
492 }
493}
494
495pub fn woodify_seq_into<'a, InnerTran, T, I>(inner: &InnerTran, v: I, output: &mut Vec<Wood>)
496where
497 InnerTran: Wooder<T>,
498 I: Iterator<Item = &'a T>,
499 T: 'a,
500{
501 for vi in v {
502 output.push(inner.woodify(vi));
503 }
504}
505pub fn dewoodify_seq_into<'a, InnerTran, T, I>(
506 inner: &InnerTran,
507 v: I,
508 output: &mut Vec<T>,
509) -> Result<(), Box<WoodError>>
510where
511 InnerTran: Dewooder<T>,
512 I: Iterator<Item = &'a Wood>,
513{
514 for vi in v {
516 match inner.dewoodify(vi) {
517 Ok(vii) => output.push(vii),
518 Err(e) => return Err(e),
519 }
520 }
521 Ok(())
522 }
529
530impl<T> Woodable for Vec<T>
531where
532 T: Woodable,
533{
534 fn woodify(&self) -> Wood {
535 let mut ret = Vec::new();
536 woodify_seq_into(&wooder::Iden, self.iter(), &mut ret);
537 ret.into()
538 }
539}
540impl<T> Dewoodable for Vec<T>
541where
542 T: Dewoodable,
543{
544 fn dewoodify(v: &Wood) -> Result<Vec<T>, Box<WoodError>> {
545 let mut ret = Vec::new();
546 dewoodify_seq_into(&wooder::Iden, v.contents(), &mut ret)?;
547 Ok(ret)
548 }
549}
550
551mod parsers;
552pub use parsers::*;
553
554pub mod wooder;
555
556#[cfg(test)]
557mod tests {
558 extern crate test;
559 use super::*;
560
561 #[test]
562 fn test_comment_stripping() {
563 let mut w = parse_multiline_termpose(
564 "
565
566#\"this function cannot be called with a false object criterion
567fn process_ishm_protocol_handling object criterion
568 if criterion(object)
569 #\"then it returns good
570 return good
571 else
572 return angered
573 return secret_egg
574
575",
576 )
577 .unwrap();
578 w.strip_comments("#");
579
580 let should_be = parse_multiline_termpose(
581 "
582
583fn process_ishm_protocol_handling object criterion
584 if criterion(object)
585 return good
586 else
587 return angered
588 return secret_egg
589
590",
591 )
592 .unwrap();
593
594 assert_eq!(&w, &should_be);
595 }
596
597 }