use crate::ast::*;
use crate::parser::Parser;
use dmc_diagnostic::Code;
use dmc_lexer::token::TokenKind;
impl<'eng, 'tokens> Parser<'eng, 'tokens> {
fn skip_jsx_ws(&mut self) {
while matches!(self.peek_kind(), Some(TokenKind::Whitespace)) {
self.advance();
}
}
pub(crate) fn parse_jsx(&mut self) -> Node {
let span = self.current_span();
self.advance();
self.skip_jsx_ws();
let name = if let Some(t) = self.peek() {
if matches!(t.kind, TokenKind::JsxTagName) {
let n = t.raw.to_string();
self.advance();
n
} else {
String::new()
}
} else {
String::new()
};
self.skip_jsx_ws();
let attrs = self.parse_jsx_attrs();
self.skip_jsx_ws();
match self.peek_kind() {
Some(TokenKind::JsxSelfClosingEnd) => {
self.advance();
return Node::JsxSelfClosing(JsxSelfClosing { name, attrs, span });
},
Some(TokenKind::JsxOpenTagEnd) => {
self.advance();
},
_ => {
self.warn(
Code::RecoveredUnterminatedJsx,
format!("unterminated JSX open tag <{name}> -- synthesizing self-close"),
);
return Node::JsxSelfClosing(JsxSelfClosing { name, attrs, span });
},
}
let mut children = Vec::new();
loop {
match self.peek_kind() {
Some(TokenKind::JsxCloseTagStart) => {
self.advance();
self.skip_jsx_ws();
if matches!(self.peek_kind(), Some(TokenKind::JsxTagName)) {
self.advance();
}
self.skip_jsx_ws();
if matches!(self.peek_kind(), Some(TokenKind::JsxCloseTagEnd)) {
self.advance();
}
break;
},
Some(TokenKind::Eof) | None => break,
_ => {
let before = self.pos;
if let Some(node) = self.parse_block() {
children.push(node);
}
if self.pos == before {
self.advance();
}
},
}
}
if name.is_empty() {
Node::JsxFragment(JsxFragment { children, span })
} else {
Node::JsxElement(JsxElement { name, attrs, children, span })
}
}
fn parse_jsx_attrs(&mut self) -> Vec<JsxAttr> {
let mut out = Vec::new();
self.skip_jsx_ws();
while let Some(TokenKind::JsxAttributeName) = self.peek_kind() {
let span = self.current_span();
let name = self.peek().unwrap().raw.to_string();
self.advance();
self.skip_jsx_ws();
let value = if matches!(self.peek_kind(), Some(TokenKind::Eq)) {
self.advance();
self.skip_jsx_ws();
match self.peek_kind() {
Some(TokenKind::String) => {
let s = self.peek().unwrap().raw.to_string();
self.advance();
JsxAttrValue::String(s)
},
Some(TokenKind::ExpressionStart) => {
self.advance();
let mut s = String::new();
while let Some(t) = self.peek() {
match &t.kind {
TokenKind::ExpressionEnd | TokenKind::Eof => break,
_ => {
s.push_str(t.raw);
self.advance();
},
}
}
if matches!(self.peek_kind(), Some(TokenKind::ExpressionEnd)) {
self.advance();
}
JsxAttrValue::Expression(s)
},
_ => JsxAttrValue::Boolean,
}
} else {
JsxAttrValue::Boolean
};
out.push(JsxAttr { name, value, span });
self.skip_jsx_ws();
}
out
}
pub(crate) fn parse_jsx_expression(&mut self) -> Node {
let span = self.current_span();
self.advance();
let mut s = String::new();
while let Some(t) = self.peek() {
match &t.kind {
TokenKind::ExpressionEnd | TokenKind::Eof => break,
_ => {
s.push_str(t.raw);
self.advance();
},
}
}
if matches!(self.peek_kind(), Some(TokenKind::ExpressionEnd)) {
self.advance();
}
Node::JsxExpression(JsxExpression { value: s, span })
}
pub(crate) fn skip_md_comment(&mut self) {
self.advance();
while let Some(t) = self.peek() {
match &t.kind {
TokenKind::MarkdownCommentEnd => {
self.advance();
break;
},
TokenKind::Eof => break,
_ => {
self.advance();
},
}
}
}
}