doccy 0.3.2

Doccy is a simple brace based markup language.
Documentation
use ast::*;

whitespace -> &'input str
    = $(' ') / $('\t') / $('\r') / $('\n')

identifier -> &'input str
    = $([a-zA-Z][a-zA-Z0-9_-]*)

pub escape -> Data
    = escape:$("\\" .)
{
    Data::Escape(escape[1..].to_string())
}

pub line_break -> Data
    = line_break:$("\n"*<1,1000>)
{
    Data::LineBreak(line_break.len())
}

pub paragraph_break -> Data
    = paragraph_break:$("\n"*<2,1000>)
{
    Data::ParagraphBreak(paragraph_break.len())
}

pub url -> Data
    = schema:url_schema host:url_host port:url_port? path:url_path? query:url_query?
{
    let mut output = String::new();

    output.push_str(&schema);
    output.push_str(&host);

    if let Some(port) = port {
        output.push_str(&port);
    }

    if let Some(path) = path {
        output.push_str(&path);
    }

    if let Some(query) = query {
        output.push_str(&query);
    }

    Data::Url(output)
}

url_schema -> String
    = schema:$([a-z]+ "://")
{
    schema.to_string()
}

url_host -> String
    = host:$([^:/]+)
{
    host.to_string()
}

url_port -> String
    = port:$(":" [0-9]+)
{
    port.to_string()
}

url_path -> String
    = path:$("/" [^:?&#]+)
{
    path.to_string()
}

url_query -> String
    = query:$([?&#] [^:]*)
{
    query.to_string()
}

pub attribute -> Data
    = class_attribute / data_attribute / id_attribute / named_attribute

pub attributes -> Vec<Data>
    = attributes:attribute ++ (whitespace*)
{
    attributes
}

pub attribute_text -> Data
    = text:$([^\\{}@#%.:]+)
{
    Data::Text(text.to_string())
}

pub attribute_children -> Data
    = url / attribute_text / escape

pub class_attribute -> Data
    = "." name:$(identifier)
{
    ClassAttribute::new(name.to_string())
}

pub data_attribute -> Data
    = "%" name:$(identifier) whitespace* children:attribute_children*
{
    let length = children.len();
    let children: Option<Vec<Data>> = match length {
        0 => None,
        _ => Some(children)
    };

    DataAttribute::new(name.to_string(), children)
}

pub id_attribute -> Data
    = "#" name:$(identifier)
{
    IdAttribute::new(name.to_string())
}

pub named_attribute -> Data
    = "@" name:$(identifier) whitespace* children:attribute_children*
{
    let length = children.len();
    let children: Option<Vec<Data>> = match length {
        0 => None,
        _ => Some(children)
    };

    NamedAttribute::new(name.to_string(), children)
}

pub element_text -> Data
    = text:$([^\\{}\n]+)
{
    Data::Text(text.to_string())
}

pub element_children -> Data
    = element_text / escape / paragraph_break / line_break / element / preformatted_element / childless_element

pub element -> Data
    = "{" name:identifier whitespace* attributes:attributes? ":" !"#" whitespace* children:element_children* "}"
{
    let length = children.len();
    let children: Option<Vec<Data>> = match length {
        0 => None,
        _ => Some(children)
    };

    Element::new(name.to_string(), attributes, children)
}

pub childless_element -> Data
    = "{" name:identifier whitespace* attributes:attributes? whitespace* "}"
{
    Element::new(name.to_string(), attributes, None)
}

pub preformatted_chunk -> &'input str
    = $("\\#}" / [^\\])

pub preformatted_chunks -> &'input str
    = $(!":#" !"#}" preformatted_chunk)

pub preformatted_children -> Option<Vec<Data>>
    = ":#" chunks:preformatted_chunks* "#}"
{
    let mut output: Vec<Data> = Vec::new();
    let mut text = String::new();

    for chunk in &chunks {
        if chunk.starts_with("\\") {
            if text.len() > 0 {
                output.push(Data::Text(text));
                text = String::new();
            }

            output.push(Data::Escape(chunk[1..].to_string()));
        } else {
            text.push_str(chunk);
        }
    }

    if text.len() > 0 {
        output.push(Data::Text(text));
    }

    if output.len() > 0 {
        Some(output)
    } else {
        None
    }
}

pub preformatted_element -> Data
    = "{" name:identifier whitespace* attributes:attributes? whitespace* children:preformatted_children
{
    Element::new(name.to_string(), attributes, children)
}

pub document -> Data
    = children:element_children* EOF
{
    Element::new(String::from("_root_"), None, Some(children))
}

EOF = #quiet<!.> / #expected("EOF")