Skip to main content

ferroui_base/data/core/parsers/
binding_expression_grammar.rs

1//! The grammar of binding expression paths (`Foo.Bar[0]`, `#name.Text`,
2//! `$parent[Border].Tag`, `!Foo`, `((ns:Type)Foo).Bar`, `Foo^`, ...).
3
4use crate::data::core::ExpressionParseException;
5use crate::utilities::character_reader::CharacterReader;
6use crate::utilities::span_helpers::{try_parse_int, NumberStyles};
7
8/// The kind of source a parsed binding expression starts from.
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10pub enum SourceMode {
11    Data,
12    Control,
13}
14
15/// A node of a parsed binding expression.
16#[derive(Clone, Debug, PartialEq)]
17pub enum Node {
18    /// The empty expression (`.`): the source itself.
19    EmptyExpression,
20    /// A property access; `accepts_null` is set for a null-conditional access (`?.`).
21    PropertyName {
22        accepts_null: bool,
23        property_name: String,
24    },
25    /// An attached property access (`(Owner.Property)` or `(ns:Owner.Property)`).
26    AttachedPropertyName {
27        accepts_null: bool,
28        namespace: String,
29        type_name: String,
30        property_name: String,
31    },
32    /// An indexer access (`[a, b]`).
33    Indexer { arguments: Vec<String> },
34    /// Logical negation (`!`). The only transform node.
35    Not,
36    /// The stream operator (`^`).
37    Stream,
38    /// The `$self` source.
39    SelfNode,
40    /// A named element source (`#name`).
41    Name { name: String },
42    /// An ancestor source (`$parent`, `$parent[Type]`, `$parent[1]`, `$parent[Type;1]`).
43    Ancestor {
44        namespace: Option<String>,
45        type_name: Option<String>,
46        level: i32,
47    },
48    /// A type cast (`(Type)` or `((ns:Type)Member)`).
49    TypeCast { namespace: String, type_name: String },
50}
51
52impl Node {
53    /// Gets a value indicating whether the node transforms the value of the
54    /// rest of the expression instead of selecting a member.
55    pub fn is_transform_node(&self) -> bool {
56        matches!(self, Node::Not)
57    }
58}
59
60/// Parser of binding expression paths.
61pub struct BindingExpressionGrammar;
62
63impl BindingExpressionGrammar {
64    /// Parses `text` into a list of nodes and the source mode.
65    pub fn parse(text: &str) -> Result<(Vec<Node>, SourceMode), ExpressionParseException> {
66        let mut r = CharacterReader::new(text);
67        Self::parse_reader(&mut r)
68    }
69
70    /// Parses the remaining input of `r` into a list of nodes and the source mode.
71    pub fn parse_reader(
72        r: &mut CharacterReader<'_>,
73    ) -> Result<(Vec<Node>, SourceMode), ExpressionParseException> {
74        let mut result = Vec::new();
75        let mode = parse(r, &mut result)?;
76        Ok((result, mode))
77    }
78}
79
80type ParseResult<T> = Result<T, ExpressionParseException>;
81
82#[derive(Clone, Copy, Debug, PartialEq, Eq)]
83enum State {
84    Start,
85    RelativeSource,
86    ElementName,
87    AfterMember,
88    BeforeMember,
89    BeforeMemberNullable,
90    AttachedProperty,
91    AttachedPropertyNullable,
92    Indexer,
93    TypeCast,
94    End,
95}
96
97struct TypeName<'a> {
98    namespace: &'a str,
99    type_name: &'a str,
100}
101
102fn parse(r: &mut CharacterReader<'_>, nodes: &mut Vec<Node>) -> ParseResult<SourceMode> {
103    let mut state = State::Start;
104    let mut mode = SourceMode::Data;
105
106    while !r.end() && state != State::End {
107        match state {
108            State::Start => state = parse_start(r, nodes),
109            State::AfterMember => state = parse_after_member(r, nodes),
110            State::BeforeMember => state = parse_before_member(r, nodes, false),
111            State::BeforeMemberNullable => state = parse_before_member(r, nodes, true),
112            State::AttachedProperty => state = parse_attached_property(r, nodes, false)?,
113            State::AttachedPropertyNullable => state = parse_attached_property(r, nodes, true)?,
114            State::Indexer => state = parse_indexer(r, nodes)?,
115            State::TypeCast => state = parse_type_cast(r, nodes)?,
116            State::ElementName => {
117                state = parse_element_name(r, nodes)?;
118                mode = SourceMode::Control;
119            }
120            State::RelativeSource => {
121                state = parse_relative_source(r, nodes)?;
122                mode = SourceMode::Control;
123            }
124            State::End => {}
125        }
126    }
127
128    if !r.end() {
129        return Err(ExpressionParseException::new(
130            r.position(),
131            "Expected end of expression.",
132        ));
133    }
134
135    if matches!(state, State::BeforeMember | State::BeforeMemberNullable) {
136        return Err(ExpressionParseException::new(
137            r.position(),
138            "Unexpected end of expression.",
139        ));
140    }
141
142    Ok(mode)
143}
144
145fn parse_start(r: &mut CharacterReader<'_>, nodes: &mut Vec<Node>) -> State {
146    if parse_not(r) {
147        nodes.push(Node::Not);
148        return State::Start;
149    } else if parse_sharp(r) {
150        return State::ElementName;
151    } else if parse_dollar_sign(r) {
152        return State::RelativeSource;
153    } else if parse_open_brace(r) {
154        if peek_open_brace(r) {
155            return State::TypeCast;
156        }
157
158        return State::AttachedProperty;
159    } else if peek_open_bracket(r) {
160        return State::Indexer;
161    } else if parse_dot(r) {
162        nodes.push(Node::EmptyExpression);
163        return State::AfterMember;
164    } else if parse_stream_operator(r) {
165        nodes.push(Node::EmptyExpression);
166        nodes.push(Node::Stream);
167        return State::AfterMember;
168    } else {
169        let identifier = r.parse_identifier();
170
171        if !identifier.is_empty() {
172            nodes.push(Node::PropertyName {
173                accepts_null: false,
174                property_name: identifier.to_string(),
175            });
176            return State::AfterMember;
177        }
178    }
179
180    State::End
181}
182
183fn parse_after_member(r: &mut CharacterReader<'_>, nodes: &mut Vec<Node>) -> State {
184    if let Some(accepts_null) = parse_member_accessor(r) {
185        return if accepts_null {
186            State::BeforeMemberNullable
187        } else {
188            State::BeforeMember
189        };
190    } else if parse_stream_operator(r) {
191        nodes.push(Node::Stream);
192        return State::AfterMember;
193    } else if peek_open_bracket(r) {
194        return State::Indexer;
195    } else if parse_open_brace(r) {
196        return State::TypeCast;
197    }
198
199    State::End
200}
201
202fn parse_before_member(
203    r: &mut CharacterReader<'_>,
204    nodes: &mut Vec<Node>,
205    accepts_null: bool,
206) -> State {
207    if parse_open_brace(r) {
208        if peek_open_brace(r) {
209            return State::TypeCast;
210        }
211
212        if accepts_null {
213            State::AttachedPropertyNullable
214        } else {
215            State::AttachedProperty
216        }
217    } else {
218        let identifier = r.parse_identifier();
219
220        if !identifier.is_empty() {
221            nodes.push(Node::PropertyName {
222                accepts_null,
223                property_name: identifier.to_string(),
224            });
225            return State::AfterMember;
226        }
227
228        State::End
229    }
230}
231
232fn parse_attached_property(
233    r: &mut CharacterReader<'_>,
234    nodes: &mut Vec<Node>,
235    accepts_null: bool,
236) -> ParseResult<State> {
237    let TypeName {
238        namespace: ns,
239        type_name: owner,
240    } = parse_type_name(r);
241
242    if !r.end() && r.take_if(')') {
243        nodes.push(Node::TypeCast {
244            namespace: ns.to_string(),
245            type_name: owner.to_string(),
246        });
247        return Ok(State::AfterMember);
248    }
249
250    if r.end() || !r.take_if('.') {
251        return Err(ExpressionParseException::new(
252            r.position(),
253            "Invalid attached property name.",
254        ));
255    }
256
257    let name = r.parse_identifier();
258
259    if name.is_empty() {
260        return Err(ExpressionParseException::new(
261            r.position(),
262            "Attached Property name expected after '.'.",
263        ));
264    }
265
266    if r.end() || !r.take_if(')') {
267        return Err(ExpressionParseException::new(r.position(), "Expected ')'."));
268    }
269
270    nodes.push(Node::AttachedPropertyName {
271        accepts_null,
272        namespace: ns.to_string(),
273        type_name: owner.to_string(),
274        property_name: name.to_string(),
275    });
276    Ok(State::AfterMember)
277}
278
279fn parse_indexer(r: &mut CharacterReader<'_>, nodes: &mut Vec<Node>) -> ParseResult<State> {
280    let args = r.parse_arguments('[', ']')?;
281
282    if args.is_empty() {
283        return Err(ExpressionParseException::new(
284            r.position(),
285            "Indexer may not be empty.",
286        ));
287    }
288
289    nodes.push(Node::Indexer { arguments: args });
290    Ok(State::AfterMember)
291}
292
293fn parse_type_cast(r: &mut CharacterReader<'_>, nodes: &mut Vec<Node>) -> ParseResult<State> {
294    let parse_member_before_add_cast = parse_open_brace(r);
295
296    let TypeName {
297        namespace: ns,
298        type_name,
299    } = parse_type_name(r);
300
301    let mut result = State::AfterMember;
302
303    if parse_member_before_add_cast {
304        if !parse_close_brace(r) {
305            return Err(ExpressionParseException::new(r.position(), "Expected ')'."));
306        }
307
308        result = parse_before_member(r, nodes, false);
309        if result == State::AttachedProperty {
310            result = parse_attached_property(r, nodes, false)?;
311        }
312
313        if r.peek() == Some('[') {
314            result = parse_indexer(r, nodes)?;
315        }
316    }
317
318    nodes.push(Node::TypeCast {
319        namespace: ns.to_string(),
320        type_name: type_name.to_string(),
321    });
322
323    if r.end() || !r.take_if(')') {
324        return Err(ExpressionParseException::new(r.position(), "Expected ')'."));
325    }
326
327    Ok(result)
328}
329
330fn parse_element_name(r: &mut CharacterReader<'_>, nodes: &mut Vec<Node>) -> ParseResult<State> {
331    let name = r.parse_identifier();
332
333    if name.is_empty() {
334        return Err(ExpressionParseException::new(
335            r.position(),
336            "Element name expected after '#'.",
337        ));
338    }
339
340    nodes.push(Node::Name {
341        name: name.to_string(),
342    });
343    Ok(State::AfterMember)
344}
345
346fn parse_relative_source(
347    r: &mut CharacterReader<'_>,
348    nodes: &mut Vec<Node>,
349) -> ParseResult<State> {
350    let mode = r.parse_identifier();
351
352    if mode == "self" {
353        nodes.push(Node::SelfNode);
354    } else if mode == "parent" {
355        let mut ancestor_namespace: Option<String> = None;
356        let mut ancestor_type: Option<String> = None;
357        let mut ancestor_level = 0;
358        if peek_open_bracket(r) {
359            let args = r.parse_arguments_with_delimiter('[', ']', ';')?;
360            if args.len() > 2 || args.is_empty() {
361                return Err(ExpressionParseException::new(
362                    r.position(),
363                    "Too many arguments in RelativeSource syntax sugar",
364                ));
365            } else if args.len() == 1 {
366                if let Some(level) = try_parse_int(&args[0], NumberStyles::INTEGER) {
367                    ancestor_type = None;
368                    ancestor_level = level;
369                } else {
370                    let mut reader = CharacterReader::new(&args[0]);
371                    let name = parse_type_name(&mut reader);
372                    ancestor_namespace = Some(name.namespace.to_string());
373                    ancestor_type = Some(name.type_name.to_string());
374                }
375            } else {
376                let mut reader = CharacterReader::new(&args[0]);
377                let name = parse_type_name(&mut reader);
378                ancestor_namespace = Some(name.namespace.to_string());
379                ancestor_type = Some(name.type_name.to_string());
380                ancestor_level = match try_parse_int(&args[1], NumberStyles::INTEGER) {
381                    Some(level) => level,
382                    None => {
383                        return Err(ExpressionParseException::new(
384                            r.position(),
385                            format!(
386                                "The input string '{}' was not in a correct format.",
387                                args[1]
388                            ),
389                        ));
390                    }
391                };
392            }
393        }
394        nodes.push(Node::Ancestor {
395            namespace: ancestor_namespace,
396            type_name: ancestor_type,
397            level: ancestor_level,
398        });
399    } else {
400        return Err(ExpressionParseException::new(
401            r.position(),
402            "Unknown RelativeSource mode.",
403        ));
404    }
405
406    Ok(State::AfterMember)
407}
408
409fn parse_type_name<'a>(r: &mut CharacterReader<'a>) -> TypeName<'a> {
410    let type_name_or_namespace = r.parse_type_identifier();
411
412    if !r.end() && r.take_if(':') {
413        TypeName {
414            namespace: type_name_or_namespace,
415            type_name: r.parse_type_identifier(),
416        }
417    } else {
418        TypeName {
419            namespace: "",
420            type_name: type_name_or_namespace,
421        }
422    }
423}
424
425fn parse_not(r: &mut CharacterReader<'_>) -> bool {
426    !r.end() && r.take_if('!')
427}
428
429/// Returns `Some(accepts_null)` when a member accessor (`.` or `?.`) was consumed.
430fn parse_member_accessor(r: &mut CharacterReader<'_>) -> Option<bool> {
431    if r.end() {
432        return None;
433    }
434
435    if r.take_if('.') {
436        return Some(false);
437    }
438
439    if r.take_if_str("?.") {
440        return Some(true);
441    }
442
443    None
444}
445
446fn parse_open_brace(r: &mut CharacterReader<'_>) -> bool {
447    !r.end() && r.take_if('(')
448}
449
450fn parse_close_brace(r: &mut CharacterReader<'_>) -> bool {
451    !r.end() && r.take_if(')')
452}
453
454fn peek_open_bracket(r: &CharacterReader<'_>) -> bool {
455    !r.end() && r.peek() == Some('[')
456}
457
458fn peek_open_brace(r: &CharacterReader<'_>) -> bool {
459    !r.end() && r.peek() == Some('(')
460}
461
462fn parse_stream_operator(r: &mut CharacterReader<'_>) -> bool {
463    !r.end() && r.take_if('^')
464}
465
466fn parse_dollar_sign(r: &mut CharacterReader<'_>) -> bool {
467    !r.end() && r.take_if('$')
468}
469
470fn parse_sharp(r: &mut CharacterReader<'_>) -> bool {
471    !r.end() && r.take_if('#')
472}
473
474fn parse_dot(r: &mut CharacterReader<'_>) -> bool {
475    !r.end() && r.take_if('.')
476}
477
478#[cfg(test)]
479mod tests {
480    use super::*;
481
482    fn parse(s: &str) -> Vec<Node> {
483        let mut r = CharacterReader::new(s);
484        BindingExpressionGrammar::parse_reader(&mut r).unwrap().0
485    }
486
487    fn parse_err(s: &str) -> ExpressionParseException {
488        BindingExpressionGrammar::parse(s).unwrap_err()
489    }
490
491    fn assert_error(s: &str, column: i32, message: &str) {
492        let e = parse_err(s);
493        assert_eq!((e.column(), e.message()), (column, message), "input: {s}");
494    }
495
496    fn assert_is_property(node: &Node, name: &str) {
497        assert_is_property_nullable(node, name, false);
498    }
499
500    fn assert_is_property_nullable(node: &Node, name: &str, accepts_null: bool) {
501        assert_eq!(
502            node,
503            &Node::PropertyName {
504                accepts_null,
505                property_name: name.to_string()
506            }
507        );
508    }
509
510    fn assert_is_attached_property(node: &Node, type_name: &str, name: &str) {
511        assert_is_attached_property_nullable(node, type_name, name, false);
512    }
513
514    fn assert_is_attached_property_nullable(
515        node: &Node,
516        expected_type_name: &str,
517        name: &str,
518        expected_accepts_null: bool,
519    ) {
520        match node {
521            Node::AttachedPropertyName {
522                accepts_null,
523                type_name,
524                property_name,
525                ..
526            } => {
527                assert_eq!(type_name, expected_type_name);
528                assert_eq!(property_name, name);
529                assert_eq!(*accepts_null, expected_accepts_null);
530            }
531            other => panic!("expected an attached property, got {other:?}"),
532        }
533    }
534
535    fn assert_is_indexer(node: &Node, args: &[&str]) {
536        match node {
537            Node::Indexer { arguments } => assert_eq!(arguments, args),
538            other => panic!("expected an indexer, got {other:?}"),
539        }
540    }
541
542    fn type_cast(namespace: &str, type_name: &str) -> Node {
543        Node::TypeCast {
544            namespace: namespace.to_string(),
545            type_name: type_name.to_string(),
546        }
547    }
548
549    // BindingExpressionGrammarTests
550
551    #[test]
552    fn should_parse_single_property() {
553        let result = parse("Foo");
554        assert_eq!(result.len(), 1);
555        assert_is_property(&result[0], "Foo");
556    }
557
558    #[test]
559    fn should_parse_underscored_property() {
560        let result = parse("_Foo");
561        assert_eq!(result.len(), 1);
562        assert_is_property(&result[0], "_Foo");
563    }
564
565    #[test]
566    fn should_parse_property_with_digits() {
567        let result = parse("F0o");
568        assert_eq!(result.len(), 1);
569        assert_is_property(&result[0], "F0o");
570    }
571
572    #[test]
573    fn should_parse_dot() {
574        let result = parse(".");
575        assert_eq!(result, [Node::EmptyExpression]);
576    }
577
578    #[test]
579    fn should_parse_single_attached_property() {
580        let result = parse("(Foo.Bar)");
581        assert_eq!(result.len(), 1);
582        assert_is_attached_property(&result[0], "Foo", "Bar");
583    }
584
585    #[test]
586    fn should_parse_property_chain() {
587        let result = parse("Foo.Bar.Baz");
588
589        assert_eq!(result.len(), 3);
590        assert_is_property(&result[0], "Foo");
591        assert_is_property(&result[1], "Bar");
592        assert_is_property(&result[2], "Baz");
593    }
594
595    #[test]
596    fn should_parse_property_chain_with_attached_property_1() {
597        let result = parse("(Foo.Bar).Baz");
598
599        assert_eq!(result.len(), 2);
600        assert_is_attached_property(&result[0], "Foo", "Bar");
601        assert_is_property(&result[1], "Baz");
602    }
603
604    #[test]
605    fn should_parse_property_chain_with_attached_property_2() {
606        let result = parse("Foo.(Bar.Baz)");
607
608        assert_eq!(result.len(), 2);
609        assert_is_property(&result[0], "Foo");
610        assert_is_attached_property(&result[1], "Bar", "Baz");
611    }
612
613    #[test]
614    fn should_parse_property_chain_with_attached_property_3() {
615        let result = parse("Foo.(Bar.Baz).Last");
616
617        assert_eq!(result.len(), 3);
618        assert_is_property(&result[0], "Foo");
619        assert_is_attached_property(&result[1], "Bar", "Baz");
620        assert_is_property(&result[2], "Last");
621    }
622
623    #[test]
624    fn should_parse_null_conditional_in_property_chain_1() {
625        let result = parse("Foo?.Bar.Baz");
626
627        assert_eq!(result.len(), 3);
628        assert_is_property(&result[0], "Foo");
629        assert_is_property_nullable(&result[1], "Bar", true);
630        assert_is_property(&result[2], "Baz");
631    }
632
633    #[test]
634    fn should_parse_null_conditional_in_property_chain_2() {
635        let result = parse("Foo.Bar?.Baz");
636
637        assert_eq!(result.len(), 3);
638        assert_is_property(&result[0], "Foo");
639        assert_is_property(&result[1], "Bar");
640        assert_is_property_nullable(&result[2], "Baz", true);
641    }
642
643    #[test]
644    fn should_parse_null_conditional_in_property_chain_3() {
645        let result = parse("Foo?.(Bar.Baz)");
646
647        assert_eq!(result.len(), 2);
648        assert_is_property(&result[0], "Foo");
649        assert_is_attached_property_nullable(&result[1], "Bar", "Baz", true);
650    }
651
652    #[test]
653    fn should_parse_negated_property_chain() {
654        let result = parse("!Foo.Bar.Baz");
655
656        assert_eq!(result.len(), 4);
657        assert_eq!(result[0], Node::Not);
658        assert_is_property(&result[1], "Foo");
659        assert_is_property(&result[2], "Bar");
660        assert_is_property(&result[3], "Baz");
661    }
662
663    #[test]
664    fn should_parse_double_negated_property_chain() {
665        let result = parse("!!Foo.Bar.Baz");
666
667        assert_eq!(result.len(), 5);
668        assert_eq!(result[0], Node::Not);
669        assert_eq!(result[1], Node::Not);
670        assert_is_property(&result[2], "Foo");
671        assert_is_property(&result[3], "Bar");
672        assert_is_property(&result[4], "Baz");
673    }
674
675    #[test]
676    fn should_parse_indexed_property() {
677        let result = parse("Foo[15]");
678
679        assert_eq!(result.len(), 2);
680        assert_is_property(&result[0], "Foo");
681        assert_is_indexer(&result[1], &["15"]);
682    }
683
684    #[test]
685    fn should_parse_indexed_property_string_index() {
686        let result = parse("Foo[Key]");
687
688        assert_eq!(result.len(), 2);
689        assert_is_property(&result[0], "Foo");
690        assert_is_indexer(&result[1], &["Key"]);
691    }
692
693    #[test]
694    fn should_parse_multiple_indexed_property() {
695        let result = parse("Foo[15,6]");
696
697        assert_eq!(result.len(), 2);
698        assert_is_property(&result[0], "Foo");
699        assert_is_indexer(&result[1], &["15", "6"]);
700    }
701
702    #[test]
703    fn should_parse_multiple_indexed_property_with_space() {
704        let result = parse("Foo[5, 16]");
705
706        assert_eq!(result.len(), 2);
707        assert_is_property(&result[0], "Foo");
708        assert_is_indexer(&result[1], &["5", "16"]);
709    }
710
711    #[test]
712    fn should_parse_consecutive_indexers() {
713        let result = parse("Foo[15][16]");
714
715        assert_eq!(result.len(), 3);
716        assert_is_property(&result[0], "Foo");
717        assert_is_indexer(&result[1], &["15"]);
718        assert_is_indexer(&result[2], &["16"]);
719    }
720
721    #[test]
722    fn should_parse_indexed_property_in_chain() {
723        let result = parse("Foo.Bar[5, 6].Baz");
724
725        assert_eq!(result.len(), 4);
726        assert_is_property(&result[0], "Foo");
727        assert_is_property(&result[1], "Bar");
728        assert_is_indexer(&result[2], &["5", "6"]);
729        assert_is_property(&result[3], "Baz");
730    }
731
732    #[test]
733    fn should_parse_stream_node() {
734        let result = parse("Foo^");
735
736        assert_eq!(result.len(), 2);
737        assert_eq!(result[1], Node::Stream);
738    }
739
740    #[test]
741    fn should_parse_stream_node_on_empty_expression() {
742        let result = parse("^");
743
744        assert_eq!(result, [Node::EmptyExpression, Node::Stream]);
745    }
746
747    #[test]
748    fn should_parse_stream_node_after_dot() {
749        let result = parse(".^");
750
751        assert_eq!(result, [Node::EmptyExpression, Node::Stream]);
752    }
753
754    #[test]
755    fn should_parse_cast_to_nested_type() {
756        let result = parse("((ns:Outer+Inner)Foo).Bar");
757
758        assert_eq!(result[1], type_cast("ns", "Outer+Inner"));
759    }
760
761    #[test]
762    fn should_parse_cast_to_non_nested_type() {
763        let result = parse("((ns:Outer)Foo).Bar");
764
765        assert_eq!(result[1], type_cast("ns", "Outer"));
766    }
767
768    // BindingExpressionGrammarTests_Errors
769
770    #[test]
771    fn identifier_cannot_start_with_digit() {
772        assert_error("1Foo", 0, "Expected end of expression.");
773    }
774
775    #[test]
776    fn identifier_cannot_start_with_symbol() {
777        assert_error("Foo.%Bar", 4, "Expected end of expression.");
778    }
779
780    #[test]
781    fn identifier_cannot_start_with_question_mark() {
782        assert_error("?Foo", 0, "Expected end of expression.");
783    }
784
785    #[test]
786    fn identifier_cannot_start_with_null_conditional() {
787        assert_error("?.Foo", 0, "Expected end of expression.");
788    }
789
790    #[test]
791    fn expression_cannot_end_with_period() {
792        assert_error("Foo.Bar.", 8, "Unexpected end of expression.");
793    }
794
795    #[test]
796    fn expression_cannot_end_with_question_mark() {
797        assert_error("Foo.Bar?", 7, "Expected end of expression.");
798    }
799
800    #[test]
801    fn expression_cannot_end_with_null_conditional() {
802        assert_error("Foo.Bar?.", 9, "Unexpected end of expression.");
803    }
804
805    #[test]
806    fn expression_cannot_start_with_period_then_token() {
807        assert_error(".Bar", 1, "Expected end of expression.");
808    }
809
810    #[test]
811    fn expression_cannot_have_empty_indexer() {
812        assert_error("Foo.Bar[]", 8, "Expected indexer argument.");
813    }
814
815    #[test]
816    fn expression_cannot_have_extra_comma_at_start_of_indexer() {
817        assert_error("Foo.Bar[,3,4]", 8, "Expected indexer argument.");
818    }
819
820    #[test]
821    fn expression_cannot_have_extra_comma_in_indexer() {
822        assert_error("Foo.Bar[3,,4]", 10, "Expected indexer argument.");
823    }
824
825    #[test]
826    fn expression_cannot_have_extra_comma_at_end_of_indexer() {
827        assert_error("Foo.Bar[3,4,]", 12, "Expected indexer argument.");
828    }
829
830    #[test]
831    fn expression_cannot_have_digit_after_indexer() {
832        assert_error("Foo.Bar[3,4]5", 12, "Expected end of expression.");
833    }
834
835    #[test]
836    fn expression_cannot_have_letter_after_indexer() {
837        assert_error("Foo.Bar[3,4]A", 12, "Expected end of expression.");
838    }
839
840    // Tests specific to this port: node kinds and error paths the upstream
841    // suite does not cover.
842
843    fn parse_with_mode(s: &str) -> (Vec<Node>, SourceMode) {
844        BindingExpressionGrammar::parse(s).unwrap()
845    }
846
847    fn property(name: &str) -> Node {
848        Node::PropertyName {
849            accepts_null: false,
850            property_name: name.to_string(),
851        }
852    }
853
854    #[test]
855    fn empty_string_parses_to_no_nodes() {
856        assert_eq!(parse_with_mode(""), (vec![], SourceMode::Data));
857    }
858
859    #[test]
860    fn data_source_mode_for_property_paths() {
861        assert_eq!(parse_with_mode("Foo.Bar").1, SourceMode::Data);
862    }
863
864    #[test]
865    fn should_parse_element_name() {
866        assert_eq!(
867            parse_with_mode("#foo.Bar"),
868            (
869                vec![
870                    Node::Name {
871                        name: "foo".to_string()
872                    },
873                    property("Bar")
874                ],
875                SourceMode::Control
876            )
877        );
878    }
879
880    #[test]
881    fn should_parse_negated_element_name() {
882        let (nodes, mode) = parse_with_mode("!#foo");
883        assert_eq!(
884            nodes,
885            [
886                Node::Not,
887                Node::Name {
888                    name: "foo".to_string()
889                }
890            ]
891        );
892        assert_eq!(mode, SourceMode::Control);
893        assert!(nodes[0].is_transform_node());
894        assert!(!nodes[1].is_transform_node());
895    }
896
897    #[test]
898    fn should_parse_self() {
899        assert_eq!(
900            parse_with_mode("$self.Foo"),
901            (vec![Node::SelfNode, property("Foo")], SourceMode::Control)
902        );
903    }
904
905    #[test]
906    fn should_parse_parent() {
907        assert_eq!(
908            parse_with_mode("$parent.Foo"),
909            (
910                vec![
911                    Node::Ancestor {
912                        namespace: None,
913                        type_name: None,
914                        level: 0
915                    },
916                    property("Foo")
917                ],
918                SourceMode::Control
919            )
920        );
921    }
922
923    #[test]
924    fn should_parse_parent_with_level() {
925        assert_eq!(
926            parse("$parent[2]"),
927            [Node::Ancestor {
928                namespace: None,
929                type_name: None,
930                level: 2
931            }]
932        );
933    }
934
935    #[test]
936    fn should_parse_parent_with_type() {
937        assert_eq!(
938            parse("$parent[Border].Tag"),
939            [
940                Node::Ancestor {
941                    namespace: Some(String::new()),
942                    type_name: Some("Border".to_string()),
943                    level: 0
944                },
945                property("Tag")
946            ]
947        );
948    }
949
950    #[test]
951    fn should_parse_parent_with_namespaced_type_and_level() {
952        assert_eq!(
953            parse("$parent[local:Border; 1]"),
954            [Node::Ancestor {
955                namespace: Some("local".to_string()),
956                type_name: Some("Border".to_string()),
957                level: 1
958            }]
959        );
960    }
961
962    #[test]
963    fn should_parse_namespaced_attached_property() {
964        assert_eq!(
965            parse("(local:Foo.Bar)"),
966            [Node::AttachedPropertyName {
967                accepts_null: false,
968                namespace: "local".to_string(),
969                type_name: "Foo".to_string(),
970                property_name: "Bar".to_string()
971            }]
972        );
973    }
974
975    #[test]
976    fn should_parse_type_cast_of_source() {
977        assert_eq!(parse("(ns:Foo)"), [type_cast("ns", "Foo")]);
978        assert_eq!(
979            parse("(Foo).Bar"),
980            [type_cast("", "Foo"), property("Bar")]
981        );
982    }
983
984    #[test]
985    fn should_parse_type_cast_after_member() {
986        assert_eq!(
987            parse("Foo(ns:Bar).Baz"),
988            [property("Foo"), type_cast("ns", "Bar"), property("Baz")]
989        );
990        assert_eq!(
991            parse("Foo.Bar((Baz)Qux)"),
992            [
993                property("Foo"),
994                property("Bar"),
995                property("Qux"),
996                type_cast("", "Baz")
997            ]
998        );
999    }
1000
1001    #[test]
1002    fn should_parse_type_cast_of_member_in_chain() {
1003        assert_eq!(
1004            parse("Foo.((ns:Type)Bar).Baz"),
1005            [
1006                property("Foo"),
1007                property("Bar"),
1008                type_cast("ns", "Type"),
1009                property("Baz")
1010            ]
1011        );
1012    }
1013
1014    #[test]
1015    fn should_parse_type_cast_of_attached_property_and_indexer() {
1016        assert_eq!(
1017            parse("((Type)(Owner.Prop))"),
1018            [
1019                Node::AttachedPropertyName {
1020                    accepts_null: false,
1021                    namespace: String::new(),
1022                    type_name: "Owner".to_string(),
1023                    property_name: "Prop".to_string()
1024                },
1025                type_cast("", "Type")
1026            ]
1027        );
1028        assert_eq!(
1029            parse("((Type)Foo[1])"),
1030            [
1031                property("Foo"),
1032                Node::Indexer {
1033                    arguments: vec!["1".to_string()]
1034                },
1035                type_cast("", "Type")
1036            ]
1037        );
1038    }
1039
1040    #[test]
1041    fn should_parse_leading_indexer() {
1042        let result = parse("[0].Foo");
1043        assert_is_indexer(&result[0], &["0"]);
1044        assert_is_property(&result[1], "Foo");
1045    }
1046
1047    #[test]
1048    fn should_parse_stream_in_chain() {
1049        assert_eq!(
1050            parse("Foo^.Bar^"),
1051            [property("Foo"), Node::Stream, property("Bar"), Node::Stream]
1052        );
1053    }
1054
1055    #[test]
1056    fn should_parse_unicode_identifiers() {
1057        assert_eq!(parse("Żółw.Imię"), [property("Żółw"), property("Imię")]);
1058    }
1059
1060    #[test]
1061    fn unterminated_prefixes_parse_to_no_nodes() {
1062        // As upstream: the loop stops at the end of the input before the
1063        // pending state is processed, and only a pending member is an error.
1064        assert_eq!(parse_with_mode("#"), (vec![], SourceMode::Data));
1065        assert_eq!(parse_with_mode("$"), (vec![], SourceMode::Data));
1066        assert_eq!(parse_with_mode("("), (vec![], SourceMode::Data));
1067        assert_eq!(parse_with_mode("!"), (vec![Node::Not], SourceMode::Data));
1068    }
1069
1070    #[test]
1071    fn element_name_errors() {
1072        assert_error("#1", 1, "Element name expected after '#'.");
1073    }
1074
1075    #[test]
1076    fn relative_source_errors() {
1077        assert_error("$foo", 4, "Unknown RelativeSource mode.");
1078        assert_error("$1", 1, "Unknown RelativeSource mode.");
1079        assert_error(
1080            "$parent[A;1;2]",
1081            14,
1082            "Too many arguments in RelativeSource syntax sugar",
1083        );
1084        assert_error(
1085            "$parent[A;B]",
1086            12,
1087            "The input string 'B' was not in a correct format.",
1088        );
1089        assert_error("$parent[A 1]", 11, "Expected ';'.");
1090        assert_error("$parent[]", 8, "Expected indexer argument.");
1091    }
1092
1093    #[test]
1094    fn attached_property_errors() {
1095        assert_error("(Foo", 4, "Invalid attached property name.");
1096        assert_error("(Foo:", 5, "Invalid attached property name.");
1097        assert_error("(Foo-Bar)", 4, "Invalid attached property name.");
1098        assert_error("(Foo.)", 5, "Attached Property name expected after '.'.");
1099        assert_error("(Foo.", 5, "Attached Property name expected after '.'.");
1100        assert_error("(Foo.Bar", 8, "Expected ')'.");
1101        assert_error("(Foo.Bar.Baz)", 8, "Expected ')'.");
1102        assert_error("Foo?.(Bar", 9, "Invalid attached property name.");
1103    }
1104
1105    #[test]
1106    fn type_cast_errors() {
1107        assert_error("((Foo", 5, "Expected ')'.");
1108        assert_error("((Foo)", 6, "Expected ')'.");
1109        assert_error("((Foo)Bar", 9, "Expected ')'.");
1110        assert_error("((Foo)Bar]", 9, "Expected ')'.");
1111        assert_error("Foo(Bar", 7, "Expected ')'.");
1112        assert_error("((Foo)Bar[])", 10, "Expected indexer argument.");
1113    }
1114
1115    #[test]
1116    fn indexer_errors() {
1117        assert_error("Foo[1", 5, "Expected ','.");
1118        assert_error("Foo[1,", 6, "Expected ']'.");
1119        assert_error("Foo[1 2]", 7, "Expected ','.");
1120    }
1121
1122    #[test]
1123    fn trailing_garbage_is_an_error() {
1124        assert_error("Foo Bar", 3, "Expected end of expression.");
1125        assert_error("Foo)", 3, "Expected end of expression.");
1126        assert_error("Foo!", 3, "Expected end of expression.");
1127    }
1128}