use crate::{ParseState, TemplateParser};
use nargo_ir::{AttributeIR, Comment, ElementIR, ExpressionIR, ForIteratorIR, ForNodeIR, IfNodeIR, TemplateNodeIR, Trivia};
use nargo_types::{is_void_element, Error, Result, Span};
pub struct VueTemplateParser;
pub fn parse(source: &str) -> Result<Vec<TemplateNodeIR>> {
let mut state = ParseState::new(source);
let parser = VueTemplateParser;
parser.parse(&mut state, "html")
}
impl TemplateParser for VueTemplateParser {
fn parse(&self, state: &mut ParseState, _lang: &str) -> Result<Vec<TemplateNodeIR>> {
let mut parser = TemplateParserImpl { state };
parser.parse()
}
}
struct TemplateParserImpl<'a, 'b> {
state: &'a mut ParseState<'b>,
}
impl<'a, 'b> TemplateParserImpl<'a, 'b> {
fn consume_trivia(&mut self) -> Trivia {
let leading_whitespace = self.state.cursor.consume_whitespace();
let mut leading_comments = Vec::new();
while self.state.cursor.peek_str("<!--") {
let start_pos = self.state.cursor.position();
self.state.cursor.consume_n(4); let start = self.state.cursor.pos;
while !self.state.cursor.is_eof() && !self.state.cursor.peek_str("-->") {
self.state.cursor.consume();
}
let content = self.state.cursor.current_str(start).to_string();
let _ = self.state.cursor.consume_n(3); let end_pos = self.state.cursor.position();
leading_comments.push(Comment { content, is_block: true, span: Span { start: start_pos, end: end_pos } });
let _extra_ws = self.state.cursor.consume_whitespace();
}
Trivia { leading_whitespace, leading_comments, trailing_comments: Vec::new() }
}
pub fn parse(&mut self) -> Result<Vec<TemplateNodeIR>> {
let mut nodes = Vec::new();
while !self.state.cursor.is_eof() {
if self.state.cursor.peek() == '{' {
nodes.push(self.parse_interpolation()?);
}
else if self.state.cursor.peek() == '<' {
if self.state.cursor.peek_str("<!--") {
nodes.push(self.parse_comment()?);
}
else if self.state.cursor.peek_str("</") {
break; }
else {
nodes.push(self.parse_element()?);
}
}
else {
nodes.push(self.parse_text()?);
}
}
Ok(nodes)
}
fn parse_element(&mut self) -> Result<TemplateNodeIR> {
let start_pos = self.state.cursor.position();
self.state.cursor.expect('<')?;
let tag = self.state.cursor.consume_while(|c| c.is_alphanumeric() || c == '-');
if tag.is_empty() {
return Err(Error::parse_error("Expected tag name".to_string(), self.state.cursor.span_at_current()));
}
println!("Parsing element: <{}>", tag);
let mut attributes = Vec::new();
let mut trivia = self.consume_trivia();
while !self.state.cursor.is_eof() && self.state.cursor.peek() != '>' && !self.state.cursor.peek_str("/>") {
let attr_start = self.state.cursor.position();
let attr_name = self.state.cursor.consume_while(|c| !c.is_whitespace() && c != '=' && c != '>' && c != '/');
let mut attr_trivia = self.consume_trivia();
let attr_value = if self.state.cursor.peek() == '=' {
self.state.cursor.consume();
attr_trivia.leading_whitespace.push('=');
attr_trivia.leading_whitespace.push_str(&self.state.cursor.consume_whitespace());
let peek = self.state.cursor.peek();
if peek == '"' || peek == '\'' {
Some(self.state.cursor.consume_string()?)
}
else if peek == '{' {
let interpolation = self.parse_interpolation()?;
if let TemplateNodeIR::Interpolation(expr) = interpolation {
Some(expr.code)
}
else {
None
}
}
else {
Some(self.state.cursor.consume_while(|c| !c.is_whitespace() && c != '>' && c != '/'))
}
}
else {
None
};
let is_directive = attr_name.contains(':');
let is_dynamic = attr_value.as_ref().map(|v| v.starts_with('{') || is_directive).unwrap_or(false);
let value_ast = None;
let attr_end = self.state.cursor.position();
attributes.push(AttributeIR { name: attr_name, value: attr_value, value_ast, argument: None, modifiers: Vec::new(), is_directive, is_dynamic, span: Span { start: attr_start, end: attr_end }, trivia: attr_trivia });
trivia = self.consume_trivia();
}
let is_self_closing = if self.state.cursor.peek_str("/>") {
self.state.cursor.consume_n(2);
true
}
else {
self.state.cursor.expect('>')?;
false
};
let mut children = Vec::new();
if !is_self_closing && !is_void_element(&tag) {
if tag == "script" || tag == "style" {
let start = self.state.cursor.pos;
let end_tag = format!("</{}>", tag);
while !self.state.cursor.is_eof() && !self.state.cursor.peek_str(&end_tag) {
self.state.cursor.consume();
}
let content = self.state.cursor.current_str(start).to_string();
if !content.is_empty() {
let text_span = self.state.cursor.span_from(start_pos);
children.push(TemplateNodeIR::Text(content, text_span, Trivia::default()));
}
self.state.cursor.expect_str(&end_tag)?;
}
else if tag == "if" {
children = self.parse()?;
self.state.cursor.expect_str("</if>")?;
let end_pos = self.state.cursor.position();
let mut condition = ExpressionIR::default();
for attr in &attributes {
if attr.name == "test" || attr.name == "condition" {
if let Some(ref value) = attr.value {
condition = ExpressionIR { code: value.clone(), ast: attr.value_ast.clone(), is_static: false, span: attr.span, trivia: attr.trivia.clone() };
}
break;
}
}
return Ok(TemplateNodeIR::If(nargo_ir::IfNodeIR { condition, consequent: children, alternate: None, else_ifs: Vec::new(), span: Span { start: start_pos, end: end_pos } }));
}
else if tag == "for" {
children = self.parse()?;
self.state.cursor.expect_str("</for>")?;
let end_pos = self.state.cursor.position();
let mut iterator = nargo_ir::ForIteratorIR::default();
for attr in &attributes {
if attr.name == "each" {
if let Some(ref value) = attr.value {
if let Some((left, right)) = value.split_once(" in ") {
let left = left.trim();
if let Some((item, index)) = left.split_once(',') {
iterator.item = item.trim().to_string();
iterator.index = Some(index.trim().to_string());
}
else {
iterator.item = left.to_string();
}
iterator.collection = ExpressionIR { code: right.trim().to_string(), ast: None, is_static: false, span: attr.span, trivia: attr.trivia.clone() };
}
}
break;
}
}
return Ok(TemplateNodeIR::For(nargo_ir::ForNodeIR { iterator, body: children, span: Span { start: start_pos, end: end_pos } }));
}
else {
children = self.parse()?;
println!("Element <{}> found {} children", tag, children.len());
self.state.cursor.expect_str(&format!("</{}>", tag))?;
}
}
let end_pos = self.state.cursor.position();
Ok(TemplateNodeIR::Element(ElementIR { tag, attributes, children, is_static: false, span: Span { start: start_pos, end: end_pos }, trivia }))
}
fn parse_interpolation(&mut self) -> Result<TemplateNodeIR> {
let start_pos = self.state.cursor.position();
self.state.cursor.expect('{')?;
let start = self.state.cursor.pos;
let mut depth = 1;
while !self.state.cursor.is_eof() && depth > 0 {
let c = self.state.cursor.consume();
if c == '{' {
depth += 1;
}
else if c == '}' {
depth -= 1;
}
}
let content = self.state.cursor.current_str(start);
let content = &content[..content.len() - 1]; let content = content.trim().to_string();
let content = if content.starts_with('{') && content.ends_with('}') { content[1..content.len() - 1].trim().to_string() } else { content };
let end_pos = self.state.cursor.position();
let ast = None;
Ok(TemplateNodeIR::Interpolation(ExpressionIR { code: content, ast, is_static: false, span: Span { start: start_pos, end: end_pos }, trivia: Trivia::default() }))
}
fn parse_text(&mut self) -> Result<TemplateNodeIR> {
let start_pos = self.state.cursor.position();
let start = self.state.cursor.pos;
while !self.state.cursor.is_eof() && self.state.cursor.peek() != '<' && self.state.cursor.peek() != '{' {
self.state.cursor.consume();
}
let end_pos = self.state.cursor.position();
Ok(TemplateNodeIR::Text(self.state.cursor.current_str(start).to_string(), Span { start: start_pos, end: end_pos }, Trivia::default()))
}
fn parse_comment(&mut self) -> Result<TemplateNodeIR> {
let start_pos = self.state.cursor.position();
self.state.cursor.expect_str("<!--")?;
let start = self.state.cursor.pos;
while !self.state.cursor.is_eof() && !self.state.cursor.peek_str("-->") {
self.state.cursor.consume();
}
let content = self.state.cursor.current_str(start).to_string();
self.state.cursor.expect_str("-->")?;
let end_pos = self.state.cursor.position();
Ok(TemplateNodeIR::Comment(content, Span { start: start_pos, end: end_pos }, Trivia::default()))
}
}