# $Id: xslt20.rnc,v 1.3 2005/04/19 11:51:35 ndw Exp $

# This was mostly generated from the syntax summary in the XSLT 1.0
# Recommendation (using XSLT of course) by James. Norm hacked at it
# a bit to make it validate XSLT 2.0.

# Issues: this validates extension elements as literal result
# elements, which is overly restrictive.

# Change Log:
#
# 2005-04-19: http://norman.walsh.name/2005/04/19/xslt20
#  Allow xsl:version on literal result elements (LREs) to have any value
#  Allow xsl:xpath-default-namespace, xsl:default-collation, xsl:type,
#        xsl:validation, and xsl:use-when to appear on LREs
#  Allow version, exclude-result-prefixes, extension-element-prefixes,
#        xpath-default-namespace, default-collation, and use-when
#        to appear on all XSL elements
#  Allow xs:schema to appear inside xsl:schema-import
#  Only allow @mode on xsl:template elements with a @match
#  Allow content *or* @select on xsl:processing-instruction, not both
#  Allow content *or* @select on xsl:comment, not both
#  Improve co-constraints on attributes on xsl:number and xsl:for-each-group
#  Allow xsl:sort to contain content
#  On xsl:output, it's @escape-uri-attribute*s* and @normalize-unicode is
#        not a simple boolean, 
#  The @name attribute is required on xsl:namespace
#  All of the xsl:output attributes are allowed on xsl:result-document
#  The xsl:sequence element only contains xsl:fallback
#  Added xsl:document
# 2005-04-07: http://norman.walsh.name/2005/04/07/xslt20
#   Fixed minor bugs
# 2004-07-25: http://norman.walsh.name/2004/07/25/xslt20
#   First published

namespace local = ""
default namespace xsl = "http://www.w3.org/1999/XSL/Transform"
namespace xs = "http://www.w3.org/2001/XMLSchema"

start =
  stylesheet.element
  | transform.element
  | literal-result-element-as-stylesheet

version = "2.0"

top-level-elements.model =
  (declaration.category | top-level-extension)*

top-level-extension =
  element * - (xsl:* | local:*) {
     anyElement
  }

anyElement =
   grammar {
      start = any
      any =
        (attribute * { text }
         | text
         | element * { any })*
    }

sequence.constructor = (instruction.category | literal-result-element | text)*

literal-result-element-as-stylesheet =
  element * - xsl:* {
    attribute xsl:version { version },
    literal-result-element-no-version.atts,
    sequence.constructor
  }

literal-result-element =
  element * - xsl:* {
     literal-result-element.atts,
     sequence.constructor
  }

literal-result-element.atts =
  literal-result-element-no-version.atts,
  attribute xsl:version { text }?

literal-result-element-no-version.atts =
   attribute * - xsl:* { avt.datatype }*
 & attribute xsl:exclude-result-prefixes { exclude.prefixes.datatype }?
 & attribute xsl:extension-element-prefixes { extension.prefixes.datatype }?
 & attribute xsl:xpath-default-namespace { xsd:anyURI }?
 & attribute xsl:default-collation { xsd:anyURI }?
 & attribute xsl:use-when { expression.datatype }?
 & attribute xsl:use-attribute-sets { qnames.datatype }?
 & (attribute xsl:type { xsd:QName }
    | attribute xsl:validation { validation.datatype })?

declaration.category =
   attribute-set.element
 | character-map.element
 | decimal-format.element
 | function.element
 | import-schema.element
 | include.element
 | key.element
 | namespace-alias.element
 | output.element
 | param.element
 | preserve-space.element
 | strip-space.element
 | template.element
 | variable.element

instruction.category =
   analyze-string.element
 | apply-imports.element
 | apply-templates.element
 | attribute.element
 | call-template.element
 | choose.element
 | comment.element
 | copy.element
 | copy-of.element
 | document.element
 | element.element
 | fallback.element
 | for-each.element
 | for-each-group.element
 | if.element
 | message.element
 | namespace.element
 | next-match.element
 | number.element
 | perform-sort.element
 | processing-instruction.element
 | result-document.element
 | sequence.element
 | text.element
 | value-of.element
 | variable.element

extension.atts = attribute * - (xsl:* | local:*) { text }*

global.atts = attribute version { text }?,
   attribute exclude-result-prefixes { exclude.prefixes.datatype }?,
   attribute extension-element-prefixes { extension.prefixes.datatype }?,
   attribute xpath-default-namespace { xsd:anyURI }?,
   attribute default-collation { text }?,
   attribute use-when { expression.datatype }?

# not defined with global.atts because I want version=2.0
stylesheet.model =
   extension.atts,
   attribute id { xsd:NCName }?,
   attribute version { version },
   attribute exclude-result-prefixes { exclude.prefixes.datatype }?,
   attribute extension-element-prefixes { extension.prefixes.datatype }?,
   attribute xpath-default-namespace { xsd:anyURI }?,
   attribute default-collation { text }?,
   attribute use-when { expression.datatype }?,
   attribute default-validation { validation.datatype }?,
   attribute input-type-annotations { "preserve" | "strip" | "unspecified" }?,
   (import.element*, top-level-elements.model)

analyze-string.element =
   element analyze-string {
      global.atts,
      extension.atts,
      attribute select { expression.datatype },
      attribute regex { avt.datatype },
      attribute flags { avt.datatype }?,
      matching-substring.element?,
      non-matching-substring.element?,
      fallback.element*
   }

apply-imports.element =
   element apply-imports {
      extension.atts,
      global.atts,
      with-param.element*
   }

apply-templates.element =
   element apply-templates {
      extension.atts,
      global.atts,
      attribute select { expression.datatype }?,
      attribute mode { "#all" | "#default" | qname.datatype }?,
      (sort.element | with-param.element)*
   }

attribute.element =
   element attribute {
      extension.atts,
      global.atts,
      attribute name { qname.datatype | expr-avt.datatype },
      attribute namespace { xsd:anyURI | brace-avt.datatype }?,
      attribute separator { avt.datatype }?,
      attribute type { xsd:QName }?,
      attribute validation { validation.datatype }?,
      (attribute select { expression.datatype }?
       | sequence.constructor)
   }

attribute-set.element =
   element attribute-set {
      extension.atts,
      global.atts,
      attribute name { qname.datatype },
      attribute use-attribute-sets { rqnames.datatype }?,
      attribute.element*
   }

call-template.element =
   element call-template {
      extension.atts,
      global.atts,
      attribute name { qname.datatype },
      with-param.element*
   }

character-map.element =
   element character-map {
      global.atts,
      extension.atts,
      attribute name { xsd:QName },
      attribute use-character-maps { rqnames.datatype }?,
      output-character.element*
   }

choose.element =
   element choose {
      global.atts,
      extension.atts,
      (when.element+, otherwise.element?)
   }

comment.element =
   element comment {
      global.atts,
      extension.atts,
      (attribute select { expression.datatype }?
       |sequence.constructor)
   }

copy.element =
   element copy {
      global.atts,
      extension.atts,
      attribute copy-namespaces { "yes" | "no" }?,
      attribute inherit-namespaces { "yes" | "no" }?,
      attribute use-attribute-sets { rqnames.datatype }?,
      attribute type { xsd:QName }?,
      attribute validation { validation.datatype }?,
      sequence.constructor
   }

copy-of.element =
   element copy-of {
      global.atts,
      extension.atts,
      attribute select { expression.datatype },
      attribute copy-namespaces { "yes" | "no" }?,
      attribute type { xsd:QName }?,
      attribute validation { validation.datatype }?,
      empty
   }

decimal-format.element =
   element decimal-format {
      global.atts,
      extension.atts,
      attribute name { qname.datatype }?,
      attribute decimal-separator { char.datatype }?,
      attribute grouping-separator { char.datatype }?,
      attribute infinity { text }?,
      attribute minus-sign { char.datatype }?,
      attribute NaN { text }?,
      attribute percent { char.datatype }?,
      attribute per-mille { char.datatype }?,
      attribute zero-digit { char.datatype }?,
      attribute digit { char.datatype }?,
      attribute pattern-separator { char.datatype }?,
      empty
   }

document.element =
   element document {
      global.atts,
      extension.atts,
      attribute type { xsd:QName }?,
      attribute validation { validation.datatype }?,
      sequence.constructor
   }

element.element =
   element element {
      extension.atts,
      global.atts,
      attribute name { qname.datatype | expr-avt.datatype },
      attribute namespace { xsd:anyURI | brace-avt.datatype }?,
      attribute inherit-namespaces { "yes" | "no" }?,
      attribute use-attribute-sets { rqnames.datatype }?,
      attribute type { xsd:QName }?,
      attribute validation { validation.datatype }?,
      sequence.constructor
   }

fallback.element =
   element fallback {
      global.atts,
      extension.atts,
      sequence.constructor
   }

for-each.element =
   element for-each {
      global.atts,
      extension.atts,
      attribute select { expression.datatype },
      (sort.element*, sequence.constructor)
   }

for-each-group.element =
   element for-each-group {
      global.atts,
      extension.atts,
      attribute select { expression.datatype },
      (attribute group-by { expression.datatype }
       |attribute group-adjacent { expression.datatype }
       |attribute group-starting-with { pattern.datatype }
       |attribute group-ending-with { pattern.datatype }),
      attribute collation { xsd:anyURI | brace-avt.datatype }?,
      (sort.element*, sequence.constructor)
   }

function.element =
   element function {
      global.atts,
      extension.atts,
      attribute name { xsd:QName },
      attribute as { sequence.datatype }?,
      attribute override { "yes" | "no" }?,
      (param.element*, sequence.constructor)
   }

if.element =
   element if {
      global.atts,
      extension.atts,
      attribute test { expression.datatype },
      sequence.constructor
   }

import.element =
  element import {
    extension.atts,
    global.atts,
    attribute href { xsd:anyURI }
  }

import-schema.element =
   element import-schema {
      extension.atts,
      global.atts,
      attribute namespace { xsd:anyURI }?,
      attribute schema-location { xsd:anyURI }?,
      element xs:schema { anyElement* }?
   }

include.element =
  element include {
    extension.atts,
    global.atts,
    attribute href { xsd:anyURI }
  }

key.element =
   element key {
      global.atts,
      extension.atts,
      attribute name { qname.datatype },
      attribute match { pattern.datatype },
      attribute use { expression.datatype }?,
      attribute collation { xsd:anyURI }?
   }

matching-substring.element =
   element matching-substring {
      global.atts,
      extension.atts,
      sequence.constructor
   }

message.element =
   element message {
      global.atts,
      extension.atts,
      attribute terminate {
         xsd:string "yes" | xsd:string "no" |  expr-avt.datatype
      }?,
      (attribute select { expression.datatype }?
       | sequence.constructor)
   }

namespace.element =
   element namespace {
      global.atts,
      extension.atts,
      attribute name { xsd:NCName },
      (attribute select { expression.datatype }
       | sequence.constructor)
   }

namespace-alias.element =
  element namespace-alias {
    extension.atts,
    global.atts,
    attribute stylesheet-prefix { prefix.datatype },
    attribute result-prefix { prefix.datatype }
  }

next-match.element =
   element next-match {
      global.atts,
      extension.atts,
      (with-param.element | fallback.element)*
   }

non-matching-substring.element =
   element non-matching-substring {
      global.atts,
      extension.atts,
      sequence.constructor
   }

number.element =
   element number {
      global.atts,
      extension.atts,
      ((attribute select { expression.datatype }?
        & attribute level { "single" | "multiple" | "any" }?
        & attribute count { pattern.datatype }?
        & attribute from { pattern.datatype }?)
       | attribute value { expression.datatype }?),
      attribute format { avt.datatype }?,
      attribute lang { xsd:NMTOKEN | expr-avt.datatype }?,
      attribute letter-value {
         xsd:string "alphabetic"
       | xsd:string "traditional"
       | expr-avt.datatype
      }?,
      attribute grouping-separator { char.datatype | expr-avt.datatype }?,
      attribute grouping-size { number.datatype | expr-avt.datatype }?,
      attribute ordinal { avt.datatype }?,
      empty
   }

otherwise.element =
   element otherwise {
      global.atts,
      extension.atts,
      sequence.constructor
   }

output.element =
   element output {
      global.atts,
      extension.atts,
      attribute name { xsd:QName }?,
      attribute method {
         xsd:string "xml"
       | xsd:string "html"
       | xsd:string "xhtml"
       | xsd:string "text"
       | qname-but-not-ncname.datatype
      }?,
      attribute byte-order-mark { "yes" | "no" }?,
      attribute cdata-section-elements { rqnames.datatype }?,
      attribute doctype-public { text }?,
      attribute doctype-system { text }?,
      attribute encoding { text }?,
      attribute escape-uri-attributes { "yes" | "no" }?,
      attribute include-content-type { "yes" | "no" }?,
      attribute indent { "yes" | "no" }?,
      attribute media-type { text }?,
      attribute normalize-unicode {
         xsd:string "NFC"
       | xsd:string "NFD"
       | xsd:string "NKFC"
       | xsd:string "NKFD"
       | xsd:string "fully-normalized"
       | xsd:string "none"
       | xsd:NMTOKEN
      }?,
      attribute omit-xml-declaration { "yes" | "no" }?,
      attribute standalone { "yes" | "no" | "omit" }?,
      attribute undeclare-prefixes { "yes" | "no" }?,
      attribute use-character-maps { rqnames.datatype }?,
      empty
   }

output-character.element =
   element output-character {
      global.atts,
      extension.atts,
      attribute character { char.datatype },
      attribute string { text }
   }

param.element =
   element param {
      global.atts,
      extension.atts,
      attribute name { qname.datatype },
      attribute as { sequence.datatype }?,
      attribute required { "yes" | "no" }?,
      attribute tunnel { "yes" | "no" }?,
      (attribute select { expression.datatype }
       | sequence.constructor)
   }

perform-sort.element =
   element perform-sort {
      global.atts,
      extension.atts,
      attribute select { expression.datatype }?,
      (sort.element+, sequence.constructor)
   }

preserve-space.element =
   element preserve-space {
      extension.atts,
      global.atts,
      attribute elements { wildcards.datatype },
      empty
   }

processing-instruction.element =
   element processing-instruction {
      global.atts,
      extension.atts,
      attribute name { xsd:NCName | expr-avt.datatype },
      (attribute select { expression.datatype }?
       |sequence.constructor)
   }

result-document.element =
   element result-document {
      global.atts,
      extension.atts,
      attribute format { xsd:QName }?,
      attribute href { xsd:anyURI | brace-avt.datatype }?,
      attribute validation { validation.datatype }?,
      attribute type { xsd:QName }?,
      attribute method {
         xsd:string "xml"
       | xsd:string "html"
       | xsd:string "xhtml"
       | xsd:string "text"
       | qname-but-not-ncname.datatype
       | expr-avt.datatype
      }?,
      attribute byte-order-mark { "yes" | "no" | expr-avt.datatype }?,
      attribute cdata-section-elements { qnames.datatype | expr-avt.datatype }?,
      attribute doctype-public { avt.datatype }?,
      attribute doctype-system { avt.datatype }?,
      attribute encoding { avt.datatype }?,
      attribute escape-uri-attributes { "yes" | "no" | expr-avt.datatype }?,
      attribute include-content-type { "yes" | "no" | expr-avt.datatype }?,
      attribute indent { "yes" | "no" | expr-avt.datatype }?,
      attribute media-type { avt.datatype }?,
      attribute normalize-unicode {
         xsd:string "NFC"
       | xsd:string "NFD"
       | xsd:string "NKFC"
       | xsd:string "NKFD"
       | xsd:string "fully-normalized"
       | xsd:string "none"
       | xsd:NMTOKEN
       | expr-avt.datatype
      }?,
      attribute omit-xml-declaration { "yes" | "no" | expr-avt.datatype }?,
      attribute standalone { "yes" | "no" | "omit" | expr-avt.datatype }?,
      attribute undeclare-prefixes { "yes" | "no" | expr-avt.datatype }?,
      attribute use-character-maps { rqnames.datatype }?,
      attribute output-version { xsd:NMTOKEN | expr-avt.datatype }?,
      sequence.constructor
   }

sequence.element =
   element sequence {
      global.atts,
      extension.atts,
      attribute select { expression.datatype },
      fallback.element*
   }

sort.element =
   element sort {
      global.atts,
      extension.atts,
      attribute select { expression.datatype }?,
      attribute lang { xsd:NMTOKEN | expr-avt.datatype }?,
      attribute collation { xsd:anyURI | brace-avt.datatype }?,
      attribute stable { "yes" | "no" | expr-avt.datatype }?,
      attribute data-type {
         xsd:string "text"
       | xsd:string "number"
       | qname-but-not-ncname.datatype
       | expr-avt.datatype
      }?,
      attribute order {
         xsd:string "ascending"
       | xsd:string "descending"
       | expr-avt.datatype
      }?,
      attribute case-order {
         xsd:string "upper-first"
       | xsd:string "lower-first"
       | expr-avt.datatype
      }?,
      sequence.constructor
   }

strip-space.element =
   element strip-space {
      extension.atts,
      global.atts,
      attribute elements { wildcards.datatype },
      empty
   }

stylesheet.element = element stylesheet { stylesheet.model }

template.element =
   element template {
      extension.atts,
      global.atts,
      (attribute match { pattern.datatype }
       & attribute mode { "#all" | "#default" | qname.datatype }?)?,
      attribute name { qname.datatype }?,
      attribute priority { number.datatype }?,
      attribute as { sequence.datatype }?,
      (param.element*, sequence.constructor)
   }

text.element =
   element text {
      extension.atts,
      global.atts,
      attribute disable-output-escaping { "yes" | "no" }?,
      text
   }

transform.element = element transform { stylesheet.model }

value-of.element =
   element value-of {
      global.atts,
      extension.atts,
      attribute separator { avt.datatype }?,
      attribute disable-output-escaping { "yes" | "no" }?,
      (attribute select { expression.datatype }?
       | sequence.constructor)
   }

variable.element =
   element variable {
      global.atts,
      extension.atts,
      attribute name { qname.datatype },
      attribute as { sequence.datatype }?,
      (attribute select { expression.datatype }
       | sequence.constructor)
   }

when.element =
   element when {
      global.atts,
      extension.atts,
      attribute test { expression.datatype },
      sequence.constructor
   }

with-param.element =
   element with-param {
      global.atts,
      extension.atts,
      attribute name { qname.datatype },
      attribute as { sequence.datatype }?,
      attribute tunnel { "yes" | "no" }?,
      (attribute select { expression.datatype }
       | sequence.constructor)
   }

extension.prefixes.datatype = list { xsd:NCName* }
exclude.prefixes.datatype = list { "#all" | (xsd:NCName | "#default")* }

prefix.datatype = xsd:NCName | "#default"
wildcards.datatype =
  list {
    (xsd:QName
     | xsd:token { pattern = "\*|\i\c*:\*" })*
  }
qname.datatype = xsd:QName
qnames.datatype = list { xsd:QName* }
rqnames.datatype = list { xsd:QName+ }
char.datatype = xsd:string { length = "1" }
number.datatype = xsd:decimal
expression.datatype = text
sequence.datatype = text
pattern.datatype = text
validation.datatype = "strict" | "lax" | "preserve" | "strip"
uri-list.datatype = list { xsd:anyURI* }
qname-but-not-ncname.datatype = xsd:QName { pattern = ".*:.*" }

# An AVT containing at least one expression.
expr-avt.datatype =
  xsd:string {
    pattern =
      """([^\{\}]|\{\{|\}\})*\{([^"'\{\}]|"[^"]*"|'[^']*')+\}([^\{\}]|\{\{|\}\}|\{([^"'\{\}]|"[^"]*"|'[^']*')+\})*"""
  }

# An AVT containing at least one brace; ie where instantiated AVT
# is not the same as the literal AVT.
brace-avt.datatype =
  xsd:string {
    pattern =
      """[^\{\}]*(\{\{|\}\}|\{([^"'\{\}]|"[^"]*"|'[^']*')+\})([^\{\}]|\{\{|\}\}|\{([^"'\{\}]|"[^"]*"|'[^']*')+\})*"""
  }

avt.datatype =
  xsd:string {
    pattern =
      """([^\{\}]|\{\{|\}\}|\{([^"'\{\}]|"[^"]*"|'[^']*')+\})*"""
  }
