use crate::ast::{
Expr, Keyword, Spanned, XmlAttribute, XmlDocumentOrContent, XmlFunc, XmlIndentOption,
XmlPassingMechanism, XmlStandalone, XmlWhitespaceOption,
};
use crate::error::ParseResult;
use crate::parser::Dialect;
use crate::parser::engine::Parser;
use crate::tokenizer::{Punctuation, Token};
use thin_vec::ThinVec;
impl<'a, D: Dialect> Parser<'a, D> {
pub(super) fn parse_xml_expr(&mut self, token: Token) -> ParseResult<Expr<D::Ext>> {
let crate::tokenizer::TokenKind::Keyword(keyword) = token.kind else {
unreachable!("parse_xml_expr is reached only with a keyword token");
};
let xml_func = match keyword {
Keyword::Xmlelement => self.parse_xml_element(token)?,
Keyword::Xmlforest => self.parse_xml_forest(token)?,
Keyword::Xmlconcat => self.parse_xml_concat(token)?,
Keyword::Xmlparse => self.parse_xml_parse(token)?,
Keyword::Xmlpi => self.parse_xml_pi(token)?,
Keyword::Xmlroot => self.parse_xml_root(token)?,
Keyword::Xmlserialize => self.parse_xml_serialize(token)?,
Keyword::Xmlexists => self.parse_xml_exists(token)?,
_ => unreachable!("parse_xml_expr dispatched a non-XML keyword"),
};
let span = token.span.union(self.preceding_span());
let meta = self.make_meta(span);
Ok(Expr::XmlFunc {
xml_func: Box::new(xml_func),
meta,
})
}
fn parse_xml_element(&mut self, token: Token) -> ParseResult<XmlFunc<D::Ext>> {
self.advance()?; self.expect_punct(Punctuation::LParen, "`(` after `xmlelement`")?;
self.expect_keyword(Keyword::Name)?;
let name = self.parse_as_alias_ident()?;
let mut attributes = ThinVec::new();
let mut content = ThinVec::new();
if self.eat_punct(Punctuation::Comma)? {
if self.peek_is_keyword(Keyword::Xmlattributes)?
&& self.peek_nth_is_punct(1, Punctuation::LParen)?
{
self.advance()?; self.expect_punct(Punctuation::LParen, "`(` after `xmlattributes`")?;
attributes = self.parse_comma_separated(Self::parse_xml_attribute)?;
self.expect_punct(Punctuation::RParen, "`)` to close `xmlattributes`")?;
if self.eat_punct(Punctuation::Comma)? {
content = self.parse_comma_separated(Self::parse_expr)?;
}
} else {
content = self.parse_comma_separated(Self::parse_expr)?;
}
}
self.expect_punct(Punctuation::RParen, "`)` to close `xmlelement`")?;
let meta = self.make_meta(token.span.union(self.preceding_span()));
Ok(XmlFunc::Element {
name,
attributes,
content,
meta,
})
}
fn parse_xml_forest(&mut self, token: Token) -> ParseResult<XmlFunc<D::Ext>> {
self.advance()?; self.expect_punct(Punctuation::LParen, "`(` after `xmlforest`")?;
let elements = self.parse_comma_separated(Self::parse_xml_attribute)?;
self.expect_punct(Punctuation::RParen, "`)` to close `xmlforest`")?;
let meta = self.make_meta(token.span.union(self.preceding_span()));
Ok(XmlFunc::Forest { elements, meta })
}
fn parse_xml_concat(&mut self, token: Token) -> ParseResult<XmlFunc<D::Ext>> {
self.advance()?; self.expect_punct(Punctuation::LParen, "`(` after `xmlconcat`")?;
let args = self.parse_comma_separated(Self::parse_expr)?;
self.expect_punct(Punctuation::RParen, "`)` to close `xmlconcat`")?;
let meta = self.make_meta(token.span.union(self.preceding_span()));
Ok(XmlFunc::Concat { args, meta })
}
fn parse_xml_parse(&mut self, token: Token) -> ParseResult<XmlFunc<D::Ext>> {
self.advance()?; self.expect_punct(Punctuation::LParen, "`(` after `xmlparse`")?;
let option = self.parse_xml_document_or_content()?;
let arg = self.parse_expr()?;
let whitespace = if self.eat_keyword(Keyword::Preserve)? {
self.expect_keyword(Keyword::Whitespace)?;
XmlWhitespaceOption::Preserve
} else if self.eat_keyword(Keyword::Strip)? {
self.expect_keyword(Keyword::Whitespace)?;
XmlWhitespaceOption::Strip
} else {
XmlWhitespaceOption::Unspecified
};
self.expect_punct(Punctuation::RParen, "`)` to close `xmlparse`")?;
let meta = self.make_meta(token.span.union(self.preceding_span()));
Ok(XmlFunc::Parse {
option,
arg: Box::new(arg),
whitespace,
meta,
})
}
fn parse_xml_pi(&mut self, token: Token) -> ParseResult<XmlFunc<D::Ext>> {
self.advance()?; self.expect_punct(Punctuation::LParen, "`(` after `xmlpi`")?;
self.expect_keyword(Keyword::Name)?;
let name = self.parse_as_alias_ident()?;
let content = if self.eat_punct(Punctuation::Comma)? {
Some(Box::new(self.parse_expr()?))
} else {
None
};
self.expect_punct(Punctuation::RParen, "`)` to close `xmlpi`")?;
let meta = self.make_meta(token.span.union(self.preceding_span()));
Ok(XmlFunc::Pi {
name,
content,
meta,
})
}
fn parse_xml_root(&mut self, token: Token) -> ParseResult<XmlFunc<D::Ext>> {
self.advance()?; self.expect_punct(Punctuation::LParen, "`(` after `xmlroot`")?;
let arg = self.parse_expr()?;
self.expect_punct(
Punctuation::Comma,
"`,` before the `xmlroot` VERSION clause",
)?;
self.expect_keyword(Keyword::Version)?;
let version =
if self.peek_is_keyword(Keyword::No)? && self.peek_nth_is_keyword(1, Keyword::Value)? {
self.advance()?; self.advance()?; None
} else {
Some(Box::new(self.parse_expr()?))
};
let standalone = if self.eat_punct(Punctuation::Comma)? {
self.expect_keyword(Keyword::Standalone)?;
if self.eat_keyword(Keyword::Yes)? {
XmlStandalone::Yes
} else if self.eat_keyword(Keyword::No)? {
if self.eat_keyword(Keyword::Value)? {
XmlStandalone::NoValue
} else {
XmlStandalone::No
}
} else {
return Err(self.unexpected("`YES`, `NO`, or `NO VALUE` after `STANDALONE`"));
}
} else {
XmlStandalone::Unspecified
};
self.expect_punct(Punctuation::RParen, "`)` to close `xmlroot`")?;
let meta = self.make_meta(token.span.union(self.preceding_span()));
Ok(XmlFunc::Root {
arg: Box::new(arg),
version,
standalone,
meta,
})
}
fn parse_xml_serialize(&mut self, token: Token) -> ParseResult<XmlFunc<D::Ext>> {
self.advance()?; self.expect_punct(Punctuation::LParen, "`(` after `xmlserialize`")?;
let option = self.parse_xml_document_or_content()?;
let arg = self.parse_expr()?;
self.expect_keyword(Keyword::As)?;
let data_type = self.parse_data_type()?;
let indent = if self.eat_keyword(Keyword::Indent)? {
XmlIndentOption::Indent
} else if self.eat_keyword(Keyword::No)? {
self.expect_keyword(Keyword::Indent)?;
XmlIndentOption::NoIndent
} else {
XmlIndentOption::Unspecified
};
self.expect_punct(Punctuation::RParen, "`)` to close `xmlserialize`")?;
let meta = self.make_meta(token.span.union(self.preceding_span()));
Ok(XmlFunc::Serialize {
option,
arg: Box::new(arg),
data_type: Box::new(data_type),
indent,
meta,
})
}
fn parse_xml_exists(&mut self, token: Token) -> ParseResult<XmlFunc<D::Ext>> {
self.advance()?; self.expect_punct(Punctuation::LParen, "`(` after `xmlexists`")?;
let path = self.parse_prefix()?.expr;
self.expect_keyword(Keyword::Passing)?;
let mechanism_before = self.parse_xml_passing_mechanism()?;
let arg = self.parse_prefix()?.expr;
let mechanism_after = self.parse_xml_passing_mechanism()?;
self.expect_punct(Punctuation::RParen, "`)` to close `xmlexists`")?;
let meta = self.make_meta(token.span.union(self.preceding_span()));
Ok(XmlFunc::Exists {
path: Box::new(path),
mechanism_before,
arg: Box::new(arg),
mechanism_after,
meta,
})
}
pub(super) fn parse_is_document_predicate(
&mut self,
expr: Expr<D::Ext>,
negated: bool,
) -> ParseResult<Expr<D::Ext>> {
self.expect_keyword(Keyword::Document)?;
let span = expr.span().union(self.preceding_span());
let meta = self.make_meta(span);
Ok(Expr::IsDocument {
expr: Box::new(expr),
negated,
meta,
})
}
fn parse_xml_attribute(&mut self) -> ParseResult<XmlAttribute<D::Ext>> {
let start = self.current_span()?;
let value = self.parse_expr()?;
let name = if self.eat_keyword(Keyword::As)? {
Some(self.parse_as_alias_ident()?)
} else {
None
};
let meta = self.make_meta(start.union(self.preceding_span()));
Ok(XmlAttribute {
value: Box::new(value),
name,
meta,
})
}
fn parse_xml_document_or_content(&mut self) -> ParseResult<XmlDocumentOrContent> {
if self.eat_keyword(Keyword::Document)? {
Ok(XmlDocumentOrContent::Document)
} else if self.eat_keyword(Keyword::Content)? {
Ok(XmlDocumentOrContent::Content)
} else {
Err(self.unexpected("`DOCUMENT` or `CONTENT`"))
}
}
pub(crate) fn parse_xml_passing_mechanism(
&mut self,
) -> ParseResult<Option<XmlPassingMechanism>> {
if !self.eat_keyword(Keyword::By)? {
return Ok(None);
}
if self.eat_keyword(Keyword::Ref)? {
Ok(Some(XmlPassingMechanism::ByRef))
} else if self.eat_keyword(Keyword::Value)? {
Ok(Some(XmlPassingMechanism::ByValue))
} else {
Err(self.unexpected("`REF` or `VALUE` after `BY`"))
}
}
}