blob: 67a25b7f34067603c974390e84b23bd3bbec2017 [file] [log] [blame]
Daniel Veillardd7306b02004-01-05 23:11:54 +00001/**
2 * rngparser.c: parser for the Relax-NG compact syntax.
3 *
4 * See Copyright for the status of this software.
5 *
6 * Daniel Veillard <veillard@redhat.com>
7 */
8
9#include <string.h>
10
11#include <libxml/parser.h>
12#include <libxml/parserInternals.h>
13#include <libxml/relaxng.h>
14#include <libxml/dict.h>
15
16#define TODO \
17 xmlGenericError(xmlGenericErrorContext, \
18 "Unimplemented block at %s:%d\n", \
19 __FILE__, __LINE__);
20
21#define MAX_TOKEN 10
22
23typedef enum {
24 CRNG_NONE = 0,
25 CRNG_OP = 1,
26 CRNG_KEYWORD,
27 CRNG_IDENTIFIER,
28 CRNG_LITERAL_SEGMENT,
29 CRNG_CNAME,
30 CRNG_QNAME,
31 CRNG_NSNAME,
32 CRNG_DOCUMENTATION
33} xmlCRNGTokType;
34
35typedef enum {
36 CRNG_OKAY = 0,
37 CRNG_MEMORY_ERROR,
38 CRNG_INVALID_CHAR_ERROR,
39 CRNG_END_ERROR,
40 CRNG_ENCODING_ERROR
41} xmlCRNGError;
42
43typedef enum {
44 XML_CRNG_ERROR = -1,
45 XML_CRNG_OK = 0,
46 XML_CRNG_EOF = 1
47} xmlCRelaxNGParserState;
48
49typedef struct _token _token;
50typedef _token *tokenPtr;
51struct _token {
52 xmlCRNGTokType toktype;
53 int toklen;
54 const xmlChar *token;
55 const xmlChar *prefix;
56};
57
58typedef struct _xmlCRelaxNGParserCtxt xmlCRelaxNGParserCtxt;
59typedef xmlCRelaxNGParserCtxt *xmlCRelaxNGParserCtxtPtr;
60struct _xmlCRelaxNGParserCtxt {
61 void *userData; /* user specific data block */
62 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
63 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
64 xmlRelaxNGValidErr err;
65
66 const xmlChar *compact;
67 const xmlChar *end;
68 const xmlChar *cur;
69 int isElem;
70 int lineno;
71 const xmlChar *linestart;
72 const char *filename;
73
74 int nbTokens;
75 int firstToken;
76 _token tokens[MAX_TOKEN];
77 int totalToken;
78
79 xmlCRelaxNGParserState state;
80
81 int nbErrors;
82
83 xmlDocPtr res; /* the result */
84 xmlNodePtr ins; /* the current insertion node */
85
86 xmlNsPtr nsDef;
87 tokenPtr token;
88
89 xmlHashTablePtr namespaces;
90 xmlHashTablePtr datatypes;
91
92 /*
93 * dictionnary and keywords
94 */
95 xmlDictPtr dict;
96 const xmlChar *key_attribute;
97 const xmlChar *key_default;
98 const xmlChar *key_datatypes;
99 const xmlChar *key_div;
100 const xmlChar *key_element;
101 const xmlChar *key_empty;
102 const xmlChar *key_external;
103 const xmlChar *key_grammar;
104 const xmlChar *key_include;
105 const xmlChar *key_inherit;
106 const xmlChar *key_list;
107 const xmlChar *key_mixed;
108 const xmlChar *key_namespace;
109 const xmlChar *key_notAllowed;
110 const xmlChar *key_parent;
111 const xmlChar *key_start;
112 const xmlChar *key_string;
113 const xmlChar *key_text;
114 const xmlChar *key_token;
115 const xmlChar *key_equal;
116 const xmlChar *key_orequal;
117 const xmlChar *key_andequal;
118 const xmlChar *key_combine;
119 const xmlChar *key_or;
120 const xmlChar *key_comma;
121 const xmlChar *key_and;
122 const xmlChar *key_choice;
123 const xmlChar *key_group;
124 const xmlChar *key_interleave;
125 const xmlChar *key_ref;
126 const xmlChar *key_define;
127
128 /* results */
129 xmlDocPtr doc; /* the resulting doc */
130 xmlNodePtr insert; /* the insertion point */
131 xmlAttrPtr attrs; /* pending attributes */
132};
133
134static const xmlChar *xmlCRelaxNGInherit = BAD_CAST "Inherit string";
135static const xmlChar *xmlCRelaxNGDefault = BAD_CAST "Default string";
136
137#define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
138/**
139 * IS_BLANK:
140 * @c: an UNICODE value (int)
141 *
142 * Macro to check the following production in the XML spec:
143 *
144 * [3] S ::= (#x20 | #x9 | #xD | #xA)+
145 */
146#ifndef IS_BLANK
147#define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
148 ((c) == 0x0D))
149#endif
150#define IS_SEPARATOR(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
151 ((c) == 0x0D) || (c == '#'))
152
153#define CRNG_ERROR0(X) \
154 { xmlCRNGErr(ctxt, X, NULL); return(0); }
155#define CRNG_ERROR(X) \
156 { xmlCRNGErr(ctxt, X, NULL); }
157
158#define CRNG_MEM_ERROR0() \
159 { xmlCRNGErr(ctxt, CRNG_MEMORY_ERROR, NULL); return(0); }
160#define CRNG_MEM_ERROR() \
161 { xmlCRNGErr(ctxt, CRNG_MEMORY_ERROR, NULL); }
162
163#define ERROR(str) xmlCRNGErr(ctxt, 0, str);
164
165static void
166xmlCRNGErr(xmlCRelaxNGParserCtxtPtr ctxt, int err_no, const char *err_msg) {
167 const xmlChar *cur;
168 xmlChar buffer[150];
169 int i, l;
170
171 if (ctxt != NULL) {
172 if (ctxt->filename != NULL)
173 fprintf(stderr, "%s:%d ", ctxt->filename, ctxt->lineno);
174 }
175 if (err_msg != NULL) {
176 fprintf(stderr, "error: %s\n", err_msg);
177 } else if (err_no != 0)
178 fprintf(stderr, "error %d\n", err_no);
179 cur = ctxt->cur;
180 while ((*cur != '\n') && (*cur != '\r') && (ctxt->cur - cur < 80)) cur--;
181 l = ctxt->cur - cur;
182 cur++;
183 for (i = 0; i < 100;i++) {
184 if ((*cur == '\n') || (*cur == '\r')) break;
185 buffer[i] = *cur++;
186 }
187 buffer[i] = 0;
188 fprintf(stderr, "%s\n", buffer);
189 for (i = 0; i < l;i++) buffer[i] = ' ';
190 buffer[i++] = '^';
191 buffer[i++] = 0;
192 fprintf(stderr, "%s\n", buffer);
193}
194
195/**
196 * IS_OP
197 * @c: an UNICODE value (int)
198 *
199 * Macro to check for operator value
200 */
201#ifndef IS_OP
202#define IS_OP(c) (((c) == ',') || ((c) == '&') || ((c) == '|') || \
203 ((c) == '?') || ((c) == '-') || ((c) == '*') || \
204 ((c) == '{') || ((c) == '}') || ((c) == '(') || \
205 ((c) == ')') || ((c) == '+') || ((c) == '=') || \
206 ((c) == ':'))
207#endif
208
209static int
210xmlCRNGIsKeyword(xmlCRelaxNGParserCtxtPtr ctxt, const xmlChar *str) {
211 if ((str == ctxt->key_attribute) ||
212 (str == ctxt->key_default) ||
213 (str == ctxt->key_datatypes) ||
214 (str == ctxt->key_div) ||
215 (str == ctxt->key_element) ||
216 (str == ctxt->key_empty) ||
217 (str == ctxt->key_external) ||
218 (str == ctxt->key_grammar) ||
219 (str == ctxt->key_include) ||
220 (str == ctxt->key_inherit) ||
221 (str == ctxt->key_list) ||
222 (str == ctxt->key_mixed) ||
223 (str == ctxt->key_namespace) ||
224 (str == ctxt->key_notAllowed) ||
225 (str == ctxt->key_parent) ||
226 (str == ctxt->key_start) ||
227 (str == ctxt->key_string) ||
228 (str == ctxt->key_text) ||
229 (str == ctxt->key_token))
230 return(1);
231 return(0);
232
233}
234
235/*
236 * xmlCRNGNextToken:
237 * ctxt: a compact RNG parser context
238 *
239 * Scan the schema to get the next token
240 *
241 * Return 0 if success and -1 in case of error
242 */
243
244static int
245xmlCRNGNextToken(xmlCRelaxNGParserCtxtPtr ctxt) {
246 const xmlChar *cur;
247 tokenPtr token;
248
249 if (ctxt == NULL) return(-1);
250 if (ctxt->nbTokens >= MAX_TOKEN) return(-1);
251 token = &(ctxt->tokens[(ctxt->firstToken + ctxt->nbTokens) % MAX_TOKEN]);
252 token->toktype = CRNG_NONE;
253
254 if (ctxt->cur == NULL) {
255 ctxt->cur = ctxt->compact;
256 }
257retry:
258 if (ctxt->cur >= ctxt->end) {
259 ctxt->state = XML_CRNG_EOF;
260 return(-1);
261 }
262 while ((ctxt->cur < ctxt->end) &&
263 (IS_BLANK(*ctxt->cur))) ctxt->cur++;
264 if (ctxt->cur >= ctxt->end) {
265 ctxt->state = XML_CRNG_EOF;
266 return(-1);
267 }
268 if (*ctxt->cur == '#') {
269 cur = ctxt->cur;
270 cur++;
271 while ((cur < ctxt->end) && (*cur != '\n') && (*cur != '\r'))
272 cur++;
273 ctxt->cur = cur;
274 goto retry;
275 } else if (*ctxt->cur == '"') {
276 /* string, check for '"""' */
277 ctxt->cur++;
278 if (ctxt->cur >= ctxt->end) goto eof;
279 cur = ctxt->cur;
280 if ((ctxt->end - ctxt->end > 2) &&
281 (*cur == '"') && (cur[1] == '"')) {
282 TODO
283 } else {
284 while ((cur < ctxt->end) && (*cur != '"')) cur++;
285 if (cur >= ctxt->end) goto eof;
286 token->toklen = cur - ctxt->cur;
287 token->token = xmlDictLookup(ctxt->dict, ctxt->cur, token->toklen);
288 token->toktype = CRNG_LITERAL_SEGMENT;
289 token->prefix = NULL;
290 cur++;
291 ctxt->cur = cur;
292 }
293 } else if (*ctxt->cur == '\'') {
294 /* string, check for "'''" */
295 TODO
296 } else if ((IS_OP(*ctxt->cur)) || (*ctxt->cur == ':')) {
297 cur = ctxt->cur;
298 cur++;
299 if ((cur < ctxt->end) &&
300 (((*cur == '=') &&
301 ((*ctxt->cur == '|') || (*ctxt->cur == '&'))) ||
302 ((*cur == '*') && (*ctxt->cur == ':')))) {
303 token->toklen = 2;
304 } else {
305 token->toklen = 1;
306 }
307 token->token = xmlDictLookup(ctxt->dict, ctxt->cur, token->toklen);
308 token->toktype = CRNG_OP;
309 token->prefix = NULL;
310 ctxt->cur += token->toklen;
311 } else {
312 int escape = 0;
313
314 cur = ctxt->cur;
315 if (*cur == '\\') {
316 escape = 1;
317 cur++;
318 ctxt->cur++;
319 }
320 while ((cur < ctxt->end) &&
321 (!(IS_SEPARATOR(*cur))) && (!(IS_OP(*cur)))) cur++;
322
323 token->toklen = cur - ctxt->cur;
324 token->token = xmlDictLookup(ctxt->dict, ctxt->cur, token->toklen);
325 token->prefix = NULL;
326 ctxt->cur = cur;
327 if ((escape == 0) && (xmlCRNGIsKeyword(ctxt, token->token)))
328 token->toktype = CRNG_KEYWORD;
329 else {
330 token->toktype = CRNG_IDENTIFIER;
331 }
332 if (*ctxt->cur == ':') {
333 ctxt->cur++;
334 if (*ctxt->cur == '*') {
335 ctxt->cur++;
336 token->toktype = CRNG_NSNAME;
337 } else {
338 cur = ctxt->cur;
339 while ((cur < ctxt->end) &&
340 (!(IS_SEPARATOR(*cur))) && (!(IS_OP(*cur)))) cur++;
341 token->prefix = token->token;
342 token->toklen = cur - ctxt->cur;
343 token->token = xmlDictLookup(ctxt->dict, ctxt->cur,
344 token->toklen);
345 ctxt->cur = cur;
346 if (xmlValidateNCName(token->token, 0) == 0)
347 token->toktype = CRNG_QNAME;
348 else {
349 TODO /* sounds like an error ! */
350 token->toktype = CRNG_IDENTIFIER;
351 }
352 }
353 }
354 }
355 ctxt->nbTokens++;
356 return(0);
357eof:
358 ctxt->state = XML_CRNG_EOF;
359 CRNG_ERROR(CRNG_END_ERROR);
360 return(-1);
361}
362
363/**
364 * xmlParseCRNGGetToken:
365 * @ctxt: a compact RNG parser context
366 * @no: the number of the token from 1 for the first one
367 * and 2, 3 ... for read-ahead
368 *
369 * Token reading interface
370 *
371 * returns a pointer to the new token, or NULL in case of error or EOF
372 */
373static tokenPtr
374xmlParseCRNGGetToken(xmlCRelaxNGParserCtxtPtr ctxt, int no) {
375 tokenPtr ret;
376 int res;
377
378 if ((no <= 0) || (no >= MAX_TOKEN)) return(NULL);
379 no--;
380 while (ctxt->nbTokens <= no) {
381 res = xmlCRNGNextToken(ctxt);
382 if (res < 0)
383 return(NULL);
384 }
385 ret = &(ctxt->tokens[(ctxt->firstToken + no) % MAX_TOKEN]);
386 return(ret);
387}
388
389/**
390 * xmlParseCRNGDropTokens:
391 * @ctxt: a compact RNG parser context
392 * @nr: the number of token marked as read
393 *
394 * mark a number of token as read and consumed.
395 *
396 * Returns -1 in case of error and 0 otherwise
397 */
398static int
399xmlParseCRNGDropTokens(xmlCRelaxNGParserCtxtPtr ctxt, int nr) {
400 if ((nr <= 0) || (nr >= MAX_TOKEN)) return(-1);
401 while ((ctxt->nbTokens >0) && (nr > 0)) {
402 ctxt->firstToken++;
403 nr--;
404 ctxt->nbTokens--;
405 ctxt->totalToken++;
406 if (ctxt->totalToken == 384)
407 fprintf(stderr, "found\n");
408 }
409 ctxt->firstToken = ctxt->firstToken % MAX_TOKEN;
410 return(0);
411}
412
413static void
414xmlParseCRNGTokenize(xmlCRelaxNGParserCtxtPtr ctxt) {
415 tokenPtr token;
416
417 token = xmlParseCRNGGetToken(ctxt, 1);
418 while (token != NULL) {
419 switch (token->toktype) {
420 case CRNG_NONE: printf("none"); break;
421 case CRNG_OP: printf("op"); break;
422 case CRNG_KEYWORD: printf("keyword"); break;
423 case CRNG_IDENTIFIER: printf("identifier"); break;
424 case CRNG_LITERAL_SEGMENT: printf("literal"); break;
425 case CRNG_CNAME: printf("cname"); break;
426 case CRNG_QNAME: printf("qname"); break;
427 case CRNG_NSNAME: printf("nsname"); break;
428 case CRNG_DOCUMENTATION: printf("doc"); break;
429 }
430 printf(":%s\n", token->token);
431 xmlParseCRNGDropTokens(ctxt, 1);
432 token = xmlParseCRNGGetToken(ctxt, 1);
433 }
434}
435
436/**
437 * xmlParseCRNG_attribute:
438 * @ctxt: a compact RNG parser context
439 * @name: the attribute name
440 * @ns: the attribute namespace
441 * @value: the attribute value
442 *
443 * implements attribute of the RELAX NG Compact Syntax Appendix A
444 *
445 * Returns 0 in case of success and -1 in case of error
446 */
447static int
448xmlParseCRNG_attribute(xmlCRelaxNGParserCtxtPtr ctxt,
449 const xmlChar *name,
450 xmlNsPtr ns,
451 const xmlChar *value)
452{
453 xmlAttrPtr attr;
454
455 attr = xmlNewNsPropEatName(NULL, ns, (xmlChar *) name, value);
456 if (attr == NULL) CRNG_MEM_ERROR0();
457 attr->next = ctxt->attrs;
458 if (ctxt->attrs != NULL)
459 ctxt->attrs->prev = attr;
460 ctxt->attrs = attr;
461 return(0);
462}
463
464/**
465 * xmlParseCRNG_bindPrefix:
466 * @ctxt: a compact RNG parser context
467 * @prefix: the namespace prefix or NULL
468 * @namespace: the namespace name
469 *
470 * implements bindPrefix of the RELAX NG Compact Syntax Appendix A
471 *
472 * Returns 0 in case of success and -1 in case of error
473 */
474static int
475xmlParseCRNG_bindPrefix(xmlCRelaxNGParserCtxtPtr ctxt,
476 const xmlChar *prefix,
477 const xmlChar *namespace)
478{
479 int ret;
480
481 if ((prefix != NULL) && (xmlStrEqual(prefix, BAD_CAST "xml")) &&
482 (!xmlStrEqual(namespace, XML_XML_NAMESPACE))) {
483 ERROR("The \"xml\" prefix must be bound to \"http://www.w3.org/XML/1998/namespace\"");
484 return(-1);
485 } else if ((xmlStrEqual(namespace, XML_XML_NAMESPACE)) &&
486 (!xmlStrEqual(prefix, BAD_CAST "xml"))) {
487 ERROR("The \"http://www.w3.org/XML/1998/namespace\" name must be bound to \"xml\" prefix");
488 return(-1);
489 }
490 if (ctxt->namespaces == NULL)
491 ctxt->namespaces = xmlHashCreate(10);
492 if (ctxt->namespaces == NULL) {
493 ERROR("Failed to create namespace hash table");
494 return(-1);
495 }
496 if (prefix == NULL)
497 ret = xmlHashAddEntry(ctxt->namespaces, xmlCRelaxNGDefault,
498 (void *) namespace);
499 else
500 ret = xmlHashAddEntry(ctxt->namespaces, prefix,
501 (void *) namespace);
502 if (ret < 0) {
503 if (prefix == NULL) {
504 ERROR("Redefinition of default namespace");
505 } else {
506 ERROR("Redefinition of namespace");
507 }
508 return(-1);
509 }
510
511 return(0);
512}
513
514/**
515 * xmlParseCRNG_bindDatatypePrefix:
516 * @ctxt: a compact RNG parser context
517 * @prefix: the datatype prefix
518 * @namespace: the datatype identifier
519 *
520 * implements bindDatatypePrefix of the RELAX NG Compact Syntax Appendix A
521 *
522 * Returns 0 in case of success and -1 in case of error
523 */
524static int
525xmlParseCRNG_bindDatatypePrefix(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
526 const xmlChar *prefix,
527 const xmlChar *namespace)
528{
529 int ret;
530
531 if ((prefix != NULL) && (xmlStrEqual(prefix, BAD_CAST "xsd")) &&
532 (!xmlStrEqual(namespace,
533 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes"))) {
534 ERROR("The \"xsd\" prefix must be bound to \"http://www.w3.org/2001/XMLSchema-datatypes\"");
535 return(-1);
536 }
537 if (ctxt->datatypes == NULL)
538 ctxt->datatypes = xmlHashCreate(10);
539 if (ctxt->datatypes == NULL) {
540 ERROR("Failed to create namespace hash table");
541 return(-1);
542 }
543 ret = xmlHashAddEntry(ctxt->datatypes, prefix,
544 (void *) namespace);
545 if (ret < 0) {
546 ERROR("Redefinition of datatype");
547 return(-1);
548 }
549 return(0);
550}
551
552/**
553 * xmlParseCRNG_lookupPrefix:
554 * @ctxt: a compact RNG parser context
555 * @prefix: the namespace prefix or NULL
556 *
557 * implements lookupPrefix of the RELAX NG Compact Syntax Appendix A
558 *
559 * Returns the prefix in case of success or NULL in case of error
560 */
561static const xmlChar *
562xmlParseCRNG_lookupPrefix(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
563 const xmlChar *prefix)
564{
565 const xmlChar *ret;
566
567 if (prefix == NULL)
568 ret = xmlHashLookup(ctxt->namespaces, xmlCRelaxNGDefault);
569 else
570 ret = xmlHashLookup(ctxt->namespaces, prefix);
571 return(ret);
572}
573
574/**
575 * xmlParseCRNG_lookupDatatypePrefix:
576 * @ctxt: a compact RNG parser context
577 * @prefix: the namespace prefix or NULL
578 *
579 * implements lookupDatatypePrefix of the RELAX NG Compact Syntax Appendix A
580 *
581 * Returns the prefix in case of success or NULL in case of error
582 */
583static const xmlChar *
584xmlParseCRNG_lookupDatatypePrefix(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
585 const xmlChar *prefix)
586{
587 const xmlChar *ret;
588 ret = xmlHashLookup(ctxt->datatypes, prefix);
589 return(ret);
590}
591
592/**
593 * xmlParseCRNG_datatypeAttributes:
594 * @ctxt: a compact RNG parser context
595 * @prefix: the namespace prefix or NULL
596 *
597 * implements lookupPrefix of the RELAX NG Compact Syntax Appendix A
598 *
599 * Returns the prefix in case of success or NULL in case of error
600 */
601static xmlAttrPtr
602xmlParseCRNG_datatypeAttributes(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
603 const xmlChar *library, const xmlChar *type)
604{
605 xmlAttrPtr lib, typ;
606
607 lib = xmlNewNsProp(NULL, NULL, BAD_CAST "datatypeLibrary", library);
608 if (lib == NULL) {
609 CRNG_MEM_ERROR();
610 return(NULL);
611 }
612 typ = xmlNewNsProp(NULL, NULL, BAD_CAST "type", type);
613 if (typ == NULL) {
614 CRNG_MEM_ERROR();
615 return(lib);
616 }
617 lib->next = typ;
618
619 return(lib);
620}
621
622/**
623 * xmlParseCRNG_XXX:
624 * @ctxt: a compact RNG parser context
625 *
626 * Parse XXX of the RELAX NG Compact Syntax Appendix A
627 *
628 * Returns 0 in case of success and -1 in case of error
629 */
630static int
631xmlParseCRNG_XXX(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
632{
633 return(0);
634}
635
636static int xmlParseCRNG_pattern(xmlCRelaxNGParserCtxtPtr ctxt);
637static int xmlParseCRNG_nameClass(xmlCRelaxNGParserCtxtPtr ctxt);
638
639/**
640 * xmlParseCRNG_params:
641 * @ctxt: a compact RNG parser context
642 *
643 * Parse params of the RELAX NG Compact Syntax Appendix A
644 *
645 * Returns 0 in case of success and -1 in case of error
646 */
647static int
648xmlParseCRNG_params(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
649{
650 TODO
651 return(0);
652}
653
654/**
655 * xmlParseCRNG_exceptNameClass:
656 * @ctxt: a compact RNG parser context
657 *
658 * Parse exceptNameClass of the RELAX NG Compact Syntax Appendix A
659 *
660 * Returns 0 in case of success and -1 in case of error
661 */
662static int
663xmlParseCRNG_exceptNameClass(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
664{
665 tokenPtr token;
666 xmlNodePtr insert = ctxt->insert, cur;
667
668 token = xmlParseCRNGGetToken(ctxt, 1);
669 if ((token->toktype == CRNG_OP) &&
670 (token->token[0] == '-') && (token->token[1] == 0)) {
671 xmlParseCRNGDropTokens(ctxt, 1);
672 cur = xmlNewNode(NULL, BAD_CAST "except");
673 if (cur == NULL) CRNG_MEM_ERROR0();
674 if (ctxt->insert != NULL)
675 xmlAddChild(ctxt->insert, cur);
676 ctxt->insert = cur;
677 xmlParseCRNG_nameClass(ctxt);
678 }
679 ctxt->insert = insert;
680 return(0);
681}
682
683/**
684 * xmlParseCRNG_innerNameClass:
685 * @ctxt: a compact RNG parser context
686 *
687 * Parse innerNameClass of the RELAX NG Compact Syntax Appendix A
688 *
689 * Returns 0 in case of success and -1 in case of error
690 */
691static int
692xmlParseCRNG_innerNameClass(xmlCRelaxNGParserCtxtPtr ctxt)
693{
694 tokenPtr token;
695 xmlNodePtr cur;
696
697 token = xmlParseCRNGGetToken(ctxt, 1);
698 if (token->toktype == CRNG_OP) {
699 if ((token->token[0] == '(') && (token->token[1] == 0)) {
700 xmlParseCRNGDropTokens(ctxt, 1);
701 xmlParseCRNG_nameClass(ctxt);
702 token = xmlParseCRNGGetToken(ctxt, 1);
703 if ((token->toktype != CRNG_OP) ||
704 (token->token[0] != ')') || (token->token[1] != 0)) {
705 ERROR("Expecting \")\" here");
706 }
707 xmlParseCRNGDropTokens(ctxt, 1);
708 } else if ((token->token[0] == '*') && (token->token[1] == 0)) {
709 xmlParseCRNGDropTokens(ctxt, 1);
710 cur = xmlNewNode(NULL, BAD_CAST "anyName");
711 if (cur == NULL) CRNG_MEM_ERROR0();
712 if (ctxt->insert != NULL)
713 xmlAddChild(ctxt->insert, cur);
714 ctxt->insert = cur;
715 xmlParseCRNG_exceptNameClass(ctxt);
716 } else {
717 TODO
718 }
719 } else if ((token->toktype == CRNG_IDENTIFIER) ||
720 (token->toktype == CRNG_KEYWORD)) {
721 cur = xmlNewNode(NULL, BAD_CAST "name");
722 if (cur == NULL) CRNG_MEM_ERROR0();
723 if (ctxt->isElem) {
724 xmlSetProp(cur, BAD_CAST "ns",
725 xmlParseCRNG_lookupPrefix(ctxt, NULL));
726 } else {
727 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
728 }
729 xmlNodeAddContent(cur, token->token);
730 if (ctxt->insert != NULL)
731 xmlAddChild(ctxt->insert, cur);
732 ctxt->insert = cur;
733 xmlParseCRNGDropTokens(ctxt, 1);
734 } else if (token->toktype == CRNG_CNAME) {
735 TODO
736 } else if (token->toktype == CRNG_NSNAME) {
737 cur = xmlNewNode(NULL, BAD_CAST "nsName");
738 if (cur == NULL) CRNG_MEM_ERROR0();
739 xmlSetProp(cur, BAD_CAST "ns",
740 xmlParseCRNG_lookupPrefix(ctxt, token->token));
741 if (ctxt->insert != NULL)
742 xmlAddChild(ctxt->insert, cur);
743 ctxt->insert = cur;
744 xmlParseCRNGDropTokens(ctxt, 1);
745 xmlParseCRNG_exceptNameClass(ctxt);
746 } else {
747 TODO /* probably an error */
748 }
749
750 return(0);
751}
752
753/**
754 * xmlParseCRNG_nameClass:
755 * @ctxt: a compact RNG parser context
756 *
757 * Parse nameClass of the RELAX NG Compact Syntax Appendix A
758 *
759 * Returns 0 in case of success and -1 in case of error
760 */
761static int
762xmlParseCRNG_nameClass(xmlCRelaxNGParserCtxtPtr ctxt)
763{
764 tokenPtr token;
765 xmlNodePtr insert = ctxt->insert, last, choice;
766
767 ctxt->insert = NULL;
768 xmlParseCRNG_innerNameClass(ctxt);
769 last = ctxt->insert;
770 token = xmlParseCRNGGetToken(ctxt, 1);
771 while ((token->toktype == CRNG_OP) &&
772 (token->token[0] == '|') && (token->token[1] == 0)) {
773 choice = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_choice);
774 xmlParseCRNGDropTokens(ctxt, 1);
775 if (choice == NULL) CRNG_MEM_ERROR0();
776 ctxt->insert = NULL;
777 xmlParseCRNG_innerNameClass(ctxt);
778 xmlAddChild(choice, last);
779 xmlAddChild(choice, ctxt->insert);
780 last = choice;
781 token = xmlParseCRNGGetToken(ctxt, 1);
782 }
783 xmlAddChild(insert, last);
784
785 ctxt->insert = insert;
786 return(0);
787}
788
789/**
790 * xmlParseCRNG_patternBlock:
791 * @ctxt: a compact RNG parser context
792 *
793 * Parse a pattern block of the RELAX NG Compact Syntax Appendix A
794 *
795 * Returns 0 in case of success and -1 in case of error
796 */
797static int
798xmlParseCRNG_patternBlock(xmlCRelaxNGParserCtxtPtr ctxt)
799{
800 tokenPtr token;
801
802 token = xmlParseCRNGGetToken(ctxt, 1);
803 if ((token->toktype != CRNG_OP) ||
804 (token->token[0] != '{') || (token->token[1] != 0)) {
805 ERROR("Expecting \"{\" here");
806 }
807 xmlParseCRNGDropTokens(ctxt, 1);
808 xmlParseCRNG_pattern(ctxt);
809 token = xmlParseCRNGGetToken(ctxt, 1);
810 if ((token->toktype != CRNG_OP) ||
811 (token->token[0] != '}') || (token->token[1] != 0)) {
812 ERROR("Expecting \"}\" here");
813 }
814 xmlParseCRNGDropTokens(ctxt, 1);
815 return(0);
816}
817
818/**
819 * xmlParseCRNG_datatype:
820 * @ctxt: a compact RNG parser context
821 *
822 * Parse datatype of the RELAX NG Compact Syntax Appendix A
823 *
824 * Returns 0 in case of success and -1 in case of error
825 */
826static int
827xmlParseCRNG_datatype(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
828{
829 tokenPtr token;
830 xmlAttrPtr attrs = NULL;
831
832 token = xmlParseCRNGGetToken(ctxt, 1);
833 if (token->toktype == CRNG_KEYWORD) {
834 if (token->token == ctxt->key_string) {
835 attrs = xmlParseCRNG_datatypeAttributes(ctxt, BAD_CAST "",
836 token->token);
837 xmlParseCRNGDropTokens(ctxt, 1);
838 } else if (token->token == ctxt->key_token) {
839 attrs = xmlParseCRNG_datatypeAttributes(ctxt, BAD_CAST "",
840 token->token);
841 xmlParseCRNGDropTokens(ctxt, 1);
842 } else {
843 TODO /* probably an error */
844 }
845 } else if (token->toktype == CRNG_LITERAL_SEGMENT) {
846 ctxt->insert = xmlNewNode(NULL, BAD_CAST "value");
847 xmlParseCRNGDropTokens(ctxt, 1);
848 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
849 xmlNodeAddContent(ctxt->insert, token->token);
850 } else if (token->toktype == CRNG_QNAME) {
851 attrs = xmlParseCRNG_datatypeAttributes(ctxt,
852 xmlParseCRNG_lookupDatatypePrefix(ctxt, token->prefix),
853 token->token);
854 } else {
855 TODO
856 }
857 if (attrs != NULL) {
858 token = xmlParseCRNGGetToken(ctxt, 1);
859 if (token->toktype == CRNG_LITERAL_SEGMENT) {
860 ctxt->insert = xmlNewNode(NULL, BAD_CAST "value");
861 xmlParseCRNGDropTokens(ctxt, 1);
862 if (ctxt->insert == NULL) {
863 xmlFreePropList(attrs);
864 CRNG_MEM_ERROR0();
865 }
866 ctxt->insert->properties = attrs;
867 xmlNodeAddContent(ctxt->insert, token->token);
868 } else if ((token->toktype == CRNG_OP) &&
869 (token->token[0] == '{') && (token->token[0] == 0)) {
870 ctxt->insert = xmlNewNode(NULL, BAD_CAST "data");
871 xmlParseCRNGDropTokens(ctxt, 1);
872 if (ctxt->insert == NULL) {
873 xmlFreePropList(attrs);
874 CRNG_MEM_ERROR0();
875 }
876 ctxt->insert->properties = attrs;
877 xmlParseCRNG_params(ctxt);
878 } else {
879 ctxt->insert = xmlNewNode(NULL, BAD_CAST "data");
880 xmlParseCRNGDropTokens(ctxt, 1);
881 if (ctxt->insert == NULL) {
882 xmlFreePropList(attrs);
883 CRNG_MEM_ERROR0();
884 }
885 ctxt->insert->properties = attrs;
886 xmlNodeAddContent(ctxt->insert, token->token);
887 }
888 }
889 return(0);
890}
891
892/**
893 * xmlParseCRNG_primary:
894 * @ctxt: a compact RNG parser context
895 *
896 * Parse primary of the RELAX NG Compact Syntax Appendix A
897 *
898 * Returns 0 in case of success and -1 in case of error
899 */
900static int
901xmlParseCRNG_primary(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
902{
903 tokenPtr token;
904
905 token = xmlParseCRNGGetToken(ctxt, 1);
906 if (token == NULL)
907 return(0);
908 if (token->toktype == CRNG_KEYWORD) {
909 if (token->token == ctxt->key_element) {
910 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
911 xmlParseCRNGDropTokens(ctxt, 1);
912 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
913 ctxt->isElem = 1;
914 xmlParseCRNG_nameClass(ctxt);
915 xmlParseCRNG_patternBlock(ctxt);
916 } else if (token->token == ctxt->key_attribute) {
917 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
918 xmlParseCRNGDropTokens(ctxt, 1);
919 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
920 ctxt->isElem = 0;
921 xmlParseCRNG_nameClass(ctxt);
922 xmlParseCRNG_patternBlock(ctxt);
923 } else if (token->token == ctxt->key_mixed) {
924 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
925 xmlParseCRNGDropTokens(ctxt, 1);
926 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
927 xmlParseCRNG_patternBlock(ctxt);
928 } else if (token->token == ctxt->key_list) {
929 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
930 xmlParseCRNGDropTokens(ctxt, 1);
931 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
932 xmlParseCRNG_patternBlock(ctxt);
933 } else if (token->token == ctxt->key_empty) {
934 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
935 xmlParseCRNGDropTokens(ctxt, 1);
936 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
937 } else if (token->token == ctxt->key_notAllowed) {
938 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
939 xmlParseCRNGDropTokens(ctxt, 1);
940 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
941 } else if (token->token == ctxt->key_text) {
942 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
943 xmlParseCRNGDropTokens(ctxt, 1);
944 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
945 } else if (token->token == ctxt->key_parent) {
946 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
947 xmlParseCRNGDropTokens(ctxt, 1);
948 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
949 TODO
950 } else if (token->token == ctxt->key_grammar) {
951 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
952 xmlParseCRNGDropTokens(ctxt, 1);
953 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
954 TODO
955 } else if (token->token == ctxt->key_external) {
956 ctxt->insert = xmlNewNode(NULL, BAD_CAST "externalRef");
957 xmlParseCRNGDropTokens(ctxt, 1);
958 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
959 TODO
960 } else {
961 TODO
962 }
963 } else if (token->toktype == CRNG_IDENTIFIER) {
964 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_ref);
965 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
966 xmlSetProp(ctxt->insert, BAD_CAST "name", token->token);
967 xmlParseCRNGDropTokens(ctxt, 1);
968 } else if (token->toktype == CRNG_QNAME) {
969 xmlParseCRNG_datatype(ctxt);
970 } else if (token->toktype == CRNG_LITERAL_SEGMENT) {
971 xmlParseCRNG_datatype(ctxt);
972 } else if ((token->toktype == CRNG_OP) &&
973 (token->token[0] == '(') && (token->token[1] == 0)) {
974 xmlParseCRNGDropTokens(ctxt, 1);
975 xmlParseCRNG_pattern(ctxt);
976 token = xmlParseCRNGGetToken(ctxt, 1);
977 if ((token->toktype != CRNG_OP) ||
978 (token->token[0] != ')') || (token->token[1] != 0)) {
979 ERROR("Expecting \")\" here");
980 }
981 xmlParseCRNGDropTokens(ctxt, 1);
982 }
983 return(0);
984}
985
986/**
987 * xmlParseCRNG_particle:
988 * @ctxt: a compact RNG parser context
989 *
990 * Parse particle of the RELAX NG Compact Syntax Appendix A
991 *
992 * Returns 0 in case of success and -1 in case of error
993 */
994static int
995xmlParseCRNG_particle(xmlCRelaxNGParserCtxtPtr ctxt)
996{
997 tokenPtr token;
998 xmlNodePtr insert = ctxt->insert, res, tmp = NULL;
999
1000 ctxt->insert = NULL;
1001 xmlParseCRNG_primary(ctxt);
1002 res = ctxt->insert;
1003 token = xmlParseCRNGGetToken(ctxt, 1);
1004 if ((token != NULL) && (token->toktype == CRNG_OP)) {
1005 if ((token->token[0] == '*') && (token->token[1] == 0)) {
1006 tmp = xmlNewNode(NULL, BAD_CAST "zeroOrMore");
1007 if (tmp == NULL) CRNG_MEM_ERROR0();
1008 } else if ((token->token[0] == '+') && (token->token[1] == 0)) {
1009 tmp = xmlNewNode(NULL, BAD_CAST "oneOrMore");
1010 if (tmp == NULL) CRNG_MEM_ERROR0();
1011 } else if ((token->token[0] == '?') && (token->token[1] == 0)) {
1012 tmp = xmlNewNode(NULL, BAD_CAST "optional");
1013 if (tmp == NULL) CRNG_MEM_ERROR0();
1014 }
1015 if (tmp != NULL) {
1016 xmlAddChild(tmp, res);
1017 res = tmp;
1018 xmlParseCRNGDropTokens(ctxt, 1);
1019 }
1020 }
1021 if (insert != NULL) {
1022 xmlAddChild(insert, res);
1023 ctxt->insert = insert;
1024 } else
1025 ctxt->insert = res;
1026 return(0);
1027}
1028
1029/**
1030 * xmlParseCRNG_pattern:
1031 * @ctxt: a compact RNG parser context
1032 *
1033 * Parse pattern of the RELAX NG Compact Syntax Appendix A
1034 *
1035 * Returns 0 in case of success and -1 in case of error
1036 */
1037static int
1038xmlParseCRNG_pattern(xmlCRelaxNGParserCtxtPtr ctxt)
1039{
1040 tokenPtr token;
1041 xmlNodePtr insert = ctxt->insert, prev, grp;
1042
1043 ctxt->insert = NULL;
1044 xmlParseCRNG_particle(ctxt);
1045 prev = ctxt->insert;
1046 token = xmlParseCRNGGetToken(ctxt, 1);
1047 while ((prev != NULL) && (token != NULL) && (token->toktype == CRNG_OP)) {
1048 if (token->token == ctxt->key_or) {
1049 grp = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_choice);
1050 if (grp == NULL) CRNG_MEM_ERROR0();
1051 } else if (token->token == ctxt->key_and) {
1052 grp = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_interleave);
1053 if (grp == NULL) CRNG_MEM_ERROR0();
1054 } else if (token->token == ctxt->key_comma) {
1055 grp = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_group);
1056 if (grp == NULL) CRNG_MEM_ERROR0();
1057 } else
1058 break;
1059 xmlParseCRNGDropTokens(ctxt, 1);
1060 ctxt->insert = NULL;
1061 xmlParseCRNG_particle(ctxt);
1062 xmlAddChild(grp, prev);
1063 xmlAddChild(grp, ctxt->insert);
1064 prev = grp;
1065 token = xmlParseCRNGGetToken(ctxt, 1);
1066 }
1067 if (insert != NULL) {
1068 xmlAddChild(insert, prev);
1069 ctxt->insert = insert;
1070 } else {
1071 ctxt->insert = prev;
1072 }
1073
1074 return(0);
1075}
1076
1077/**
1078 * xmlParseCRNG_component:
1079 * @ctxt: a compact RNG parser context
1080 *
1081 * Parse component of the RELAX NG Compact Syntax Appendix A
1082 *
1083 * Returns 0 in case of success and -1 in case of error
1084 */
1085static int
1086xmlParseCRNG_component(xmlCRelaxNGParserCtxtPtr ctxt)
1087{
1088 tokenPtr token, tok2;
1089 xmlNodePtr insert = ctxt->insert;
1090
1091 token = xmlParseCRNGGetToken(ctxt, 1);
1092 if (token == NULL)
1093 return(0);
1094 if (token->toktype == CRNG_KEYWORD) {
1095 if (token->token == ctxt->key_start) {
1096 xmlNodePtr start;
1097
1098 start = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_start);
1099 if (start == NULL) CRNG_MEM_ERROR0();
1100 if (ctxt->insert != NULL)
1101 xmlAddChild(ctxt->insert, start);
1102 ctxt->insert = start;
1103 xmlParseCRNGDropTokens(ctxt, 1);
1104 token = xmlParseCRNGGetToken(ctxt, 1);
1105
1106 if ((token->toktype == CRNG_OP) &&
1107 (token->token == ctxt->key_equal)) {
1108 } else if ((token->toktype == CRNG_OP) &&
1109 (token->token == ctxt->key_orequal)) {
1110 xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
1111 BAD_CAST "choice");
1112 } else if ((token->toktype == CRNG_OP) &&
1113 (token->token == ctxt->key_andequal)) {
1114 xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
1115 BAD_CAST "interleave");
1116 } else {
1117 ERROR("expecting \"=\" or \"&=\" or \"|=\" here")
1118 return(-1);
1119 }
1120 start->properties = ctxt->attrs;
1121 ctxt->attrs = NULL;
1122 xmlParseCRNGDropTokens(ctxt, 1);
1123 xmlParseCRNG_pattern(ctxt);
1124
1125 } else if (token->token == ctxt->key_include) {
1126 TODO
1127 } else if (token->token == ctxt->key_div) {
1128 TODO
1129 } else {
1130 return(-1);
1131 }
1132 } else if (token->toktype == CRNG_IDENTIFIER) {
1133 xmlNodePtr define;
1134 const xmlChar *identifier;
1135
1136 identifier = token->token;
1137 tok2 = xmlParseCRNGGetToken(ctxt, 2);
1138 if ((tok2->toktype == CRNG_OP) &&
1139 (tok2->token == ctxt->key_equal)) {
1140 } else if ((tok2->toktype == CRNG_OP) &&
1141 (tok2->token == ctxt->key_orequal)) {
1142 xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
1143 BAD_CAST "choice");
1144 } else if ((tok2->toktype == CRNG_OP) &&
1145 (tok2->token == ctxt->key_andequal)) {
1146 xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
1147 BAD_CAST "interleave");
1148 } else {
1149 ERROR("expecting \"=\" or \"&=\" or \"|=\" here")
1150 return(-1);
1151 }
1152 xmlParseCRNGDropTokens(ctxt, 2);
1153
1154 define = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_define);
1155 if (define == NULL) CRNG_MEM_ERROR0();
1156 define->properties = ctxt->attrs;
1157 ctxt->attrs = NULL;
1158 xmlSetProp(define, BAD_CAST "name", identifier);
1159 if (ctxt->insert != NULL)
1160 xmlAddChild(ctxt->insert, define);
1161 ctxt->insert = define;
1162 xmlParseCRNG_pattern(ctxt);
1163 } else {
1164 return(-1);
1165 }
1166 ctxt->insert = insert;
1167 return(0);
1168}
1169
1170/**
1171 * xmlParseCRNG_grammar:
1172 * @ctxt: a compact RNG parser context
1173 *
1174 * Parse grammar of the RELAX NG Compact Syntax Appendix A
1175 *
1176 * Returns 0 in case of success and -1 in case of error
1177 */
1178static int
1179xmlParseCRNG_grammar(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
1180{
1181 tokenPtr token;
1182 int ret;
1183
1184 token = xmlParseCRNGGetToken(ctxt, 1);
1185 while (token != NULL) {
1186 ret = xmlParseCRNG_component(ctxt);
1187 if (ret != 0)
1188 break;
1189 token = xmlParseCRNGGetToken(ctxt, 1);
1190 }
1191 return(0);
1192}
1193
1194/**
1195 * xmlParseCRNG_topLevelBody:
1196 * @ctxt: a compact RNG parser context
1197 *
1198 * Parse topLevelBody of the RELAX NG Compact Syntax Appendix A
1199 *
1200 * Returns 0 in case of success and -1 in case of error
1201 */
1202static int
1203xmlParseCRNG_topLevelBody(xmlCRelaxNGParserCtxtPtr ctxt)
1204{
1205 tokenPtr token, tok2;
1206
1207 token = xmlParseCRNGGetToken(ctxt, 1);
1208 if (token->toktype == CRNG_KEYWORD) {
1209 if ((token->token == ctxt->key_start) ||
1210 (token->token == ctxt->key_include) ||
1211 (token->token == ctxt->key_div)) {
1212 xmlNodePtr grammar;
1213
1214 grammar = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_grammar);
1215 if (grammar == NULL) CRNG_MEM_ERROR0();
1216 xmlDocSetRootElement(ctxt->doc, grammar);
1217 ctxt->insert = grammar;
1218
1219 xmlParseCRNG_grammar(ctxt);
1220 } else {
1221 xmlParseCRNG_pattern(ctxt);
1222 }
1223 } else {
1224 tok2 = xmlParseCRNGGetToken(ctxt, 2);
1225 if ((tok2->toktype == CRNG_OP) &&
1226 ((tok2->token == ctxt->key_equal) ||
1227 (tok2->token == ctxt->key_orequal) ||
1228 (tok2->token == ctxt->key_andequal))) {
1229 xmlNodePtr grammar;
1230
1231 grammar = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_grammar);
1232 if (grammar == NULL) CRNG_MEM_ERROR0();
1233 xmlDocSetRootElement(ctxt->doc, grammar);
1234 ctxt->insert = grammar;
1235
1236 xmlParseCRNG_grammar(ctxt);
1237 } else {
1238 xmlParseCRNG_pattern(ctxt);
1239 }
1240 }
1241 return(0);
1242}
1243
1244/**
1245 * xmlParseCRNG_namespacePrefix:
1246 * @ctxt: a compact RNG parser context
1247 *
1248 * Parse namespacePrefix of the RELAX NG Compact Syntax Appendix A
1249 *
1250 * Returns the prefix or NULL in case of error
1251 */
1252static const xmlChar *
1253xmlParseCRNG_namespacePrefix(xmlCRelaxNGParserCtxtPtr ctxt)
1254{
1255 tokenPtr token;
1256 const xmlChar *prefix = NULL;
1257
1258 token = xmlParseCRNGGetToken(ctxt, 1);
1259 if (token->toktype == CRNG_IDENTIFIER) {
1260 prefix = token->token;
1261 } else if (token->toktype == CRNG_OP) {
1262 if ((token->token[0] == '=') && (token->token[1] == 0))
1263 return(NULL);
1264 prefix = token->token;
1265 } else {
1266 ERROR("Expecting a namespace prefix");
1267 return(NULL);
1268 }
1269 xmlParseCRNGDropTokens(ctxt, 1);
1270
1271 if (xmlStrEqual(prefix, BAD_CAST "xmlns")) {
1272 ERROR("Namespace prefix \"xmlns\" is forbidden");
1273 }
1274 return(prefix);
1275}
1276
1277/**
1278 * xmlParseCRNG_decl:
1279 * @ctxt: a compact RNG parser context
1280 *
1281 * Parse decl of the RELAX NG Compact Syntax Appendix A
1282 *
1283 * Returns 0 in case of success and -1 in case of error
1284 */
1285static int
1286xmlParseCRNG_decl(xmlCRelaxNGParserCtxtPtr ctxt)
1287{
1288 const xmlChar *prefix = NULL;
1289 const xmlChar *namespace = NULL;
1290 tokenPtr token;
1291
1292 token = xmlParseCRNGGetToken(ctxt, 1);
1293 if (token->toktype != CRNG_KEYWORD) return(-1);
1294 if (token->token == ctxt->key_default) {
1295 xmlParseCRNGDropTokens(ctxt, 1);
1296 token = xmlParseCRNGGetToken(ctxt, 1);
1297 if ((token->toktype != CRNG_KEYWORD) ||
1298 (token->token != ctxt->key_namespace)) {
1299 ERROR("Expecting keyword \"namespace\" after \"default\"");
1300 }
1301 xmlParseCRNGDropTokens(ctxt, 1);
1302 prefix = xmlParseCRNG_namespacePrefix(ctxt);
1303 token = xmlParseCRNGGetToken(ctxt, 1);
1304 if ((token->toktype != CRNG_OP) ||
1305 (token->token[0] != '=') || (token->token[1] != 0)) {
1306 ERROR("Expecting keyword \"=\" here");
1307 }
1308 xmlParseCRNGDropTokens(ctxt, 1);
1309 token = xmlParseCRNGGetToken(ctxt, 1);
1310 if ((token->toktype == CRNG_KEYWORD) &&
1311 (token->token == ctxt->key_inherit)) {
1312 namespace = xmlCRelaxNGInherit;
1313 } else if (token->toktype == CRNG_LITERAL_SEGMENT) {
1314 namespace = token->token;
1315 } else {
1316 ERROR("Expecting an URI or \"inherit\" value");
1317 }
1318 xmlParseCRNGDropTokens(ctxt, 1);
1319 if (namespace != NULL) {
1320 if (prefix != NULL)
1321 xmlParseCRNG_bindPrefix(ctxt, prefix, namespace);
1322 xmlParseCRNG_bindPrefix(ctxt, NULL, namespace);
1323 }
1324 } else if (token->token == ctxt->key_namespace) {
1325 xmlParseCRNGDropTokens(ctxt, 1);
1326 prefix = xmlParseCRNG_namespacePrefix(ctxt);
1327 token = xmlParseCRNGGetToken(ctxt, 1);
1328 if ((token->toktype != CRNG_OP) ||
1329 (token->token[0] != '=') || (token->token[1] != 0)) {
1330 ERROR("Expecting keyword \"=\" here");
1331 }
1332 xmlParseCRNGDropTokens(ctxt, 1);
1333 token = xmlParseCRNGGetToken(ctxt, 1);
1334 if ((token->toktype == CRNG_KEYWORD) &&
1335 (token->token == ctxt->key_inherit)) {
1336 namespace = xmlCRelaxNGInherit;
1337 } else if (token->toktype == CRNG_LITERAL_SEGMENT) {
1338 namespace = token->token;
1339 } else {
1340 ERROR("Expecting an URI or \"inherit\" value");
1341 }
1342 xmlParseCRNGDropTokens(ctxt, 1);
1343 if (namespace != NULL)
1344 xmlParseCRNG_bindPrefix(ctxt, prefix, namespace);
1345 } else if (token->token == ctxt->key_datatypes) {
1346 xmlParseCRNGDropTokens(ctxt, 1);
1347
1348 token = xmlParseCRNGGetToken(ctxt, 1);
1349 if ((token->toktype != CRNG_KEYWORD) &&
1350 (token->toktype != CRNG_IDENTIFIER)) {
1351 ERROR("Expecting a datatype prefix identifier here");
1352 } else
1353 prefix = token->token;
1354 xmlParseCRNGDropTokens(ctxt, 1);
1355 token = xmlParseCRNGGetToken(ctxt, 1);
1356 if ((token->toktype != CRNG_OP) ||
1357 (token->token[0] != '=') || (token->token[1] != 0)) {
1358 ERROR("Expecting keyword \"=\" here");
1359 }
1360 xmlParseCRNGDropTokens(ctxt, 1);
1361 token = xmlParseCRNGGetToken(ctxt, 1);
1362 if (token->toktype == CRNG_LITERAL_SEGMENT) {
1363 namespace = token->token;
1364 } else {
1365 ERROR("Expecting a literal value for the datatype identifier");
1366 }
1367 xmlParseCRNGDropTokens(ctxt, 1);
1368 if ((namespace != NULL) && (prefix != NULL))
1369 xmlParseCRNG_bindDatatypePrefix(ctxt, prefix, namespace);
1370 }
1371
1372 return(0);
1373}
1374
1375/**
1376 * xmlParseCRNG_preamble:
1377 * @ctxt: a compact RNG parser context
1378 *
1379 * Parse preamble of the RELAX NG Compact Syntax Appendix A
1380 *
1381 * Returns 0 in case of success and -1 in case of error
1382 */
1383static int
1384xmlParseCRNG_preamble(xmlCRelaxNGParserCtxtPtr ctxt)
1385{
1386 tokenPtr token;
1387
1388 token = xmlParseCRNGGetToken(ctxt, 1);
1389 while (token != NULL) {
1390 if (token == NULL) return(-1);
1391 if ((token->toktype == CRNG_KEYWORD) &&
1392 ((token->token == ctxt->key_default) ||
1393 (token->token == ctxt->key_namespace) ||
1394 (token->token == ctxt->key_datatypes))) {
1395 xmlParseCRNG_decl(ctxt);
1396 } else
1397 break;
1398 token = xmlParseCRNGGetToken(ctxt, 1);
1399 }
1400 return(0);
1401}
1402
1403/**
1404 * xmlParseCRNG_topLevel:
1405 * @ctxt: a compact RNG parser context
1406 *
1407 * Parse topLevel of the RELAX NG Compact Syntax Appendix A
1408 *
1409 * Returns 0 in case of success and -1 in case of error
1410 */
1411static int
1412xmlParseCRNG_topLevel(xmlCRelaxNGParserCtxtPtr ctxt)
1413{
1414 xmlParseCRNG_preamble(ctxt);
1415 xmlParseCRNG_topLevelBody(ctxt);
1416 return(0);
1417}
1418
1419/**
1420 * xmlParseCRNG:
1421 * @schemas: pointer to the text of the compact schemas
1422 * @len: length of the schemas in bytes (or 0)
1423 *
1424 * Compiles the schemas into the equivalent Relax-NG XML structure
1425 *
1426 * Returns the xmlDocPtr resulting from the compilation or
1427 * NULL in case of error
1428 */
1429static xmlDocPtr
1430xmlParseCRNG(const xmlChar *schemas, int len) {
1431 struct _xmlCRelaxNGParserCtxt ctxt;
1432 xmlDocPtr ret = NULL;
1433
1434 if (schemas == NULL) return(NULL);
1435 if (len <= 5) len = xmlStrlen(schemas);
1436 if (len <= 0) return(NULL);
1437
1438 memset(&ctxt, 0, sizeof(ctxt));
1439 ctxt.compact = schemas;
1440 ctxt.cur = schemas;
1441 ctxt.end = &schemas[len];
1442 ctxt.dict = xmlDictCreate();
1443 if (ctxt.dict == NULL)
1444 return(NULL);
1445 ctxt.doc = xmlNewDoc(NULL);
1446 if (ctxt.doc == NULL) {
1447 xmlDictFree(ctxt.dict);
1448 return(NULL);
1449 }
1450 ctxt.doc->dict = ctxt.dict;
1451 xmlDictReference(ctxt.dict);
1452
1453 ctxt.nbTokens = 0;
1454 ctxt.firstToken = 0;
1455 ctxt.key_attribute = xmlDictLookup(ctxt.dict, BAD_CAST "attribute", -1);
1456 ctxt.key_default = xmlDictLookup(ctxt.dict, BAD_CAST "default", -1);
1457 ctxt.key_datatypes = xmlDictLookup(ctxt.dict, BAD_CAST "datatypes", -1);
1458 ctxt.key_div = xmlDictLookup(ctxt.dict, BAD_CAST "div", -1);
1459 ctxt.key_element = xmlDictLookup(ctxt.dict, BAD_CAST "element", -1);
1460 ctxt.key_empty = xmlDictLookup(ctxt.dict, BAD_CAST "empty", -1);
1461 ctxt.key_external = xmlDictLookup(ctxt.dict, BAD_CAST "external", -1);
1462 ctxt.key_grammar = xmlDictLookup(ctxt.dict, BAD_CAST "grammar", -1);
1463 ctxt.key_include = xmlDictLookup(ctxt.dict, BAD_CAST "include", -1);
1464 ctxt.key_inherit = xmlDictLookup(ctxt.dict, BAD_CAST "inherit", -1);
1465 ctxt.key_list = xmlDictLookup(ctxt.dict, BAD_CAST "list", -1);
1466 ctxt.key_mixed = xmlDictLookup(ctxt.dict, BAD_CAST "mixed", -1);
1467 ctxt.key_namespace = xmlDictLookup(ctxt.dict, BAD_CAST "namespace", -1);
1468 ctxt.key_notAllowed = xmlDictLookup(ctxt.dict, BAD_CAST "notAllowed", -1);
1469 ctxt.key_parent = xmlDictLookup(ctxt.dict, BAD_CAST "parent", -1);
1470 ctxt.key_start = xmlDictLookup(ctxt.dict, BAD_CAST "start", -1);
1471 ctxt.key_string = xmlDictLookup(ctxt.dict, BAD_CAST "string", -1);
1472 ctxt.key_text = xmlDictLookup(ctxt.dict, BAD_CAST "text", -1);
1473 ctxt.key_token = xmlDictLookup(ctxt.dict, BAD_CAST "token", -1);
1474 ctxt.key_equal = xmlDictLookup(ctxt.dict, BAD_CAST "=", 1);
1475 ctxt.key_orequal = xmlDictLookup(ctxt.dict, BAD_CAST "|=", 2);
1476 ctxt.key_andequal = xmlDictLookup(ctxt.dict, BAD_CAST "&=", 2);
1477 ctxt.key_combine = xmlDictLookup(ctxt.dict, BAD_CAST "&=", 2);
1478 ctxt.key_or = xmlDictLookup(ctxt.dict, BAD_CAST "|", 1);
1479 ctxt.key_comma = xmlDictLookup(ctxt.dict, BAD_CAST ",", 1);
1480 ctxt.key_and = xmlDictLookup(ctxt.dict, BAD_CAST "&", 1);
1481 ctxt.key_choice = xmlDictLookup(ctxt.dict, BAD_CAST "choice", -1);
1482 ctxt.key_group = xmlDictLookup(ctxt.dict, BAD_CAST "group", -1);
1483 ctxt.key_interleave = xmlDictLookup(ctxt.dict, BAD_CAST "interleave", -1);
1484 ctxt.key_ref = xmlDictLookup(ctxt.dict, BAD_CAST "ref", 3);
1485 ctxt.key_define = xmlDictLookup(ctxt.dict, BAD_CAST "define", 6);
1486
1487 /* xmlParseCRNGTokenize(&ctxt); */
1488 xmlParseCRNG_topLevel(&ctxt);
1489
1490 xmlDictFree(ctxt.dict);
1491
1492 ret = ctxt.doc;
1493 return(ret);
1494}
1495
1496const xmlChar *schemas =
1497"# RELAX NG XML syntax specified in compact syntax.\n\
1498\n\
1499default namespace rng = \"http://relaxng.org/ns/structure/1.0\"\n\
1500namespace local = \"\"\n\
1501datatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\
1502\n\
1503start = pattern\n\
1504\n\
1505pattern =\n\
1506 element element { (nameQName | nameClass), (common & pattern+) }\n\
1507 | element attribute { (nameQName | nameClass), (common & pattern?) }\n\
1508 | element group|interleave|choice|optional\n\
1509 |zeroOrMore|oneOrMore|list|mixed { common & pattern+ }\n\
1510 | element ref|parentRef { nameNCName, common }\n\
1511 | element empty|notAllowed|text { common }\n\
1512 | element data { type, param*, (common & exceptPattern?) }\n\
1513 | element value { commonAttributes, type?, xsd:string }\n\
1514 | element externalRef { href, common }\n\
1515 | element grammar { common & grammarContent* }\n\
1516\n\
1517param = element param { commonAttributes, nameNCName, xsd:string }\n\
1518\n\
1519exceptPattern = element except { common & pattern+ }\n\
1520\n\
1521grammarContent =\n\
1522 definition\n\
1523 | element div { common & grammarContent* }\n\
1524 | element include { href, (common & includeContent*) }\n\
1525\n\
1526includeContent =\n\
1527 definition\n\
1528 | element div { common & includeContent* }\n\
1529\n\
1530definition =\n\
1531 element start { combine?, (common & pattern+) }\n\
1532 | element define { nameNCName, combine?, (common & pattern+) }\n\
1533\n\
1534combine = attribute combine { \"choice\" | \"interleave\" }\n\
1535\n\
1536nameClass =\n\
1537 element name { commonAttributes, xsd:QName }\n\
1538 | element anyName { common & exceptNameClass? }\n\
1539 | element nsName { common & exceptNameClass? }\n\
1540 | element choice { common & nameClass+ }\n\
1541\n\
1542exceptNameClass = element except { common & nameClass+ }\n\
1543\n\
1544nameQName = attribute name { xsd:QName }\n\
1545nameNCName = attribute name { xsd:NCName }\n\
1546href = attribute href { xsd:anyURI }\n\
1547type = attribute type { xsd:NCName }\n\
1548\n\
1549common = commonAttributes, foreignElement*\n\
1550\n\
1551commonAttributes =\n\
1552 attribute ns { xsd:string }?,\n\
1553 attribute datatypeLibrary { xsd:anyURI }?,\n\
1554 foreignAttribute*\n\
1555\n\
1556foreignElement = element * - rng:* { (anyAttribute | text | anyElement)* }\n\
1557foreignAttribute = attribute * - (rng:*|local:*) { text }\n\
1558anyElement = element * { (anyAttribute | text | anyElement)* }\n\
1559anyAttribute = attribute * { text }\n\
1560";
1561
1562int main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
1563 xmlDocPtr res;
1564
1565 res = xmlParseCRNG(schemas, -1);
1566 if (res != NULL) {
1567 xmlDocFormatDump(stdout, res, 1);
1568 xmlFreeDoc(res);
1569 }
1570 return(0);
1571}