use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
mod binary;
mod lex;
mod node_test;
use binary::chain;
use super::ast::{Axis, BinaryOp, Expr, Filter, Func, NodeTest, Step};
use crate::Limits;
use crate::tree::ExpandedName;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct XPathError {
pub message: String,
pub offset: usize,
}
impl core::fmt::Display for XPathError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "at {}: {}", self.offset, self.message)
}
}
#[cfg(feature = "std")]
impl std::error::Error for XPathError {}
struct P<'a> {
s: &'a str,
b: &'a [u8],
i: usize,
depth: usize,
max_depth: usize,
ops: usize,
max_ops: usize,
nodes: usize,
max_nodes: usize,
max_arguments: usize,
namespaces: &'a [(&'a str, &'a str)],
variables: Vec<ExpandedName>,
}
pub struct Compiled {
pub expr: Expr,
pub variables: Vec<ExpandedName>,
}
pub fn compile(
expr: &str,
namespaces: &[(&str, &str)],
limits: &Limits,
) -> Result<Compiled, XPathError> {
if limits.max_xpath_bytes.is_some_and(|max| expr.len() > max) {
return Err(error_at("expression exceeds the byte limit", 0));
}
let mut p = P {
s: expr,
b: expr.as_bytes(),
i: 0,
depth: 0,
max_depth: limits.max_xpath_depth,
ops: 0,
max_ops: limits.max_xpath_operators,
nodes: 0,
max_nodes: limits.max_xpath_nodes,
max_arguments: limits.max_xpath_arguments,
namespaces,
variables: Vec::new(),
};
let e = p.parse_or()?;
p.ws();
if p.i < p.b.len() {
return Err(p.err("unexpected trailing input"));
}
Ok(Compiled {
expr: e,
variables: p.variables,
})
}
impl P<'_> {
fn err(&self, m: &str) -> XPathError {
error_at(m, self.i)
}
fn parse_or(&mut self) -> Result<Expr, XPathError> {
if self.depth > self.max_depth {
return Err(self.err("expression nested too deeply"));
}
self.depth += 1;
let result = self.parse_binary(0);
self.depth -= 1;
result
}
fn charge(&mut self) -> Result<(), XPathError> {
self.charge_node()?;
self.ops += 1;
if self.ops > self.max_ops {
return Err(self.err("expression has too many operators"));
}
Ok(())
}
fn charge_node(&mut self) -> Result<(), XPathError> {
if self.nodes >= self.max_nodes {
return Err(self.err("expression has too many syntax nodes"));
}
self.nodes += 1;
Ok(())
}
fn parse_union(&mut self) -> Result<Expr, XPathError> {
let first = self.parse_path_expr()?;
let mut rest = Vec::new();
while self.eat("|") {
self.charge()?;
rest.push((BinaryOp::Union, self.parse_path_expr()?));
}
Ok(chain(first, rest))
}
fn parse_path_expr(&mut self) -> Result<Expr, XPathError> {
match self.try_primary()? {
Some(primary) => self.parse_filter_tail(primary),
None => self.parse_path(),
}
}
fn try_primary(&mut self) -> Result<Option<Expr>, XPathError> {
match self.peek() {
Some(b'(') => self.parse_parenthesised().map(Some),
Some(b'\'' | b'"') => self.parse_literal().map(Some),
Some(b'$') => self.parse_variable().map(Some),
Some(c) if c.is_ascii_digit() => self.parse_number().map(Some),
Some(b'.')
if self.b.get(self.i + 1).is_some_and(u8::is_ascii_digit) =>
{
self.parse_number().map(Some)
}
_ => self.try_function_call(),
}
}
fn parse_parenthesised(&mut self) -> Result<Expr, XPathError> {
self.i += 1;
let e = self.parse_or()?;
if !self.eat(")") {
return Err(self.err("expected )"));
}
Ok(e)
}
fn parse_filter_tail(&mut self, base: Expr) -> Result<Expr, XPathError> {
let predicates = self.parse_predicates()?;
let mut steps = Vec::new();
match self.eat_separator() {
Some(Separator::Double) => {
steps.push(self.descendant_step()?);
self.parse_relative_path(&mut steps)?;
}
Some(Separator::Single) => self.parse_relative_path(&mut steps)?,
None => {}
}
if predicates.is_empty() && steps.is_empty() {
return Ok(base);
}
Ok(Expr::Filter(Box::new(Filter {
base,
predicates,
steps,
})))
}
fn parse_variable(&mut self) -> Result<Expr, XPathError> {
self.i += 1;
let at = self.i;
let Some(name) = self.try_name() else {
return Err(error_at("expected a variable name after `$`", at));
};
let resolved = match name.split_once(':') {
Some((prefix, local)) => {
ExpandedName::qualified(self.resolve_prefix(prefix)?, local)
}
None => ExpandedName::local(name),
};
if !self.variables.contains(&resolved) {
self.variables.push(resolved.clone());
}
Ok(Expr::Variable(Box::new(resolved)))
}
fn parse_literal(&mut self) -> Result<Expr, XPathError> {
let at = self.i;
self.try_literal()
.map(Expr::Literal)
.ok_or_else(|| error_at("unterminated string literal", at))
}
fn parse_number(&mut self) -> Result<Expr, XPathError> {
let start = self.i;
while self.i < self.b.len()
&& (self.b[self.i].is_ascii_digit() || self.b[self.i] == b'.')
{
self.i += 1;
}
self.s[start..self.i]
.parse::<f64>()
.map(Expr::Number)
.map_err(|_| self.err("invalid number"))
}
fn try_function_call(&mut self) -> Result<Option<Expr>, XPathError> {
self.ws();
let at = self.i;
let Some(name) = self.try_name() else {
return Ok(None);
};
if self.peek() != Some(b'(') || is_node_type(&name) {
self.i = at;
return Ok(None);
}
let Some(func) = Func::from_name(&name) else {
return Err(error_at("unknown function", at));
};
self.i += 1;
self.charge()?;
let args = self.parse_arguments()?;
let (min, max) = func.arity();
if args.len() < min || args.len() > max {
return Err(error_at("wrong number of arguments", at));
}
Ok(Some(Expr::Function { func, args }))
}
fn parse_arguments(&mut self) -> Result<Vec<Expr>, XPathError> {
let mut args = Vec::new();
if self.peek() != Some(b')') {
loop {
if args.len() >= self.max_arguments {
return Err(self.err("function has too many arguments"));
}
args.push(self.parse_or()?);
if !self.eat(",") {
break;
}
}
}
if !self.eat(")") {
return Err(self.err("expected ) after arguments"));
}
Ok(args)
}
fn parse_path(&mut self) -> Result<Expr, XPathError> {
let leading = self.eat_separator();
let absolute = leading.is_some();
let mut steps = Vec::new();
if leading == Some(Separator::Double) {
steps.push(self.descendant_step()?);
} else if absolute && self.at_path_end() {
return Ok(Expr::Path { absolute, steps });
}
loop {
steps.push(self.parse_step()?);
if !self.more_path_steps(&mut steps)? {
return Ok(Expr::Path { absolute, steps });
}
}
}
fn parse_relative_path(
&mut self,
steps: &mut Vec<Step>,
) -> Result<(), XPathError> {
loop {
steps.push(self.parse_step()?);
if !self.more_path_steps(steps)? {
return Ok(());
}
}
}
fn more_path_steps(
&mut self,
steps: &mut Vec<Step>,
) -> Result<bool, XPathError> {
match self.eat_separator() {
Some(Separator::Double) => {
steps.push(self.descendant_step()?);
Ok(true)
}
Some(Separator::Single) => Ok(true),
None => Ok(false),
}
}
fn eat_separator(&mut self) -> Option<Separator> {
if self.eat("//") {
Some(Separator::Double)
} else if self.eat("/") {
Some(Separator::Single)
} else {
None
}
}
fn at_path_end(&mut self) -> bool {
match self.peek() {
None => true,
Some(c) => {
!(c.is_ascii_alphanumeric()
|| matches!(c, b'_' | b'*' | b'@' | b'.' | b':'))
}
}
}
fn descendant_step(&mut self) -> Result<Step, XPathError> {
self.charge()?;
Ok(Step {
axis: Axis::DescendantOrSelf,
test: NodeTest::Any,
predicates: Vec::new(),
})
}
fn parse_step(&mut self) -> Result<Step, XPathError> {
self.charge()?;
self.ws();
if let Some(axis) = self.eat_abbreviated_step() {
return Ok(Step {
axis,
test: NodeTest::Any,
predicates: self.parse_predicates()?,
});
}
let axis = self.parse_axis()?;
let test = self.parse_node_test()?;
let predicates = self.parse_predicates()?;
Ok(Step {
axis,
test,
predicates,
})
}
fn parse_predicates(&mut self) -> Result<Vec<Expr>, XPathError> {
let mut out = Vec::new();
while self.eat("[") {
self.charge()?;
out.push(self.parse_or()?);
if !self.eat("]") {
return Err(self.err("expected ]"));
}
}
Ok(out)
}
}
fn error_at(m: &str, offset: usize) -> XPathError {
XPathError {
message: m.to_owned(),
offset,
}
}
fn is_node_type(name: &str) -> bool {
matches!(name, "text" | "comment" | "node" | "processing-instruction")
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Separator {
Single,
Double,
}