esprit 0.0.5

An ECMAScript parser library.
Documentation
use std::fmt;
use std::fmt::{Display, Formatter};
use std::{cmp, usize};
use joker::track::span;
use easter::expr::Expr;
use easter::punc::{Binop, Logop, Precedence};

#[derive(Debug)]
pub enum Infix {
    Binop(Binop),
    Logop(Logop)
}

impl Infix {
    fn groups_left(&self, right: &Infix) -> bool {
        self.precedence() >= right.precedence()
    }
}

impl Display for Infix {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        match *self {
            Infix::Binop(ref op) => op.fmt(fmt),
            Infix::Logop(ref op) => op.fmt(fmt)
        }
    }
}

impl Precedence for Infix {
    fn precedence(&self) -> u32 {
        match *self {
            Infix::Binop(ref op) => op.precedence(),
            Infix::Logop(ref op) => op.precedence()
        }
    }
}

#[derive(Debug)]
struct Frame {
    left: Expr,
    op: Infix
}

impl Precedence for Frame {
    fn precedence(&self) -> u32 {
        self.op.precedence()
    }
}

impl Frame {
    fn fill(self, right: Expr) -> Expr {
        let location = span(&self.left, &right);
        match self.op {
            Infix::Binop(op) => Expr::Binop(location, op, Box::new(self.left), Box::new(right)),
            Infix::Logop(op) => Expr::Logop(location, op, Box::new(self.left), Box::new(right))
        }
    }
}

impl Frame {
    fn width(&self) -> usize {
        FrameExpr(&self.left).width() + 1 + self.op.to_string().len() + 1 + 2
    }
}

impl Display for Frame {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        fmt.write_fmt(format_args!("{} {} []", FrameExpr(&self.left), self.op))
    }
}

trait StringExt {
    fn repeat(&self, usize) -> String;
}

impl StringExt for String {
    fn repeat(&self, n: usize) -> String {
        let len = self.len() * n;
        let mut result = String::with_capacity(len);
        for _ in 0..n {
            result.push_str(&self[..]);
        }
        result
    }
}

struct FrameExpr<'a>(&'a Expr);

impl<'a> FrameExpr<'a> {
    fn width(&self) -> usize {
        match *self {
            FrameExpr(&Expr::Binop(_, ref op, ref left, ref right)) => {
                1 + FrameExpr(left).width() + 1 + op.to_string().len() + 1 + FrameExpr(right).width() + 1
            }
            FrameExpr(&Expr::Logop(_, ref op, ref left, ref right)) => {
                1 + FrameExpr(left).width() + 1 + op.to_string().len() + 1 + FrameExpr(right).width() + 1
            }
            _ => 1
        }
    }
}

impl<'a> Display for FrameExpr<'a> {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        match *self {
            FrameExpr(&Expr::Binop(_, ref op, ref left, ref right)) => {
                fmt.write_fmt(format_args!("({} {} {})", FrameExpr(left), op, FrameExpr(right)))
            }
            FrameExpr(&Expr::Logop(_, ref op, ref left, ref right)) => {
                fmt.write_fmt(format_args!("({} {} {})", FrameExpr(left), op, FrameExpr(right)))
            }
            _ => fmt.write_str("_")
        }
    }
}

#[derive(Debug)]
pub struct Stack {
    frames: Vec<Frame>
}

impl Stack {
    pub fn new() -> Stack {
        Stack { frames: Vec::new() }
    }

    pub fn extend(&mut self, mut left: Expr, op: Infix) {
        let mut len;
        while { len = self.frames.len(); len > 0 } && self.frames[len - 1].op.groups_left(&op) {
            left = self.frames.pop().unwrap().fill(left);
        }
        self.frames.push(Frame { left: left, op: op });
    }

    pub fn finish(mut self, mut right: Expr) -> Expr {
        while self.frames.len() > 0 {
            right = self.frames.pop().unwrap().fill(right);
        }
        right
    }
}

impl Display for Stack {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        if self.frames.is_empty() {
            return fmt.write_str("[]");
        }
        let width = self.frames.iter().fold(usize::MIN, |max, f| cmp::max(max, f.width()));
        let border = format!("+{}+", "-".to_string().repeat(width + 2));
        for frame in self.frames.iter() {
            try!(fmt.write_str(&border[..]));
            try!(fmt.write_fmt(format_args!("\n| {}{} |\n", frame, " ".to_string().repeat(width - frame.width()))));
        }
        try!(fmt.write_str(&border[..]));
        try!(fmt.write_str("\n"));
        Ok(())
    }
}