Skip to main content

glitter_lang/
interpreter.rs

1//! Interpreter which transforms expressions into the desired output
2
3use crate::ast::{self, CompleteStyle, Delimiter, Expression, Name, Tree};
4use crate::color::*;
5use crate::git::Stats;
6
7use std::{fmt, io};
8
9/// Various types of Interpreter errors
10#[derive(Debug)]
11pub enum InterpreterErr {
12    UnexpectedArgs { exp: Expression },
13    WriteError(io::Error),
14}
15
16impl From<io::Error> for InterpreterErr {
17    fn from(e: io::Error) -> Self {
18        InterpreterErr::WriteError(e)
19    }
20}
21
22type Result<T = bool> = std::result::Result<T, InterpreterErr>;
23
24/// The interpreter which transforms a gist expression using the provided stats
25#[derive(Debug, PartialEq, Eq, Default, Clone)]
26pub struct Interpreter {
27    stats: Stats,
28    allow_color: bool,
29    bash_prompt: bool,
30    command_queue: Vec<WriteCommand>,
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
34enum WriteCommand {
35    WriteContext(CompleteStyle),
36    WriteStr(&'static str),
37    #[allow(unused)] // unused variant left in case of extension
38    WriteString(String),
39}
40
41impl Interpreter {
42    /// Create a new Interpreter with the given stats
43    pub fn new(stats: Stats, allow_color: bool, bash_prompt: bool) -> Interpreter {
44        Interpreter {
45            stats,
46            allow_color,
47            bash_prompt,
48            command_queue: Vec::with_capacity(32),
49        }
50    }
51
52    fn drain_queue(&mut self, i: usize) {
53        self.command_queue.truncate(self.command_queue.len() - i);
54    }
55
56    fn queue_str(&mut self, s: &'static str) {
57        self.command_queue.push(WriteCommand::WriteStr(s));
58    }
59
60    #[inline(always)]
61    fn with_command<F, C, W>(&mut self, w: &mut W, c: WriteCommand, execute: F, close: C) -> Result
62    where
63        W: io::Write,
64        F: Fn(&mut Self, &mut W) -> Result,
65        C: Fn(&mut Self, &mut W) -> Result<()>,
66    {
67        self.command_queue.push(c);
68        execute(self, w).and_then(|wrote| {
69            if wrote {
70                close(self, w).map(|_| true)
71            } else {
72                self.command_queue.pop();
73                Ok(false)
74            }
75        })
76    }
77
78    /// Evaluate an expression tree and return the resulting formatted `String`
79    pub fn evaluate<W: io::Write>(&mut self, exps: &Tree, w: &mut W) -> Result<()> {
80        if self.allow_color {
81            self.queue_str(if self.bash_prompt {
82                "\u{01}\x1B[0m\u{02}"
83            } else {
84                "\x1B[0m"
85            });
86        }
87
88        if self.interpret_tree(w, &exps, CompleteStyle::default())? && self.allow_color {
89            if self.bash_prompt {
90                write!(w, "\u{01}\x1B[0m\u{02}")?;
91            } else {
92                write!(w, "\x1B[0m")?;
93            }
94        }
95
96        self.command_queue.clear();
97
98        Ok(())
99    }
100
101    #[inline(always)]
102    fn write_queue<W: io::Write>(&mut self, w: &mut W) -> Result<()> {
103        for command in self.command_queue.drain(..) {
104            use WriteCommand::*;
105            match command {
106                WriteString(s) => write!(w, "{}", s)?,
107                WriteContext(c) => c.write_to(w, self.bash_prompt)?,
108                WriteStr(s) => write!(w, "{}", s)?,
109            }
110        }
111
112        Ok(())
113    }
114
115    fn interpret_tree<W: io::Write>(
116        &mut self,
117        w: &mut W,
118        exps: &Tree,
119        context: CompleteStyle,
120    ) -> Result {
121        use Expression::*;
122        let mut wrote = false;
123        let mut separator_count = 0;
124        for e in &exps.0 {
125            match e {
126                Separator(s) => {
127                    // Queue all separators if anything has been written in this
128                    // tree so far
129                    if wrote {
130                        separator_count += 1;
131                        self.queue_str(s.as_str());
132                    }
133                }
134                e => {
135                    // Clear separators between previous expression and the current
136                    // one which was not written, to prevent accumulating separators
137                    // between elements which were not supposed to have them
138                    if self.interpret(w, &e, context)? {
139                        wrote = true;
140                    } else {
141                        self.drain_queue(separator_count);
142                    }
143                    separator_count = 0;
144                }
145            }
146        }
147        self.drain_queue(separator_count);
148        Ok(wrote)
149    }
150
151    fn interpret<W: io::Write>(
152        &mut self,
153        w: &mut W,
154        exp: &Expression,
155        ctx: CompleteStyle,
156    ) -> Result {
157        use ast::Expression::*;
158
159        match exp {
160            Named { name, ref sub } => self.interpret_named(w, *name, sub, ctx),
161            Group { d, ref sub } => self.interpret_group(w, *d, sub, ctx),
162            Format { ref style, ref sub } => self.interpret_format(w, *style, sub, ctx),
163            Literal(ref literal) => {
164                self.write_queue(w)?;
165                write!(w, "{}", literal)?;
166                Ok(true)
167            }
168            Separator(_) => unreachable!("Separator must be handled in tree interpreter"),
169        }
170    }
171
172    fn interpret_group<W: io::Write>(
173        &mut self,
174        w: &mut W,
175        d: Delimiter,
176        sub: &Tree,
177        style: CompleteStyle,
178    ) -> Result {
179        if sub.0.len() > 0 {
180            self.with_command(
181                w,
182                WriteCommand::WriteStr(d.left()),
183                |i, w| i.interpret_tree(w, &sub, style),
184                |_, w| write!(w, "{}", d.right()).map_err(|e| e.into()),
185            )
186        } else {
187            Ok(false)
188        }
189    }
190
191    #[inline(always)]
192    fn optional_prefix<W: io::Write, V1: fmt::Display + Empty, V2: fmt::Display>(
193        &mut self,
194        w: &mut W,
195        sub: &Tree,
196        val: V1,
197        prefix: V2,
198        ctx: CompleteStyle,
199    ) -> Result {
200        if val.is_empty() {
201            return Ok(false);
202        }
203
204        self.write_queue(w)?;
205
206        if sub.0.is_empty() {
207            write!(w, "{}{}", prefix, val)?;
208            return Ok(true);
209        }
210
211        if self.interpret_tree(w, sub, ctx)? {
212            write!(w, "{}", val)?;
213        } else {
214            write!(w, "{}{}", prefix, val)?;
215        }
216
217        Ok(true)
218    }
219
220    #[inline(always)]
221    fn interpret_literal<W: io::Write>(&mut self, w: &mut W, sub: &Tree, s: &str) -> Result {
222        if sub.0.is_empty() {
223            write!(w, "{}", s)?;
224            Ok(true)
225        } else {
226            Err(InterpreterErr::UnexpectedArgs {
227                exp: Expression::Named {
228                    name: Name::Quote,
229                    sub: sub.clone(),
230                },
231            })
232        }
233    }
234
235    #[inline(always)]
236    fn interpret_named<W: io::Write>(
237        &mut self,
238        w: &mut W,
239        name: Name,
240        sub: &Tree,
241        ctx: CompleteStyle,
242    ) -> Result {
243        use ast::Name::*;
244        match name {
245            Branch => self.optional_prefix(w, sub, self.stats.branch.clone(), "", ctx),
246            Remote => self.optional_prefix(w, sub, self.stats.remote.clone(), "", ctx),
247            Ahead => self.optional_prefix(w, sub, self.stats.ahead, "+", ctx),
248            Behind => self.optional_prefix(w, sub, self.stats.behind, "-", ctx),
249            Conflict => self.optional_prefix(w, sub, self.stats.conflicts, "U", ctx),
250            Added => self.optional_prefix(w, sub, self.stats.added_staged, "A", ctx),
251            Untracked => self.optional_prefix(w, sub, self.stats.untracked, "?", ctx),
252            Modified => self.optional_prefix(w, sub, self.stats.modified_staged, "M", ctx),
253            Unstaged => self.optional_prefix(w, sub, self.stats.modified, "M", ctx),
254            Deleted => self.optional_prefix(w, sub, self.stats.deleted, "D", ctx),
255            DeletedStaged => self.optional_prefix(w, sub, self.stats.deleted_staged, "D", ctx),
256            Renamed => self.optional_prefix(w, sub, self.stats.renamed, "R", ctx),
257            Stashed => self.optional_prefix(w, sub, self.stats.stashes, "H", ctx),
258            Quote => self.interpret_literal(w, sub, "'"),
259        }
260    }
261
262    fn interpret_format<W: io::Write>(
263        &mut self,
264        w: &mut W,
265        style: CompleteStyle,
266        sub: &Tree,
267        mut context: CompleteStyle,
268    ) -> Result {
269        let prev = context;
270        context += style;
271
272        self.with_command(
273            w,
274            WriteCommand::WriteContext(context),
275            |i, w| i.interpret_tree(w, sub, context),
276            |i, w| {
277                prev.write_difference(w, &context, i.bash_prompt)
278                    .map_err(|e| e.into())
279            },
280        )
281    }
282}
283
284/// Trait which determines what is empty in the eyes of the Interpreter
285///
286/// The interpreter simply ignores the macros which correspond to "empty" values.
287trait Empty {
288    fn is_empty(&self) -> bool;
289}
290
291impl Empty for u16 {
292    fn is_empty(&self) -> bool {
293        *self == 0
294    }
295}
296
297impl Empty for str {
298    fn is_empty(&self) -> bool {
299        self.is_empty()
300    }
301}
302
303impl Empty for String {
304    fn is_empty(&self) -> bool {
305        self.is_empty()
306    }
307}
308
309impl<T> Empty for Vec<T> {
310    fn is_empty(&self) -> bool {
311        self.is_empty()
312    }
313}
314
315#[cfg(test)]
316mod test {
317
318    use super::*;
319    use crate::git::Stats;
320    use ast;
321    use ast::{Delimiter, Expression, Name, Tree};
322    use proptest::arbitrary::any;
323    use proptest::collection::vec;
324    use proptest::strategy::Strategy;
325
326    proptest! {
327        #[test]
328        fn empty_stats_empty_result(
329            name in ast::arb_name()
330                .prop_filter("Quote is never empty".to_owned(),
331                             |n| *n != Name::Quote)
332        ) {
333
334            let stats: Stats = Default::default();
335
336            let mut interpreter = Interpreter::new(stats, false, false);
337
338            let exp = Expression::Named { name, sub: Tree::new() };
339
340            let mut output = Vec::new();
341            match interpreter.evaluate(&Tree(vec![exp.clone()]), &mut output) {
342                Ok(()) => {
343                    println!("interpreted {} as {} ({:?})", exp, String::from_utf8_lossy(&output), output);
344                    assert!(output.is_empty())
345                },
346                Err(e) => {
347                    println!("{:?}", e);
348                    panic!("Error in proptest")
349                }
350            }
351        }
352
353        #[test]
354        fn empty_group_empty_result(
355            name in ast::arb_name()
356                .prop_filter("Quote is never empty".to_owned(),
357                             |n| *n != Name::Quote)
358        ) {
359            let stats = Stats::default();
360            let interior = Expression::Named { name, sub: Tree::new(), };
361            let exp = Expression::Group {
362                d: Delimiter::Curly,
363                sub: Tree(vec![interior]),
364            };
365
366            let mut interpreter = Interpreter::new(stats, false, false);
367
368            let mut output = Vec::with_capacity(32);
369            match interpreter.evaluate(&Tree(vec![exp.clone()]), &mut output) {
370                Ok(()) => {
371                    println!(
372                        "interpreted {} as \"{}\" ({:?})",
373                        exp,
374                        String::from_utf8(output.clone()).unwrap(),
375                        output
376                    );
377                    prop_assert!(output.is_empty());
378                }
379                Err(e) => {
380                    println!("{:?} printing {}", e,  String::from_utf8(output).unwrap());
381                    prop_assert!(false, "Failed to interpret tree");
382                }
383            }
384        }
385
386        #[test]
387        fn empty_format_empty_result(
388            name in ast::arb_name()
389                .prop_filter("Quote is never empty".to_owned(),
390                             |n| *n != Name::Quote),
391            style in vec(ast::arb_style(), 1..10),
392            bash_prompt in any::<bool>()
393        ) {
394            let stats = Stats::default();
395            let interior = Expression::Named { name, sub: Tree::new(), };
396            let exp = Expression::Format {
397                style: style.iter().collect(),
398                sub: Tree(vec![interior]),
399            };
400
401            let mut interpreter = Interpreter::new(stats, true, bash_prompt);
402            let mut output = Vec::with_capacity(32);
403            match interpreter.evaluate(&Tree(vec![exp.clone()]), &mut output) {
404                Ok(()) => {
405                    println!(
406                        "interpreted {} as {} ({:?})",
407                        exp,
408                        String::from_utf8(output.clone()).unwrap(),
409                        output
410                    );
411                    prop_assert!(output.is_empty());
412                }
413                Err(e) => {
414                    println!("{:?} printing {}", e,  String::from_utf8(output.clone()).unwrap());
415                    prop_assert!(false, "Failed to interpret tree");
416                }
417            }
418        }
419    }
420}