use std::iter::Peekable;
use self::Error::*;
use ::Value;
use ::token::{Token, AxisName, NodeTestName};
use ::tokenizer::{self, TokenResult};
use ::axis::{Axis, AxisLike, PrincipalNodeType};
use ::expression::{self, SubExpression};
use ::node_test::{self, SubNodeTest};
#[allow(missing_copy_implementations)]
pub struct Parser;
impl Parser {
pub fn new() -> Parser {
Parser
}
}
quick_error! {
#[derive(Debug, Clone, PartialEq)]
pub enum Error {
EmptyPredicate {
description("empty predicate")
}
ExtraUnparsedTokens {
description("extra unparsed tokens")
}
RanOutOfInput {
description("ran out of input")
}
RightHandSideExpressionMissing {
description("right hand side of expression is missing")
}
ArgumentMissing {
description("function argument is missing")
}
Tokenizer(err: tokenizer::Error) {
from()
cause(err)
description(err.description())
display("tokenizer error: {}", err)
}
TrailingSlash {
description("trailing slash")
}
UnexpectedToken(token: Token) {
from()
description("unexpected token")
display("unexpected token: {:?}", token)
}
}
}
pub type ParseResult = Result<Option<SubExpression>, Error>;
type BinaryExpressionBuilder = fn(SubExpression, SubExpression) -> SubExpression;
struct BinaryRule {
token: Token,
builder: BinaryExpressionBuilder,
}
struct LeftAssociativeBinaryParser {
rules: Vec<BinaryRule>,
}
type TokenSource<'a, I> = &'a mut Peekable<I>;
trait XCompat {
fn has_more_tokens(&mut self) -> bool;
fn next_token_is(&mut self, token: &Token) -> bool;
fn consume(&mut self, token: &Token) -> Result<(), Error>;
}
impl<I> XCompat for Peekable<I>
where I: Iterator<Item = TokenResult>
{
fn has_more_tokens(&mut self) -> bool {
self.peek().is_some()
}
fn next_token_is(&mut self, token: &Token) -> bool {
match self.peek() {
Some(&Ok(ref t)) => t == token,
_ => false
}
}
fn consume(&mut self, token: &Token) -> Result<(), Error> {
match self.next() {
None => Err(RanOutOfInput),
Some(Err(x)) => Err(Tokenizer(x)),
Some(Ok(x)) =>
if &x == token {
Ok(())
} else {
Err(UnexpectedToken(x))
},
}
}
}
macro_rules! consume_value(
($source:expr, Token::$token:ident) => (
match $source.next() {
None => return Err(RanOutOfInput),
Some(Err(x)) => return Err(Tokenizer(x)),
Some(Ok(Token::$token(x))) => x,
Some(Ok(x)) => return Err(UnexpectedToken(x)),
}
);
);
macro_rules! next_token_is(
($source:expr, Token::$token:ident) => (
match $source.peek() {
Some(&Ok(Token::$token(_))) => true,
_ => false,
}
);
);
impl LeftAssociativeBinaryParser {
fn new(rules: Vec<BinaryRule>) -> LeftAssociativeBinaryParser {
LeftAssociativeBinaryParser {
rules: rules,
}
}
fn parse<F, I>(&self, source: TokenSource<I>, child_parse: F) -> ParseResult
where F: Fn(TokenSource<I>) -> ParseResult,
I: Iterator<Item = TokenResult>
{
let left = try!(child_parse(source));
let mut left = match left {
None => return Ok(None),
Some(x) => x,
};
while source.has_more_tokens() {
let mut found = false;
for rule in &self.rules {
if source.next_token_is(&rule.token) {
try!(source.consume(&rule.token));
let right = try!(child_parse(source));
let right = match right {
None => return Err(RightHandSideExpressionMissing),
Some(x) => x,
};
left = (rule.builder)(left, right);
found = true;
break;
}
}
if !found { break; }
}
Ok(Some(left))
}
}
type Rule<'a, I> = Fn(TokenSource<I>) -> ParseResult + 'a;
fn first_matching_rule<I>(child_parses: &[&Rule<I>],
source: TokenSource<I>)
-> ParseResult
where I: Iterator<Item = TokenResult>
{
for child_parse in child_parses.iter() {
let expr = try!((*child_parse)(source));
if expr.is_some() {
return Ok(expr);
}
}
Ok(None)
}
impl Parser {
fn parse_axis<I>(&self, source: TokenSource<I>) -> Result<Axis, Error>
where I: Iterator<Item = TokenResult>
{
if next_token_is!(source, Token::Axis) {
let name = consume_value!(source, Token::Axis);
match name {
AxisName::Child => Ok(Axis::Child),
AxisName::SelfAxis => Ok(Axis::SelfAxis),
AxisName::Parent => Ok(Axis::Parent),
AxisName::Descendant => Ok(Axis::Descendant),
AxisName::DescendantOrSelf => Ok(Axis::DescendantOrSelf),
AxisName::Attribute => Ok(Axis::Attribute),
AxisName::Namespace => Ok(Axis::Namespace),
AxisName::Ancestor => Ok(Axis::Ancestor),
AxisName::AncestorOrSelf => Ok(Axis::AncestorOrSelf),
AxisName::PrecedingSibling => Ok(Axis::PrecedingSibling),
AxisName::FollowingSibling => Ok(Axis::FollowingSibling),
AxisName::Preceding => Ok(Axis::Preceding),
AxisName::Following => Ok(Axis::Following),
}
} else {
Ok(Axis::Child)
}
}
fn parse_node_test<I>(&self, source: TokenSource<I>) -> Result<Option<SubNodeTest>, Error>
where I: Iterator<Item = TokenResult>
{
if next_token_is!(source, Token::NodeTest) {
let name = consume_value!(source, Token::NodeTest);
match name {
NodeTestName::Node => Ok(Some(Box::new(node_test::Node))),
NodeTestName::Text => Ok(Some(Box::new(node_test::Text))),
NodeTestName::Comment => Ok(Some(Box::new(node_test::Comment))),
NodeTestName::ProcessingInstruction(target) =>
Ok(Some(Box::new(node_test::ProcessingInstruction::new(target)))),
}
} else {
Ok(None)
}
}
fn default_node_test<I>(&self, source: TokenSource<I>, axis: Axis) -> Result<Option<SubNodeTest>, Error>
where I: Iterator<Item = TokenResult>
{
if next_token_is!(source, Token::NameTest) {
let name = consume_value!(source, Token::NameTest);
let test: SubNodeTest = match axis.principal_node_type() {
PrincipalNodeType::Attribute => Box::new(node_test::Attribute::new(name)),
PrincipalNodeType::Element => Box::new(node_test::Element::new(name)),
PrincipalNodeType::Namespace => Box::new(node_test::Namespace::new(name)),
};
Ok(Some(test))
} else {
Ok(None)
}
}
fn parse_nested_expression<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
if source.next_token_is(&Token::LeftParen) {
try!(source.consume(&Token::LeftParen));
let result = try!(self.parse_expression(source));
try!(source.consume(&Token::RightParen));
Ok(result)
} else {
Ok(None)
}
}
fn parse_variable_reference<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
if next_token_is!(source, Token::Variable) {
let name = consume_value!(source, Token::Variable);
Ok(Some(Box::new(expression::Variable { name: name })))
} else {
Ok(None)
}
}
fn parse_string_literal<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
if next_token_is!(source, Token::Literal) {
let value = consume_value!(source, Token::Literal);
Ok(Some(Box::new(expression::Literal::from(Value::String(value)))))
} else {
Ok(None)
}
}
fn parse_numeric_literal<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
if next_token_is!(source, Token::Number) {
let value = consume_value!(source, Token::Number);
Ok(Some(Box::new(expression::Literal::from(Value::Number(value)))))
} else {
Ok(None)
}
}
fn parse_function_args_tail<I>(&self, source: TokenSource<I>, mut arguments: Vec<SubExpression>)
-> Result<Vec<SubExpression>, Error>
where I: Iterator<Item = TokenResult>
{
while source.next_token_is(&Token::Comma) {
try!(source.consume(&Token::Comma));
match try!(self.parse_expression(source)) {
Some(arg) => arguments.push(arg),
None => return Err(ArgumentMissing),
}
}
Ok(arguments)
}
fn parse_function_args<I>(&self, source: TokenSource<I>)
-> Result<Vec<SubExpression>, Error>
where I: Iterator<Item = TokenResult>
{
let mut arguments = Vec::new();
match try!(self.parse_expression(source)) {
Some(arg) => arguments.push(arg),
None => return Ok(arguments),
}
self.parse_function_args_tail(source, arguments)
}
fn parse_function_call<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
if next_token_is!(source, Token::Function) {
let name = consume_value!(source, Token::Function);
try!(source.consume(&Token::LeftParen));
let arguments = try!(self.parse_function_args(source));
try!(source.consume(&Token::RightParen));
Ok(Some(Box::new(expression::Function{ name: name, arguments: arguments })))
} else {
Ok(None)
}
}
fn parse_primary_expression<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
let rules: &[&Rule<I>] = &[
&|src: TokenSource<I>| self.parse_variable_reference(src),
&|src: TokenSource<I>| self.parse_nested_expression(src),
&|src: TokenSource<I>| self.parse_string_literal(src),
&|src: TokenSource<I>| self.parse_numeric_literal(src),
&|src: TokenSource<I>| self.parse_function_call(src),
];
first_matching_rule(rules, source)
}
fn parse_predicate_expression<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
if source.next_token_is(&Token::LeftBracket) {
try!(source.consume(&Token::LeftBracket));
match try!(self.parse_expression(source)) {
Some(predicate) => {
try!(source.consume(&Token::RightBracket));
Ok(Some(predicate))
},
None => Err(EmptyPredicate),
}
} else {
Ok(None)
}
}
fn parse_predicates<I>(&self, source: TokenSource<I>) -> Result<Vec<SubExpression>, Error>
where I: Iterator<Item = TokenResult>
{
let mut predicates = Vec::new();
while let Some(predicate) = try!(self.parse_predicate_expression(source)) {
predicates.push(predicate)
}
Ok(predicates)
}
fn parse_step<I>(&self, source: TokenSource<I>) -> Result<Option<expression::Step>, Error>
where I: Iterator<Item = TokenResult>
{
let axis = try!(self.parse_axis(source));
let node_test = match try!(self.parse_node_test(source)) {
Some(test) => Some(test),
None => try!(self.default_node_test(source, axis)),
};
let node_test = match node_test {
Some(test) => test,
None => return Ok(None),
};
let predicates = try!(self.parse_predicates(source));
Ok(Some(expression::Step::new(axis, node_test, predicates)))
}
fn parse_relative_location_path_raw<I>(&self,
source: TokenSource<I>,
start_point: SubExpression) -> ParseResult
where I: Iterator<Item = TokenResult>
{
match try!(self.parse_step(source)) {
Some(step) => {
let mut steps = vec![step];
while source.next_token_is(&Token::Slash) {
try!(source.consume(&Token::Slash));
match try!(self.parse_step(source)) {
Some(next) => steps.push(next),
None => return Err(TrailingSlash),
}
}
Ok(Some(expression::Path::new(start_point, steps)))
},
None => Ok(None),
}
}
fn parse_relative_location_path<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
let start_point = Box::new(expression::ContextNode);
self.parse_relative_location_path_raw(source, start_point)
}
fn parse_absolute_location_path<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
if source.next_token_is(&Token::Slash) {
try!(source.consume(&Token::Slash));
let start_point = Box::new(expression::RootNode);
match try!(self.parse_relative_location_path_raw(source, start_point)) {
Some(expr) => Ok(Some(expr)),
None => Ok(Some(Box::new(expression::RootNode))),
}
} else {
Ok(None)
}
}
fn parse_location_path<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
let rules: &[&Rule<I>] = &[
&|source: TokenSource<I>| self.parse_relative_location_path(source),
&|source: TokenSource<I>| self.parse_absolute_location_path(source),
];
first_matching_rule(rules, source)
}
fn parse_filter_expression<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
match try!(self.parse_primary_expression(source)) {
Some(expr) => {
let predicates = try!(self.parse_predicates(source));
Ok(Some(predicates.into_iter().fold(expr, |expr, pred| {
expression::Filter::new(expr, pred)
})))
},
None => Ok(None),
}
}
fn parse_path_expression<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
let expr = try!(self.parse_location_path(source));
if expr.is_some() {
return Ok(expr);
}
match try!(self.parse_filter_expression(source)) {
Some(expr) =>
if source.next_token_is(&Token::Slash) {
try!(source.consume(&Token::Slash));
match try!(self.parse_relative_location_path_raw(source, expr)) {
Some(expr) => Ok(Some(expr)),
None => Err(TrailingSlash),
}
} else {
Ok(Some(expr))
},
None => Ok(None),
}
}
fn parse_union_expression<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
let rules = vec![
BinaryRule { token: Token::Pipe, builder: expression::Union::new }
];
let parser = LeftAssociativeBinaryParser::new(rules);
parser.parse(source, |source| self.parse_path_expression(source))
}
fn parse_unary_expression<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
let expr = try!(self.parse_union_expression(source));
if expr.is_some() {
return Ok(expr);
}
if source.next_token_is(&Token::MinusSign) {
try!(source.consume(&Token::MinusSign));
let expr = try!(self.parse_unary_expression(source));
match expr {
Some(expr) => {
let expr: SubExpression = Box::new(expression::Negation { expression: expr });
Ok(Some(expr))
},
None => Err(RightHandSideExpressionMissing),
}
} else {
Ok(None)
}
}
fn parse_multiplicative_expression<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
let rules = vec![
BinaryRule { token: Token::Multiply, builder: expression::Math::multiplication },
BinaryRule { token: Token::Divide, builder: expression::Math::division },
BinaryRule { token: Token::Remainder, builder: expression::Math::remainder }
];
let parser = LeftAssociativeBinaryParser::new(rules);
parser.parse(source, |source| self.parse_unary_expression(source))
}
fn parse_additive_expression<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
let rules = vec![
BinaryRule { token: Token::PlusSign, builder: expression::Math::addition },
BinaryRule { token: Token::MinusSign, builder: expression::Math::subtraction }
];
let parser = LeftAssociativeBinaryParser::new(rules);
parser.parse(source, |source| self.parse_multiplicative_expression(source))
}
fn parse_relational_expression<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
let rules = vec![
BinaryRule { token: Token::LessThan,
builder: expression::Relational::less_than },
BinaryRule { token: Token::LessThanOrEqual,
builder: expression::Relational::less_than_or_equal },
BinaryRule { token: Token::GreaterThan,
builder: expression::Relational::greater_than },
BinaryRule { token: Token::GreaterThanOrEqual,
builder: expression::Relational::greater_than_or_equal },
];
let parser = LeftAssociativeBinaryParser::new(rules);
parser.parse(source, |source| self.parse_additive_expression(source))
}
fn parse_equality_expression<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
let rules = vec![
BinaryRule { token: Token::Equal, builder: expression::Equal::new },
BinaryRule { token: Token::NotEqual, builder: expression::NotEqual::new },
];
let parser = LeftAssociativeBinaryParser::new(rules);
parser.parse(source, |source| self.parse_relational_expression(source))
}
fn parse_and_expression<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
let rules = vec![
BinaryRule { token: Token::And, builder: expression::And::new }
];
let parser = LeftAssociativeBinaryParser::new(rules);
parser.parse(source, |source| self.parse_equality_expression(source))
}
fn parse_or_expression<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
let rules = vec![
BinaryRule { token: Token::Or, builder: expression::Or::new }
];
let parser = LeftAssociativeBinaryParser::new(rules);
parser.parse(source, |source| self.parse_and_expression(source))
}
fn parse_expression<I>(&self, source: TokenSource<I>) -> ParseResult
where I: Iterator<Item = TokenResult>
{
self.parse_or_expression(source)
}
pub fn parse<I>(&self, source: I) -> ParseResult
where I: Iterator<Item = TokenResult>
{
let mut source = source.peekable();
let expr = try!(self.parse_or_expression(&mut source));
if source.has_more_tokens() {
return Err(ExtraUnparsedTokens);
}
Ok(expr)
}
}
#[cfg(test)]
mod test {
use std::borrow::ToOwned;
use sxd_document::Package;
use sxd_document::dom::{self, Document, Root, Element, Text};
use ::Value;
use ::Value::{Boolean, Number, String};
use ::context::{self, Context};
use ::expression::{Expression, SubExpression};
use ::node_test;
use ::nodeset::Node;
use ::token::{Token, AxisName, NodeTestName};
use ::tokenizer::{self, TokenResult};
use super::{Parser, ParseResult};
use super::Error::*;
macro_rules! tokens(
($($e:expr),*) => ({
let mut _temp: Vec<TokenResult> = ::std::vec::Vec::new();
$(_temp.push(Ok($e));)*
_temp
});
($($e:expr),+,) => (tokens!($($e),+))
);
fn name_test(local_part: &str) -> Token {
Token::NameTest(node_test::NameTest {
prefix: None,
local_part: local_part.to_owned()
})
}
trait ApproxEq {
fn is_approx_eq(&self, other: &Self) -> bool;
}
impl ApproxEq for f64 {
fn is_approx_eq(&self, other: &f64) -> bool {
(*self - *other).abs() < 1.0e-6
}
}
impl<'d> ApproxEq for Value<'d> {
fn is_approx_eq(&self, other: &Value<'d>) -> bool {
match (self, other) {
(&Number(ref x), &Number(ref y)) => x.is_approx_eq(y),
_ => panic!("It's nonsensical to compare these quantities"),
}
}
}
macro_rules! assert_approx_eq(
($a:expr, $b:expr) => ({
let (a, b) = (&$a, &$b);
assert!(a.is_approx_eq(b),
"{:?} is not approximately equal to {:?}", *a, *b);
})
);
struct TestDoc<'d>(Document<'d>);
impl<'d> TestDoc<'d> {
fn root(&'d self) -> Root<'d> {
let &TestDoc(ref doc) = self;
doc.root()
}
fn top_node(&'d self) -> Element<'d> {
let &TestDoc(ref doc) = self;
let kids = doc.root().children();
match kids.len() {
0 => {
let n = doc.create_element("the-top-node");
doc.root().append_child(n);
n
},
1 => {
kids[0].element().expect("not an element")
},
_ => panic!("Too many top nodes"),
}
}
fn add_top_child(&'d self, name: &str) -> Element<'d> {
self.add_child(self.top_node(), name)
}
fn add_child(&'d self, parent: Element<'d>, name: &str) -> Element<'d> {
let &TestDoc(ref doc) = self;
let n = doc.create_element(name);
parent.append_child(n);
n
}
fn add_text(&'d self, parent: Element<'d>, value: &str) -> Text<'d> {
let &TestDoc(ref doc) = self;
let tn = doc.create_text(value);
parent.append_child(tn);
tn
}
fn add_comment(&'d self, parent: Element<'d>, value: &str) -> dom::Comment<'d> {
let cn = self.0.create_comment(value);
parent.append_child(cn);
cn
}
fn add_processing_instruction(&'d self, parent: Element<'d>, name: &str, value: Option<&str>) -> dom::ProcessingInstruction<'d> {
let pi = self.0.create_processing_instruction(name, value);
parent.append_child(pi);
pi
}
}
struct Exercise<'d> {
doc: &'d TestDoc<'d>,
context: Context<'d>,
parser: Parser,
}
impl<'d> Exercise<'d> {
fn new(doc: &'d TestDoc<'d>) -> Exercise<'d> {
Exercise {
doc: doc,
context: Context::new(),
parser: Parser::new(),
}
}
fn parse_raw(&self, tokens: Vec<TokenResult>) -> ParseResult {
self.parser.parse(tokens.into_iter())
}
fn parse(&self, tokens: Vec<TokenResult>) -> SubExpression {
self.parse_raw(tokens)
.expect("Unable to parse expression")
.expect("Expression missing")
}
fn evaluate<E>(&self, expr: E) -> Value<'d>
where E: Expression,
{
self.evaluate_on(expr, self.doc.top_node())
}
fn evaluate_on<E, N>(&self, expr: E, node: N) -> Value<'d>
where E: Expression,
N: Into<Node<'d>>,
{
let context = context::Evaluation::new(&self.context, node.into());
expr.evaluate(&context).expect("Unable to evaluate expression")
}
}
#[test]
fn parses_string_as_child() {
let tokens = tokens![name_test("hello")];
let package = Package::new();
let doc = TestDoc(package.as_document());
let hello = doc.add_top_child("hello");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![hello], ex.evaluate_on(expr, doc.top_node()));
}
#[test]
fn parses_two_strings_as_grandchild() {
let tokens = tokens![
name_test("hello"),
Token::Slash,
name_test("world")
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let hello = doc.add_top_child("hello");
let world = doc.add_child(hello, "world");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![world], ex.evaluate_on(expr, doc.top_node()));
}
#[test]
fn parses_self_axis() {
let tokens = tokens![
Token::Axis(AxisName::SelfAxis),
name_test("the-top-node")
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![doc.top_node()], ex.evaluate_on(expr, doc.top_node()));
}
#[test]
fn parses_parent_axis() {
let tokens = tokens![
Token::Axis(AxisName::Parent),
name_test("the-top-node")
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let hello = doc.add_top_child("hello");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![doc.top_node()], ex.evaluate_on(expr, hello));
}
#[test]
fn parses_child_axis() {
let tokens = tokens![
Token::Axis(AxisName::Child),
name_test("*")
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let one = doc.add_top_child("one");
let two = doc.add_child(one, "two");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![two], ex.evaluate_on(expr, one));
}
#[test]
fn parses_descendant_axis() {
let tokens = tokens![
Token::Axis(AxisName::Descendant),
name_test("two")
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let one = doc.add_top_child("one");
let two = doc.add_child(one, "two");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![two], ex.evaluate_on(expr, doc.top_node()));
}
#[test]
fn parses_descendant_or_self_axis() {
let tokens = tokens![
Token::Axis(AxisName::DescendantOrSelf),
name_test("*")
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let one = doc.add_top_child("one");
let two = doc.add_child(one, "two");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![one, two], ex.evaluate_on(expr, one));
}
#[test]
fn parses_attribute_axis() {
let tokens = tokens![
Token::Axis(AxisName::Attribute),
name_test("*")
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let one = doc.add_top_child("one");
let attr = one.set_attribute_value("hello", "world");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![attr], ex.evaluate_on(expr, one));
}
#[test]
fn parses_namespace_axis() {
let tokens = tokens![
Token::Axis(AxisName::Namespace),
name_test("prefix")
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let one = doc.add_top_child("one");
one.register_prefix("prefix", "uri");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
match ex.evaluate_on(expr, one) {
Value::Nodeset(ns) => {
assert_eq!(1, ns.size());
match ns.into_iter().next() {
Some(Node::Namespace(ns)) => {
assert_eq!("prefix", ns.prefix());
assert_eq!("uri", ns.uri());
},
_ => panic!("Not a namespace node"),
}
},
_ => panic!("Did not get the namespace node"),
}
}
#[test]
fn parses_child_with_same_name_as_an_axis() {
let tokens = tokens![name_test("self")];
let package = Package::new();
let doc = TestDoc(package.as_document());
let element = doc.add_top_child("self");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![element], ex.evaluate_on(expr, doc.top_node()));
}
#[test]
fn parses_node_node_test() {
let tokens = tokens![Token::NodeTest(NodeTestName::Node)];
let package = Package::new();
let doc = TestDoc(package.as_document());
let one = doc.add_top_child("one");
let two = doc.add_child(one, "two");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![two], ex.evaluate_on(expr, one));
}
#[test]
fn parses_comment_node_test() {
let tokens = tokens![Token::NodeTest(NodeTestName::Comment)];
let package = Package::new();
let doc = TestDoc(package.as_document());
let one = doc.add_top_child("one");
let two = doc.add_comment(one, "two");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![two], ex.evaluate_on(expr, one));
}
#[test]
fn parses_text_node_test() {
let tokens = tokens![Token::NodeTest(NodeTestName::Text)];
let package = Package::new();
let doc = TestDoc(package.as_document());
let one = doc.add_top_child("one");
let text = doc.add_text(one, "text");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![text], ex.evaluate_on(expr, one));
}
#[test]
fn parses_processing_instruction_node_test() {
let tokens = tokens![Token::NodeTest(NodeTestName::ProcessingInstruction(Some("name".to_owned())))];
let package = Package::new();
let doc = TestDoc(package.as_document());
let one = doc.add_top_child("one");
let two = doc.add_processing_instruction(one, "name", None);
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![two], ex.evaluate_on(expr, one));
}
#[test]
fn parses_axis_and_node_test() {
let tokens = tokens![
Token::Axis(AxisName::SelfAxis),
Token::NodeTest(NodeTestName::Text),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let one = doc.add_top_child("one");
let text = doc.add_text(one, "text");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![text], ex.evaluate_on(expr, text));
}
#[test]
fn numeric_predicate_selects_indexed_node() {
let tokens = tokens![
name_test("*"),
Token::LeftBracket,
Token::Number(2.0),
Token::RightBracket
];
let package = Package::new();
let doc = TestDoc(package.as_document());
doc.add_top_child("first");
let second = doc.add_top_child("second");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![second], ex.evaluate_on(expr, doc.top_node()));
}
#[test]
fn string_literal() {
let tokens = tokens![Token::Literal("string".to_owned())];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(String("string".to_owned()), ex.evaluate(expr));
}
#[test]
fn predicate_accepts_any_expression() {
let tokens = tokens![
name_test("*"),
Token::LeftBracket,
Token::Function("true".into()),
Token::LeftParen,
Token::RightParen,
Token::Or,
Token::Function("false".into()),
Token::LeftParen,
Token::RightParen,
Token::RightBracket
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let first = doc.add_top_child("first");
let second = doc.add_top_child("second");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![first, second], ex.evaluate_on(expr, doc.top_node()));
}
#[test]
fn true_function_predicate_selects_all_nodes() {
let tokens = tokens![
name_test("*"),
Token::LeftBracket,
Token::Function("true".into()),
Token::LeftParen,
Token::RightParen,
Token::RightBracket
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let first = doc.add_top_child("first");
let second = doc.add_top_child("second");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![first, second], ex.evaluate_on(expr, doc.top_node()));
}
#[test]
fn false_function_predicate_selects_no_nodes() {
let tokens = tokens![
name_test("*"),
Token::LeftBracket,
Token::Function("false".into()),
Token::LeftParen,
Token::RightParen,
Token::RightBracket
];
let package = Package::new();
let doc = TestDoc(package.as_document());
doc.add_top_child("first");
doc.add_top_child("second");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![], ex.evaluate_on(expr, doc.top_node()));
}
#[test]
fn multiple_predicates() {
let tokens = tokens![
name_test("*"),
Token::LeftBracket,
Token::Number(2.0),
Token::RightBracket,
Token::LeftBracket,
Token::Number(1.0),
Token::RightBracket
];
let package = Package::new();
let doc = TestDoc(package.as_document());
doc.add_top_child("first");
let second = doc.add_top_child("second");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![second], ex.evaluate_on(expr, doc.top_node()));
}
#[test]
fn functions_accept_arguments() {
let tokens = tokens![
Token::Function("not".into()),
Token::LeftParen,
Token::Function("true".into()),
Token::LeftParen,
Token::RightParen,
Token::RightParen,
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(Boolean(false), ex.evaluate(expr));
}
#[test]
fn functions_accept_any_expression_as_an_argument() {
let tokens = tokens![
Token::Function("not".into()),
Token::LeftParen,
Token::Function("true".into()),
Token::LeftParen,
Token::RightParen,
Token::Or,
Token::Function("false".into()),
Token::LeftParen,
Token::RightParen,
Token::RightParen,
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(Boolean(false), ex.evaluate(expr));
}
#[test]
fn numeric_literal() {
let tokens = tokens![Token::Number(3.2)];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_approx_eq!(Number(3.2), ex.evaluate(expr));
}
#[test]
fn addition_of_two_numbers() {
let tokens = tokens![
Token::Number(1.1),
Token::PlusSign,
Token::Number(2.2)
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_approx_eq!(Number(3.3), ex.evaluate(expr));
}
#[test]
fn addition_of_multiple_numbers() {
let tokens = tokens![
Token::Number(1.1),
Token::PlusSign,
Token::Number(2.2),
Token::PlusSign,
Token::Number(3.3)
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_approx_eq!(Number(6.6), ex.evaluate(expr));
}
#[test]
fn subtraction_of_two_numbers() {
let tokens = tokens![
Token::Number(1.1),
Token::MinusSign,
Token::Number(2.2),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_approx_eq!(Number(-1.1), ex.evaluate(expr));
}
#[test]
fn additive_expression_is_left_associative() {
let tokens = tokens![
Token::Number(1.1),
Token::MinusSign,
Token::Number(2.2),
Token::MinusSign,
Token::Number(3.3),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_approx_eq!(Number(-4.4), ex.evaluate(expr));
}
#[test]
fn multiplication_of_two_numbers() {
let tokens = tokens![
Token::Number(1.1),
Token::Multiply,
Token::Number(2.2),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_approx_eq!(Number(2.42), ex.evaluate(expr));
}
#[test]
fn division_of_two_numbers() {
let tokens = tokens![
Token::Number(7.1),
Token::Divide,
Token::Number(0.1),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_approx_eq!(Number(71.0), ex.evaluate(expr));
}
#[test]
fn remainder_of_two_numbers() {
let tokens = tokens![
Token::Number(7.1),
Token::Remainder,
Token::Number(3.0),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_approx_eq!(Number(1.1), ex.evaluate(expr));
}
#[test]
fn unary_negation() {
let tokens = tokens![
Token::MinusSign,
Token::Number(7.2),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_approx_eq!(Number(-7.2), ex.evaluate(expr));
}
#[test]
fn repeated_unary_negation() {
let tokens = tokens![
Token::MinusSign,
Token::MinusSign,
Token::MinusSign,
Token::Number(7.2),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_approx_eq!(Number(-7.2), ex.evaluate(expr));
}
#[test]
fn top_level_function_call() {
let tokens = tokens![
Token::Function("true".into()),
Token::LeftParen,
Token::RightParen,
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(Boolean(true), ex.evaluate(expr));
}
#[test]
fn or_expression() {
let tokens = tokens![
Token::Function("true".into()),
Token::LeftParen,
Token::RightParen,
Token::Or,
Token::Function("false".into()),
Token::LeftParen,
Token::RightParen,
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(Boolean(true), ex.evaluate(expr));
}
#[test]
fn and_expression() {
let tokens = tokens![
Token::Number(1.2),
Token::And,
Token::Number(0.0),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(Boolean(false), ex.evaluate(expr));
}
#[test]
fn equality_expression() {
let tokens = tokens![
Token::Number(1.2),
Token::Equal,
Token::Number(1.1),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(Boolean(false), ex.evaluate(expr));
}
#[test]
fn inequality_expression() {
let tokens = tokens![
Token::Number(1.2),
Token::NotEqual,
Token::Number(1.2),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(Boolean(false), ex.evaluate(expr));
}
#[test]
fn less_than_expression() {
let tokens = tokens![
Token::Number(1.2),
Token::LessThan,
Token::Number(1.2),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(Boolean(false), ex.evaluate(expr));
}
#[test]
fn less_than_or_equal_expression() {
let tokens = tokens![
Token::Number(1.2),
Token::LessThanOrEqual,
Token::Number(1.2),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(Boolean(true), ex.evaluate(expr));
}
#[test]
fn greater_than_expression() {
let tokens = tokens![
Token::Number(1.2),
Token::GreaterThan,
Token::Number(1.2),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(Boolean(false), ex.evaluate(expr));
}
#[test]
fn greater_than_or_equal_expression() {
let tokens = tokens![
Token::Number(1.2),
Token::GreaterThanOrEqual,
Token::Number(1.2),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(Boolean(true), ex.evaluate(expr));
}
#[test]
fn nested_expression() {
let tokens = tokens![
Token::LeftParen,
Token::Number(1.1),
Token::RightParen,
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_approx_eq!(Number(1.1), ex.evaluate(expr));
}
#[test]
fn variable_reference() {
let tokens = tokens![Token::Variable("variable-name".into())];
let package = Package::new();
let doc = TestDoc(package.as_document());
let mut ex = Exercise::new(&doc);
ex.context.set_variable("variable-name", 12.3);
let expr = ex.parse(tokens);
assert_approx_eq!(Number(12.3), ex.evaluate(expr));
}
#[test]
fn variable_reference_prefixed_name() {
let tokens = tokens![Token::Variable(("ns", "variable-name").into())];
let package = Package::new();
let doc = TestDoc(package.as_document());
let mut ex = Exercise::new(&doc);
ex.context.set_namespace("ns", "uri:vars");
ex.context.set_variable(("uri:vars", "variable-name"), 12.3);
let expr = ex.parse(tokens);
assert_approx_eq!(Number(12.3), ex.evaluate(expr));
}
#[test]
fn filter_expression() {
let tokens = tokens![
Token::Variable("variable".into()),
Token::LeftBracket,
Token::Number(0.0),
Token::RightBracket,
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let value = nodeset![
doc.add_top_child("first-node"),
doc.add_top_child("second-node"),
];
let mut ex = Exercise::new(&doc);
ex.context.set_variable("variable", value);
let expr = ex.parse(tokens);
assert_eq!(nodeset![], ex.evaluate(expr));
}
#[test]
fn filter_expression_and_relative_path() {
let tokens = tokens![
Token::Variable("variable".into()),
Token::Slash,
name_test("child"),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let parent = doc.add_top_child("parent");
let child = doc.add_child(parent, "child");
let value = nodeset![parent];
let mut ex = Exercise::new(&doc);
ex.context.set_variable("variable", value);
let expr = ex.parse(tokens);
assert_eq!(nodeset![child], ex.evaluate(expr));
}
#[test]
fn union_expression() {
let tokens = tokens![
Token::Variable("variable1".into()),
Token::Pipe,
Token::Variable("variable2".into()),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let node1 = doc.add_top_child("first-node");
let node2 = doc.add_top_child("second-node");
let mut ex = Exercise::new(&doc);
ex.context.set_variable("variable1", nodeset![node1]);
ex.context.set_variable("variable2", nodeset![node2]);
let expr = ex.parse(tokens);
assert_eq!(nodeset![node1, node2], ex.evaluate(expr));
}
#[test]
fn absolute_path_expression() {
let tokens = tokens![
Token::Slash,
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let node1 = doc.add_top_child("first-node");
let node2 = doc.add_child(node1, "second-node");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![doc.root()], ex.evaluate_on(expr, node2));
}
#[test]
fn absolute_path_with_child_expression() {
let tokens = tokens![
Token::Slash,
name_test("*"),
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let node1 = doc.add_top_child("first-node");
let node2 = doc.add_child(node1, "second-node");
let ex = Exercise::new(&doc);
let expr = ex.parse(tokens);
assert_eq!(nodeset![doc.top_node()], ex.evaluate_on(expr, node2));
}
#[test]
fn unexpected_token_is_reported_as_an_error() {
let tokens = tokens![
Token::Function("does-not-matter".into()),
Token::RightParen
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let res = ex.parser.parse(tokens.into_iter());
assert_eq!(Some(UnexpectedToken(Token::RightParen)), res.err());
}
#[test]
fn binary_operator_without_right_hand_side_is_reported_as_an_error() {
let tokens = tokens![
Token::Literal("left".to_owned()),
Token::And
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let res = ex.parse_raw(tokens);
assert_eq!(Some(RightHandSideExpressionMissing), res.err());
}
#[test]
fn unary_operator_without_right_hand_side_is_reported_as_an_error() {
let tokens = tokens![
Token::MinusSign,
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let res = ex.parser.parse(tokens.into_iter());
assert_eq!(Some(RightHandSideExpressionMissing), res.err());
}
#[test]
fn empty_predicate_is_reported_as_an_error() {
let tokens = tokens![
name_test("*"),
Token::LeftBracket,
Token::RightBracket,
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let res = ex.parse_raw(tokens);
assert_eq!(Some(EmptyPredicate), res.err());
}
#[test]
fn relative_path_with_trailing_slash_is_reported_as_an_error() {
let tokens = tokens![
name_test("*"),
Token::Slash,
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let res = ex.parse_raw(tokens);
assert_eq!(Some(TrailingSlash), res.err());
}
#[test]
fn filter_expression_with_trailing_slash_is_reported_as_an_error() {
let tokens = tokens![
Token::Variable("variable".into()),
Token::Slash,
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let res = ex.parse_raw(tokens);
assert_eq!(Some(TrailingSlash), res.err());
}
#[test]
fn running_out_of_input_is_reported_as_an_error() {
let tokens = tokens![Token::Function("func".into())];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let res = ex.parse_raw(tokens);
assert_eq!(Some(RanOutOfInput), res.err());
}
#[test]
fn having_extra_tokens_is_reported_as_an_error() {
let tokens = tokens![Token::LeftBracket];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let res = ex.parse_raw(tokens);
assert_eq!(Some(ExtraUnparsedTokens), res.err());
}
#[test]
fn a_tokenizer_error_is_reported_as_an_error() {
let tokens = vec![
Ok(Token::Function("func".into())),
Err(tokenizer::Error::UnableToCreateToken)
];
let package = Package::new();
let doc = TestDoc(package.as_document());
let ex = Exercise::new(&doc);
let res = ex.parse_raw(tokens);
assert_eq!(Some(Tokenizer(tokenizer::Error::UnableToCreateToken)), res.err());
}
}