This is a very first version of an implementation of RDF/A using XSLT2.
The basic idea is to express RDF/A sections 4 and 5 as a set of simple rules in XML. These provide rules for matching subjects, predicates, and objects. These rules are combined using a first XSL Transform, to give a set of more complex triple rules. A second transform then takes the complex rules and converts them to be an XSLT program, which implements RDF/A.
The goal of this somewhat convoluted approach is to make sure that there is a good correspondence to what this program does and what is written in RDF/A.
The text of sections 4 and 5 is expressed as simple rules using XPath expressions e.g.
  <subject
    x:para='4.2.4.1'
    a:match='[not(@about)][not(@id)][not(@nodeID)][../@nodeID]'
    rdf:nodeID="concat('u.',../@nodeID)"
   />
Says that paragraph 4.2.4.1 says that if an element matches the expression (no @about, @id or @nodeID attribute, with parent with a @nodeID attribute) then add an rdf:nodeID attriute with value calculated as given to give the subject (in RDF/XML).
The predicate rules are similar
  <predicate
    a:reversed='true'
    a:object='resource'
    x:para='4.3.3'
    a:match='[@rev]'
    name='@rev'
   />
This rule requires a resource object, is defined in para 4.3.3, matches when there is an @rev attribute, and the name of the predicate is the value of the @rev attribute.
Objects may be resource or literal valued, here is a resourced valued one:
  <object
    a:object='resource'
    x:para='4.4.2'
    rdf:resource='resolve-uri(@href)'
    a:match='[@href]'
   />
The @a:object value of resource shows that we can be used with the previous predicate rule. Any subject rule can be used. The @rdf:resource value is to be applied to the proeprty element in the generated RDF/XML.
The combineRules transform, takes all combinations of S and P and O rules subject to the single constraint that the P and O rules have the same @a:object value, and makes a new set of longer triple oriented rules.
These three are combined to:
   <match select="xhtml2:*[not(@about)][not(@id)][not(@nodeID)][../@nodeID][@rev][@href]">
      <subject x:para="4.4.2" rdf:about="resolve-uri(@href)"/>
      <predicate x:para="4.3.3" name="@rev"/>
      <object x:para="4.2.4.1" rdf:nodeID="concat('u.',../@nodeID)"/>
   </match>
Because of the @a:reversed='true' value the subject and object have been swapped, and the swapping knows to change the @rdf:resource on the object into an @rdf:about on the subject. The @a:* attributes have been stripped.
This rule is then transformed by the rules2xslt transform to give the following XSLT2 fragment, which matches relevant XHTML2 fragments and gives an RDF/XML fragment, implementing the combination of the three paras of the RDF/A document.
<xsl:for-each select="//xhtml2:*[not(@about)][not(@id)][not(@nodeID)][../@nodeID][@rev][@href]">
  <rdf:Description>
    <xsl:attribute name="rdf:about" select="resolve-uri(@href)"/>
    <xsl:element name="{@rev}" namespace="{namespace-uri-for-prefix(substring-before(@rev,':'),.)}">
       <xsl:attribute name="rdf:nodeID" select="concat('u.',../@nodeID)"/>
    </xsl:element>
  </rdf:Description>
</xsl:for-each>
Para 4.4.3 is particularly difficult, and I use an x:foreach attribute. The approach slightly inverts the wording of the paragrpah since I am searching for a context statement without an @href, and then apply the rule foreach matching child. The simple rule looks like:
  <object
    a:object='resource'
    x:para='4.4.3'
    a:match='[not(@href)]'
    rdf:nodeID="concat('u.',@nodeID)"
    x:foreach='*[@nodeID]'
   />
The a:object attribute is used to match with appropriate predicate rules. This rule applies when there is not an @href attribute, and para 4.4.3 applies (within that para the [not(@href)] applies to the context statement. This particular rule considers the case where the current statement in that paragraph has a nodeID, foreach of these cases we can generate a triple with an rdf:nodeID attribute on the object constructed as shown.
The combined rule (with the previous S and P rules) is:
   <match select="xhtml2:*[not(@about)][not(@id)][not(@nodeID)][../@nodeID][@rev][not(@href)]">
      <subject x:para="4.4.3" rdf:nodeID="concat('u.',@nodeID)" x:foreach="*[@nodeID]"/>
      <predicate x:para="4.3.3" name="@rev"/>
      <object x:para="4.2.4.1" rdf:nodeID="concat('u.',../@nodeID)"/>
   </match>
The corresponding XSLT is horrendous, partially because it is autogenerated:
<xsl:for-each select="//xhtml2:*[not(@about)][not(@id)][not(@nodeID)][../@nodeID][@rev][not(@href)]">
  <xsl:variable name="c" select="."/>
  <xsl:for-each select="*[@about]">
    <rdf:Description>
      <xsl:attribute name="rdf:about" select="resolve-uri(@about)"/>
      <xsl:for-each select="$c">
        <xsl:element name="{@rev}" namespace="{namespace-uri-for-prefix(substring-before(@rev,':'),.)}">
          <xsl:attribute name="rdf:nodeID" select="concat('u.',../@nodeID)"/>
        </xsl:element>
      </xsl:for-each>
    </rdf:Description>
  </xsl:for-each>
</xsl:for-each>
Note the following features of XSLT2 that are being used:
    The constructs concat('g.',generate-id(.))
    and concat('u.',@nodeID) prevent name collisions
    between user defined nodeIDs and system gensyms.
Next steps include: