seam

Symbolic-Expressions As Markup.
git clone git://git.knutsen.co/seam
Log | Files | Refs | README | LICENSE

xml.rs (5167B)


//! Assembles an expanded tree into valid XML.
use super::{MarkupDisplay, GenerationError, Formatter};
use crate::parse::parser::{self, ParseNode, ParseTree};

#[derive(Debug, Clone)]
pub struct XMLFormatter {
    pub tree : ParseTree
}

impl XMLFormatter {
    pub fn new(tree : ParseTree) -> Self {
        Self { tree }
    }

    fn display_attribute(&self, attr : &parser::AttributeNode)
    -> Result<String, GenerationError> {
        if let Some(symbol) = (*attr.node).atomic() {
            Ok(format!("{}=\"{}\"", attr.keyword, symbol.value))
        } else {
            Err(GenerationError::new("XML",
                "Attribute can only contain symbols, numbers or strings",
                &(*attr.node).site()))
        }
    }

}

pub const DEFAULT : &str =
    "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";

impl MarkupDisplay for XMLFormatter {
    fn document(&self) -> Result<String, GenerationError> {
        let mut doc = String::new();
        if self.tree.is_empty() {
            return Ok(String::from(DEFAULT));
        }
        let stripped = parser::strip(&self.tree, true);
        let current_node = stripped.get(0);

        // Check if declaration exists.
        let mut has_declaration = false;
        if let Some(ParseNode::List(list)) = current_node.as_ref() {
            if let Some(ParseNode::Symbol(declaration)) = list.get(0) {
                if declaration.value.to_lowercase() == "?xml" {
                    has_declaration = true;
                }
            }
        }

        if !has_declaration {
            doc += DEFAULT;
        }

        // Populate.
        doc += &self.display()?;
        doc += "<!-- Generated by SEAM, from symbolic-expressions \
                     into XML. -->\n";
        Ok(doc)
    }

    fn generate(&self, f : Formatter)
    -> Result<(), GenerationError> {
        let mut tree_iter = self.tree.iter().peekable();
        while let Some(node) = tree_iter.next() {
            match node {
                ParseNode::Symbol(node)
                | ParseNode::Number(node) => {
                    // If the node ahead is so-called "symbolic", we can
                    // infere there was a space between them.
                    write!(f, "{}", node.value)?;
                    if let Some(peek) = tree_iter.peek() {
                        if peek.symbolic().is_some() {
                            write!(f, " ")?
                        }
                    }
                },
                ParseNode::String(node) => write!(f, "{}", node.value)?,
                ParseNode::List(list) => {
                    let head = list.first();
                    let mut tag = "";
                    if let Some(head_node) = head {
                        if let ParseNode::Symbol(head_symbol) = head_node {
                            tag = &head_symbol.value;
                            write!(f, "<{}", tag)?;
                        } else {
                            // Error, tags can only have symbol values.
                        }
                    } else {
                        // Error, empty tags not supported.
                    }

                    let mut rest = &list[1..];

                    // Declarations behave differently.
                    let front = tag.as_bytes()[0] as char;
                    if front == '!' || front == '?' {
                        while !rest.is_empty() {
                            if let ParseNode::List(_list) = &rest[0] {
                                // TODO: Throw error.
                            } else {
                                if let Some(node) = rest[0].symbolic() {
                                    write!(f, "{}", node.value)?;
                                } else if let ParseNode::Attribute(a) = &rest[0] {
                                    write!(f, " {}", self.display_attribute(a)?)?;
                                } else {
                                    // Error.
                                }
                            }
                            rest = &rest[1..];
                        }
                        if front == '?' {
                            write!(f, " ?>")?;
                        } else {
                            write!(f, ">")?;
                        }
                        continue;
                    }

                    while let Some(ParseNode::Attribute(attr)) = rest.first() {
                        write!(f, " {}", self.display_attribute(&attr)?)?;
                        rest = &rest[1..];
                    }
                    write!(f, ">")?;

                    let xml_fmt = XMLFormatter::new(rest.to_owned());
                    xml_fmt.generate(f)?;
                    write!(f, "</{}>", tag)?;
                },
                _ => return Err(GenerationError::new("XML",
                        "Unknonw node encountered.", &node.site()))
            }
        }
        Ok(())
    }
}


// TODO: Convert special characters to XML compatible ones.
// e.g.
//      <  =>  &lt;
//      >  =>  &gt;
//      &  =>  &amp;
//      "  =>  &quot;
//      !  =>  &excl;
//      etc.