import 'package:petitparser/definition.dart'; import 'package:petitparser/expression.dart'; import 'package:petitparser/parser.dart'; import '../xml/entities/null_mapping.dart'; import '../xml_events/parser.dart'; import 'evaluation/expression.dart'; import 'evaluation/values.dart'; import 'expressions/axis.dart'; import 'expressions/filters.dart'; import 'expressions/function.dart'; import 'expressions/path.dart'; import 'expressions/variable.dart'; import 'functions/boolean.dart' as boolean; import 'functions/nodes.dart' as nodes; import 'functions/number.dart' as number; // XPath 1.0: https://www.w3.org/TR/1999/REC-xpath-19991116/ // XPath 2.0: https://www.w3.org/TR/xpath20/ // XPath 3.0: https://www.w3.org/TR/xpath-30/ // XPath 3.1: https://www.w3.org/TR/xpath-31/ // https://www.w3.org/TR/xpath-functions-30/ class XPathParser { const XPathParser(); Parser build() => resolve(ref0(start).end()); Parser start() => [ref0(path), ref0(expression)].toChoiceParser(); Parser expression() { final builder = ExpressionBuilder(); builder ..primitive(ref0(literal)) ..primitive(ref0(variable)) ..primitive(ref0(function)) ..primitive(ref0(path)); builder.group().wrapper(_t('('), _t(')'), (_, expr, _) => expr); builder.group() ..prefix(_t('-'), (t, a) => _SFE(t, number.neg, [a])) ..prefix(_t('+'), (t, a) => a); builder.group() ..left(_t('intersect'), (a, t, b) => _SFE(t, nodes.intersect, [a, b])) ..left(_t('except'), (a, t, b) => _SFE(t, nodes.except, [a, b])); builder.group() ..left(_t('union'), (a, t, b) => _SFE(t, nodes.union, [a, b])) ..left(_t('|'), (a, t, b) => _SFE(t, nodes.union, [a, b])); builder.group() ..left(_t('*'), (a, t, b) => _SFE(t, number.mul, [a, b])) ..left(_t('div'), (a, t, b) => _SFE(t, number.div, [a, b])) ..left(_t('idiv'), (a, t, b) => _SFE(t, number.idiv, [a, b])) ..left(_t('mod'), (a, t, b) => _SFE(t, number.mod, [a, b])); builder.group() ..left(_t('+'), (a, t, b) => _SFE(t, number.add, [a, b])) ..left(_t('-'), (a, t, b) => _SFE(t, number.sub, [a, b])); builder.group() ..left(_t('='), (a, t, b) => _SFE(t, boolean.equal, [a, b])) ..left(_t('!='), (a, t, b) => _SFE(t, boolean.notEqual, [a, b])) ..left(_t('<='), (a, t, b) => _SFE(t, boolean.lessThanOrEqual, [a, b])) ..left(_t('<'), (a, t, b) => _SFE(t, boolean.lessThan, [a, b])) ..left(_t('>='), (a, t, b) => _SFE(t, boolean.greaterThanOrEqual, [a, b])) ..left(_t('>'), (a, t, b) => _SFE(t, boolean.greaterThan, [a, b])); builder.group().left(_t('and'), (a, t, b) => _SFE(t, boolean.and, [a, b])); builder.group().left(_t('or'), (a, t, b) => _SFE(t, boolean.or, [a, b])); return builder.build(); } Parser path() => [ ref0(absolutePath), ref0(relativePath), ].toChoiceParser().map(SequenceExpression.new); Parser> absolutePath() => seq2( _t('/'), ref0(relativePath).optionalWith([]), ).map2((_, path) => [RootAxisExpression(), ...path]); Parser> relativePath() => ref0(step) .plusSeparated(_t('/')) .map((list) => list.elements.map(SequenceExpression.new).toList()); Parser> step() => ref0(axisStep); Parser> axisStep() => seq2( [ref0(reverseStep), ref0(forwardStep)].toChoiceParser(), ref0(predicate).star(), ).map2((step, predicates) => [...step, ...predicates]); Parser> reverseStep() => [ [ref0(reverseAxis), ref0(nodeTest)].toSequenceParser(), ref0(abbrevReverseStep), ].toChoiceParser(); Parser reverseAxis() => [ _t('ancestor-or-self::').map((_) => AncestorOrSelfAxisExpression()), _t('ancestor::').map((_) => AncestorAxisExpression()), _t('parent::').map((_) => ParentAxisExpression()), _t('preceding-sibling::').map((_) => PrecedingSiblingAxisExpression()), _t('preceding::').map((_) => PrecedingAxisExpression()), ].toChoiceParser(); Parser> abbrevReverseStep() => [ _t('..').map((_) => [ParentAxisExpression()]), _t('.').map((_) => [SelfAxisExpression()]), ].toChoiceParser(); Parser> forwardStep() => [ [ref0(forwardAxis), ref0(nodeTest)].toSequenceParser(), ref0(abbrevForwardStep), ].toChoiceParser(); Parser forwardAxis() => [ _t('attribute::').map((_) => AttributeAxisExpression()), _t('child::').map((_) => ChildAxisExpression()), _t('descendant-or-self::').map((_) => DescendantOrSelfAxisExpression()), _t('descendant::').map((_) => DescendantAxisExpression()), _t('following-sibling::').map((_) => FollowingSiblingAxisExpression()), _t('following::').map((_) => FollowingAxisExpression()), _t('self::').map((_) => SelfAxisExpression()), ].toChoiceParser(); Parser> abbrevForwardStep() => [ seq2( _t('/'), ref0(nodeTest), ).map2((_, test) => [DescendantOrSelfAxisExpression(), test]), seq2( _t('@'), ref0(nodeTest), ).map2((_, test) => [AttributeAxisExpression(), test]), ref0(nodeTest).map((test) => [ChildAxisExpression(), test]), ].toChoiceParser(); Parser nodeTest() => [ref0(kindTest), ref0(nameTest)].toChoiceParser(); Parser kindTest() => [ _t('comment()').map((_) => CommentTypeExpression()), _t('node()').map((_) => NodeTypeExpression()), seq3( _t('processing-instruction('), ref0(string).optional(), char(')'), ).map3((_, target, _) => ProcessingTypeExpression(target)), _t('text()').map((_) => TextTypeExpression()), ].toChoiceParser(); Parser nameTest() => [ _t('*').map((_) => HasNameExpression()), seq2( ref0(name), char('(').not(), ).map2((name, _) => QualifiedNameExpression(name)), ].toChoiceParser(); Parser predicate() => seq3( char('['), ref0(expression), char(']'), ).map3((_, expr, _) => PredicateExpression(expr)); Parser literal() => [ref0(numberLiteral), ref0(stringLiteral)].toChoiceParser(); Parser numberLiteral() => seq3( digit().plus(), seq2(char('.'), digit().plus()).optional(), seq3(anyOf('eE'), anyOf('+-').optional(), digit().plus()).optional(), ).flatten(message: 'number').map((value) => XPathNumber(num.parse(value))); Parser stringLiteral() => ref0(string).map(XPathString.new); Parser string() => [ ref0(eventParser.attributeValueDoubleQuote), ref0(eventParser.attributeValueSingleQuote), ].toChoiceParser().map2((value, _) => value); Parser variable() => seq2( char('\$'), ref0(name), ).trim().map2((_, name) => VariableExpression(name)); Parser function() => seq5( ref0(name).trim(), _t('('), ref0( expression, ).starSeparated(char(',').trim()).map((list) => list.elements), _t(',').optional(), _t(')'), ).map5((name, _, args, _, _) => _DFE(name, args)); Parser name() => ref0(eventParser.nameToken); static const eventParser = XmlEventParser(XmlNullEntityMapping()); } Parser _t(String value) => value.toParser().trim(); typedef _SFE = StaticFunctionExpression; typedef _DFE = DynamicFunctionExpression;