# $Id: xpath-grammar,v 1.1.1.1 2003/10/29 20:55:31 mjb47 Exp $

{ sub stringify {
    return ref($_[0]) ? join('', map(stringify($_), @{$_[0]})) : $_[0];
  }
}

LocationPath: RelativeLocationPath | AbsoluteLocationPath
AbsoluteLocationPath: '//' <commit> RelativeLocationPath
                      | '/' RelativeLocationPath(?)
RelativeLocationPath: <leftop: Step /(\/\/?)/ Step >
Step: AxisSpecifier(?) NodeTest <commit> Predicate(s?)
      | AbbreviatedStep
AxisSpecifier: ( AxisName '::' | '@' )
AxisName: ( 'ancestor'
          | 'ancestor-or-self'
          | 'attribute'
          | 'child'
          | 'descendant'
          | 'descendant-or-self'
          | 'following'
          | 'following-sibling'
          | 'namespace'
          | 'parent'
          | 'preceding'
          | 'preceding-sibling'
          | 'self' ) ...'..'
  { $item[1] }
NodeTest: NodeType '(' ')' 
          | 'processing-instruction' '(' <commit> Literal ')' 
          | NameTest
Predicate: '[' Expr ']'
AbbreviatedStep: /\.\.?/

Pattern: <leftop: LocationPathPattern '|' LocationPathPattern >
  { [ join('|', @{$item[1]}), $text ] }
LocationPathPattern: '//' <commit> RelativePathPattern
                     | RelativePathPattern
#IdKeyPattern: 'id' '(' <commit> Literal ')'
              | 'key' '(' <commit> Literal ',' Literal ')'
RelativePathPattern: <leftop: StepPattern /(\/\/?)/ StepPattern >
StepPattern: ChildStepPattern | AttributeStepPattern
ChildStepPattern: ChildAxisSpecifier(?) NodeTest Predicate(s?)
ChildAxisSpecifier: 'child' '::'
AttributeStepPattern: AttributeAxisSpecifier ANodeTest Predicate(s?)
AttributeAxisSpecifier: 'attribute' '::' | '@'
ANodeTest: NodeType '(' ')' | ANameTest

Expr: OrExpr
OrExpr: <leftop: AndExpr 'or' AndExpr >
  { join(' or ', @{$item[1]}) }
AndExpr: <leftop: EqualityExpr 'and' EqualityExpr >
  { join(' and ', @{$item[1]}) }
EqualityExpr: <leftop: RelationalExpr /(!?=)/ RelationalExpr >
RelationalExpr: <leftop: AdditiveExpr /([<>]=?)/ AdditiveExpr >
AdditiveExpr: <leftop: MultiplicativeExpr /([+-])/ MultiplicativeExpr >
MultiplicativeExpr: <leftop: UnaryExpr /(\*|div|mod)/ UnaryExpr >
  { join(' ', @{$item[1]}) }
UnaryExpr: UnionExpr | '-' UnaryExpr
UnionExpr: <rightop: PathExpr '|' PathExpr >
  { join('|', @{$item[1]}) }
PathExpr: LocationPath
          | FilterExpr /\/\/?/ RelativeLocationPath
          | FilterExpr
FilterExpr: PrimaryExpr Predicate(s?)
PrimaryExpr: VariableReference 
             | '(' <commit> Expr ')'
             | Literal
             | Number
             | FunctionCall
FunctionCall: FunctionName '(' Expr(s? /,/) ')'
  { "$item[1](" . join(',', @{$item[3]}) . ')' }

Literal: /'[^']*'|"[^"]*"/
Number: /(\d+(\.\d+)?|.\d+)/
VariableReference: '$' QName
ANameTest: '*' | NCName ':' '*' | QName
NameTest: '*' | NCName ':' '*' | CorrectedQName
NodeType: ( 'comment' | 'text' | 'processing-instruction' | 'node' ) ...'('
  { $item[1] }
CorrectedQName: NCName ':' NCName 
  | ( 'fields' | 'pagesize' | 'page' )
  | NCName { $::XPATH_DEFAULT_NAMESPACE 
             ? $::XPATH_DEFAULT_NAMESPACE . ":$item[1]"
             : $item[1] }
QName: NCName ':' NCName | NCName
NCName: NCNameX ...!/\(|::/
  { $item[1] }
NCNameX: /[A-Za-z_][-A-Za-z0-9._]*/
FunctionName: ...!NodeType NCNameX ...'('
  { $item[2] }