use super::ast::{
Axis, BinaryOp, Expr, ItemType, NameTest, NodeTest, Occurrence, PathExpr, PathStart,
Quantifier, SequenceType, Step, TypeOp,
};
use super::lexer::{tokenize, Token, TokenKind};
pub const MAX_RECURSION_DEPTH: usize = 32;
#[derive(Debug, Clone)]
pub struct ParseError {
pub position: usize,
pub message: String,
}
impl std::fmt::Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} at offset {}", self.message, self.position)
}
}
impl std::error::Error for ParseError {}
pub fn parse(input: &str) -> Result<Expr, ParseError> {
let tokens = tokenize(input).map_err(|e| ParseError {
position: e.position,
message: e.message,
})?;
let mut parser = Parser {
tokens,
index: 0,
depth: 0,
length: input.len(),
};
let expr = parser.parse_expr()?;
if parser.index < parser.tokens.len() {
let token = parser.tokens[parser.index].clone();
return Err(ParseError {
position: token.position,
message: format!("unexpected {} after a complete expression", token.kind),
});
}
Ok(expr)
}
struct Parser {
tokens: Vec<Token>,
index: usize,
depth: usize,
length: usize,
}
impl Parser {
fn peek(&self) -> Option<&TokenKind> {
self.tokens.get(self.index).map(|t| &t.kind)
}
fn position(&self) -> usize {
self.tokens
.get(self.index)
.map_or(self.length, |t| t.position)
}
fn error<T>(&self, message: impl Into<String>) -> Result<T, ParseError> {
Err(ParseError {
position: self.position(),
message: message.into(),
})
}
fn advance(&mut self) -> Option<TokenKind> {
let token = self.tokens.get(self.index).map(|t| t.kind.clone());
if token.is_some() {
self.index += 1;
}
token
}
fn at_name(&self, text: &str) -> bool {
matches!(
self.peek(),
Some(TokenKind::Name(name) | TokenKind::FunctionName(name)) if name == text
)
}
fn eat_name(&mut self, text: &str) -> bool {
if self.at_name(text) {
self.index += 1;
return true;
}
false
}
fn eat(&mut self, kind: &TokenKind) -> bool {
if self.peek() == Some(kind) {
self.index += 1;
true
} else {
false
}
}
fn expect(&mut self, kind: &TokenKind) -> Result<(), ParseError> {
if self.eat(kind) {
Ok(())
} else {
let found = self
.peek()
.map_or_else(|| "end of expression".to_string(), ToString::to_string);
self.error(format!("expected {kind} but found {found}"))
}
}
fn enter(&mut self) -> Result<(), ParseError> {
self.depth += 1;
if self.depth > MAX_RECURSION_DEPTH {
return self.error(format!(
"expression nested deeper than the limit of {MAX_RECURSION_DEPTH}"
));
}
Ok(())
}
fn leave(&mut self) {
self.depth -= 1;
}
fn parse_expr(&mut self) -> Result<Expr, ParseError> {
let first = self.parse_expr_single()?;
if self.peek() != Some(&TokenKind::Comma) {
return Ok(first);
}
let mut members = vec![first];
while self.eat(&TokenKind::Comma) {
members.push(self.parse_expr_single()?);
}
Ok(Expr::Sequence(members))
}
fn parse_expr_single(&mut self) -> Result<Expr, ParseError> {
self.enter()?;
let expr = self.parse_expr_single_inner();
self.leave();
expr
}
fn parse_expr_single_inner(&mut self) -> Result<Expr, ParseError> {
if let Some(TokenKind::Name(name) | TokenKind::FunctionName(name)) = self.peek().cloned() {
let binds_a_variable =
matches!(self.tokens.get(self.index + 1).map(|t| &t.kind), Some(TokenKind::Variable(_)));
if binds_a_variable {
match name.as_str() {
"for" => return self.parse_for(),
"let" => return self.parse_let(),
"some" => return self.parse_quantified(Quantifier::Some),
"every" => return self.parse_quantified(Quantifier::Every),
_ => {}
}
}
}
self.parse_or()
}
fn parse_let(&mut self) -> Result<Expr, ParseError> {
self.index += 1; let variable = self.expect_variable()?;
if !self.eat(&TokenKind::Assign) {
return self.error("expected `:=` after the variable of a `let` expression");
}
let value = self.parse_expr_single()?;
if !self.eat_name("return") {
return self.error("expected `return` after the value of a `let` expression");
}
let body = self.parse_expr_single()?;
Ok(Expr::Let {
variable,
value: Box::new(value),
body: Box::new(body),
})
}
fn parse_for(&mut self) -> Result<Expr, ParseError> {
self.index += 1; let variable = self.expect_variable()?;
if !self.eat_name("in") {
return self.error("expected `in` after the variable of a `for` expression");
}
let input = self.parse_expr_single()?;
if !self.eat_name("return") {
return self.error("expected `return` after the sequence of a `for` expression");
}
let body = self.parse_expr_single()?;
Ok(Expr::For {
variable,
input: Box::new(input),
body: Box::new(body),
})
}
fn parse_quantified(&mut self, quantifier: Quantifier) -> Result<Expr, ParseError> {
self.index += 1; let variable = self.expect_variable()?;
if !self.eat_name("in") {
return self.error(format!(
"expected `in` after the variable of a `{}` expression",
quantifier.as_str()
));
}
let input = self.parse_expr_single()?;
if !self.eat_name("satisfies") {
return self.error(format!(
"expected `satisfies` in a `{}` expression",
quantifier.as_str()
));
}
let test = self.parse_expr_single()?;
Ok(Expr::Quantified {
quantifier,
variable,
input: Box::new(input),
test: Box::new(test),
})
}
fn expect_variable(&mut self) -> Result<NameTest, ParseError> {
if let Some(TokenKind::Variable(name)) = self.advance() {
return Ok(NameTest::parse(&name));
}
self.index = self.index.saturating_sub(1);
self.error("expected a variable, written `$name`")
}
fn parse_binary_chain(
&mut self,
operand: fn(&mut Self) -> Result<Expr, ParseError>,
op_of: fn(&TokenKind) -> Option<BinaryOp>,
) -> Result<Expr, ParseError> {
let mut left = operand(self)?;
let mut chained = 0usize;
while let Some(op) = self.peek().and_then(op_of) {
self.index += 1;
self.enter()?;
chained += 1;
match operand(self) {
Ok(right) => left = Expr::Binary(op, Box::new(left), Box::new(right)),
Err(error) => {
for _ in 0..chained {
self.leave();
}
return Err(error);
}
}
}
for _ in 0..chained {
self.leave();
}
Ok(left)
}
fn parse_or(&mut self) -> Result<Expr, ParseError> {
self.parse_binary_chain(Self::parse_and, |kind| {
matches!(kind, TokenKind::Or).then_some(BinaryOp::Or)
})
}
fn parse_and(&mut self) -> Result<Expr, ParseError> {
self.parse_binary_chain(Self::parse_equality, |kind| {
matches!(kind, TokenKind::And).then_some(BinaryOp::And)
})
}
fn parse_equality(&mut self) -> Result<Expr, ParseError> {
self.parse_binary_chain(Self::parse_relational, |kind| match kind {
TokenKind::Equal => Some(BinaryOp::Equal),
TokenKind::NotEqual => Some(BinaryOp::NotEqual),
TokenKind::ValueEqual => Some(BinaryOp::ValueEqual),
TokenKind::ValueNotEqual => Some(BinaryOp::ValueNotEqual),
TokenKind::NodeIs => Some(BinaryOp::NodeIs),
TokenKind::NodeBefore => Some(BinaryOp::NodeBefore),
TokenKind::NodeAfter => Some(BinaryOp::NodeAfter),
_ => None,
})
}
fn parse_relational(&mut self) -> Result<Expr, ParseError> {
self.parse_binary_chain(Self::parse_concat, |kind| match kind {
TokenKind::Less => Some(BinaryOp::Less),
TokenKind::LessEqual => Some(BinaryOp::LessEqual),
TokenKind::Greater => Some(BinaryOp::Greater),
TokenKind::GreaterEqual => Some(BinaryOp::GreaterEqual),
TokenKind::ValueLess => Some(BinaryOp::ValueLess),
TokenKind::ValueLessEqual => Some(BinaryOp::ValueLessEqual),
TokenKind::ValueGreater => Some(BinaryOp::ValueGreater),
TokenKind::ValueGreaterEqual => Some(BinaryOp::ValueGreaterEqual),
_ => None,
})
}
fn parse_concat(&mut self) -> Result<Expr, ParseError> {
self.parse_binary_chain(Self::parse_range, |kind| {
matches!(kind, TokenKind::DoublePipe).then_some(BinaryOp::Concat)
})
}
fn parse_range(&mut self) -> Result<Expr, ParseError> {
let left = self.parse_additive()?;
if !self.at_name("to") {
return Ok(left);
}
self.index += 1;
let right = self.parse_additive()?;
Ok(Expr::Range(Box::new(left), Box::new(right)))
}
fn parse_additive(&mut self) -> Result<Expr, ParseError> {
self.parse_binary_chain(Self::parse_multiplicative, |kind| match kind {
TokenKind::Plus => Some(BinaryOp::Add),
TokenKind::Minus => Some(BinaryOp::Subtract),
_ => None,
})
}
fn parse_multiplicative(&mut self) -> Result<Expr, ParseError> {
self.parse_binary_chain(Self::parse_unary, |kind| match kind {
TokenKind::Multiply => Some(BinaryOp::Multiply),
TokenKind::Div => Some(BinaryOp::Divide),
TokenKind::Mod => Some(BinaryOp::Modulo),
_ => None,
})
}
fn parse_unary(&mut self) -> Result<Expr, ParseError> {
if self.eat(&TokenKind::Minus) {
self.enter()?;
let operand = self.parse_unary();
self.leave();
return Ok(Expr::Negate(Box::new(operand?)));
}
self.parse_type_operators()
}
fn parse_type_operators(&mut self) -> Result<Expr, ParseError> {
let mut value = self.parse_arrow()?;
for (first, second, op) in [
("cast", "as", TypeOp::CastAs),
("castable", "as", TypeOp::CastableAs),
("treat", "as", TypeOp::TreatAs),
("instance", "of", TypeOp::InstanceOf),
] {
if !self.at_name(first) {
continue;
}
let partner = self
.tokens
.get(self.index + 1)
.map(|token| &token.kind);
let follows = matches!(
partner,
Some(TokenKind::Name(name) | TokenKind::FunctionName(name)) if name == second
);
if !follows {
continue;
}
self.index += 2;
let sequence_type = self.parse_sequence_type(op)?;
value = Expr::TypeOp {
op,
value: Box::new(value),
sequence_type,
};
}
Ok(value)
}
fn parse_sequence_type(&mut self, op: TypeOp) -> Result<SequenceType, ParseError> {
let item_type = self.parse_item_type()?;
if item_type == ItemType::EmptySequence {
return Ok(SequenceType {
item_type,
occurrence: Occurrence::ZeroOrMore,
});
}
let occurrence = if self.eat(&TokenKind::QuestionMark) {
Occurrence::ZeroOrOne
} else if op.takes_single_type() {
Occurrence::One
} else if self.eat(&TokenKind::Star) || self.eat(&TokenKind::Multiply) {
Occurrence::ZeroOrMore
} else if self.eat(&TokenKind::Plus) {
Occurrence::OneOrMore
} else {
Occurrence::One
};
if op.takes_single_type() && !matches!(item_type, ItemType::Atomic(_)) {
return self.error(format!(
"`{}` takes an atomic type such as xs:date, because casting a node \
or a sequence has no meaning",
op.as_str()
));
}
Ok(SequenceType {
item_type,
occurrence,
})
}
fn parse_item_type(&mut self) -> Result<ItemType, ParseError> {
use crate::xml::NodeKind;
match self.advance() {
Some(TokenKind::NodeType(name) | TokenKind::FunctionName(name)) => {
self.expect(&TokenKind::LeftParen)?;
let kind = match name.as_str() {
"item" => {
self.expect(&TokenKind::RightParen)?;
return Ok(ItemType::AnyItem);
}
"empty-sequence" => {
self.expect(&TokenKind::RightParen)?;
return Ok(ItemType::EmptySequence);
}
"node" => None,
"element" => Some(NodeKind::Element),
"attribute" => Some(NodeKind::Attribute),
"text" => Some(NodeKind::Text),
"comment" => Some(NodeKind::Comment),
"processing-instruction" => Some(NodeKind::ProcessingInstruction),
"document-node" => Some(NodeKind::Root),
"namespace-node" => Some(NodeKind::Namespace),
other => {
return self.error(format!("{other}() is not a known item type"))
}
};
let named = if let Some(TokenKind::Name(name)) = self.peek().cloned() {
self.index += 1;
Some(NameTest::parse(&name))
} else {
None
};
self.expect(&TokenKind::RightParen)?;
Ok(ItemType::Node { kind, name: named })
}
Some(TokenKind::Name(name)) => Ok(ItemType::Atomic(name)),
other => {
self.index = self.index.saturating_sub(1);
let found = other
.map_or_else(|| "end of expression".to_string(), |kind| kind.to_string());
self.error(format!("expected a type but found {found}"))
}
}
}
fn parse_arrow(&mut self) -> Result<Expr, ParseError> {
let mut value = self.parse_union()?;
let mut chained = 0usize;
while self.peek() == Some(&TokenKind::Arrow) {
self.index += 1; self.enter()?;
chained += 1;
match self.parse_arrow_call(value) {
Ok(called) => value = Expr::Arrow(Box::new(called)),
Err(error) => {
for _ in 0..chained {
self.leave();
}
return Err(error);
}
}
}
for _ in 0..chained {
self.leave();
}
Ok(value)
}
fn parse_arrow_call(&mut self, input: Expr) -> Result<Expr, ParseError> {
match self.advance() {
Some(TokenKind::FunctionName(name)) => {
let mut args = vec![input];
args.extend(self.parse_arguments()?);
Ok(Expr::Function { name, args })
}
Some(TokenKind::Variable(name)) => {
let target = Expr::Variable(NameTest::parse(&name));
let mut args = vec![input];
args.extend(self.parse_arguments()?);
Ok(Expr::DynamicCall {
function: Box::new(target),
args,
})
}
Some(TokenKind::LeftParen) => {
let target = self.parse_expr()?;
self.expect(&TokenKind::RightParen)?;
let mut args = vec![input];
args.extend(self.parse_arguments()?);
Ok(Expr::DynamicCall {
function: Box::new(target),
args,
})
}
other => {
self.index = self.index.saturating_sub(1);
let found = other.map_or_else(
|| "end of expression".to_string(),
|kind| kind.to_string(),
);
self.error(format!(
"expected a function name, a variable, or a parenthesized \
expression after '=>', but found {found}"
))
}
}
}
fn parse_union(&mut self) -> Result<Expr, ParseError> {
self.parse_binary_chain(Self::parse_simple_map, |kind| {
matches!(kind, TokenKind::Pipe).then_some(BinaryOp::Union)
})
}
fn parse_simple_map(&mut self) -> Result<Expr, ParseError> {
self.parse_binary_chain(Self::parse_path_expr, |kind| {
matches!(kind, TokenKind::Bang).then_some(BinaryOp::SimpleMap)
})
}
fn at_step_start(&self) -> bool {
if matches!(self.peek(), Some(TokenKind::Name(_)))
&& matches!(
self.tokens.get(self.index + 1).map(|t| &t.kind),
Some(TokenKind::Hash)
)
{
return false;
}
matches!(
self.peek(),
Some(
TokenKind::Name(_)
| TokenKind::EQName(_)
| TokenKind::Star
| TokenKind::NodeType(_)
| TokenKind::AxisName(_)
| TokenKind::At
| TokenKind::Dot
| TokenKind::DoubleDot
)
)
}
fn parse_path_expr(&mut self) -> Result<Expr, ParseError> {
if matches!(self.peek(), Some(TokenKind::Slash | TokenKind::DoubleSlash)) {
return self.parse_absolute_path();
}
if self.at_step_start() {
let steps = self.parse_relative_path()?;
return Ok(Expr::Path(Box::new(PathExpr {
start: PathStart::Context,
steps,
})));
}
let mut primary = self.parse_primary()?;
let mut chained = 0usize;
while self.peek() == Some(&TokenKind::LeftParen) {
self.enter()?;
chained += 1;
let args = match self.parse_arguments() {
Ok(args) => args,
Err(error) => {
for _ in 0..chained {
self.leave();
}
return Err(error);
}
};
primary = Expr::DynamicCall {
function: Box::new(primary),
args,
};
}
for _ in 0..chained {
self.leave();
}
let predicates = self.parse_predicates()?;
if matches!(self.peek(), Some(TokenKind::Slash | TokenKind::DoubleSlash)) {
let steps = self.parse_path_tail()?;
return Ok(Expr::Path(Box::new(PathExpr {
start: PathStart::Expr(Box::new(primary), predicates),
steps,
})));
}
if predicates.is_empty() {
Ok(primary)
} else {
Ok(Expr::Path(Box::new(PathExpr {
start: PathStart::Expr(Box::new(primary), predicates),
steps: Vec::new(),
})))
}
}
fn parse_absolute_path(&mut self) -> Result<Expr, ParseError> {
let steps = if self.eat(&TokenKind::DoubleSlash) {
let mut steps = vec![descendant_or_self_step()];
steps.extend(self.parse_relative_path()?);
steps
} else {
self.expect(&TokenKind::Slash)?;
if self.at_step_start() {
self.parse_relative_path()?
} else {
Vec::new()
}
};
Ok(Expr::Path(Box::new(PathExpr {
start: PathStart::Root,
steps,
})))
}
fn parse_path_tail(&mut self) -> Result<Vec<Step>, ParseError> {
let mut steps = Vec::new();
loop {
if self.eat(&TokenKind::DoubleSlash) {
steps.push(descendant_or_self_step());
} else if !self.eat(&TokenKind::Slash) {
break;
}
steps.push(self.parse_step()?);
}
Ok(steps)
}
fn parse_relative_path(&mut self) -> Result<Vec<Step>, ParseError> {
let mut steps = vec![self.parse_step()?];
steps.extend(self.parse_path_tail()?);
Ok(steps)
}
fn parse_step(&mut self) -> Result<Step, ParseError> {
if self.eat(&TokenKind::Dot) {
return Ok(Step {
axis: Axis::SelfAxis,
node_test: NodeTest::AnyNode,
predicates: Vec::new(),
});
}
if self.eat(&TokenKind::DoubleDot) {
return Ok(Step {
axis: Axis::Parent,
node_test: NodeTest::AnyNode,
predicates: Vec::new(),
});
}
let mut written = true;
let axis = if self.eat(&TokenKind::At) {
Axis::Attribute
} else if let Some(TokenKind::AxisName(name)) = self.peek().cloned() {
self.index += 1;
self.expect(&TokenKind::ColonColon)?;
match Axis::from_name(&name) {
Some(axis) => axis,
None => return self.error(format!("unknown axis {name:?}")),
}
} else {
written = false;
Axis::Child
};
let node_test = self.parse_node_test(axis)?;
let axis = if !written
&& matches!(
node_test,
NodeTest::Kind {
kind: crate::xml::NodeKind::Attribute,
..
}
) {
Axis::Attribute
} else {
axis
};
let predicates = self.parse_predicates()?;
Ok(Step {
axis,
node_test,
predicates,
})
}
fn parse_node_test(&mut self, axis: Axis) -> Result<NodeTest, ParseError> {
match self.advance() {
Some(TokenKind::Star) => Ok(NodeTest::Wildcard),
Some(TokenKind::Name(name)) => {
if let Some(prefix) = name.strip_suffix(":*") {
Ok(NodeTest::NamespaceWildcard(prefix.to_string()))
} else {
Ok(NodeTest::Name(NameTest::parse(&name)))
}
}
Some(TokenKind::EQName(pair)) => {
let (uri, local) = *pair;
Ok(NodeTest::Name(NameTest::eqname(uri, local)))
}
Some(TokenKind::NodeType(kind)) => {
self.expect(&TokenKind::LeftParen)?;
let test = match kind.as_str() {
"node" => NodeTest::AnyNode,
"text" => NodeTest::Text,
"comment" => NodeTest::Comment,
"processing-instruction" => {
if let Some(TokenKind::Literal(target)) = self.peek().cloned() {
self.index += 1;
NodeTest::ProcessingInstruction(Some(target))
} else {
NodeTest::ProcessingInstruction(None)
}
}
"element" | "attribute" | "document-node" => {
let node_kind = match kind.as_str() {
"element" => crate::xml::NodeKind::Element,
"attribute" => crate::xml::NodeKind::Attribute,
_ => crate::xml::NodeKind::Root,
};
let mut name = None;
if node_kind != crate::xml::NodeKind::Root {
match self.peek().cloned() {
Some(TokenKind::Star) => self.index += 1,
Some(TokenKind::Name(written)) => {
self.index += 1;
name = Some(NameTest::parse(&written));
}
_ => {}
}
}
NodeTest::Kind {
kind: node_kind,
name,
}
}
other => return self.error(format!("unknown node type {other}()")),
};
self.expect(&TokenKind::RightParen)?;
Ok(test)
}
other => {
self.index = self.index.saturating_sub(1);
let found = other.map_or_else(
|| "end of expression".to_string(),
|kind| kind.to_string(),
);
self.error(format!(
"expected a node test after {}:: but found {found}",
axis.as_str()
))
}
}
}
fn parse_predicates(&mut self) -> Result<Vec<Expr>, ParseError> {
let mut predicates = Vec::new();
while self.eat(&TokenKind::LeftBracket) {
predicates.push(self.parse_expr_single()?);
self.expect(&TokenKind::RightBracket)?;
}
Ok(predicates)
}
fn parse_primary(&mut self) -> Result<Expr, ParseError> {
match self.advance() {
Some(TokenKind::Variable(name)) => Ok(Expr::Variable(NameTest::parse(&name))),
Some(TokenKind::Literal(text)) => Ok(Expr::Literal(text)),
Some(TokenKind::Number(value, numeric_type)) => Ok(Expr::Number(value, numeric_type)),
Some(TokenKind::LeftParen) => {
if self.eat(&TokenKind::RightParen) {
return Ok(Expr::Sequence(Vec::new()));
}
let inner = self.parse_expr()?;
self.expect(&TokenKind::RightParen)?;
Ok(inner)
}
Some(TokenKind::FunctionName(name)) if name == "if" => {
self.expect(&TokenKind::LeftParen)?;
let condition = self.parse_expr()?;
self.expect(&TokenKind::RightParen)?;
if !self.eat_name("then") {
return self.error(
"expected `then` after `if (…)`; XPath 2.0 conditionals are \
written `if (test) then value else value`",
);
}
let then_branch = self.parse_expr()?;
if !self.eat_name("else") {
return self.error(
"expected `else`; an XPath 2.0 conditional must have both \
branches",
);
}
let else_branch = self.parse_expr()?;
Ok(Expr::If {
condition: Box::new(condition),
then_branch: Box::new(then_branch),
else_branch: Box::new(else_branch),
})
}
Some(TokenKind::FunctionName(name)) if name == "function" => {
self.parse_inline_function()
}
Some(TokenKind::FunctionName(name)) => {
let args = self.parse_arguments()?;
Ok(Expr::Function { name, args })
}
Some(TokenKind::Name(name)) if self.peek() == Some(&TokenKind::Hash) => {
self.index += 1; match self.advance() {
Some(TokenKind::Number(value, super::value::NumericType::Integer))
if value >= 0.0 =>
{
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
Ok(Expr::NamedFunctionRef {
name,
arity: value as usize,
})
}
other => {
self.index = self.index.saturating_sub(1);
let found = other.map_or_else(
|| "end of expression".to_string(),
|kind| kind.to_string(),
);
self.error(format!(
"expected a non-negative integer arity after `{name}#`, \
but found {found}"
))
}
}
}
other => {
self.index = self.index.saturating_sub(1);
let found = other.map_or_else(
|| "end of expression".to_string(),
|kind| kind.to_string(),
);
self.error(format!("expected an expression but found {found}"))
}
}
}
fn parse_arguments(&mut self) -> Result<Vec<Expr>, ParseError> {
self.expect(&TokenKind::LeftParen)?;
let mut args = Vec::new();
if !self.eat(&TokenKind::RightParen) {
loop {
args.push(self.parse_expr_single()?);
if self.eat(&TokenKind::Comma) {
continue;
}
self.expect(&TokenKind::RightParen)?;
break;
}
}
Ok(args)
}
fn parse_inline_function(&mut self) -> Result<Expr, ParseError> {
self.expect(&TokenKind::LeftParen)?;
let mut params = Vec::new();
if !self.eat(&TokenKind::RightParen) {
loop {
params.push(self.expect_variable()?.to_string());
if self.eat(&TokenKind::Comma) {
continue;
}
self.expect(&TokenKind::RightParen)?;
break;
}
}
self.expect(&TokenKind::LeftBrace)?;
self.enter()?;
let body = if self.peek() == Some(&TokenKind::RightBrace) {
Expr::Sequence(Vec::new())
} else {
self.parse_expr()?
};
self.leave();
self.expect(&TokenKind::RightBrace)?;
Ok(Expr::InlineFunction {
params,
body: std::sync::Arc::new(body),
})
}
}
fn descendant_or_self_step() -> Step {
Step {
axis: Axis::DescendantOrSelf,
node_test: NodeTest::AnyNode,
predicates: Vec::new(),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn path(input: &str) -> PathExpr {
match parse(input).unwrap() {
Expr::Path(p) => *p,
other => panic!("expected a path, got {other:?}"),
}
}
#[test]
fn parses_a_simple_name_step() {
let p = path("a");
assert_eq!(p.start, PathStart::Context);
assert_eq!(p.steps.len(), 1);
assert_eq!(p.steps[0].axis, Axis::Child);
}
#[test]
fn parses_an_absolute_path() {
let p = path("/a/b");
assert_eq!(p.start, PathStart::Root);
assert_eq!(p.steps.len(), 2);
}
#[test]
fn lone_slash_is_the_root_with_no_steps() {
let p = path("/");
assert_eq!(p.start, PathStart::Root);
assert!(p.steps.is_empty());
}
#[test]
fn double_slash_expands_to_descendant_or_self() {
let p = path("//a");
assert_eq!(p.steps[0].axis, Axis::DescendantOrSelf);
assert_eq!(p.steps[0].node_test, NodeTest::AnyNode);
assert_eq!(p.steps[1].axis, Axis::Child);
}
#[test]
fn at_abbreviates_the_attribute_axis() {
assert_eq!(path("@x").steps[0].axis, Axis::Attribute);
}
#[test]
fn dot_and_double_dot_abbreviate_self_and_parent() {
assert_eq!(path(".").steps[0].axis, Axis::SelfAxis);
assert_eq!(path("..").steps[0].axis, Axis::Parent);
}
#[test]
fn parses_explicit_axes() {
assert_eq!(path("ancestor::a").steps[0].axis, Axis::Ancestor);
assert_eq!(
path("preceding-sibling::a").steps[0].axis,
Axis::PrecedingSibling
);
}
#[test]
fn parses_node_type_tests() {
assert_eq!(path("text()").steps[0].node_test, NodeTest::Text);
assert_eq!(path("comment()").steps[0].node_test, NodeTest::Comment);
assert_eq!(
path("processing-instruction('t')").steps[0].node_test,
NodeTest::ProcessingInstruction(Some("t".into()))
);
}
#[test]
fn parses_predicates_in_order() {
let p = path("a[1][@x]");
assert_eq!(p.steps[0].predicates.len(), 2);
}
#[test]
fn respects_operator_precedence() {
let expr = parse("1 + 2 * 3").unwrap();
match expr {
Expr::Binary(BinaryOp::Add, _, right) => {
assert!(matches!(*right, Expr::Binary(BinaryOp::Multiply, _, _)));
}
other => panic!("unexpected shape: {other:?}"),
}
}
#[test]
fn or_binds_looser_than_and() {
let expr = parse("a or b and c").unwrap();
match expr {
Expr::Binary(BinaryOp::Or, _, right) => {
assert!(matches!(*right, Expr::Binary(BinaryOp::And, _, _)));
}
other => panic!("unexpected shape: {other:?}"),
}
}
#[test]
fn parses_function_calls_with_arguments() {
let expr = parse("concat('a', 'b', 'c')").unwrap();
match expr {
Expr::Function { name, args } => {
assert_eq!(name, "concat");
assert_eq!(args.len(), 3);
}
other => panic!("unexpected shape: {other:?}"),
}
}
#[test]
fn parses_a_filter_expression_followed_by_a_path() {
let p = path("id('x')/a");
assert!(matches!(p.start, PathStart::Expr(_, _)));
assert_eq!(p.steps.len(), 1);
}
#[test]
fn parses_unions() {
assert!(matches!(
parse("a | b").unwrap(),
Expr::Binary(BinaryOp::Union, _, _)
));
}
#[test]
fn rejects_trailing_junk() {
assert!(parse("a b").is_err());
}
#[test]
fn rejects_unbalanced_parentheses() {
assert!(parse("count(a").is_err());
assert!(parse("(a").is_err());
assert!(parse("a[1").is_err());
}
#[test]
fn reports_the_offset_of_the_problem() {
let e = parse("count(a").unwrap_err();
assert_eq!(e.position, 7);
}
#[test]
fn refuses_absurd_nesting_instead_of_overflowing() {
let deep = format!("{}a{}", "(".repeat(5000), ")".repeat(5000));
let error = parse(&deep).unwrap_err();
assert!(error.message.contains("nested deeper"), "{}", error.message);
}
#[test]
fn accepts_nesting_a_person_might_actually_write() {
let depth = MAX_RECURSION_DEPTH / 2;
let nested = format!("{}a{}", "(".repeat(depth), ")".repeat(depth));
assert!(parse(&nested).is_ok());
}
#[test]
fn long_location_paths_do_not_count_as_nesting() {
let long = (0..500).map(|_| "a").collect::<Vec<_>>().join("/");
assert!(parse(&long).is_ok());
}
#[test]
fn a_long_chain_of_dynamic_calls_is_bounded_unlike_a_path() {
let chain = format!("a{}", "()".repeat(5000));
let error = parse(&chain).unwrap_err();
assert!(error.message.contains("nested deeper"), "{}", error.message);
let shallow = format!("a{}", "()".repeat(MAX_RECURSION_DEPTH / 2));
assert!(parse(&shallow).is_ok());
}
#[test]
fn a_long_chain_of_any_repeating_binary_operator_is_bounded() {
for operator in ["or", "and", "=", "<", "||", "+", "*", "|", "!"] {
let chain = format!("a{}", format!(" {operator} a").repeat(5000));
let error = parse(&chain).unwrap_err();
assert!(
error.message.contains("nested deeper"),
"operator {operator:?}: {}",
error.message
);
let shallow = format!("a{}", format!(" {operator} a").repeat(MAX_RECURSION_DEPTH / 2));
assert!(parse(&shallow).is_ok(), "operator {operator:?}");
}
}
#[test]
fn parses_string_concatenation() {
assert!(matches!(
parse("'a' || 'b'").unwrap(),
Expr::Binary(BinaryOp::Concat, _, _)
));
}
#[test]
fn string_concat_binds_tighter_than_equality_but_looser_than_additive() {
let expr = parse("'a' || 'b' = 'ab'").unwrap();
match expr {
Expr::Binary(BinaryOp::Equal, left, _) => {
assert!(matches!(*left, Expr::Binary(BinaryOp::Concat, _, _)));
}
other => panic!("unexpected shape: {other:?}"),
}
let expr = parse("1 || 2 + 3").unwrap();
match expr {
Expr::Binary(BinaryOp::Concat, _, right) => {
assert!(matches!(*right, Expr::Binary(BinaryOp::Add, _, _)));
}
other => panic!("unexpected shape: {other:?}"),
}
}
#[test]
fn parses_an_arrow_call_to_a_named_function() {
let expr = parse("$x => f(1)").unwrap();
match expr {
Expr::Arrow(called) => match *called {
Expr::Function { name, args } => {
assert_eq!(name, "f");
assert_eq!(args.len(), 2);
assert!(matches!(args[0], Expr::Variable(_)));
}
other => panic!("unexpected call shape: {other:?}"),
},
other => panic!("unexpected shape: {other:?}"),
}
}
#[test]
fn parses_an_arrow_call_to_a_dynamic_target() {
for source in ["$x => $f(1)", "$x => (g#1)(1)"] {
let expr = parse(source).unwrap();
match expr {
Expr::Arrow(called) => {
assert!(matches!(*called, Expr::DynamicCall { .. }), "{source}");
}
other => panic!("unexpected shape for {source}: {other:?}"),
}
}
}
#[test]
fn arrow_calls_chain() {
let expr = parse("$x => f() => g()").unwrap();
match expr {
Expr::Arrow(outer) => match *outer {
Expr::Function { name, args } => {
assert_eq!(name, "g");
assert!(matches!(args[0], Expr::Arrow(_)));
}
other => panic!("unexpected shape: {other:?}"),
},
other => panic!("unexpected shape: {other:?}"),
}
}
#[test]
fn cast_as_binds_looser_than_arrow() {
let expr = parse("$x => f() cast as xs:string").unwrap();
match expr {
Expr::TypeOp { value, .. } => {
assert!(matches!(*value, Expr::Arrow(_)));
}
other => panic!("unexpected shape: {other:?}"),
}
}
#[test]
fn a_long_chain_of_arrow_calls_is_bounded() {
let chain = format!("a{}", " => f()".repeat(5000));
let error = parse(&chain).unwrap_err();
assert!(error.message.contains("nested deeper"), "{}", error.message);
let shallow = format!("a{}", " => f()".repeat(MAX_RECURSION_DEPTH / 2));
assert!(parse(&shallow).is_ok());
}
#[test]
fn parses_the_simple_map_operator() {
assert!(matches!(
parse("a ! b").unwrap(),
Expr::Binary(BinaryOp::SimpleMap, _, _)
));
}
#[test]
fn simple_map_chains_left_associatively() {
let expr = parse("a ! b ! c").unwrap();
match expr {
Expr::Binary(BinaryOp::SimpleMap, left, _) => {
assert!(matches!(*left, Expr::Binary(BinaryOp::SimpleMap, _, _)));
}
other => panic!("unexpected shape: {other:?}"),
}
}
#[test]
fn simple_map_binds_tighter_than_union() {
let expr = parse("a ! b | c").unwrap();
match expr {
Expr::Binary(BinaryOp::Union, left, _) => {
assert!(matches!(*left, Expr::Binary(BinaryOp::SimpleMap, _, _)));
}
other => panic!("unexpected shape: {other:?}"),
}
}
#[test]
fn range_binds_looser_than_simple_map() {
let expr = parse("1 to 2 ! f()").unwrap();
match expr {
Expr::Range(_, right) => {
assert!(matches!(*right, Expr::Binary(BinaryOp::SimpleMap, _, _)));
}
other => panic!("unexpected shape: {other:?}"),
}
}
#[test]
fn a_long_chain_of_simple_maps_is_bounded() {
let chain = format!("a{}", " ! a".repeat(5000));
let error = parse(&chain).unwrap_err();
assert!(error.message.contains("nested deeper"), "{}", error.message);
let shallow = format!("a{}", " ! a".repeat(MAX_RECURSION_DEPTH / 2));
assert!(parse(&shallow).is_ok());
}
#[test]
fn parses_a_let_expression() {
let expr = parse("let $x := 1 return $x").unwrap();
match expr {
Expr::Let {
variable,
value,
body,
} => {
assert_eq!(variable.to_string(), "x");
assert!(matches!(*value, Expr::Number(1.0, _)));
assert!(matches!(*body, Expr::Variable(_)));
}
other => panic!("unexpected shape: {other:?}"),
}
}
#[test]
fn let_is_still_a_name_without_a_following_variable() {
let p = path("let");
assert_eq!(p.steps[0].node_test, NodeTest::Name(NameTest::parse("let")));
}
#[test]
fn a_let_expression_needs_colon_equal() {
let error = parse("let $x 1 return $x").unwrap_err();
assert!(error.message.contains(":="), "{}", error.message);
}
#[test]
fn parses_an_eqname_step() {
let p = path("Q{http://example.com/ns}local");
assert_eq!(
p.steps[0].node_test,
NodeTest::Name(NameTest::eqname(
"http://example.com/ns".to_string(),
"local".to_string()
))
);
}
#[test]
fn parses_an_eqname_after_an_axis_and_on_an_attribute() {
assert_eq!(
path("child::Q{u}local").steps[0].node_test,
NodeTest::Name(NameTest::eqname("u".to_string(), "local".to_string()))
);
let p = path("@Q{u}local");
assert_eq!(p.steps[0].axis, Axis::Attribute);
assert_eq!(
p.steps[0].node_test,
NodeTest::Name(NameTest::eqname("u".to_string(), "local".to_string()))
);
}
}