impral 0.1.6

A command parsing and evaluation library for a LISP dialect, specialized for commandline input.
Documentation
use std::fmt::*;

use crate::lexer::bareword_format;

/// A trait for formatting something as html.
pub trait FormatHtml {
    /// Dumps content as html.
    fn to_html(&self) -> String {
        let mut out = String::new();
        FormatHtml::fmt(self, &mut out).unwrap();
        out
    }
    
    /// Dumps content as html.
    fn fmt(&self, f: &mut String) -> std::fmt::Result;
    
    /// Write a single separating space.
    fn space(f: &mut String) -> std::fmt::Result {
        write!(f, " ")
    }
    
    /// Write the start of a tag.
    fn tag_start(f: &mut String, tag: &str, class: &str) -> std::fmt::Result {
        write!(f, "<{tag} class='{class}'>")
    }
    
    /// Write the end of a tag.
    fn tag_end(f: &mut String, tag: &str) -> std::fmt::Result {
        write!(f, "</{tag}>")
    }
    
    /// Write a tag.
    fn tag(
        f: &mut String,
        tag: &str,
        class: &str,
        inner: impl FnOnce(&mut String) -> std::fmt::Result
    ) -> std::fmt::Result {
        Self::tag_start(f, tag, class)?;
        inner(f)?;
        Self::tag_end(f, tag)?;
        Ok(())
    }
    
    /// Write a span with content.
    fn ispan(
        f: &mut String,
        class: &str,
        inner: impl FnOnce(&mut String) -> std::fmt::Result
    ) -> std::fmt::Result {
        Self::tag_start(f, "span", class)?;
        inner(f)?;
        Self::tag_end(f, "span")?;
        Ok(())
    }
    
    /// Write a span with content.
    fn fspan(
        f: &mut String,
        class: &str,
        inner: &impl FormatHtml
    ) -> std::fmt::Result {
        Self::tag_start(f, "span", class)?;
        FormatHtml::fmt(inner, f)?;
        Self::tag_end(f, "span")?;
        Ok(())
    }
    
    /// Write a span with text.
    fn tspan(
        f: &mut String,
        class: &str,
        inner: &str
    ) -> std::fmt::Result {
        Self::tag_start(f, "span", class)?;
        write!(f, "{inner}")?;
        Self::tag_end(f, "span")?;
        Ok(())
    }
}

impl FormatHtml for super::Expression {
    fn fmt(&self, f: &mut String) -> std::fmt::Result {
        Self::ispan(f,
            "expression",
            |f| match self {
                super::Expression::Empty => Self::tspan(f, "empty", "_"),
                
                super::Expression::Value(v) => FormatHtml::fmt(v, f),
                
                super::Expression::Invoke(i) => FormatHtml::fmt(i.as_ref(), f),
                
                super::Expression::Field(e, i)
                => Self::ispan(f, "field", |f| {
                    Self::fspan(f, "target", e.as_ref())?;
                    Self::tspan(f, "separator", ".")?;
                    Self::tspan(f, "member", i)?;
                    Ok(())
                }),
                
                super::Expression::Index(e, i)
                => Self::ispan(f, "index", |f| {
                    Self::fspan(f, "target", e.as_ref())?;
                    Self::tspan(f, "separator", "[")?;
                    Self::fspan(f, "index", i.as_ref())?;
                    Self::tspan(f, "separator", "]")?;
                    Ok(())
                }),
                
                super::Expression::Range(s, e, inc)
                => Self::ispan(f, "range", |f| {
                    Self::fspan(f, "start", s.as_ref())?;
                    Self::tspan(f, "separator", "..")?;
                    if *inc {Self::tspan(f, "inclusive", "=")?;}
                    Self::fspan(f, "end", e.as_ref())?;
                    Ok(())
                }),
                
                super::Expression::Try(e, t) => Self::ispan(f, "try", |f| {
                    Self::fspan(f, "target", e.as_ref())?;
                    Self::tspan(f, "operator", "?")?;
                    if *t {Self::tspan(f, "operator", "!")?};
                    Ok(())
                }),
                
                super::Expression::Pipe(p) => FormatHtml::fmt(p.as_ref(), f),
            }

        )
    }
}

impl FormatHtml for super::Literal {
    fn fmt(&self, f: &mut String) -> std::fmt::Result {
        match self {
            crate::lexer::Literal::Nil => Self::tspan(f, "literal null", "null"),
            crate::lexer::Literal::Bool(l) => Self::ispan(f, "literal bool", |f| write!(f, "{l:?}")),
            crate::lexer::Literal::Int(l) => Self::ispan(f, "literal int", |f| write!(f, "{l:?}")),
            crate::lexer::Literal::Dec(l) => Self::ispan(f, "literal dec", |f| write!(f, "{l:?}")),
            crate::lexer::Literal::Uid(l) => Self::ispan(f, "literal uid", |f| write!(f, "U{l:?}")),
            crate::lexer::Literal::Str(l) => Self::tspan(f, "literal str", &bareword_format(l)),
            crate::lexer::Literal::Byt(_l) => Self::ispan(f, "literal byt", |f| write!(f, "BINARY-DATA")),
            crate::lexer::Literal::RefRes => Self::tspan(f, "literal ref-res", "$"),
            crate::lexer::Literal::RefCtx => Self::tspan(f, "literal ref-ctx", "$$"),
            crate::lexer::Literal::RefVar(l) => Self::ispan(f, "literal ref-var", |f| write!(f, "${}", bareword_format(l))),
            crate::lexer::Literal::ObjIdx(l) => Self::ispan(f, "literal obj-idx", |f| write!(f, "@{l:?}")),
            crate::lexer::Literal::ObjUid(l) => Self::ispan(f, "literal obj-uid", |f| write!(f, "@{l:?}")),
            crate::lexer::Literal::ObjKey(l) => Self::ispan(f, "literal obj-key", |f| write!(f, "@{}", bareword_format(l))),
        }
    }
}

impl FormatHtml for super::Invoke {
    fn fmt(&self, f: &mut String) -> std::fmt::Result {
        Self::ispan(f, "invoke", |f| {
            Self::tspan(f, "name", &bareword_format(&self.name))?;
            
            for arg in &self.pos_args {
                Self::space(f)?;
                Self::fspan(f, "val", arg)?;
            }
            
            for (key, arg) in &self.nom_args {
                Self::space(f)?;
                Self::ispan(f, "key-val", |f| {
                    Self::tspan(f, "key", &bareword_format(key))?;
                    Self::tspan(f, "separator", "=")?;
                    Self::fspan(f, "val", arg)?;
                    Ok(())
                })?;
            }
            
            Ok(())
        })
    }
}

impl FormatHtml for super::Pipe {
    fn fmt(&self, f: &mut String) -> std::fmt::Result {
        Self::ispan(f, "pipe", |f| {
            Self::fspan(f, "source", &self.source)?;
            
            for seg in &self.stages {
                Self::space(f)?;
                Self::tspan(f, "separator", "|")?;
                if seg.filter {
                    Self::tspan(f, "filter", "?")?;
                }
                Self::space(f)?;
                Self::fspan(f, "segment", &seg.invoke)?;
            }
            
            Ok(())
        })
    }
}