Blob Blame History Raw
Path expressions
================

In the public API, especially for aug_match and aug_get, tree nodes can be
identified with a powerful syntax, that is modelled on the XPath syntax for
XML documents.

In the simplest case, a path expression just lists a path to some node in
the tree, for example,

   /files/etc/hosts/1/ipaddr

If multiple nodes have the same label, one of them can be picked out by
providing its position, either counting from the first such node (at
position 1) or counting from the end. For example, the second alias of the
first host entry is

   /files/etc/hosts/1/alias[2]

and the penultimate alias is

   /files/etc/hosts/1/alias[last() - 1]

For /etc/hosts, each entry can be thought of as a primary key (the ipaddr)
and additional attributes relating to that primary key, namely the
canonical host name and its aliases. It is therefore natural to refer to
host entries by their ipaddr, not by the synthetic sequence number in their
path. The canonical name of the host entry with ipaddr 127.0.0.1 can be
found with

   /files/etc/hosts/*[ipaddr = "127.0.0.1"]/canonical

or, equivalently, with

   /files/etc/hosts/*/canonical[../ipaddr = "127.0.0.1"]

The canonical names of all hosts that have at least one alias:

   /files/etc/hosts/*/canonical[../alias]

It is also possible to search bigger parts of the tree by using '//'. For
example, all nodes called 'ipaddr' underneath /files/etc can be found with

   /files/etc//ipaddr

This is handy for finding errors reported by Augeas underneath /augeas:

   /augeas//error

A lazy way to find localhost is

  /files/etc//ipaddr[. = '127.0.0.1']

The vardir entry in the main section of puppet.conf is at

  /files/etc/puppet/puppet.conf/section[. = "main"]/vardir

All pam entries that use the system-auth module:

  /files/etc/pam.d/*[.//module = "system-auth"]

More examples can be found in tests/xpath.tests

One further extension that might be useful is to add boolean operators for
predicates, so that we can write

 /files/etc/hosts/ipaddr[../alias = 'localhost' or ../canonical = 'localhost']

Grammar for path expressions
============================

Formally, path expressions are generated by this grammar. The grammar uses
nonterminals from the XPath 1.0 grammar to point out the similarities
between XPath and Augeas path expressions.

Unfortunately, the production for PathExpr is ambiguous, since Augeas is
too liberal in what it allows as labels for tree nodes: the expression '42'
can either be the number 42 (a PrimaryExpr) or the RelativeLocationPath
'child::42'. The reason for this ambiguity is that we allow node names like
'42' in the tree; rather than forbid them, we resolve the ambiguity by
always parsing '42' as a number, and requiring that the user write the
RelativeLocationPath in a different form, e.g. 'child::42' or './42'.

LocationPath ::= RelativeLocationPath | AbsoluteLocationPath

AbsoluteLocationPath ::= '/' RelativeLocationPath?
                       | AbbreviatedAbsoluteLocationPath
AbbreviatedAbsoluteLocationPath ::= '//' RelativeLocationPath

RelativeLocationPath ::= Step
                       | RelativeLocationPath '/' Step
                       | AbbreviatedRelativeLocationPath
AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step

Step ::= AxisSpecifier NameTest Predicate* | '.' | '..'
AxisSpecifier ::= AxisName '::' | <epsilon>
AxisName ::= 'ancestor'
           | 'ancestor-or-self'
           | 'child'
           | 'descendant'
           | 'descendant-or-self'
           | 'parent'
           | 'self'
           | 'root'
NameTest     ::= '*' | Name
Predicate    ::= "[" Expr "]" *

PathExpr ::= LocationPath
           | FilterExpr
           | FilterExpr '/' RelativeLocationPath
           | FilterExpr '//' RelativeLocationPath

FilterExpr ::= PrimaryExpr Predicate

PrimaryExpr ::= Literal
              | Number
              | FunctionCall
              | VariableReference
              | '(' Expr ')'

FunctionCall ::= Name '(' ( Expr ( ',' Expr )* )? ')'

Expr ::= EqualityExpr
EqualityExpr ::= RelationalExpr (EqualityOp RelationalExpr)? | ReMatchExpr
ReMatchExpr ::= RelationalExpr MatchOp RelationalExpr
MatchOp ::= "=~" | "!~"
EqualityOp ::= "=" | "!="
RelationalExpr ::= AdditiveExpr (RelationalOp AdditiveExpr)?
RelationalOp ::= ">" | "<" | ">=" | "<="
AdditiveExpr ::= MultiplicativeExpr (AdditiveOp MultiplicativeExpr)*
AdditiveOp   ::= '+' | '-'
MultiplicativeExpr ::= UnionExpr ('*' UnionExpr)*
UnionExpr ::= PathExpr ("|" PathExpr)*

Literal ::= '"' /[^"]* / '"' | "'" /[^']* / "'"
Number       ::= /[0-9]+/
/* Names can contain whitespace in the interior */
NameNoWS ::= [^][|/\= \t\n] | \\.
NameWS   ::= [^][|/\=] | \\.
Name ::= NameNoWS NameWS* NameNoWS | NameNoWS

VariableReference ::= '$' /[a-zA-Z_][a-zA-Z0-9_]*/

Additional stuff
================

Just for reference, not really interesting as documentation

XPath 1.0 (from http://www.w3.org/TR/xpath)
-------------------------------------------

[ 1] LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
[ 2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
                            | AbbreviatedAbsoluteLocationPath
[ 3] RelativeLocationPath ::= Step
			    | RelativeLocationPath '/' Step
			    | AbbreviatedRelativeLocationPath
[ 4] Step ::= AxisSpecifier NodeTest Predicate*
            | AbbreviatedStep
[ 5] AxisSpecifier ::= AxisName '::'
                     | AbbreviatedAxisSpecifier
[ 6] AxisName ::= 'ancestor'
                | 'ancestor-or-self'
                | 'attribute'
                | 'child'
                | 'descendant'
                | 'descendant-or-self'
                | 'following'
                | 'following-sibling'
                | 'namespace'
                | 'parent'
                | 'preceding'
                | 'preceding-sibling'
                | 'self'
[ 7] NodeTest ::= NameTest
                | NodeType '(' ')'
                | 'processing-instruction' '(' Literal ')'
[ 8] Predicate ::= '[' PredicateExpr ']'
[ 9] PredicateExpr ::= Expr
[10] AbbreviatedAbsoluteLocationPath ::= '//' RelativeLocationPath
[11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
[12] AbbreviatedStep ::= '.' | '..'
[13] AbbreviatedAxisSpecifier ::= '@'?
[14] Expr ::= OrExpr
[15] PrimaryExpr ::= VariableReference
                   | '(' Expr ')'
                   | Literal
                   | Number
                   | FunctionCall
[16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument )* )? ')'
[17] Argument ::= Expr
[18] UnionExpr ::= PathExpr
                 | UnionExpr '|' PathExpr
[19] PathExpr ::= LocationPath
                | FilterExpr
                | FilterExpr '/' RelativeLocationPath
                | FilterExpr '//' RelativeLocationPath
[20] FilterExpr ::= PrimaryExpr
                  | FilterExpr Predicate
[21] OrExpr ::= AndExpr | OrExpr 'or' AndExpr
[22] AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
[23] EqualityExpr ::= RelationalExpr
                    | EqualityExpr '=' RelationalExpr
                    | EqualityExpr '!=' RelationalExpr
[24] RelationalExpr ::= AdditiveExpr
                      | RelationalExpr '<' AdditiveExpr
                      | RelationalExpr '>' AdditiveExpr
                      | RelationalExpr '<=' AdditiveExpr
                      | RelationalExpr '>=' AdditiveExpr

[25] AdditiveExpr ::= MultiplicativeExpr
                    | AdditiveExpr '+' MultiplicativeExpr
                    | AdditiveExpr '-' MultiplicativeExpr
[26] MultiplicativeExpr ::= UnaryExpr
                        | MultiplicativeExpr MultiplyOperator UnaryExpr
                        | MultiplicativeExpr 'div' UnaryExpr
                        | MultiplicativeExpr 'mod' UnaryExpr
[27] UnaryExpr ::= UnionExpr
                 | '-' UnaryExpr
[28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
                 | NameTest
                 | NodeType
                 | Operator
                 | FunctionName
                 | AxisName
                 | Literal
                 | Number
                 | VariableReference
[29] Literal ::= '"' [^"]* '"'
               | "'" [^']* "'"
[30] Number ::= Digits ('.' Digits?)? | '.' Digits
[31] Digits ::= [0-9]+
[32] Operator ::= OperatorName
                | MultiplyOperator
                | '/' | '//' | '|' | '+' | '-' | '=' | '!=' | '<' | '<=' | '>' | '>='
[33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
[34] MultiplyOperator ::= '*'
[35] FunctionName ::= QName - NodeType
[36] VariableReference ::= '$' QName
[37] NameTest ::= '*' | NCName ':' '*' | QName
[38] NodeType ::= 'comment' | 'text'
                | 'processing-instruction'
                | 'node'
[39] ExprWhitespace ::= (#x20 | #x9 | #xD | #xA)+

Useful subset
-------------

Big swath of XPath 1.0 that might be interesting for Augeas

start symbol [14] Expr

[14] Expr ::= OrExpr
[21] OrExpr ::= AndExpr ('or' AndExpr)*
[22] AndExpr ::= EqualityExpr ('and' EqualityExpr)*
[23] EqualityExpr ::= RelationalExpr
                    | EqualityExpr '=' RelationalExpr
                    | EqualityExpr '!=' RelationalExpr
[24] RelationalExpr ::= AdditiveExpr
                      | RelationalExpr '<' AdditiveExpr
                      | RelationalExpr '>' AdditiveExpr
                      | RelationalExpr '<=' AdditiveExpr
                      | RelationalExpr '>=' AdditiveExpr
[25] AdditiveExpr ::= MultiplicativeExpr
                    | AdditiveExpr '+' MultiplicativeExpr
                    | AdditiveExpr '-' MultiplicativeExpr
[26] MultiplicativeExpr ::= UnaryExpr
                        | MultiplicativeExpr MultiplyOperator UnaryExpr
[27] UnaryExpr ::= UnionExpr | '-' UnaryExpr
[18] UnionExpr ::= PathExpr ('|' PathExpr)*

[19] PathExpr ::= LocationPath
                | FilterExpr
                | FilterExpr '/' RelativeLocationPath
                | FilterExpr '//' RelativeLocationPath

[ 1] LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
[ 3] RelativeLocationPath ::= Step
			    | RelativeLocationPath '/' Step
			    | AbbreviatedRelativeLocationPath
[11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
[ 2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
                            | AbbreviatedAbsoluteLocationPath
[10] AbbreviatedAbsoluteLocationPath ::= '//' RelativeLocationPath

[ 4] Step ::= AxisSpecifier NameTest Predicate* | '.' | '..'
[ 5] AxisSpecifier ::= AxisName '::' | <epsilon>

[ 6] AxisName ::= 'ancestor'
                | 'ancestor-or-self'
                | 'attribute'
                | 'child'
                | 'descendant'
                | 'descendant-or-self'
                | 'following'
                | 'following-sibling'
                | 'namespace'
                | 'parent'
                | 'preceding'
                | 'preceding-sibling'
                | 'self'

[ 8] Predicate ::= '[' Expr ']'

[20] FilterExpr ::= PrimaryExpr Predicate*
[15] PrimaryExpr ::= '(' Expr ')'
                   | Literal
                   | Number
                   | FunctionCall
[16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument )* )? ')'
[17] Argument ::= Expr

Lexical structure

[28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
                 | NameTest
                 | NodeType
                 | Operator
                 | FunctionName
                 | AxisName
                 | Literal
                 | Number
                 | VariableReference
[29] Literal ::= '"' [^"]* '"'
               | "'" [^']* "'"
[30] Number ::= Digits ('.' Digits?)? | '.' Digits
[31] Digits ::= [0-9]+
[32] Operator ::= OperatorName
                | MultiplyOperator
                | '/' | '//' | '|' | '+' | '-' | '=' | '!=' | '<' | '<=' | '>' | '>='
[33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
[34] MultiplyOperator ::= '*'
[35] FunctionName ::= QName - NodeType
[36] VariableReference ::= '$' QName
[37] NameTest ::= '*' | QName
[38] NodeType ::= 'comment' | 'text'
                | 'processing-instruction'
                | 'node'
[39] ExprWhitespace ::= (#x20 | #x9 | #xD | #xA)+