<?xml version='1.0'?>
<bisongen version='1.0' name='XPointerParser' project='Ft.Xml.XPointer'>

  <modules>
    <import from="Ft.Xml.XPointer">XPointer</import>
    <import from="Ft.Xml.XPointer">XPtrPart</import>
  </modules>

  <tokens>
    <token>NCName</token>
    <token>QName</token>
    <token>Integer</token>
    <token>StringWithoutParens</token>
  </tokens>

  <grammar>
    
    <!-- XPointer:  Name
                 |  ChildSeq
                 |  FullXPtr
    -->
    <production>
      <non-terminal>XPointer</non-terminal>
      <rule>
        <symbol>Name</symbol>
        <code language="c">
          $$ = PyObject_CallMethod(XPointer, "BareName", "O", $1);
        </code>
        <code language="python">
          $$ = XPointer.BareName($1)
        </code>
      </rule>
      <rule>
        <symbol>ChildSeq</symbol>
        <code language="c">
          $$ = PyObject_CallMethod(XPointer, "ChildSequence", "O", $1);
        </code>
        <code language="python">
          $$ = XPointer.ChildSequence($1)
        </code>
      </rule>
      <rule>
        <symbol>FullXPtr</symbol>
        <code language="c">
          $$ = PyObject_CallMethod(XPointer, "FullXPtr", "O", $1);
        </code>
        <code language="python">
          $$ = XPointer.FullXPtr($1)
        </code>
      </rule>
    </production>

    <!-- ChildSeq: Name? ('/' [1-9][0-9]*)+
    -->
    <production>
      <non-terminal>ChildSeq</non-terminal>
      <rule>
        <symbol>Sequence</symbol>
      </rule>
      <rule>
        <symbol>Name</symbol>
        <symbol>Sequence</symbol>
        <code language="c">
          PyObject *bare_name = PyObject_CallMethod(XPointer, "BareName", "O", $1);
          PyList_Insert($2, 0, bare_name);
          Py_DECREF(bare_name);
          Py_INCREF($2);
          $$ = $2;
        </code>
        <code language="python">
          $2.insert(0, XPointer.BareName($1))
          $$ = $2
        </code>
      </rule>
    </production>

    <production>
      <non-terminal>Sequence</non-terminal>
      <rule>
        <symbol>'/'</symbol>
        <symbol>Integer</symbol>
        <code language="c">
          PyObject *integer = PyInt_FromLong(atol(PyString_AsString($2)));
          $$ = PyList_New(1);
          PyList_SET_ITEM($$, 0, integer);
        </code>
        <code language="python">
          $$ = [int($2)]
        </code>
      </rule>
      <rule>
        <symbol>Sequence</symbol>
        <symbol>'/'</symbol>
        <symbol>Integer</symbol>
        <code language="c">
          PyObject *integer = PyInt_FromLong(atol(PyString_AsString($3)));
          PyList_Append($1, integer);
          Py_INCREF($1);
          $$ = $1;
        </code>
        <code language="python">
          $1.append(int($3))
          $$ = $1
        </code>
      </rule>
    </production>

    <!-- Name: (NCName:)?NCName -->
    <production>
      <non-terminal>Name</non-terminal>
      <rule>
        <symbol>NCName</symbol>
      </rule>
      <rule>
        <symbol>QName</symbol>
      </rule>
    </production>          

    <!-- FullXPtr: XPtrPart+
    -->
    <production>
      <non-terminal>FullXPtr</non-terminal>
      <rule>
        <symbol>XPtrPart</symbol>
        <code language="c">
          $$ = PyList_New(1);
          PyList_SET_ITEM($$, 0, $1);
          Py_INCREF($1);
        </code>
        <code language="python">
          $$ = [$1]
        </code>
      </rule>
      <rule>
        <symbol>FullXPtr</symbol>
        <symbol>XPtrPart</symbol>
        <code language="c">
          PyList_Append($1, $2);
          Py_INCREF($1);
          $$ = $1;
        </code>
        <code language="python">
          $1.append($2)
          $$ = $1
        </code>
      </rule>
    </production>

    <!-- XPtrPart: 'xpointer' '(' XPtrExpr ')'
                 | 'xmlns' '(' XPtrNsDecl? ')'
                 | Scheme '(' SchemeSpecificExpr ')'
    -->
    <production>
      <non-terminal>XPtrPart</non-terminal>
      <rule>
        <symbol>Scheme</symbol>
        <symbol>'('</symbol>
        <symbol>SchemeSpecificExpr</symbol>
        <symbol>')'</symbol>
        <code language="c">
          lexer->state = LEXER_INITIAL;
          $$ = PyObject_CallMethod(XPtrPart, "Scheme", "OO", $1, $3);
        </code>
        <code language="python">
          lexer.state = INITIAL;
          $$ = XPtrPart.Scheme($1, $3)
        </code>
      </rule>
    </production>

    <!-- Scheme: NCName -->
    <production>
      <non-terminal>Scheme</non-terminal>
      <rule>
        <symbol>NCName</symbol>
      </rule>
    </production>          

    <!-- SchemeSpecificExpr: StringWithBalancedParens -->
    <production>
      <non-terminal>SchemeSpecificExpr</non-terminal>
      <rule>
        <symbol>StringWithBalancedParens</symbol>
      </rule>
      <rule>
        <symbol>SchemeSpecificExpr</symbol>
        <symbol>StringWithBalancedParens</symbol>
        <code language="c">
          int i;
          int index = 0;
          $$ = PyList_New(PyList_GET_SIZE($1) + PyList_GET_SIZE($2));
          for (i = 0; i &lt; PyList_GET_SIZE($1); i++) {
            PyObject *item = PyList_GET_ITEM($1, i);
            PyList_SET_ITEM($$, index++, item);
            Py_INCREF(item);
          }
          for (i = 0; i &lt; PyList_GET_SIZE($2); i++) {
            PyObject *item = PyList_GET_ITEM($2, i);
            PyList_SET_ITEM($$, index++, item);
            Py_INCREF(item);
          }
        </code>
        <code language="python">
          $1.extend($2)
          $$ = $1
        </code>
      </rule>
    </production>

    <!-- StringWithBalancedParens:
            [^()]*
            '(' StringWithBalancedParens ')'
            '(' StringWithBalancedParens ')' [^()]*
            [^()]* '(' StringWithBalancedParens ')' 
            [^()]* '(' StringWithBalancedParens ')' [^()]*
            [^()]* ('(' StringWithBalancedParens ')' [^()]*)*
    -->
    <production>
      <non-terminal>StringWithBalancedParens</non-terminal>
      <rule>
        <code language="c">
          $$ = PyList_New(0);
        </code>
        <code language="python">
          $$ = []
        </code>
      </rule>
      <rule>
        <symbol>StringWithoutParens</symbol>
        <code language="c">
          $$ = PyList_New(1);
          PyList_SET_ITEM($$, 0, $1);
          Py_INCREF($1);
        </code>
        <code language="python">
          $$ = [$1]
        </code>
      </rule>
      <rule>
        <symbol>'('</symbol>
        <symbol>StringWithBalancedParens</symbol>
        <symbol>')'</symbol>
        <code language="c">
          int len = PyList_GET_SIZE($2);
          int i;
          int j = 0;
          $$ = PyList_New(len + 2);
          PyList_SET_ITEM($$, j++, $1);
          Py_INCREF($1);
          for (i = 0; i &lt; len; i++) {
            PyObject *item = PyList_GET_ITEM($2, i);
            Py_INCREF(item);
            PyList_SET_ITEM($$, j++, item);
          }
          PyList_SET_ITEM($$, j++, $3);
          Py_INCREF($3);
        </code>
        <code language="python">
          $$ = [$1] + $2 + [$3]
        </code>
      </rule>
      <rule>
        <symbol>'('</symbol>
        <symbol>StringWithBalancedParens</symbol>
        <symbol>')'</symbol>
        <symbol>StringWithoutParens</symbol>
        <code language="c">
          int len = PyList_GET_SIZE($2);
          int i;
          int j = 0;
          $$ = PyList_New(len + 3);
          PyList_SET_ITEM($$, j++, $1);
          Py_INCREF($1);
          for (i = 0; i &lt; len; i++) {
            PyObject *item = PyList_GET_ITEM($2, i);
            Py_INCREF(item);
            PyList_SET_ITEM($$, j++, item);
          }
          PyList_SET_ITEM($$, j++, $3);
          Py_INCREF($3);
          PyList_SET_ITEM($$, j++, $4);
          Py_INCREF($4);
        </code>
        <code language="python">
          $$ = [$1] + $2 + [$3, $4]
        </code>
      </rule>
      <rule>
        <symbol>StringWithoutParens</symbol>
        <symbol>'('</symbol>
        <symbol>StringWithBalancedParens</symbol>
        <symbol>')'</symbol>
        <code language="c">
          int len = PyList_GET_SIZE($3);
          int i;
          int j = 0;
          $$ = PyList_New(len + 3);
          PyList_SET_ITEM($$, j++, $1);
          Py_INCREF($1);
          PyList_SET_ITEM($$, j++, $2);
          Py_INCREF($2);
          for (i = 0; i &lt; len; i++) {
            PyObject *item = PyList_GET_ITEM($3, i);
            Py_INCREF(item);
            PyList_SET_ITEM($$, j++, item);
          }
          PyList_SET_ITEM($$, j++, $4);
          Py_INCREF($4);
        </code>
        <code language="python">
          $$ = [$1, $2] + $3 + [$4]
        </code>
      </rule>
      <rule>
        <symbol>StringWithoutParens</symbol>
        <symbol>'('</symbol>
        <symbol>StringWithBalancedParens</symbol>
        <symbol>')'</symbol>
        <symbol>StringWithoutParens</symbol>
        <code language="c">
          int len = PyList_GET_SIZE($3);
          int i;
          int j = 0;
          $$ = PyList_New(len + 4);
          PyList_SET_ITEM($$, j++, $1);
          Py_INCREF($1);
          PyList_SET_ITEM($$, j++, $2);
          Py_INCREF($2);
          for (i = 0; i &lt; len; i++) {
            PyObject *item = PyList_GET_ITEM($3, i);
            Py_INCREF(item);
            PyList_SET_ITEM($$, j++, item);
          }
          PyList_SET_ITEM($$, j++, $4);
          Py_INCREF($4);
          PyList_SET_ITEM($$, j++, $5);
          Py_INCREF($5);
        </code>
        <code language="python">
          $$ = [$1, $2] + $3 + [$4, $5]
        </code>
      </rule>
    </production>

  </grammar>

  <lexer>
    <defines>
      <?include ../XPath/XPathLexerDefines.bgen.frag?>
    </defines>

    <states>
      <exclusive>SCHEME_EXPR</exclusive>
    </states>

    <!-- QName -->
    <pattern expression="{NCName}">
      <token>NCName</token>
    </pattern>

    <!-- QName -->
    <pattern expression="{NCName}:{NCName}">
      <token>QName</token>
    </pattern>

    <!-- integer for child sequence -->
    <pattern expression="[1-9][0-9]*">
      <token>Integer</token>
    </pattern>

    <pattern expression='\('>
      <begin>SCHEME_EXPR</begin>
      <token>@ASCII@</token>
    </pattern>

    <pattern expression='{S}+'/>

    <pattern expression='.'>
      <token>@ASCII@</token>
    </pattern>

    <!-- String with parenthesis escaping -->
    <scope state='SCHEME_EXPR'>
      <pattern expression='\(|\)'>
        <token>@ASCII@</token>
      </pattern>

      <pattern expression="(\^[)\^]|[^()])*">
        <token>StringWithoutParens</token>
      </pattern>
    </scope>

  </lexer>

</bisongen>