././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711714602.3558218 rnc2rng-2.7.0/0000755000076500000240000000000000000000000011517 5ustar00djcstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/AUTHORS0000644000076500000240000000036500000000000012573 0ustar00djcstaffDavid Mertz Dirkjan Ochtman Dustin J. Mitchell John Vandenberg Low Kian Seong Timmy Zhu William Muir ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/LICENSE0000644000076500000240000000206000000000000012522 0ustar00djcstaffMIT License Copyright (c) 2020 Dirkjan Ochtman Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/MANIFEST.in0000644000076500000240000000010700000000000013253 0ustar00djcstaffinclude AUTHORS include README.rst include test.py include tests/*.rn* ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711714602.3557599 rnc2rng-2.7.0/PKG-INFO0000644000076500000240000000545700000000000012627 0ustar00djcstaffMetadata-Version: 2.1 Name: rnc2rng Version: 2.7.0 Summary: RELAX NG Compact to regular syntax conversion library Home-page: https://github.com/djc/rnc2rng Author: David Mertz Maintainer: Dirkjan Ochtman Maintainer-email: dirkjan@ochtman.nl Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Operating System :: OS Independent Classifier: License :: OSI Approved :: MIT License Classifier: Topic :: Text Processing :: Markup :: XML Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 License-File: LICENSE License-File: AUTHORS Requires-Dist: rply RELAX NG Compact to RELAX NG conversion library =============================================== .. image:: https://github.com/djc/rnc2rng/workflows/CI/badge.svg :target: https://github.com/djc/rnc2rng/actions?query=workflow%3ACI .. image:: https://coveralls.io/repos/djc/rnc2rng/badge.svg?branch=master&service=github :target: https://coveralls.io/github/djc/rnc2rng?branch=master Converts RELAX NG schemata in Compact syntax (`rnc`) to the equivalent schema in the XML-based default RELAX NG syntax. Dependencies: - Python 3.x (tested with 3.7, 3.8, 3.9) - `rply`_ Feedback welcome on `GitHub`_. Please consider funding continued maintenance of this project through `Patreon`_ or `GitHub Sponsors`_. .. _GitHub: https://github.com/djc/rnc2rng .. _rply: https://pypi.python.org/pypi/rply .. _Patreon: https://patreon.com/dochtman .. _GitHub Sponsors: https://github.com/sponsors/djc History ------- rnc2rng was originally written by `David Mertz`_ in 2003 and published as part of a collection of files around RELAX NG `on his site`_ into the Public Domain. `Hartmut Goebel`_ published it as a package on PyPI to make it easier to access. It was mirrored on GitHub by `Dustin J. Mitchell`_ in 2010 after he fixed some bugs. `Timmy Zhu`_ forked his repository and contributed further enhancements. Recently, I (Dirkjan Ochtman) was interested in playing with RELAX NG Compact and started making further updates. I asked Hartmut for maintainership on PyPI and received it. While I cannot promise many updates, I should be responsive to bug reports and (especially!) pull requests. .. _David Mertz: http://www.gnosis.cx/publish/ .. _on his site: http://www.gnosis.cx/download/relax/ .. _Hartmut Goebel: http://www.goebel-consult.de/ .. _Dustin J. Mitchell: http://code.v.igoro.us/ .. _Timmy Zhu: https://github.com/nattofriends How to install -------------- The usual should work: .. code-block:: shell $ sudo pip install . Getting started --------------- .. code-block:: shell $ python -m rnc2rng test.rnc > test.rng License ------- All of the code is released under MIT License. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/README.rst0000644000076500000240000000407300000000000013212 0ustar00djcstaffRELAX NG Compact to RELAX NG conversion library =============================================== .. image:: https://github.com/djc/rnc2rng/workflows/CI/badge.svg :target: https://github.com/djc/rnc2rng/actions?query=workflow%3ACI .. image:: https://coveralls.io/repos/djc/rnc2rng/badge.svg?branch=master&service=github :target: https://coveralls.io/github/djc/rnc2rng?branch=master Converts RELAX NG schemata in Compact syntax (`rnc`) to the equivalent schema in the XML-based default RELAX NG syntax. Dependencies: - Python 3.x (tested with 3.7, 3.8, 3.9) - `rply`_ Feedback welcome on `GitHub`_. Please consider funding continued maintenance of this project through `Patreon`_ or `GitHub Sponsors`_. .. _GitHub: https://github.com/djc/rnc2rng .. _rply: https://pypi.python.org/pypi/rply .. _Patreon: https://patreon.com/dochtman .. _GitHub Sponsors: https://github.com/sponsors/djc History ------- rnc2rng was originally written by `David Mertz`_ in 2003 and published as part of a collection of files around RELAX NG `on his site`_ into the Public Domain. `Hartmut Goebel`_ published it as a package on PyPI to make it easier to access. It was mirrored on GitHub by `Dustin J. Mitchell`_ in 2010 after he fixed some bugs. `Timmy Zhu`_ forked his repository and contributed further enhancements. Recently, I (Dirkjan Ochtman) was interested in playing with RELAX NG Compact and started making further updates. I asked Hartmut for maintainership on PyPI and received it. While I cannot promise many updates, I should be responsive to bug reports and (especially!) pull requests. .. _David Mertz: http://www.gnosis.cx/publish/ .. _on his site: http://www.gnosis.cx/download/relax/ .. _Hartmut Goebel: http://www.goebel-consult.de/ .. _Dustin J. Mitchell: http://code.v.igoro.us/ .. _Timmy Zhu: https://github.com/nattofriends How to install -------------- The usual should work: .. code-block:: shell $ sudo pip install . Getting started --------------- .. code-block:: shell $ python -m rnc2rng test.rnc > test.rng License ------- All of the code is released under MIT License. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711714602.3489857 rnc2rng-2.7.0/rnc2rng/0000755000076500000240000000000000000000000013072 5ustar00djcstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/rnc2rng/__init__.py0000644000076500000240000000045400000000000015206 0ustar00djcstafffrom . import parser, serializer def load(f): return parser.parse(f=f) def loads(src): return parser.parse(src) def dump(root, f, indent=None): f.write(serializer.XMLSerializer(indent).toxml(root)) def dumps(root, indent=None): return serializer.XMLSerializer(indent).toxml(root) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/rnc2rng/__main__.py0000755000076500000240000000100000000000000015156 0ustar00djcstaff#!/usr/bin/env python from __future__ import print_function from . import parser, serializer import sys def main(): args = sys.argv[1:] input = open(args[0]) if len(args) > 0 else sys.stdin try: xml = serializer.XMLSerializer().toxml(parser.parse(f=input)) except parser.ParseError as e: print('parse error ' + e.msg) sys.exit(1) if len(args) > 1: open(sys.argv[2], 'w').write(xml + '\n') else: print(xml) if __name__ == '__main__': main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/rnc2rng/parser.py0000644000076500000240000005074600000000000014754 0ustar00djcstafffrom codecs import BOM_UTF16_BE, BOM_UTF16_LE from urllib.request import urlopen from urllib.parse import urljoin, urlparse import rply, sys, os KEYWORDS = set([ 'attribute', 'datatypes', 'default', 'div', 'element', 'empty', 'external', 'grammar', 'include', 'inherit', 'list', 'mixed', 'namespace', 'notAllowed', 'parent', 'start', 'string', 'text', 'token', ]) NCNAME = r'[A-Za-z_][\w.-]*' def lexer(): lg = rply.LexerGenerator() lg.add('LPAREN', r'\(') lg.add('RPAREN', r'\)') lg.add('LBRACE', r'{') lg.add('RBRACE', r'}') lg.add('LBRACKET', r'\[') lg.add('RBRACKET', r'\]') lg.add('COMBINE', r'\|=|&=') lg.add('EQUAL', r'=') lg.add('PIPE', r'[|]') lg.add('COMMA', r',') lg.add('AMP', r'&') lg.add('MINUS', r'[-]') lg.add('STAR', r'[*]') lg.add('PLUS', r'[+]') lg.add('QMARK', r'[?]') lg.add('CNAME', r'%s:(%s|\*)' % (NCNAME, NCNAME)) lg.add('QID', r'\\%s' % NCNAME) lg.add('ID', NCNAME) lg.add('LITERAL', r'".*?"') lg.add('DOCUMENTATION', r'##.*') lg.add('COMMENT', r'#.*') lg.add('TILDE', r'~') lg.ignore(r'\s+') return lg.build() LEXER = lexer() def lex(src): for t in LEXER.lex(src): if t.name == 'ID' and t.value in KEYWORDS: t.name = t.value.upper() elif t.name == 'LITERAL': t.value = t.value[1:-1] elif t.name == 'COMMENT': continue yield t pg = rply.ParserGenerator([ 'AMP', 'CNAME', 'COMBINE', 'COMMA', 'DOCUMENTATION', 'EQUAL', 'ID', 'LBRACE', 'LBRACKET', 'LPAREN', 'LIST', 'LITERAL', 'MINUS', 'MIXED', 'PLUS', 'PIPE', 'QID', 'QMARK', 'RBRACE', 'RBRACKET', 'RPAREN', 'STAR', 'TILDE', ] + [s.upper() for s in KEYWORDS], precedence=[("left", ['TILDE'])]) class Node(object): __slots__ = 'type', 'name', 'value' def __init__(self, type, name, value=None): self.type = type self.name = name self.value = value or [] assert isinstance(self.value, list), self.value def __repr__(self): bits = [(k, getattr(self, k, None)) for k in self.__slots__] strs = ['%s=%r' % (k, v) for (k, v) in bits if v is not None] return 'Node(%s)' % ', '.join(strs) def pprint(n, level=0): if isinstance(n, list): print('[') for v in n: pprint(v, level + 2) print('%s]' % (' ' * level)) else: print('%s%s' % (' ' * level, n.type), end=' ') if n.name is not None: print(n.name, end=' ') if not n.value: print('[]') else: pprint(n.value, level) NODE_TYPES = [ 'ANNO_ATTR', 'ANNOTATION', 'ANY', 'ASSIGN', 'ATTR', 'CHOICE', 'DATATAG', 'DATATYPES', 'DEFAULT_NS', 'DEFINE', 'DIV', 'DOCUMENTATION', 'ELEM', 'EMPTY', 'EXCEPT', 'GRAMMAR', 'GROUP', 'INTERLEAVE', 'LIST', 'LITERAL', 'MAYBE', 'MIXED', 'NAME', 'NOT_ALLOWED', 'NS', 'PARAM', 'PARENT', 'REF', 'ROOT', 'SEQ', 'SOME', 'TEXT', 'LITERAL_TYPE' ] for _node_type in NODE_TYPES: globals()[_node_type] = _node_type @pg.production('start : preamble top-level-body') def start(s, p): return Node('ROOT', None, p[0] + p[1]) @pg.production('strlit : LITERAL') def strlit_literal(s, p): # from datatypeValue return p[0] @pg.production('strlit : strlit TILDE strlit') def strlit_concat(s, p): p[0].value += p[2].value return p[0] @pg.production('preamble : decl preamble') def preamble_multi(s, p): p[1].insert(0, p[0]) return p[1] @pg.production('preamble : ') def preamble_empty(s, p): return [] @pg.production('decl : DEFAULT NAMESPACE EQUAL strlit') def decl_default_ns(s, p): return Node('DEFAULT_NS', None, [p[3].value.strip('"')]) @pg.production('decl : DEFAULT NAMESPACE id-or-kw EQUAL strlit') def decl_default_names_ns(s, p): return Node('DEFAULT_NS', p[2].name, [p[4].value.strip(' "')]) @pg.production('decl : NAMESPACE id-or-kw EQUAL strlit') def decl_ns(s, p): return Node('NS', p[1].name, [p[3].value.strip(' "')]) @pg.production('decl : DATATYPES id-or-kw EQUAL strlit') def decl_datatypes(s, p): return Node('DATATYPES', p[1].name, [p[3].value.strip('"')]) @pg.production('top-level-body : alt-top-level') def top_level_body(s, p): if isinstance(p[0], list): return p[0] elif p[0].type == 'ELEM': return [Node('DEFINE', 'start', [Node('ASSIGN', '=', [p[0]])])] elif p[0].type == 'GRAMMAR': return [p[0]] @pg.production('alt-top-level : grammar-content') def top_level_grammar_content(s, p): return p[0] @pg.production('alt-top-level : annotations element-primary') def top_level_element(s, p): p[1].value = p[0] + p[1].value return p[1] @pg.production('alt-top-level : grammar') def top_level_grammar(s, p): return p[0] @pg.production('grammar-content : member grammar-content') def grammar_multi(s, p): p[1].insert(0, p[0]) return p[1] @pg.production('grammar-content : ') def grammar_empty(s, p): return [] @pg.production('member : annotations component') def member_annotated_component(s, p): p[1].value = p[0] + p[1].value return p[1] @pg.production('member : CNAME annotation-attributes-content') def member_foreign_element_annotation(s, p): return Node('ANNOTATION', p[0].value, p[1]) @pg.production('component : define') def component_define(s, p): return p[0] @pg.production('component : grammar-start') def component_start(s, p): return p[0] @pg.production('define : identifier definition') def define(s, p): return Node('DEFINE', p[0].name, [p[1]]) @pg.production('grammar-start : START definition') def grammar_start(s, p): return Node('DEFINE', 'start', [p[1]]) @pg.production('definition : EQUAL pattern') def definition_equal(s, p): return Node('ASSIGN', p[0].value, p[1]) @pg.production('definition : COMBINE pattern') def definition_combine(s, p): return Node('ASSIGN', p[0].value, p[1]) @pg.production('component : DIV LBRACE grammar-content RBRACE') def component_div(s, p): return Node('DIV', None, p[2]) @pg.production('component : INCLUDE strlit opt-inherit opt-include-content') def component_include(s, p): if is_url(s.path) or is_url(p[1].value): # it's a URL url = urljoin(s.path, p[1].value) else: url = os.path.join(s.path, p[1].value) return parse(f=url) @pg.production('opt-inherit : INHERIT EQUAL id-or-kw') def opt_inherit(s, p): return Node('INHERIT', p[2]) @pg.production('opt-inherit : ') def opt_inherit_none(s, p): return None @pg.production('opt-include-content : LBRACE include-body RBRACE') def opt_include_content(s, p): return p[1] @pg.production('opt-include-content : ') def opt_include_content_none(s, p): return [] @pg.production('include-body : include-member include-body') def include_content_multi(s, p): p[1].insert(0, p[0]) return p[1] @pg.production('include-body : ') def include_content_empty(s, p): return [] @pg.production('include-member : annotations include-component') def include_member(s, p): p[1].value = p[0] + p[1].value return p[1] @pg.production('include-component : define') def include_component_define(s, p): return p[0] @pg.production('include-component : grammar-start') def include_component_start(s, p): return p[0] @pg.production('include-component : DIV LBRACE include-body RBRACE') def include_component_div(s, p): return Node('DIV', None, p[2]) @pg.production('annotation-attributes-content : LBRACKET start-annotation-content RBRACKET') def annotation_attributes_content(s, p): return p[1] @pg.production('annotations : documentations LBRACKET start-annotations RBRACKET') def annotations_multi(s, p): p[0] += p[2] return p[0] @pg.production('annotations : documentations') def annotations_empty(s, p): return p[0] @pg.production('start-annotation-content : CNAME cname-annotation-content') def start_annotation_content_cname(s, p): p[1][0].name = p[0].value return p[1] @pg.production('start-annotation-content : ID EQUAL strlit start-annotation-content') def start_annotation_content_id(s, p): return [Node('ANNO_ATTR', p[0].value, [p[2].value])] + p[3] @pg.production('start-annotation-content : strlit annotation-content') def start_annotation_content_literal(s, p): return [Node('LITERAL', p[0].value)] + p[1] @pg.production('start-annotation-content : ') def start_annotation_content_empty(s, p): return [] @pg.production('cname-annotation-content : EQUAL strlit start-annotation-content') def cname_annotation_content_attribute(s, p): return [Node('ANNO_ATTR', None, [p[1].value])] + p[2] @pg.production('cname-annotation-content : annotation-attributes-content annotation-content') def cname_annotation_content_element(s, p): return [Node('ANNOTATION', None, p[0])] + p[1] @pg.production('start-annotations : CNAME cname-annotations') def start_annotations_cname(s, p): p[1][0].name = p[0].value return p[1] @pg.production('start-annotations : ') def start_annotations_empty(s, p): return [] @pg.production('cname-annotations : EQUAL strlit start-annotations') def cname_annotations_attrib(s, p): return [Node('ANNO_ATTR', None, [p[1].value])] + p[2] @pg.production('cname-annotations : annotation-attributes-content annotation-elements') def cname_annotations_element(s, p): return [Node('ANNOTATION', None, p[0])] + p[1] @pg.production('annotation-content : annotation-element annotation-content') def annotation_content_nested(s, p): return [p[0]] + p[1] @pg.production('annotation-content : strlit annotation-content') def annotation_content_literal(s, p): return [Node('LITERAL', p[0].value)] + p[1] @pg.production('annotation-content : ') def annotation_content_empty(s, p): return [] @pg.production('annotation-elements : annotation-element annotation-elements') def annotation_elements_multi(s, p): p[1].insert(0, p[0]) return p[1] @pg.production('annotation-elements : ') def annotation_elements_empty(s, p): return [] @pg.production('annotation-element : CNAME annotation-attributes-content') def nested_annotation_element(s, p): return Node('ANNOTATION', p[0].value, p[1]) @pg.production('pattern : particle') def pattern_particle(s, p): return [p[0]] @pg.production('pattern : particle-choice') def pattern_choice(s, p): return [p[0]] @pg.production('particle-choice : particle PIPE particle-choice') def particle_choice_multi(s, p): p[2].value.insert(0, p[0]) return p[2] @pg.production('particle-choice : particle PIPE particle') def particle_choice_single(s, p): return Node('CHOICE', None, [p[0], p[2]]) @pg.production('pattern : particle-group') def pattern_seq(s, p): return [p[0]] @pg.production('particle-group : particle COMMA particle-group') def particle_group_multi(s, p): p[2].value.insert(0, p[0]) return p[2] @pg.production('particle-group : particle COMMA particle') def particle_group_single(s, p): return Node('SEQ', None, [p[0], p[2]]) @pg.production('pattern : particle-interleave') def pattern_interleave(s, p): return [p[0]] @pg.production('particle-interleave : particle AMP particle-interleave') def particle_interleave_multi(s, p): p[2].value.insert(0, p[0]) return p[2] @pg.production('particle-interleave : particle AMP particle') def particle_interleave_single(s, p): return Node('INTERLEAVE', None, [p[0], p[2]]) @pg.production('particle : annotated-primary QMARK') def particle_maybe(s, p): return Node('MAYBE', None, [p[0]]) @pg.production('particle : annotated-primary STAR') def particle_any(s, p): return Node('ANY', None, [p[0]]) @pg.production('particle : annotated-primary PLUS') def particle_some(s, p): return Node('SOME', None, [p[0]]) @pg.production('particle : annotated-primary') def particle_primary(s, p): return p[0] @pg.production('primary : LPAREN pattern RPAREN') def annotated_primary_group(s, p): return Node('GROUP', None, p[1]) @pg.production('annotated-primary : annotations primary') def annotated_primary_annotated(s, p): p[1].value = p[0] + p[1].value return p[1] @pg.production('primary : element-primary') def primary_element(s, p): return p[0] @pg.production('element-primary : ELEMENT name-class LBRACE pattern RBRACE') def element_primary(s, p): return Node('ELEM', None, p[1] + p[3]) @pg.production('primary : ATTRIBUTE name-class LBRACE pattern RBRACE') def primary_attrib(s, p): return Node('ATTR', None, p[1] + p[3]) @pg.production('primary : MIXED LBRACE pattern RBRACE') def primary_mixed(s, p): return Node('MIXED', None, p[2]) @pg.production('primary : LIST LBRACE pattern RBRACE') def primary_list(s, p): return Node('LIST', None, p[2]) @pg.production('primary : strlit') def primary_literal(s, p): # from datatypeValue return Node('LITERAL', p[0].value) @pg.production('primary : CNAME') def primary_cname(s, p): return Node('DATATAG', p[0].value) @pg.production('primary : CNAME strlit') def primary_ctyped_string(s, p): return Node('LITERAL', p[1].value, [Node('LITERAL_TYPE', p[0].value)]) @pg.production('primary : CNAME LBRACE params RBRACE') def primary_type_params(s, p): return Node('DATATAG', p[0].value, p[2]) @pg.production('primary : STRING') def primary_string(s, p): return Node('DATATAG', 'string') @pg.production('primary : STRING strlit') def primary_typed_string(s, p): return Node('LITERAL', p[1].value, [Node('LITERAL_TYPE', 'string')]) @pg.production('primary : STRING LBRACE params RBRACE') def primary_string_parametrized(s, p): return Node('DATATAG', 'string', p[2]) @pg.production('primary : TOKEN') def primary_text(s, p): return Node('DATATAG', 'token') @pg.production('primary : TOKEN strlit') def primary_text(s, p): return Node('LITERAL', p[1].value) # the default type is token, so no LITERAL_TYPE @pg.production('primary : TEXT') def primary_text(s, p): return Node('TEXT', None) @pg.production('primary : EMPTY') def primary_empty(s, p): return Node('EMPTY', None) @pg.production('primary : identifier') def primary_id(s, p): return Node('REF', p[0].name) @pg.production('primary : NOTALLOWED') def primary_notallowed(s, p): return Node('NOT_ALLOWED', None) @pg.production('primary : PARENT ID') def primary_parent(s, p): return Node('PARENT', p[1].value) @pg.production('primary : grammar') def primary_grammar(s, p): return p[0] @pg.production('grammar : GRAMMAR LBRACE grammar-content RBRACE') def grammar(s, p): return Node('GRAMMAR', None, p[2]) @pg.production('params : params param') def params_multi(s, p): p[0].append(p[1]) return p[0] @pg.production('params : ') def params_empty(s, p): return [] @pg.production('param : id-or-kw EQUAL strlit') def param_single(s, p): return Node('PARAM', p[0].name, [p[2].value]) @pg.production('name-class : simple-name-class') def name_class_name(s, p): return p[0] @pg.production('name-class : name-class-choice') def name_class_choice(s, p): return p[0] @pg.production('name-class : except-name-class') def name_class_except(s, p): return p[0] @pg.production('except-name-class : simple-name-class MINUS except-name-class') def except_name_class_nested(s, p): p[0].value = p[2] return p[0] @pg.production('except-name-class : simple-name-class MINUS simple-name-class') def except_name_class_simple(s, p): p[0][0].value = [Node('EXCEPT', None, p[2])] return p[0] @pg.production('name-class-choice : simple-name-class PIPE name-class-choice') def name_class_choice_nested(s, p): p[2][0].value.insert(0, p[0][0]) return p[2] @pg.production('name-class-choice : simple-name-class PIPE simple-name-class') def name_class_choice_simple(s, p): return [Node('CHOICE', None, p[0] + p[2])] @pg.production('simple-name-class : STAR') def simple_name_class_any(s, p): return [Node('NAME', p[0].value)] @pg.production('simple-name-class : name') def simple_name_class_name(s, p): return [p[0]] @pg.production('simple-name-class : LPAREN name-class RPAREN') def name_class_group(s, p): return p[1] @pg.production('documentations : DOCUMENTATION documentations') def documentations_multi(s, p): cur = Node('DOCUMENTATION', None, []) if not p[1] else p[1][0] content = p[0].value.lstrip('#').rstrip('\r') # strip all leading "#" ( left-recursion in documentationLineContent) if content.startswith(' '): content = content[1:] # strip *one* " ", but no more (now the production is readOfLine) cur.value.insert(0, content) return [cur] @pg.production('documentations : ') def documentations_empty(s, p): return [] @pg.production('name : CNAME') def name_cname(s, p): return Node('NAME', p[0].value) @pg.production('name : id-or-kw') def name_id(s, p): return p[0] @pg.production('id-or-kw : identifier') def id_or_kw_identifier(s, p): return p[0] @pg.production('id-or-kw : keyword') def id_or_kw_keyword(s, p): return p[0] @pg.production('identifier : ID') def id_kw_id(s, p): return Node('NAME', p[0].value) @pg.production('identifier : QID') def id_kw_quoted_identifier(s, p): return Node('NAME', p[0].value[1:]) @pg.production('keyword : ATTRIBUTE') def keyword_attr(s, p): return Node('NAME', p[0].value) @pg.production('keyword : DATATYPES') def keyword_dtypes(s, p): return Node('NAME', p[0].value) @pg.production('keyword : DEFAULT') def keyword_default(s, p): return Node('NAME', p[0].value) @pg.production('keyword : DIV') def keyword_div(s, p): return Node('NAME', p[0].value) @pg.production('keyword : ELEMENT') def keyword_elem(s, p): return Node('NAME', p[0].value) @pg.production('keyword : EMPTY') def keyword_empty(s, p): return Node('NAME', p[0].value) @pg.production('keyword : EXTERNAL') def keyword_external(s, p): return Node('NAME', p[0].value) @pg.production('keyword : GRAMMAR') def keyword_grammar(s, p): return Node('NAME', p[0].value) @pg.production('keyword : INCLUDE') def keyword_include(s, p): return Node('NAME', p[0].value) @pg.production('keyword : INHERIT') def keyword_inherit(s, p): return Node('NAME', p[0].value) @pg.production('keyword : LIST') def keyword_list(s, p): return Node('NAME', p[0].value) @pg.production('keyword : MIXED') def keyword_mixed(s, p): return Node('NAME', p[0].value) @pg.production('keyword : NAMESPACE') def keyword_namespace(s, p): return Node('NAME', p[0].value) @pg.production('keyword : NOTALLOWED') def keyword_notallowed(s, p): return Node('NAME', p[0].value) @pg.production('keyword : PARENT') def keyword_parent(s, p): return Node('NAME', p[0].value) @pg.production('keyword : START') def keyword_start(s, p): return Node('NAME', p[0].value) @pg.production('keyword : STRING') def keyword_string(s, p): return Node('NAME', p[0].value) @pg.production('keyword : TEXT') def keyword_text(s, p): return Node('NAME', p[0].value) @pg.production('keyword : TOKEN') def keyword_token(s, p): return Node('NAME', p[0].value) class ParseError(Exception): def __init__(self, t, fn, ln, col, line): self.token = t self.location = fn, ln, col self.line = line fn = fn if fn is not None else '(unknown)' loc = 'in %s [%s:%s]' % (fn, ln + 1, col + 1) spaces = col + 3 * min(col, line.count('\t')) line = line.replace('\t', ' ' * 4).rstrip() self.msg = '\n'.join((loc, line, ' ' * spaces + '^')) Exception.__init__(self, self.msg) @pg.error def error(s, t): ln = t.source_pos.lineno - 1 if t.value and t.value[0] == '\n': ln -= 1 col = t.source_pos.colno - 1 line = s.lines[ln] if ln < len(s.lines) else '' raise ParseError(t, s.fn, ln, col, line) parser = pg.build() class State(object): def __init__(self, fn, src): self.fn = fn self.path = os.getcwd() if fn is not None: self.path = os.path.dirname(os.path.abspath(fn)) if not is_url(fn) else fn self.lines = src.splitlines() if sys.version_info[0] < 3: str_types = str, bytes, unicode # noqa: unicode not defined in Python 3 else: str_types = str, bytes def is_url(fn): parse_result = urlparse(fn) return parse_result.scheme in ('http', 'https', 'file') def parse(src=None, f=None): assert src is None or f is None if f is not None and isinstance(f, str_types): fn = f if is_url(fn): with urlopen(fn) as f: bytes = f.read() else: with open(fn, 'rb') as f: bytes = f.read() bom = bytes[:2] in {BOM_UTF16_BE, BOM_UTF16_LE} src = bytes.decode('utf-16' if bom else 'utf-8') elif f is not None: fn, src = f.name, f.read() else: # Caller only gave source code, no filename. fn = None return parser.parse(lex(src), state=State(fn, src)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/rnc2rng/rnctree.py0000644000076500000240000000060300000000000015105 0ustar00djcstaff#!/usr/bin/env python # Compatibility API for rnc2rng 1.0 from . import parser, serializer class Tree(object): def __init__(self, root): self.root = root def toxml(self): return serializer.XMLSerializer().toxml(self.root) def token_list(src): return parser.lex(src) def make_nodetree(tokens): return Tree(parser.parser.parse(tokens, parser.State())) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/rnc2rng/serializer.py0000644000076500000240000002577300000000000015633 0ustar00djcstaff# Convert an RELAX NG compact syntax schema to a Node tree # This file released to the Public Domain by David Mertz from . import parser from rnc2rng.parser import ( ANNO_ATTR, ANNOTATION, ANY, ASSIGN, ATTR, CHOICE, DATATAG, DATATYPES, DEFAULT_NS, DEFINE, DIV, DOCUMENTATION, ELEM, EMPTY, EXCEPT, GRAMMAR, GROUP, INTERLEAVE, LIST, LITERAL, MAYBE, MIXED, NAME, NOT_ALLOWED, NS, PARAM, PARENT, REF, ROOT, SEQ, SOME, TEXT, LITERAL_TYPE ) import html QUANTS = {SOME: 'oneOrMore', MAYBE: 'optional', ANY: 'zeroOrMore'} TYPELIBS = { 'xsd': 'http://www.w3.org/2001/XMLSchema-datatypes' } NAMESPACES = { 'a': 'http://relaxng.org/ns/compatibility/annotations/1.0', 'xml': 'http://www.w3.org/XML/1998/namespace', } class XMLSerializer(object): def __init__(self, indent=None): self.indent = indent or ' ' self.reset() def reset(self): self.buf = [] self.ns = {} self.typelibs = {} self.default = '' self.level = 0 def write(self, s): self.buf.append(self.indent * self.level + s) def datatype_library(self, prefix): assert prefix in self.typelibs or prefix in TYPELIBS, prefix if prefix not in self.typelibs: self.typelibs[prefix] = TYPELIBS[prefix] return self.typelibs[prefix] def namespace(self, ns): assert ns in self.ns or ns in NAMESPACES, ns if ns not in self.ns: self.ns[ns] = NAMESPACES[ns] return self.ns[ns] def toxml(self, node): self.reset() for n in node.value: if n.type == DATATYPES: self.typelibs[n.name] = n.value[0] elif n.type == DEFAULT_NS: self.default = n.value[0] if n.name is not None: self.ns[n.name] = n.value[0] elif n.type == NS: self.ns[n.name] = n.value[0] prelude = [''] prelude.append('' self.write('') return '\n'.join(prelude + self.buf) def anno_attrs(self, nodes): select = lambda n: isinstance(n, parser.Node) and n.type == ANNO_ATTR pairs = [(n.name, html.escape(n.value[0])) for n in nodes if select(n)] if not pairs: return '' return ' ' + ' '.join('%s="%s"' % attr for attr in pairs) def type_attrs(self, name): if ':' in name: prefix, name = name.split(':', 1) ns = self.datatype_library(prefix) else: assert name in ('string', 'token') # these are the only "built-in" datatypes ns = "" attrs = ' type="%s"' % name if ns != TYPELIBS['xsd']: attrs += ' datatypeLibrary="%s"' % ns # write all exceptions explicitly return attrs def visit(self, nodes, ctx=None, indent=True): '''Visiting a list of nodes, writes out the XML content to the internal line-based buffer. By default, adds one level of indentation to the output compared to the caller's level; passing False as the second argument will prevent this from happening.''' if indent: self.level += 1 for x in nodes: if not isinstance(x, parser.Node): raise TypeError("Not a Node: " + repr(x)) elif x.type in set([ANNO_ATTR, LITERAL_TYPE, DATATYPES, DEFAULT_NS, NS]): continue attribs = self.anno_attrs(x.value) if x.type == DEFINE: for op in (x.name for x in x.value if x.type == 'ASSIGN'): modes = {'|=': 'choice', '&=': 'interleave'} if op in modes: attribs = (' combine="%s"' % modes[op]) + attribs break; if x.name == 'start': self.write('' % attribs) else: bits = x.name, attribs self.write('' % bits) self.visit(x.value) if x.name == 'start': self.write('') else: self.write('') elif x.type == ASSIGN: self.visit(x.value, indent=False) elif x.type == GRAMMAR: self.write('') self.visit(x.value) self.write('') elif x.type in set([MAYBE, SOME, ANY]): self.write('<%s>' % QUANTS[x.type]) self.visit(x.value) self.write('' % QUANTS[x.type]) elif x.type in set([INTERLEAVE, CHOICE, MIXED, LIST, DIV]): self.write('<%s>' % x.type.lower()) self.visit(x.value) self.write('' % x.type.lower()) elif x.type == EXCEPT: self.write('') self.visit(x.value, ctx=ctx) self.write('') elif x.type == NAME: if not x.value and '*' in x.name: if x.name == '*': self.write('') else: uri = self.ns[x.name.split(':', 1)[0]] self.write('' % uri) elif x.value: if x.name == '*': self.write('') else: uri = self.ns[x.name.split(':', 1)[0]] self.write('' % uri) self.visit(x.value, ctx=ctx) if x.name == '*': self.write('') else: self.write('') else: ns = '' if ctx == 'ATTR' else self.default name = x.name if ':' in name: parts = x.name.split(':', 1) ns = self.namespace(parts[0]) name = parts[1] self.write('%s' % (ns, name)) elif x.type in set([REF, PARENT]): bits = x.type.lower(), x.name, attribs if not x.value: # no parameters self.write('<%s name="%s"%s/>' % bits) else: self.write('<%s name="%s"%s>' % bits) self.visit(x.value) self.write('' % x.type.lower()) elif x.type == LITERAL: types = [n.name for n in x.value if isinstance(n, parser.Node) and n.type == LITERAL_TYPE] if types: assert len(types) == 1 attribs += self.type_attrs(types[0]) bits = attribs, html.escape(x.name) self.write('%s' % bits) self.visit(x.value, indent=False) elif x.type == ANNOTATION: literals, rest = [], [] for n in x.value: if n.type == LITERAL: literals.append(n.name) elif n.type != ANNO_ATTR: rest.append(n) end = '/' if not (literals or rest) else '' tail = '' if literals and not rest: tail = html.escape(''.join(literals)) + '' % x.name bits = x.name, attribs, end, tail if ':' in x.name: parts = x.name.split(':', 1) ns = self.namespace(parts[0]) self.write('<%s%s%s>%s' % bits) if not rest: continue for n in x.value: if n.type == ANNO_ATTR: continue elif n.type == LITERAL: self.level += 1 self.write(html.escape(n.name)) self.level -= 1 else: self.visit([n]) self.write('' % x.name) elif x.type == DOCUMENTATION: xmlns_attr = '' if self.namespace('a') != NAMESPACES['a']: xmlns_attr = ' xmlns:a="%s"' % NAMESPACES['a'] # the user is already using namespace a: for something else fmt = '%s' self.write(fmt % (xmlns_attr, html.escape('\n'.join(x.value)))) elif x.type == GROUP: if len(x.value) == 1 and x.value[0].type != SEQ: self.visit(x.value, indent=False) else: self.write('<%s>' % x.type.lower()) self.visit(x.value) self.write('' % x.type.lower()) elif x.type == NOT_ALLOWED: self.write('') elif x.type in set([TEXT, EMPTY]): self.write('<%s/>' % x.type.lower()) elif x.type == SEQ: self.visit(x.value, indent=False) elif x.type == DATATAG: if not x.value: # no parameters self.write('' % self.type_attrs(x.name)) else: self.write('' % self.type_attrs(x.name)) self.visit(x.value) self.write('') elif x.type == PARAM: bits = x.name, html.escape(x.value[0]) self.write('%s' % bits) elif x.type == ELEM: self.write('' % attribs) self.visit(x.value) self.write('') elif x.type == ATTR: self.write('' % attribs) self.visit(x.value, ctx=x.type) self.write('') elif x.type == ROOT: for n in x.value: # Record included document's custom datatypes if n.type == DATATYPES: self.typelibs[n.name] = n.value[0] # Verify the included document has the same metadata if n.type == DEFAULT_NS: assert self.default == n.value[0] elif n.type == NS: assert n.name in self.ns assert self.ns[n.name] == n.value[0] self.visit(x.value, indent=False) else: assert False, x if indent: self.level -= 1 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711714602.3555543 rnc2rng-2.7.0/rnc2rng.egg-info/0000755000076500000240000000000000000000000014564 5ustar00djcstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714602.0 rnc2rng-2.7.0/rnc2rng.egg-info/PKG-INFO0000644000076500000240000000545700000000000015674 0ustar00djcstaffMetadata-Version: 2.1 Name: rnc2rng Version: 2.7.0 Summary: RELAX NG Compact to regular syntax conversion library Home-page: https://github.com/djc/rnc2rng Author: David Mertz Maintainer: Dirkjan Ochtman Maintainer-email: dirkjan@ochtman.nl Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Operating System :: OS Independent Classifier: License :: OSI Approved :: MIT License Classifier: Topic :: Text Processing :: Markup :: XML Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 License-File: LICENSE License-File: AUTHORS Requires-Dist: rply RELAX NG Compact to RELAX NG conversion library =============================================== .. image:: https://github.com/djc/rnc2rng/workflows/CI/badge.svg :target: https://github.com/djc/rnc2rng/actions?query=workflow%3ACI .. image:: https://coveralls.io/repos/djc/rnc2rng/badge.svg?branch=master&service=github :target: https://coveralls.io/github/djc/rnc2rng?branch=master Converts RELAX NG schemata in Compact syntax (`rnc`) to the equivalent schema in the XML-based default RELAX NG syntax. Dependencies: - Python 3.x (tested with 3.7, 3.8, 3.9) - `rply`_ Feedback welcome on `GitHub`_. Please consider funding continued maintenance of this project through `Patreon`_ or `GitHub Sponsors`_. .. _GitHub: https://github.com/djc/rnc2rng .. _rply: https://pypi.python.org/pypi/rply .. _Patreon: https://patreon.com/dochtman .. _GitHub Sponsors: https://github.com/sponsors/djc History ------- rnc2rng was originally written by `David Mertz`_ in 2003 and published as part of a collection of files around RELAX NG `on his site`_ into the Public Domain. `Hartmut Goebel`_ published it as a package on PyPI to make it easier to access. It was mirrored on GitHub by `Dustin J. Mitchell`_ in 2010 after he fixed some bugs. `Timmy Zhu`_ forked his repository and contributed further enhancements. Recently, I (Dirkjan Ochtman) was interested in playing with RELAX NG Compact and started making further updates. I asked Hartmut for maintainership on PyPI and received it. While I cannot promise many updates, I should be responsive to bug reports and (especially!) pull requests. .. _David Mertz: http://www.gnosis.cx/publish/ .. _on his site: http://www.gnosis.cx/download/relax/ .. _Hartmut Goebel: http://www.goebel-consult.de/ .. _Dustin J. Mitchell: http://code.v.igoro.us/ .. _Timmy Zhu: https://github.com/nattofriends How to install -------------- The usual should work: .. code-block:: shell $ sudo pip install . Getting started --------------- .. code-block:: shell $ python -m rnc2rng test.rnc > test.rng License ------- All of the code is released under MIT License. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714602.0 rnc2rng-2.7.0/rnc2rng.egg-info/SOURCES.txt0000644000076500000240000000225700000000000016456 0ustar00djcstaffAUTHORS LICENSE MANIFEST.in README.rst setup.cfg setup.py test.py rnc2rng/__init__.py rnc2rng/__main__.py rnc2rng/parser.py rnc2rng/rnctree.py rnc2rng/serializer.py rnc2rng.egg-info/PKG-INFO rnc2rng.egg-info/SOURCES.txt rnc2rng.egg-info/dependency_links.txt rnc2rng.egg-info/entry_points.txt rnc2rng.egg-info/requires.txt rnc2rng.egg-info/top_level.txt tests/annotations.rnc tests/annotations.rng tests/attr-no-default-ns.rnc tests/attr-no-default-ns.rng tests/datatypes.rnc tests/datatypes.rng tests/default-ns.rnc tests/default-ns.rng tests/documentation.rnc tests/documentation.rng tests/encoding.rng tests/encoding_utf16be.rnc tests/encoding_utf16be.rng tests/encoding_utf16le.rnc tests/encoding_utf16le.rng tests/encoding_utf8.rnc tests/encoding_utf8.rng tests/features.rnc tests/features.rng tests/include.rnc tests/include.rng tests/ns-concat.rnc tests/ns-concat.rng tests/ns.rnc tests/ns.rng tests/qid.rnc tests/qid.rng tests/relax.rnc tests/relax.rng tests/simple-escape.rnc tests/simple-escape.rng tests/simple.rnc tests/simple.rng tests/spec-6.1-1.rnc tests/spec-6.1-1.rng tests/spec-6.1-2.rnc tests/spec-6.1-2.rng tests/svn.rnc tests/svn.rng tests/type-pattern.rnc tests/type-pattern.rng././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714602.0 rnc2rng-2.7.0/rnc2rng.egg-info/dependency_links.txt0000644000076500000240000000000100000000000020632 0ustar00djcstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714602.0 rnc2rng-2.7.0/rnc2rng.egg-info/entry_points.txt0000644000076500000240000000006200000000000020060 0ustar00djcstaff[console_scripts] rnc2rng = rnc2rng.__main__:main ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714602.0 rnc2rng-2.7.0/rnc2rng.egg-info/requires.txt0000644000076500000240000000000500000000000017157 0ustar00djcstaffrply ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714602.0 rnc2rng-2.7.0/rnc2rng.egg-info/top_level.txt0000644000076500000240000000001000000000000017305 0ustar00djcstaffrnc2rng ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711714602.3562596 rnc2rng-2.7.0/setup.cfg0000644000076500000240000000015400000000000013340 0ustar00djcstaff[flake8] ignore = E261,E301,E302,E305,E401,E731 max_line_length = 99 [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/setup.py0000644000076500000240000000172600000000000013237 0ustar00djcstafffrom setuptools import setup setup( name='rnc2rng', version='2.7.0', url='https://github.com/djc/rnc2rng', author='David Mertz', description='RELAX NG Compact to regular syntax conversion library', long_description=open('README.rst').read(), maintainer='Dirkjan Ochtman', maintainer_email='dirkjan@ochtman.nl', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Operating System :: OS Independent', 'License :: OSI Approved :: MIT License', 'Topic :: Text Processing :: Markup :: XML', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', ], packages=['rnc2rng'], entry_points={ 'console_scripts': [ 'rnc2rng = rnc2rng.__main__:main', ], }, install_requires=['rply'], ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/test.py0000644000076500000240000000370500000000000013055 0ustar00djcstaffimport rnc2rng import unittest, os import sys if sys.version_info[0] < 3: from urllib import pathname2url, url2pathname from urlparse import urljoin, urlparse else: from urllib.parse import urljoin, urlparse from urllib.request import pathname2url, url2pathname class TestUtils(unittest.TestCase): def assertBestEqual(self, expected, actual): if hasattr(self, 'assertMultiLineEqual'): self.assertMultiLineEqual(expected, actual) else: self.assertEqual(expected, actual) class FileTest(TestUtils): def __init__(self, fn): unittest.TestCase.__init__(self) self.fn = fn self.maxDiff = None def __str__(self): return 'TestCase(%r)' % self.fn def runTest(self): root = rnc2rng.load(self.fn) ref = self.fn.replace('.rnc', '.rng') if ref.startswith('file:'): parse_result = urlparse(ref) ref = url2pathname(parse_result.path) with open(ref) as f: expected = f.read().rstrip() actual = rnc2rng.dumps(root).strip() self.assertBestEqual(expected, actual) class APITests(TestUtils): def test_from_string(self): with open('tests/features.rnc') as f: src = f.read() with open('tests/features.rng') as f: expected = f.read().rstrip() actual = rnc2rng.dumps(rnc2rng.loads(src)).strip() self.assertBestEqual(expected, actual) def suite(): suite = unittest.TestLoader().loadTestsFromTestCase(APITests) for fn in os.listdir('tests'): if not fn.endswith('.rnc'): continue fn = os.path.join('tests', fn) suite.addTest(FileTest(fn)) # synthesize a test that reads its input from a URL url_test_path = os.path.abspath('tests/include.rnc') suite.addTest(FileTest(urljoin('file:', pathname2url(url_test_path)))) return suite if __name__ == '__main__': unittest.main(defaultTest='suite') ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711714602.3553848 rnc2rng-2.7.0/tests/0000755000076500000240000000000000000000000012661 5ustar00djcstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/annotations.rnc0000644000076500000240000000230700000000000015724 0ustar00djcstaffnamespace x = "http://www.example.com" namespace dc = "http://purl.org/dc/elements/1.1/" namespace sch = "http://www.ascc.net/xml/schematron" namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0" x:entity [ name="picture" systemId="picture.jpg" notation="jpg" ] dc:title [ "Foo without contents & escaped" ] sch:ns [ uri = "http://purl.org/dc/elements/1.1" prefix = "dc" ] sch:pattern [ name = "Some thing & other" sch:rule [ context = "//foo" sch:assert [ test = "@bar = /@bar" "attrib matches top-level attribute" ] ] sch:rule [ context = "//barfoo" sch:assert [ test = "@quick = @fast" "tautology of speediness" ] ] "one literal & next:" "two literal" ] [ a:documentation [ dc:title [ "schema starts here" ] ] ] div { foo = element foo { [ a:defaultValue = "1.0" ] attribute version { "1.0" } } } start = foo ## documentation for definition ## indented continuation on the next line ## # subheading with leading # (perhaps markdown-style head) bar = element bar { empty } baz = element baz { ## documentation for a group ( foo, ## documentation for a ref bar ) } ## combining definition baz |= empty ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/annotations.rng0000644000076500000240000000370200000000000015730 0ustar00djcstaff Foo without contents & escaped attrib matches top-level attribute tautology of speediness one literal & next: two literal
schema starts here foo version 1.0
documentation for definition indented continuation on the next line # subheading with leading # (perhaps markdown-style head) bar baz documentation for a group documentation for a ref combining definition
././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/attr-no-default-ns.rnc0000644000076500000240000000014100000000000017005 0ustar00djcstaffdefault namespace = "https://namespace.com" MyEl = element elem { attribute * - utils { text } } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/attr-no-default-ns.rng0000644000076500000240000000064500000000000017022 0ustar00djcstaff elem utils ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/datatypes.rnc0000644000076500000240000000074700000000000015373 0ustar00djcstaffdatatypes xsd = "http://www.w3.org/2001/XMLSchema-datatypes" datatypes custom="uri:custom-datatype-library" start = element token { token }, element token_abc { token "abc" }, element string { string }, element string_abc { string "abc" }, element xsd_string { xsd:string }, element xsd_string_abc { xsd:string "abc" }, element xsd_double { xsd:double }, element xsd_double_42 { xsd:double "42" }, element custom_foo { custom:foo }, element custom_foo_abc { custom:foo "abc" } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/datatypes.rng0000644000076500000240000000243400000000000015372 0ustar00djcstaff token token_abc abc string string_abc abc xsd_string xsd_string_abc abc xsd_double xsd_double_42 42 custom_foo custom_foo_abc abc ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/default-ns.rnc0000644000076500000240000000012200000000000015422 0ustar00djcstaffdefault namespace = "http://example.com" element foo { attribute bar { string } } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/default-ns.rng0000644000076500000240000000053700000000000015440 0ustar00djcstaff foo bar ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/documentation.rnc0000644000076500000240000000012600000000000016235 0ustar00djcstaff## Represents a language element lang { ## English "en" | ## Japanese "jp" } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/documentation.rng0000644000076500000240000000077000000000000016246 0ustar00djcstaff Represents a language lang en English jp Japanese ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/encoding.rng0000644000076500000240000000030500000000000015155 0ustar00djcstaff foo ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/encoding_utf16be.rnc0000644000076500000240000000011000000000000016477 0ustar00djcstaffþÿelement foo { text } # en-dash ( ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/encoding_utf16be.rng0000644000076500000240000000030500000000000016511 0ustar00djcstaff foo ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/encoding_utf16le.rnc0000644000076500000240000000011000000000000016511 0ustar00djcstaffÿþelement foo { text } # en-dash ( ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/encoding_utf16le.rng0000644000076500000240000000030500000000000016523 0ustar00djcstaff foo ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/encoding_utf8.rnc0000644000076500000240000000004500000000000016120 0ustar00djcstaffelement foo { text } # en-dash (–) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/encoding_utf8.rng0000644000076500000240000000030500000000000016123 0ustar00djcstaff foo ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/features.rnc0000644000076500000240000000137600000000000015212 0ustar00djcstaffZ = element z { empty } Foo = "foo" start = element a { attribute b { "c" }, element d { Z }*, # e element f { "g & gg" }?, element h { (element i { empty } | element j { empty }) & (element k { xsd:integer { maxInclusive = "65535" } }) }+, element l { (attribute m { "n" } & attribute o { "p" | "q" } & attribute r { text }), attribute s { string { pattern = "t" } }, attribute t { parent Z }?, attribute u { Foo } }, element v { mixed { element w { empty }, element y { empty } }, list { element z { notAllowed } } } } div { start = element X { empty } start |= element Y { empty } Z &= element zz { empty } } aa = element aa { (attribute ab { empty }, attribute ac { empty }?) } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/features.rng0000644000076500000240000000660000000000000015211 0ustar00djcstaff z foo a b c d f g & gg h i j k 65535 l m n o p q r s t t u v w y z
X Y zz
aa ab ac
././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/include.rnc0000644000076500000240000000015200000000000015006 0ustar00djcstaffdatatypes xsd = "http://www.w3.org/2001/XMLSchema-datatypes" include "datatypes.rnc" include "simple.rnc" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/include.rng0000644000076500000240000000257100000000000015021 0ustar00djcstaff token token_abc abc string string_abc abc xsd_string xsd_string_abc abc xsd_double xsd_double_42 42 custom_foo custom_foo_abc abc foo ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/ns-concat.rnc0000644000076500000240000000015500000000000015253 0ustar00djcstaffnamespace rng = "http://" ~ "relaxng.org/" ~ "ns/" ~ "structure/" ~ "1.0" element rng:text { empty } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/ns-concat.rng0000644000076500000240000000044300000000000015257 0ustar00djcstaff text ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/ns.rnc0000644000076500000240000000012100000000000013777 0ustar00djcstaffnamespace rng = "http://relaxng.org/ns/structure/1.0" element rng:text { empty } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/ns.rng0000644000076500000240000000044300000000000014012 0ustar00djcstaff text ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/qid.rnc0000644000076500000240000000012000000000000014133 0ustar00djcstaffdefault namespace = "http://example.com" foo = element bar { \baz } \baz = text ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/qid.rng0000644000076500000240000000047500000000000014154 0ustar00djcstaff bar ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/relax.rnc0000644000076500000240000000407600000000000014507 0ustar00djcstaff# RELAX NG XML syntax specified in compact syntax. default namespace rng = "http://relaxng.org/ns/structure/1.0" namespace local = "" datatypes xsd = "http://www.w3.org/2001/XMLSchema-datatypes" start = pattern pattern = element element { (nameQName | nameClass), (common & pattern+) } | element attribute { (nameQName | nameClass), (common & pattern?) } | element group|interleave|choice|optional |zeroOrMore|oneOrMore|list|mixed { common & pattern+ } | element ref|parentRef { nameNCName, common } | element empty|notAllowed|text { common } | element data { type, param*, (common & exceptPattern?) } | element value { commonAttributes, type?, xsd:string } | element externalRef { href, common } | element grammar { common & grammarContent* } param = element param { commonAttributes, nameNCName, xsd:string } exceptPattern = element except { common & pattern+ } grammarContent = definition | element div { common & grammarContent* } | element include { href, (common & includeContent*) } includeContent = definition | element div { common & includeContent* } definition = element start { combine?, (common & pattern+) } | element define { nameNCName, combine?, (common & pattern+) } combine = attribute combine { "choice" | "interleave" } nameClass = element name { commonAttributes, xsd:QName } | element anyName { common & exceptNameClass? } | element nsName { common & exceptNameClass? } | element choice { common & nameClass+ } exceptNameClass = element except { common & nameClass+ } nameQName = attribute name { xsd:QName } nameNCName = attribute name { xsd:NCName } href = attribute href { xsd:anyURI } type = attribute type { xsd:NCName } common = commonAttributes, foreignElement* commonAttributes = attribute ns { xsd:string }?, attribute datatypeLibrary { xsd:anyURI }?, foreignAttribute* foreignElement = element * - rng:* { (anyAttribute | text | anyElement)* } foreignAttribute = attribute * - (rng:*|local:*) { text } anyElement = element * { (anyAttribute | text | anyElement)* } anyAttribute = attribute * { text } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/relax.rng0000644000076500000240000002224300000000000014507 0ustar00djcstaff element attribute group interleave choice optional zeroOrMore oneOrMore list mixed ref parentRef empty notAllowed text data value externalRef grammar param except div include div start define combine choice interleave name anyName nsName choice except name name href type ns datatypeLibrary ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/simple-escape.rnc0000644000076500000240000000004200000000000016110 0ustar00djcstaffelement string { text } # comment ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/simple-escape.rng0000644000076500000240000000031000000000000016112 0ustar00djcstaff string ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/simple.rnc0000644000076500000240000000003700000000000014656 0ustar00djcstaffelement foo { text } # comment ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/simple.rng0000644000076500000240000000030500000000000014660 0ustar00djcstaff foo ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/spec-6.1-1.rnc0000644000076500000240000000003400000000000014754 0ustar00djcstaffstart = element * { empty } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/spec-6.1-1.rng0000644000076500000240000000027200000000000014764 0ustar00djcstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/spec-6.1-2.rnc0000644000076500000240000000003200000000000014753 0ustar00djcstaffelement * - foo { empty } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/spec-6.1-2.rng0000644000076500000240000000041600000000000014765 0ustar00djcstaff foo ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/svn.rnc0000644000076500000240000000067500000000000014203 0ustar00djcstaffnamespace xsd = "http://www.w3.org/2001/XMLSchema" grammar { start = element svn { # Path to the svn dump file attribute dump-file { xsd:string }?, # Content of the .SVNAccessFile inline in the XML (element access-file { xsd:string }? # E-mail subscriptions & element notification { attribute path { xsd:string }, attribute emails { xsd:string } }*) } } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/svn.rng0000644000076500000240000000213300000000000014176 0ustar00djcstaff svn dump-file access-file notification path emails ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/type-pattern.rnc0000644000076500000240000000005600000000000016022 0ustar00djcstaffelement foo { string { pattern = "[abc]+" } } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711714489.0 rnc2rng-2.7.0/tests/type-pattern.rng0000644000076500000240000000044000000000000016023 0ustar00djcstaff foo [abc]+