/**
 * rngparser.c: parser for the Relax-NG compact syntax.
 *
 * Based on:
 *   RELAX NG Compact Syntax
 *   Committee Specification 21 November 2002
 *   http://www.oasis-open.org/committees/relax-ng/compact-20021121.html
 *
 * See Copyright for the status of this software.
 *
 * Daniel Veillard <veillard@redhat.com>
 */

#include <string.h>

#include <libxml/parser.h>
#include <libxml/parserInternals.h>
#include <libxml/relaxng.h>
#include <libxml/dict.h>

#define TODO								\
    xmlGenericError(xmlGenericErrorContext,				\
	    "Unimplemented block at %s:%d\n",				\
            __FILE__, __LINE__);

#define MAX_TOKEN 10

typedef enum {
    CRNG_NONE = 0,
    CRNG_OP = 1,
    CRNG_KEYWORD,
    CRNG_IDENTIFIER,
    CRNG_LITERAL_SEGMENT,
    CRNG_CNAME,
    CRNG_QNAME,
    CRNG_NSNAME,
    CRNG_DOCUMENTATION
} xmlCRNGTokType;

typedef enum {
    CRNG_OKAY = 0,
    CRNG_MEMORY_ERROR,
    CRNG_INVALID_CHAR_ERROR,
    CRNG_END_ERROR,
    CRNG_ENCODING_ERROR
} xmlCRNGError;

typedef enum {
    XML_CRNG_ERROR = -1,
    XML_CRNG_OK = 0,
    XML_CRNG_EOF = 1
} xmlCRelaxNGParserState;

typedef struct _token _token;
typedef _token *tokenPtr;
struct _token {
    xmlCRNGTokType toktype;
    int toklen;
    const xmlChar *token;
    const xmlChar *prefix;
};

typedef struct _xmlCRelaxNGParserCtxt xmlCRelaxNGParserCtxt;
typedef xmlCRelaxNGParserCtxt *xmlCRelaxNGParserCtxtPtr;
struct _xmlCRelaxNGParserCtxt {
    void *userData;			/* user specific data block */
    xmlRelaxNGValidityErrorFunc error;	/* the callback in case of errors */
    xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
    xmlRelaxNGValidErr err;

    const xmlChar *compact;
    const xmlChar *end;
    const xmlChar *cur;
    int isElem;
    int lineno;
    const xmlChar *linestart;
    const char *filename;

    int  nbTokens;
    int  firstToken;
    _token tokens[MAX_TOKEN];
    int  totalToken;

    xmlCRelaxNGParserState state;

    int            nbErrors;

    xmlDocPtr      res;			/* the result */
    xmlNodePtr     ins;			/* the current insertion node */

    xmlNsPtr       nsDef;
    tokenPtr token;

    xmlHashTablePtr namespaces;
    xmlHashTablePtr datatypes;

    /*
     * dictionary and keywords
     */
    xmlDictPtr     dict;
    const xmlChar *key_attribute;
    const xmlChar *key_default;
    const xmlChar *key_datatypes;
    const xmlChar *key_div;
    const xmlChar *key_element;
    const xmlChar *key_empty;
    const xmlChar *key_external;
    const xmlChar *key_grammar;
    const xmlChar *key_include;
    const xmlChar *key_inherit;
    const xmlChar *key_list;
    const xmlChar *key_mixed;
    const xmlChar *key_namespace;
    const xmlChar *key_notAllowed;
    const xmlChar *key_parent;
    const xmlChar *key_start;
    const xmlChar *key_string;
    const xmlChar *key_text;
    const xmlChar *key_token;
    const xmlChar *key_equal;
    const xmlChar *key_orequal;
    const xmlChar *key_andequal;
    const xmlChar *key_combine;
    const xmlChar *key_or;
    const xmlChar *key_comma;
    const xmlChar *key_and;
    const xmlChar *key_choice;
    const xmlChar *key_group;
    const xmlChar *key_interleave;
    const xmlChar *key_ref;
    const xmlChar *key_define;

    /* results */
    xmlDocPtr doc;	/* the resulting doc */
    xmlNodePtr insert;	/* the insertion point */
    xmlAttrPtr attrs;   /* pending attributes */
};

static const xmlChar *xmlCRelaxNGInherit = BAD_CAST "Inherit string";
static const xmlChar *xmlCRelaxNGDefault = BAD_CAST "Default string";

#define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
/**
 * IS_BLANK:
 * @c:  an UNICODE value (int)
 *
 * Macro to check the following production in the XML spec:
 *
 * [3] S ::= (#x20 | #x9 | #xD | #xA)+
 */
#ifndef IS_BLANK
#define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) ||	\
                     ((c) == 0x0D))
#endif
#define IS_SEPARATOR(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
                     ((c) == 0x0D) || (c == '#'))

#define CRNG_ERROR0(X)							\
    { xmlCRNGErr(ctxt, X, NULL); return(0); }
#define CRNG_ERROR(X)							\
    { xmlCRNGErr(ctxt, X, NULL); }

#define CRNG_MEM_ERROR0()						\
    { xmlCRNGErr(ctxt, CRNG_MEMORY_ERROR, NULL); return(0); }
#define CRNG_MEM_ERROR()						\
    { xmlCRNGErr(ctxt, CRNG_MEMORY_ERROR, NULL); }

#define ERROR(str) xmlCRNGErr(ctxt, 0, str);

static void
xmlCRNGErr(xmlCRelaxNGParserCtxtPtr ctxt, int err_no, const char *err_msg) {
    const xmlChar *cur;
    xmlChar buffer[150];
    int i, l;

    if (ctxt != NULL) {
        if (ctxt->filename != NULL)
	    fprintf(stderr, "%s:%d ", ctxt->filename, ctxt->lineno);
    }
    if (err_msg != NULL) {
	fprintf(stderr, "error: %s\n", err_msg);
    } else if (err_no != 0)
	fprintf(stderr, "error %d\n", err_no);
    cur = ctxt->cur;
    while ((*cur != '\n') && (*cur != '\r') && (ctxt->cur - cur < 80)) cur--;
    l = ctxt->cur - cur;
    cur++;
    for (i = 0; i < 100;i++) {
        if ((*cur == '\n') || (*cur == '\r')) break;
        buffer[i] = *cur++;
    }
    buffer[i] = 0;
    fprintf(stderr, "%s\n", buffer);
    for (i = 0; i < l;i++) buffer[i] = ' ';
    buffer[i++] = '^';
    buffer[i++] = 0;
    fprintf(stderr, "%s\n", buffer);
}

/**
 * IS_OP
 * @c:  an UNICODE value (int)
 *
 * Macro to check for operator value
 */
#ifndef IS_OP
#define IS_OP(c) (((c) == ',') || ((c) == '&') || ((c) == '|') ||	\
		  ((c) == '?') || ((c) == '-') || ((c) == '*') ||	\
		  ((c) == '{') || ((c) == '}') || ((c) == '(') ||	\
		  ((c) == ')') || ((c) == '+') || ((c) == '=') ||	\
		  ((c) == ':'))
#endif

static int
xmlCRNGIsKeyword(xmlCRelaxNGParserCtxtPtr ctxt, const xmlChar *str) {
    if ((str == ctxt->key_attribute) ||
        (str == ctxt->key_default) ||
        (str == ctxt->key_datatypes) ||
        (str == ctxt->key_div) ||
        (str == ctxt->key_element) ||
        (str == ctxt->key_empty) ||
        (str == ctxt->key_external) ||
        (str == ctxt->key_grammar) ||
        (str == ctxt->key_include) ||
        (str == ctxt->key_inherit) ||
        (str == ctxt->key_list) ||
        (str == ctxt->key_mixed) ||
        (str == ctxt->key_namespace) ||
        (str == ctxt->key_notAllowed) ||
        (str == ctxt->key_parent) ||
        (str == ctxt->key_start) ||
        (str == ctxt->key_string) ||
        (str == ctxt->key_text) ||
        (str == ctxt->key_token))
	return(1);
    return(0);

}

/*
 * xmlCRNGNextToken:
 * ctxt:  a compact RNG parser context
 *
 * Scan the schema to get the next token
 *
 * Return 0 if success and -1 in case of error
 */

static int
xmlCRNGNextToken(xmlCRelaxNGParserCtxtPtr ctxt) {
    const xmlChar *cur;
    tokenPtr token;

    if (ctxt == NULL) return(-1);
    if (ctxt->nbTokens >= MAX_TOKEN) return(-1);
    token = &(ctxt->tokens[(ctxt->firstToken + ctxt->nbTokens) % MAX_TOKEN]);
    token->toktype = CRNG_NONE;

    if (ctxt->cur == NULL) {
        ctxt->cur = ctxt->compact;
    }
retry:
    if (ctxt->cur >= ctxt->end) {
	ctxt->state = XML_CRNG_EOF;
	return(-1);
    }
    while ((ctxt->cur < ctxt->end) &&
           (IS_BLANK(*ctxt->cur))) ctxt->cur++;
    if (ctxt->cur >= ctxt->end) {
	ctxt->state = XML_CRNG_EOF;
	return(-1);
    }
    if (*ctxt->cur == '#') {
        cur = ctxt->cur;
	cur++;
	while ((cur < ctxt->end) && (*cur != '\n') && (*cur != '\r'))
	    cur++;
        ctxt->cur = cur;
	goto retry;
    } else if (*ctxt->cur == '"') {
        /* string, check for '"""' */
	ctxt->cur++;
	if (ctxt->cur >= ctxt->end) goto eof;
	cur = ctxt->cur;
        if ((ctxt->end - ctxt->end > 2) &&
	    (*cur == '"') && (cur[1] == '"')) {
	    TODO
	} else {
	    while ((cur < ctxt->end) && (*cur != '"')) cur++;
	    if (cur >= ctxt->end) goto eof;
	    token->toklen = cur - ctxt->cur;
	    token->token = xmlDictLookup(ctxt->dict, ctxt->cur, token->toklen);
	    token->toktype = CRNG_LITERAL_SEGMENT;
	    token->prefix = NULL;
	    cur++;
	    ctxt->cur = cur;
	}
    } else if (*ctxt->cur == '\'') {
        /* string, check for "'''" */
	TODO
    } else if ((IS_OP(*ctxt->cur)) || (*ctxt->cur == ':')) {
        cur = ctxt->cur;
	cur++;
	if ((cur < ctxt->end) &&
	    (((*cur == '=') &&
	      ((*ctxt->cur == '|') || (*ctxt->cur == '&'))) ||
	     ((*cur == '*') && (*ctxt->cur == ':')))) {
	    token->toklen = 2;
	} else {
	    token->toklen = 1;
	}
	token->token = xmlDictLookup(ctxt->dict, ctxt->cur, token->toklen);
	token->toktype = CRNG_OP;
	token->prefix = NULL;
	ctxt->cur += token->toklen;
    } else {
        int escape = 0;

        cur = ctxt->cur;
        if (*cur == '\\') {
	    escape = 1;
	    cur++;
	    ctxt->cur++;
	}
	while ((cur < ctxt->end) &&
	       (!(IS_SEPARATOR(*cur))) && (!(IS_OP(*cur)))) cur++;

	token->toklen = cur - ctxt->cur;
	token->token = xmlDictLookup(ctxt->dict, ctxt->cur, token->toklen);
	token->prefix = NULL;
	ctxt->cur = cur;
	if ((escape == 0) && (xmlCRNGIsKeyword(ctxt, token->token)))
	    token->toktype = CRNG_KEYWORD;
	else {
	    token->toktype = CRNG_IDENTIFIER;
	}
	if (*ctxt->cur == ':') {
	    ctxt->cur++;
	    if (*ctxt->cur == '*') {
		ctxt->cur++;
		token->toktype = CRNG_NSNAME;
	    } else {
	        cur = ctxt->cur;
		while ((cur < ctxt->end) &&
		       (!(IS_SEPARATOR(*cur))) && (!(IS_OP(*cur)))) cur++;
		token->prefix = token->token;
		token->toklen = cur - ctxt->cur;
		token->token = xmlDictLookup(ctxt->dict, ctxt->cur,
		                             token->toklen);
		ctxt->cur = cur;
		if (xmlValidateNCName(token->token, 0) == 0)
		    token->toktype = CRNG_QNAME;
		else {
		    TODO /* sounds like an error ! */
		    token->toktype = CRNG_IDENTIFIER;
		}
	    }
	}
    }
    ctxt->nbTokens++;
    return(0);
eof:
    ctxt->state = XML_CRNG_EOF;
    CRNG_ERROR(CRNG_END_ERROR);
    return(-1);
}

/**
 * xmlParseCRNGGetToken:
 * @ctxt: a compact RNG parser context
 * @no: the number of the token from 1 for the first one
 *      and 2, 3 ... for read-ahead
 *
 * Token reading interface
 *
 * returns a pointer to the new token, or NULL in case of error or EOF
 */
static tokenPtr
xmlParseCRNGGetToken(xmlCRelaxNGParserCtxtPtr ctxt, int no) {
    tokenPtr ret;
    int res;

    if ((no <= 0) || (no >= MAX_TOKEN)) return(NULL);
    no--;
    while (ctxt->nbTokens <= no) {
        res = xmlCRNGNextToken(ctxt);
	if (res < 0)
	    return(NULL);
    }
    ret = &(ctxt->tokens[(ctxt->firstToken + no) % MAX_TOKEN]);
    return(ret);
}

/**
 * xmlParseCRNGDropTokens:
 * @ctxt: a compact RNG parser context
 * @nr: the number of token marked as read
 *
 * mark a number of token as read and consumed.
 *
 * Returns -1 in case of error and 0 otherwise
 */
static int
xmlParseCRNGDropTokens(xmlCRelaxNGParserCtxtPtr ctxt, int nr) {
    if ((nr <= 0) || (nr >= MAX_TOKEN)) return(-1);
    while ((ctxt->nbTokens >0) && (nr > 0)) {
        ctxt->firstToken++;
	nr--;
	ctxt->nbTokens--;
	ctxt->totalToken++;
	if (ctxt->totalToken == 384)
	    fprintf(stderr, "found\n");
    }
    ctxt->firstToken = ctxt->firstToken % MAX_TOKEN;
    return(0);
}

static void
xmlParseCRNGTokenize(xmlCRelaxNGParserCtxtPtr ctxt) {
    tokenPtr token;

    token = xmlParseCRNGGetToken(ctxt, 1);
    while (token != NULL) {
        switch (token->toktype) {
            case CRNG_NONE: printf("none"); break;
            case CRNG_OP: printf("op"); break;
            case CRNG_KEYWORD: printf("keyword"); break;
            case CRNG_IDENTIFIER: printf("identifier"); break;
            case CRNG_LITERAL_SEGMENT: printf("literal"); break;
            case CRNG_CNAME: printf("cname"); break;
            case CRNG_QNAME: printf("qname"); break;
            case CRNG_NSNAME: printf("nsname"); break;
            case CRNG_DOCUMENTATION: printf("doc"); break;
	}
        printf(":%s\n", token->token);
	xmlParseCRNGDropTokens(ctxt, 1);
	token = xmlParseCRNGGetToken(ctxt, 1);
    }
}

/**
 * xmlParseCRNG_attribute:
 * @ctxt: a compact RNG parser context
 * @name: the attribute name
 * @ns: the attribute namespace
 * @value: the attribute value
 *
 * implements attribute of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_attribute(xmlCRelaxNGParserCtxtPtr ctxt,
                       const xmlChar *name,
                       xmlNsPtr ns,
		       const xmlChar *value)
{
    xmlAttrPtr attr;

    attr = xmlNewNsPropEatName(NULL, ns, (xmlChar *) name, value);
    if (attr == NULL) CRNG_MEM_ERROR0();
    attr->next = ctxt->attrs;
    if (ctxt->attrs != NULL)
        ctxt->attrs->prev = attr;
    ctxt->attrs = attr;
    return(0);
}

/**
 * xmlParseCRNG_bindPrefix:
 * @ctxt: a compact RNG parser context
 * @prefix: the namespace prefix or NULL
 * @namespace: the namespace name
 *
 * implements bindPrefix of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_bindPrefix(xmlCRelaxNGParserCtxtPtr ctxt,
                        const xmlChar *prefix,
			const xmlChar *namespace)
{
    int ret;

    if ((prefix != NULL) && (xmlStrEqual(prefix, BAD_CAST "xml"))  &&
        (!xmlStrEqual(namespace, XML_XML_NAMESPACE))) {
	ERROR("The \"xml\" prefix must be bound to \"http://www.w3.org/XML/1998/namespace\"");
	return(-1);
    } else if ((xmlStrEqual(namespace, XML_XML_NAMESPACE)) &&
               (!xmlStrEqual(prefix, BAD_CAST "xml"))) {
	ERROR("The \"http://www.w3.org/XML/1998/namespace\" name must be bound to \"xml\" prefix");
	return(-1);
    }
    if (ctxt->namespaces == NULL)
        ctxt->namespaces = xmlHashCreate(10);
    if (ctxt->namespaces == NULL) {
        ERROR("Failed to create namespace hash table");
	return(-1);
    }
    if (prefix == NULL)
        ret = xmlHashAddEntry(ctxt->namespaces, xmlCRelaxNGDefault,
	                      (void *) namespace);
    else
        ret = xmlHashAddEntry(ctxt->namespaces, prefix,
	                      (void *) namespace);
    if (ret < 0) {
        if (prefix == NULL) {
	    ERROR("Redefinition of default namespace");
	} else {
	    ERROR("Redefinition of namespace");
	}
	return(-1);
    }

    return(0);
}

/**
 * xmlParseCRNG_bindDatatypePrefix:
 * @ctxt: a compact RNG parser context
 * @prefix: the datatype prefix
 * @namespace: the datatype identifier
 *
 * implements bindDatatypePrefix of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_bindDatatypePrefix(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
                                const xmlChar *prefix,
			        const xmlChar *namespace)
{
    int ret;

    if ((prefix != NULL) && (xmlStrEqual(prefix, BAD_CAST "xsd"))  &&
        (!xmlStrEqual(namespace,
		  BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes"))) {
	ERROR("The \"xsd\" prefix must be bound to \"http://www.w3.org/2001/XMLSchema-datatypes\"");
	return(-1);
    }
    if (ctxt->datatypes == NULL)
        ctxt->datatypes = xmlHashCreate(10);
    if (ctxt->datatypes == NULL) {
        ERROR("Failed to create namespace hash table");
	return(-1);
    }
    ret = xmlHashAddEntry(ctxt->datatypes, prefix,
                          (void *) namespace);
    if (ret < 0) {
	ERROR("Redefinition of datatype");
	return(-1);
    }
    return(0);
}

/**
 * xmlParseCRNG_lookupPrefix:
 * @ctxt: a compact RNG parser context
 * @prefix: the namespace prefix or NULL
 *
 * implements lookupPrefix of the RELAX NG Compact Syntax Appendix A
 *
 * Returns the prefix in case of success or NULL in case of error
 */
static const xmlChar *
xmlParseCRNG_lookupPrefix(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
                        const xmlChar *prefix)
{
    const xmlChar *ret;

    if (prefix == NULL)
        ret = xmlHashLookup(ctxt->namespaces, xmlCRelaxNGDefault);
    else
        ret = xmlHashLookup(ctxt->namespaces, prefix);
    return(ret);
}

/**
 * xmlParseCRNG_lookupDatatypePrefix:
 * @ctxt: a compact RNG parser context
 * @prefix: the namespace prefix or NULL
 *
 * implements lookupDatatypePrefix of the RELAX NG Compact Syntax Appendix A
 *
 * Returns the prefix in case of success or NULL in case of error
 */
static const xmlChar *
xmlParseCRNG_lookupDatatypePrefix(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
                        const xmlChar *prefix)
{
    const xmlChar *ret;
    ret = xmlHashLookup(ctxt->datatypes, prefix);
    return(ret);
}

/**
 * xmlParseCRNG_datatypeAttributes:
 * @ctxt: a compact RNG parser context
 * @prefix: the namespace prefix or NULL
 *
 * implements lookupPrefix of the RELAX NG Compact Syntax Appendix A
 *
 * Returns the prefix in case of success or NULL in case of error
 */
static xmlAttrPtr
xmlParseCRNG_datatypeAttributes(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
                        const xmlChar *library, const xmlChar *type)
{
    xmlAttrPtr lib, typ;

    lib = xmlNewNsProp(NULL, NULL, BAD_CAST "datatypeLibrary", library);
    if (lib == NULL) {
        CRNG_MEM_ERROR();
	return(NULL);
    }
    typ = xmlNewNsProp(NULL, NULL, BAD_CAST "type", type);
    if (typ == NULL) {
        CRNG_MEM_ERROR();
	return(lib);
    }
    lib->next = typ;

    return(lib);
}

/**
 * xmlParseCRNG_XXX:
 * @ctxt: a compact RNG parser context
 *
 * Parse XXX of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_XXX(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
{
    return(0);
}

static int xmlParseCRNG_pattern(xmlCRelaxNGParserCtxtPtr ctxt);
static int xmlParseCRNG_nameClass(xmlCRelaxNGParserCtxtPtr ctxt);

/**
 * xmlParseCRNG_params:
 * @ctxt: a compact RNG parser context
 *
 * Parse params of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_params(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
{
    TODO
    return(0);
}

/**
 * xmlParseCRNG_exceptNameClass:
 * @ctxt: a compact RNG parser context
 *
 * Parse exceptNameClass of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_exceptNameClass(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
{
    tokenPtr token;
    xmlNodePtr insert = ctxt->insert, cur;

    token = xmlParseCRNGGetToken(ctxt, 1);
    if ((token->toktype == CRNG_OP) &&
        (token->token[0] == '-') && (token->token[1] == 0)) {
	xmlParseCRNGDropTokens(ctxt, 1);
	cur = xmlNewNode(NULL, BAD_CAST "except");
	if (cur == NULL) CRNG_MEM_ERROR0();
	if (ctxt->insert != NULL)
	    xmlAddChild(ctxt->insert, cur);
	ctxt->insert = cur;
	xmlParseCRNG_nameClass(ctxt);
    }
    ctxt->insert = insert;
    return(0);
}

/**
 * xmlParseCRNG_innerNameClass:
 * @ctxt: a compact RNG parser context
 *
 * Parse innerNameClass of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_innerNameClass(xmlCRelaxNGParserCtxtPtr ctxt)
{
    tokenPtr token;
    xmlNodePtr cur;

    token = xmlParseCRNGGetToken(ctxt, 1);
    if (token->toktype == CRNG_OP) {
        if ((token->token[0] == '(') && (token->token[1] == 0)) {
	    xmlParseCRNGDropTokens(ctxt, 1);
	    xmlParseCRNG_nameClass(ctxt);
	    token = xmlParseCRNGGetToken(ctxt, 1);
	    if ((token->toktype != CRNG_OP) ||
	        (token->token[0] != ')') || (token->token[1] != 0)) {
		ERROR("Expecting \")\" here");
	    }
	    xmlParseCRNGDropTokens(ctxt, 1);
	} else if ((token->token[0] == '*') && (token->token[1] == 0)) {
	    xmlParseCRNGDropTokens(ctxt, 1);
	    cur = xmlNewNode(NULL, BAD_CAST "anyName");
	    if (cur == NULL) CRNG_MEM_ERROR0();
	    if (ctxt->insert != NULL)
		xmlAddChild(ctxt->insert, cur);
	    ctxt->insert = cur;
	    xmlParseCRNG_exceptNameClass(ctxt);
	} else {
	    TODO
	}
    } else if ((token->toktype == CRNG_IDENTIFIER) ||
               (token->toktype == CRNG_KEYWORD)) {
	cur = xmlNewNode(NULL, BAD_CAST "name");
	if (cur == NULL) CRNG_MEM_ERROR0();
	if (ctxt->isElem) {
	    xmlSetProp(cur, BAD_CAST "ns",
	               xmlParseCRNG_lookupPrefix(ctxt, NULL));
	} else {
	    xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
	}
	xmlNodeAddContent(cur, token->token);
	if (ctxt->insert != NULL)
	    xmlAddChild(ctxt->insert, cur);
	ctxt->insert = cur;
	xmlParseCRNGDropTokens(ctxt, 1);
    } else if (token->toktype == CRNG_CNAME) {
        TODO
    } else if (token->toktype == CRNG_NSNAME) {
	cur = xmlNewNode(NULL, BAD_CAST "nsName");
	if (cur == NULL) CRNG_MEM_ERROR0();
        xmlSetProp(cur, BAD_CAST "ns",
	           xmlParseCRNG_lookupPrefix(ctxt, token->token));
	if (ctxt->insert != NULL)
	    xmlAddChild(ctxt->insert, cur);
	ctxt->insert = cur;
	xmlParseCRNGDropTokens(ctxt, 1);
	xmlParseCRNG_exceptNameClass(ctxt);
    } else {
        TODO /* probably an error */
    }

    return(0);
}

/**
 * xmlParseCRNG_nameClass:
 * @ctxt: a compact RNG parser context
 *
 * Parse nameClass of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_nameClass(xmlCRelaxNGParserCtxtPtr ctxt)
{
    tokenPtr token;
    xmlNodePtr insert = ctxt->insert, last, choice;

    ctxt->insert = NULL;
    xmlParseCRNG_innerNameClass(ctxt);
    last = ctxt->insert;
    token = xmlParseCRNGGetToken(ctxt, 1);
    while ((token->toktype == CRNG_OP) &&
        (token->token[0] == '|') && (token->token[1] == 0)) {
	choice = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_choice);
	xmlParseCRNGDropTokens(ctxt, 1);
	if (choice == NULL) CRNG_MEM_ERROR0();
	ctxt->insert = NULL;
	xmlParseCRNG_innerNameClass(ctxt);
	xmlAddChild(choice, last);
	xmlAddChild(choice, ctxt->insert);
	last = choice;
	token = xmlParseCRNGGetToken(ctxt, 1);
    }
    xmlAddChild(insert, last);

    ctxt->insert = insert;
    return(0);
}

/**
 * xmlParseCRNG_patternBlock:
 * @ctxt: a compact RNG parser context
 *
 * Parse a pattern block of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_patternBlock(xmlCRelaxNGParserCtxtPtr ctxt)
{
    tokenPtr token;

    token = xmlParseCRNGGetToken(ctxt, 1);
    if ((token->toktype != CRNG_OP) ||
	(token->token[0] != '{') || (token->token[1] != 0)) {
	ERROR("Expecting \"{\" here");
    }
    xmlParseCRNGDropTokens(ctxt, 1);
    xmlParseCRNG_pattern(ctxt);
    token = xmlParseCRNGGetToken(ctxt, 1);
    if ((token->toktype != CRNG_OP) ||
	(token->token[0] != '}') || (token->token[1] != 0)) {
	ERROR("Expecting \"}\" here");
    }
    xmlParseCRNGDropTokens(ctxt, 1);
    return(0);
}

/**
 * xmlParseCRNG_datatype:
 * @ctxt: a compact RNG parser context
 *
 * Parse datatype of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_datatype(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
{
    tokenPtr token;
    xmlAttrPtr attrs = NULL;

    token = xmlParseCRNGGetToken(ctxt, 1);
    if (token->toktype == CRNG_KEYWORD) {
	if (token->token == ctxt->key_string) {
	    attrs = xmlParseCRNG_datatypeAttributes(ctxt, BAD_CAST "",
	                                            token->token);
	    xmlParseCRNGDropTokens(ctxt, 1);
	} else if (token->token == ctxt->key_token) {
	    attrs = xmlParseCRNG_datatypeAttributes(ctxt, BAD_CAST "",
	                                            token->token);
	    xmlParseCRNGDropTokens(ctxt, 1);
	} else {
	    TODO /* probably an error */
	}
    } else if (token->toktype == CRNG_LITERAL_SEGMENT) {
	ctxt->insert = xmlNewNode(NULL, BAD_CAST "value");
	xmlParseCRNGDropTokens(ctxt, 1);
	if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
	xmlNodeAddContent(ctxt->insert, token->token);
    } else if (token->toktype == CRNG_QNAME) {
	attrs = xmlParseCRNG_datatypeAttributes(ctxt,
	            xmlParseCRNG_lookupDatatypePrefix(ctxt, token->prefix),
		    token->token);
    } else {
        TODO
    }
    if (attrs != NULL) {
	token = xmlParseCRNGGetToken(ctxt, 1);
	if (token->toktype == CRNG_LITERAL_SEGMENT) {
	    ctxt->insert = xmlNewNode(NULL, BAD_CAST "value");
	    xmlParseCRNGDropTokens(ctxt, 1);
	    if (ctxt->insert == NULL) {
	        xmlFreePropList(attrs);
		CRNG_MEM_ERROR0();
	    }
	    ctxt->insert->properties = attrs;
	    xmlNodeAddContent(ctxt->insert, token->token);
	} else if ((token->toktype == CRNG_OP) &&
	           (token->token[0] == '{') && (token->token[0] == 0)) {
	    ctxt->insert = xmlNewNode(NULL, BAD_CAST "data");
	    xmlParseCRNGDropTokens(ctxt, 1);
	    if (ctxt->insert == NULL) {
	        xmlFreePropList(attrs);
		CRNG_MEM_ERROR0();
	    }
	    ctxt->insert->properties = attrs;
	    xmlParseCRNG_params(ctxt);
        } else {
	    ctxt->insert = xmlNewNode(NULL, BAD_CAST "data");
	    xmlParseCRNGDropTokens(ctxt, 1);
	    if (ctxt->insert == NULL) {
	        xmlFreePropList(attrs);
		CRNG_MEM_ERROR0();
	    }
	    ctxt->insert->properties = attrs;
	    xmlNodeAddContent(ctxt->insert, token->token);
	}
    }
    return(0);
}

/**
 * xmlParseCRNG_primary:
 * @ctxt: a compact RNG parser context
 *
 * Parse primary of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_primary(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
{
    tokenPtr token;

    token = xmlParseCRNGGetToken(ctxt, 1);
    if (token == NULL)
        return(0);
    if (token->toktype == CRNG_KEYWORD) {
        if (token->token == ctxt->key_element) {
	    ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
	    xmlParseCRNGDropTokens(ctxt, 1);
	    if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
	    ctxt->isElem = 1;
	    xmlParseCRNG_nameClass(ctxt);
	    xmlParseCRNG_patternBlock(ctxt);
	} else if (token->token == ctxt->key_attribute) {
	    ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
	    xmlParseCRNGDropTokens(ctxt, 1);
	    if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
	    ctxt->isElem = 0;
	    xmlParseCRNG_nameClass(ctxt);
	    xmlParseCRNG_patternBlock(ctxt);
	} else if (token->token == ctxt->key_mixed) {
	    ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
	    xmlParseCRNGDropTokens(ctxt, 1);
	    if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
	    xmlParseCRNG_patternBlock(ctxt);
	} else if (token->token == ctxt->key_list) {
	    ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
	    xmlParseCRNGDropTokens(ctxt, 1);
	    if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
	    xmlParseCRNG_patternBlock(ctxt);
	} else if (token->token == ctxt->key_empty) {
	    ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
	    xmlParseCRNGDropTokens(ctxt, 1);
	    if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
	} else if (token->token == ctxt->key_notAllowed) {
	    ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
	    xmlParseCRNGDropTokens(ctxt, 1);
	    if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
	} else if (token->token == ctxt->key_text) {
	    ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
	    xmlParseCRNGDropTokens(ctxt, 1);
	    if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
	} else if (token->token == ctxt->key_parent) {
	    ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
	    xmlParseCRNGDropTokens(ctxt, 1);
	    if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
	    TODO
	} else if (token->token == ctxt->key_grammar) {
	    ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
	    xmlParseCRNGDropTokens(ctxt, 1);
	    if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
	    TODO
	} else if (token->token == ctxt->key_external) {
	    ctxt->insert = xmlNewNode(NULL, BAD_CAST "externalRef");
	    xmlParseCRNGDropTokens(ctxt, 1);
	    if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
	    TODO
	} else {
	   TODO
	}
    } else if (token->toktype == CRNG_IDENTIFIER) {
	ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_ref);
	if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
	xmlSetProp(ctxt->insert, BAD_CAST "name", token->token);
	xmlParseCRNGDropTokens(ctxt, 1);
    } else if (token->toktype == CRNG_QNAME) {
        xmlParseCRNG_datatype(ctxt);
    } else if (token->toktype == CRNG_LITERAL_SEGMENT) {
        xmlParseCRNG_datatype(ctxt);
    } else if ((token->toktype == CRNG_OP) &&
               (token->token[0] == '(') && (token->token[1] == 0)) {
	xmlParseCRNGDropTokens(ctxt, 1);
	xmlParseCRNG_pattern(ctxt);
	token = xmlParseCRNGGetToken(ctxt, 1);
	if ((token->toktype != CRNG_OP) ||
	    (token->token[0] != ')') || (token->token[1] != 0)) {
	    ERROR("Expecting \")\" here");
	}
	xmlParseCRNGDropTokens(ctxt, 1);
    }
    return(0);
}

/**
 * xmlParseCRNG_particle:
 * @ctxt: a compact RNG parser context
 *
 * Parse particle of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_particle(xmlCRelaxNGParserCtxtPtr ctxt)
{
    tokenPtr token;
    xmlNodePtr insert = ctxt->insert, res, tmp = NULL;

    ctxt->insert = NULL;
    xmlParseCRNG_primary(ctxt);
    res = ctxt->insert;
    token = xmlParseCRNGGetToken(ctxt, 1);
    if ((token != NULL) && (token->toktype == CRNG_OP)) {
        if ((token->token[0] == '*') && (token->token[1] == 0)) {
	    tmp = xmlNewNode(NULL, BAD_CAST "zeroOrMore");
	    if (tmp == NULL) CRNG_MEM_ERROR0();
	} else if ((token->token[0] == '+') && (token->token[1] == 0)) {
	    tmp = xmlNewNode(NULL, BAD_CAST "oneOrMore");
	    if (tmp == NULL) CRNG_MEM_ERROR0();
	} else if ((token->token[0] == '?') && (token->token[1] == 0)) {
	    tmp = xmlNewNode(NULL, BAD_CAST "optional");
	    if (tmp == NULL) CRNG_MEM_ERROR0();
	}
	if (tmp != NULL) {
	    xmlAddChild(tmp, res);
	    res = tmp;
	    xmlParseCRNGDropTokens(ctxt, 1);
	}
    }
    if (insert != NULL) {
        xmlAddChild(insert, res);
	ctxt->insert = insert;
    } else
        ctxt->insert = res;
    return(0);
}

/**
 * xmlParseCRNG_pattern:
 * @ctxt: a compact RNG parser context
 *
 * Parse pattern of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_pattern(xmlCRelaxNGParserCtxtPtr ctxt)
{
    tokenPtr token;
    xmlNodePtr insert = ctxt->insert, prev, grp;

    ctxt->insert = NULL;
    xmlParseCRNG_particle(ctxt);
    prev = ctxt->insert;
    token = xmlParseCRNGGetToken(ctxt, 1);
    while ((prev != NULL) && (token != NULL) && (token->toktype == CRNG_OP)) {
        if (token->token == ctxt->key_or) {
	    grp = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_choice);
	    if (grp == NULL) CRNG_MEM_ERROR0();
	} else if (token->token == ctxt->key_and) {
	    grp = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_interleave);
	    if (grp == NULL) CRNG_MEM_ERROR0();
	} else if (token->token == ctxt->key_comma) {
	    grp = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_group);
	    if (grp == NULL) CRNG_MEM_ERROR0();
	} else
	   break;
	xmlParseCRNGDropTokens(ctxt, 1);
        ctxt->insert = NULL;
	xmlParseCRNG_particle(ctxt);
	xmlAddChild(grp, prev);
	xmlAddChild(grp, ctxt->insert);
	prev = grp;
	token = xmlParseCRNGGetToken(ctxt, 1);
    }
    if (insert != NULL) {
	xmlAddChild(insert, prev);
	ctxt->insert = insert;
    } else {
	ctxt->insert = prev;
    }

    return(0);
}

/**
 * xmlParseCRNG_component:
 * @ctxt: a compact RNG parser context
 *
 * Parse component of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_component(xmlCRelaxNGParserCtxtPtr ctxt)
{
    tokenPtr token, tok2;
    xmlNodePtr insert = ctxt->insert;

    token = xmlParseCRNGGetToken(ctxt, 1);
    if (token == NULL)
        return(0);
    if (token->toktype == CRNG_KEYWORD) {
        if (token->token == ctxt->key_start) {
	    xmlNodePtr start;

	    start = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_start);
	    if (start == NULL) CRNG_MEM_ERROR0();
	    if (ctxt->insert != NULL)
	        xmlAddChild(ctxt->insert, start);
	    ctxt->insert = start;
            xmlParseCRNGDropTokens(ctxt, 1);
	    token = xmlParseCRNGGetToken(ctxt, 1);

            if ((token->toktype == CRNG_OP) &&
	        (token->token == ctxt->key_equal)) {
	    } else if ((token->toktype == CRNG_OP) &&
	               (token->token == ctxt->key_orequal)) {
		xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
		                       BAD_CAST "choice");
	    } else if ((token->toktype == CRNG_OP) &&
	               (token->token == ctxt->key_andequal)) {
		xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
		                       BAD_CAST "interleave");
	    } else {
	        ERROR("expecting \"=\" or \"&=\" or \"|=\" here")
		return(-1);
	    }
	    start->properties = ctxt->attrs;
	    ctxt->attrs = NULL;
            xmlParseCRNGDropTokens(ctxt, 1);
	    xmlParseCRNG_pattern(ctxt);

	} else if (token->token == ctxt->key_include) {
	    TODO
	} else if (token->token == ctxt->key_div) {
	    TODO
	} else {
	    return(-1);
	}
    } else if (token->toktype == CRNG_IDENTIFIER) {
        xmlNodePtr define;
	const xmlChar *identifier;

        identifier = token->token;
	tok2 = xmlParseCRNGGetToken(ctxt, 2);
	if ((tok2->toktype == CRNG_OP) &&
	    (tok2->token == ctxt->key_equal)) {
	} else if ((tok2->toktype == CRNG_OP) &&
		   (tok2->token == ctxt->key_orequal)) {
	    xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
				   BAD_CAST "choice");
	} else if ((tok2->toktype == CRNG_OP) &&
		   (tok2->token == ctxt->key_andequal)) {
	    xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
				   BAD_CAST "interleave");
	} else {
	    ERROR("expecting \"=\" or \"&=\" or \"|=\" here")
	    return(-1);
	}
	xmlParseCRNGDropTokens(ctxt, 2);

	define = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_define);
	if (define == NULL) CRNG_MEM_ERROR0();
	define->properties = ctxt->attrs;
	ctxt->attrs = NULL;
	xmlSetProp(define, BAD_CAST "name", identifier);
	if (ctxt->insert != NULL)
	    xmlAddChild(ctxt->insert, define);
	ctxt->insert = define;
	xmlParseCRNG_pattern(ctxt);
    } else {
	return(-1);
    }
    ctxt->insert = insert;
    return(0);
}

/**
 * xmlParseCRNG_grammar:
 * @ctxt: a compact RNG parser context
 *
 * Parse grammar of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_grammar(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
{
    tokenPtr token;
    int ret;

    token = xmlParseCRNGGetToken(ctxt, 1);
    while (token != NULL) {
        ret = xmlParseCRNG_component(ctxt);
	if (ret != 0)
	    break;
	token = xmlParseCRNGGetToken(ctxt, 1);
    }
    return(0);
}

/**
 * xmlParseCRNG_topLevelBody:
 * @ctxt: a compact RNG parser context
 *
 * Parse topLevelBody of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_topLevelBody(xmlCRelaxNGParserCtxtPtr ctxt)
{
    tokenPtr token, tok2;

    token = xmlParseCRNGGetToken(ctxt, 1);
    if (token->toktype == CRNG_KEYWORD) {
        if ((token->token == ctxt->key_start) ||
	    (token->token == ctxt->key_include) ||
	    (token->token == ctxt->key_div)) {
	    xmlNodePtr grammar;

	    grammar = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_grammar);
	    if (grammar == NULL) CRNG_MEM_ERROR0();
	    xmlDocSetRootElement(ctxt->doc, grammar);
	    ctxt->insert = grammar;

	    xmlParseCRNG_grammar(ctxt);
	} else {
	    xmlParseCRNG_pattern(ctxt);
	}
    } else {
        tok2 = xmlParseCRNGGetToken(ctxt, 2);
	if ((tok2->toktype == CRNG_OP) &&
	    ((tok2->token == ctxt->key_equal) ||
	     (tok2->token == ctxt->key_orequal) ||
	     (tok2->token == ctxt->key_andequal))) {
	    xmlNodePtr grammar;

	    grammar = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_grammar);
	    if (grammar == NULL) CRNG_MEM_ERROR0();
	    xmlDocSetRootElement(ctxt->doc, grammar);
	    ctxt->insert = grammar;

	    xmlParseCRNG_grammar(ctxt);
	} else {
	    xmlParseCRNG_pattern(ctxt);
	}
    }
    return(0);
}

/**
 * xmlParseCRNG_namespacePrefix:
 * @ctxt: a compact RNG parser context
 *
 * Parse namespacePrefix of the RELAX NG Compact Syntax Appendix A
 *
 * Returns the prefix or NULL in case of error
 */
static const xmlChar *
xmlParseCRNG_namespacePrefix(xmlCRelaxNGParserCtxtPtr ctxt)
{
    tokenPtr token;
    const xmlChar *prefix = NULL;

    token = xmlParseCRNGGetToken(ctxt, 1);
    if (token->toktype == CRNG_IDENTIFIER) {
        prefix = token->token;
    } else if (token->toktype == CRNG_OP) {
	if ((token->token[0] == '=') && (token->token[1] == 0))
	    return(NULL);
        prefix = token->token;
    } else {
	ERROR("Expecting a namespace prefix");
	return(NULL);
    }
    xmlParseCRNGDropTokens(ctxt, 1);

    if (xmlStrEqual(prefix, BAD_CAST "xmlns")) {
	ERROR("Namespace prefix \"xmlns\" is forbidden");
    }
    return(prefix);
}

/**
 * xmlParseCRNG_decl:
 * @ctxt: a compact RNG parser context
 *
 * Parse decl of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_decl(xmlCRelaxNGParserCtxtPtr ctxt)
{
    const xmlChar *prefix = NULL;
    const xmlChar *namespace = NULL;
    tokenPtr token;

    token = xmlParseCRNGGetToken(ctxt, 1);
    if (token->toktype != CRNG_KEYWORD) return(-1);
    if (token->token == ctxt->key_default) {
        xmlParseCRNGDropTokens(ctxt, 1);
        token = xmlParseCRNGGetToken(ctxt, 1);
        if ((token->toktype != CRNG_KEYWORD) ||
	    (token->token != ctxt->key_namespace)) {
	    ERROR("Expecting keyword \"namespace\" after \"default\"");
	}
        xmlParseCRNGDropTokens(ctxt, 1);
	prefix = xmlParseCRNG_namespacePrefix(ctxt);
        token = xmlParseCRNGGetToken(ctxt, 1);
        if ((token->toktype != CRNG_OP) ||
	    (token->token[0] != '=') || (token->token[1] != 0)) {
	    ERROR("Expecting keyword \"=\" here");
	}
        xmlParseCRNGDropTokens(ctxt, 1);
        token = xmlParseCRNGGetToken(ctxt, 1);
        if ((token->toktype == CRNG_KEYWORD) &&
	    (token->token == ctxt->key_inherit)) {
	    namespace = xmlCRelaxNGInherit;
	} else if (token->toktype == CRNG_LITERAL_SEGMENT) {
	    namespace = token->token;
	} else {
	    ERROR("Expecting an URI or \"inherit\" value");
	}
        xmlParseCRNGDropTokens(ctxt, 1);
        if (namespace != NULL) {
	    if (prefix != NULL)
		xmlParseCRNG_bindPrefix(ctxt, prefix, namespace);
            xmlParseCRNG_bindPrefix(ctxt, NULL, namespace);
	}
    } else if (token->token == ctxt->key_namespace) {
        xmlParseCRNGDropTokens(ctxt, 1);
	prefix = xmlParseCRNG_namespacePrefix(ctxt);
        token = xmlParseCRNGGetToken(ctxt, 1);
        if ((token->toktype != CRNG_OP) ||
	    (token->token[0] != '=') || (token->token[1] != 0)) {
	    ERROR("Expecting keyword \"=\" here");
	}
        xmlParseCRNGDropTokens(ctxt, 1);
        token = xmlParseCRNGGetToken(ctxt, 1);
        if ((token->toktype == CRNG_KEYWORD) &&
	    (token->token == ctxt->key_inherit)) {
	    namespace = xmlCRelaxNGInherit;
	} else if (token->toktype == CRNG_LITERAL_SEGMENT) {
	    namespace = token->token;
	} else {
	    ERROR("Expecting an URI or \"inherit\" value");
	}
        xmlParseCRNGDropTokens(ctxt, 1);
        if (namespace != NULL)
	    xmlParseCRNG_bindPrefix(ctxt, prefix, namespace);
    } else if (token->token == ctxt->key_datatypes) {
        xmlParseCRNGDropTokens(ctxt, 1);

        token = xmlParseCRNGGetToken(ctxt, 1);
	if ((token->toktype != CRNG_KEYWORD) &&
	    (token->toktype != CRNG_IDENTIFIER)) {
	    ERROR("Expecting a datatype prefix identifier here");
	} else
	    prefix = token->token;
        xmlParseCRNGDropTokens(ctxt, 1);
        token = xmlParseCRNGGetToken(ctxt, 1);
        if ((token->toktype != CRNG_OP) ||
	    (token->token[0] != '=') || (token->token[1] != 0)) {
	    ERROR("Expecting keyword \"=\" here");
	}
        xmlParseCRNGDropTokens(ctxt, 1);
        token = xmlParseCRNGGetToken(ctxt, 1);
	if (token->toktype == CRNG_LITERAL_SEGMENT) {
	    namespace = token->token;
	} else {
	    ERROR("Expecting a literal value for the datatype identifier");
	}
        xmlParseCRNGDropTokens(ctxt, 1);
        if ((namespace != NULL) && (prefix != NULL))
	    xmlParseCRNG_bindDatatypePrefix(ctxt, prefix, namespace);
    }

    return(0);
}

/**
 * xmlParseCRNG_preamble:
 * @ctxt: a compact RNG parser context
 *
 * Parse preamble of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_preamble(xmlCRelaxNGParserCtxtPtr ctxt)
{
    tokenPtr token;

    token = xmlParseCRNGGetToken(ctxt, 1);
    while (token != NULL) {
	if (token == NULL) return(-1);
	if ((token->toktype == CRNG_KEYWORD) &&
	    ((token->token == ctxt->key_default) ||
	     (token->token == ctxt->key_namespace) ||
	     (token->token == ctxt->key_datatypes))) {
	    xmlParseCRNG_decl(ctxt);
	} else
	    break;
	token = xmlParseCRNGGetToken(ctxt, 1);
    }
    return(0);
}

/**
 * xmlParseCRNG_topLevel:
 * @ctxt: a compact RNG parser context
 *
 * Parse topLevel of the RELAX NG Compact Syntax Appendix A
 *
 * Returns 0 in case of success and -1 in case of error
 */
static int
xmlParseCRNG_topLevel(xmlCRelaxNGParserCtxtPtr ctxt)
{
    xmlParseCRNG_preamble(ctxt);
    xmlParseCRNG_topLevelBody(ctxt);
    return(0);
}

/**
 * xmlConvertCRNG:
 * @schemas:  pointer to the text of the compact schemas
 * @len:  length of the schemas in bytes (or 0)
 * @encoding:  encoding indicated by the context or NULL
 *
 * Compiles the schemas into the equivalent Relax-NG XML structure
 *
 * Returns the xmlDocPtr resulting from the compilation or
 *         NULL in case of error
 */
xmlDocPtr
xmlConvertCRNG(const char *schemas, int len, const char *encoding) {
    struct _xmlCRelaxNGParserCtxt ctxt;
    xmlDocPtr ret = NULL;

    if (schemas == NULL) return(NULL);
    if (len <= 5) len = xmlStrlen((const unsigned char *) schemas);
    if (len <= 0) return(NULL);

    memset(&ctxt, 0, sizeof(ctxt));
    ctxt.compact = (const unsigned char *) schemas;
    ctxt.cur = (const unsigned char *) schemas;
    ctxt.end = (const unsigned char *) &schemas[len];
    ctxt.dict = xmlDictCreate();
    if (ctxt.dict == NULL)
        return(NULL);
    ctxt.doc = xmlNewDoc(NULL);
    if (ctxt.doc == NULL) {
	xmlDictFree(ctxt.dict);
	return(NULL);
    }
    ctxt.doc->dict = ctxt.dict;
    xmlDictReference(ctxt.dict);

    ctxt.nbTokens = 0;
    ctxt.firstToken = 0;
    ctxt.key_attribute = xmlDictLookup(ctxt.dict, BAD_CAST "attribute", -1);
    ctxt.key_default = xmlDictLookup(ctxt.dict, BAD_CAST "default", -1);
    ctxt.key_datatypes = xmlDictLookup(ctxt.dict, BAD_CAST "datatypes", -1);
    ctxt.key_div = xmlDictLookup(ctxt.dict, BAD_CAST "div", -1);
    ctxt.key_element = xmlDictLookup(ctxt.dict, BAD_CAST "element", -1);
    ctxt.key_empty = xmlDictLookup(ctxt.dict, BAD_CAST "empty", -1);
    ctxt.key_external = xmlDictLookup(ctxt.dict, BAD_CAST "external", -1);
    ctxt.key_grammar = xmlDictLookup(ctxt.dict, BAD_CAST "grammar", -1);
    ctxt.key_include = xmlDictLookup(ctxt.dict, BAD_CAST "include", -1);
    ctxt.key_inherit = xmlDictLookup(ctxt.dict, BAD_CAST "inherit", -1);
    ctxt.key_list = xmlDictLookup(ctxt.dict, BAD_CAST "list", -1);
    ctxt.key_mixed = xmlDictLookup(ctxt.dict, BAD_CAST "mixed", -1);
    ctxt.key_namespace = xmlDictLookup(ctxt.dict, BAD_CAST "namespace", -1);
    ctxt.key_notAllowed = xmlDictLookup(ctxt.dict, BAD_CAST "notAllowed", -1);
    ctxt.key_parent = xmlDictLookup(ctxt.dict, BAD_CAST "parent", -1);
    ctxt.key_start = xmlDictLookup(ctxt.dict, BAD_CAST "start", -1);
    ctxt.key_string = xmlDictLookup(ctxt.dict, BAD_CAST "string", -1);
    ctxt.key_text = xmlDictLookup(ctxt.dict, BAD_CAST "text", -1);
    ctxt.key_token = xmlDictLookup(ctxt.dict, BAD_CAST "token", -1);
    ctxt.key_equal = xmlDictLookup(ctxt.dict, BAD_CAST "=", 1);
    ctxt.key_orequal = xmlDictLookup(ctxt.dict, BAD_CAST "|=", 2);
    ctxt.key_andequal = xmlDictLookup(ctxt.dict, BAD_CAST "&=", 2);
    ctxt.key_combine = xmlDictLookup(ctxt.dict, BAD_CAST "&=", 2);
    ctxt.key_or = xmlDictLookup(ctxt.dict, BAD_CAST "|", 1);
    ctxt.key_comma = xmlDictLookup(ctxt.dict, BAD_CAST ",", 1);
    ctxt.key_and = xmlDictLookup(ctxt.dict, BAD_CAST "&", 1);
    ctxt.key_choice = xmlDictLookup(ctxt.dict, BAD_CAST "choice", -1);
    ctxt.key_group = xmlDictLookup(ctxt.dict, BAD_CAST "group", -1);
    ctxt.key_interleave = xmlDictLookup(ctxt.dict, BAD_CAST "interleave", -1);
    ctxt.key_ref = xmlDictLookup(ctxt.dict, BAD_CAST "ref", 3);
    ctxt.key_define = xmlDictLookup(ctxt.dict, BAD_CAST "define", 6);

    /* xmlConvertCRNGTokenize(&ctxt); */
    xmlConvertCRNG_topLevel(&ctxt);

    xmlDictFree(ctxt.dict);

    ret = ctxt.doc;
    return(ret);
}

/**
 * xmlConvertCRNGFile:
 * @URL: URL or filename for the resource
 * @encoding:  encoding indicated by the context or NULL
 *
 * Compiles the schemas into the equivalent Relax-NG XML structure
 *
 * Returns the xmlDocPtr resulting from the compilation or
 *         NULL in case of error
 */
xmlDocPtr
xmlConvertCRNGFile(const char *URL, const char *encoding) {
}

#ifdef STANDALONE
const xmlChar *schemas =
"# RELAX NG XML syntax specified in compact syntax.\n\
\n\
default namespace rng = \"http://relaxng.org/ns/structure/1.0\"\n\
namespace local = \"\"\n\
datatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\
\n\
start = pattern\n\
\n\
pattern =\n\
  element element { (nameQName | nameClass), (common & pattern+) }\n\
  | element attribute { (nameQName | nameClass), (common & pattern?) }\n\
  | element group|interleave|choice|optional\n\
            |zeroOrMore|oneOrMore|list|mixed { common & pattern+ }\n\
  | element ref|parentRef { nameNCName, common }\n\
  | element empty|notAllowed|text { common }\n\
  | element data { type, param*, (common & exceptPattern?) }\n\
  | element value { commonAttributes, type?, xsd:string }\n\
  | element externalRef { href, common }\n\
  | element grammar { common & grammarContent* }\n\
\n\
param = element param { commonAttributes, nameNCName, xsd:string }\n\
\n\
exceptPattern = element except { common & pattern+ }\n\
\n\
grammarContent =\n\
  definition\n\
  | element div { common & grammarContent* }\n\
  | element include { href, (common & includeContent*) }\n\
\n\
includeContent =\n\
  definition\n\
  | element div { common & includeContent* }\n\
\n\
definition =\n\
  element start { combine?, (common & pattern+) }\n\
  | element define { nameNCName, combine?, (common & pattern+) }\n\
\n\
combine = attribute combine { \"choice\" | \"interleave\" }\n\
\n\
nameClass =\n\
  element name { commonAttributes, xsd:QName }\n\
  | element anyName { common & exceptNameClass? }\n\
  | element nsName { common & exceptNameClass? }\n\
  | element choice { common & nameClass+ }\n\
\n\
exceptNameClass = element except { common & nameClass+ }\n\
\n\
nameQName = attribute name { xsd:QName }\n\
nameNCName = attribute name { xsd:NCName }\n\
href = attribute href { xsd:anyURI }\n\
type = attribute type { xsd:NCName }\n\
\n\
common = commonAttributes, foreignElement*\n\
\n\
commonAttributes =\n\
  attribute ns { xsd:string }?,\n\
  attribute datatypeLibrary { xsd:anyURI }?,\n\
  foreignAttribute*\n\
\n\
foreignElement = element * - rng:* { (anyAttribute | text | anyElement)* }\n\
foreignAttribute = attribute * - (rng:*|local:*) { text }\n\
anyElement = element * { (anyAttribute | text | anyElement)* }\n\
anyAttribute = attribute * { text }\n\
";

int main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
    xmlDocPtr res;

    res = xmlConvertCRNG(schemas, -1);
    if (res != NULL) {
        xmlDocFormatDump(stdout, res, 1);
	xmlFreeDoc(res);
    }
    return(0);
}
#endif
