rng 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/.rubocop_todo.yml +64 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/Gemfile +3 -1
- data/README.adoc +402 -0
- data/lib/rng/any_name.rb +26 -0
- data/lib/rng/attribute.rb +58 -4
- data/lib/rng/choice.rb +60 -0
- data/lib/rng/data.rb +32 -0
- data/lib/rng/define.rb +51 -3
- data/lib/rng/element.rb +62 -16
- data/lib/rng/empty.rb +23 -0
- data/lib/rng/except.rb +62 -0
- data/lib/rng/external_ref.rb +28 -0
- data/lib/rng/grammar.rb +36 -0
- data/lib/rng/group.rb +60 -0
- data/lib/rng/include.rb +24 -0
- data/lib/rng/interleave.rb +58 -0
- data/lib/rng/list.rb +56 -0
- data/lib/rng/mixed.rb +58 -0
- data/lib/rng/name.rb +28 -0
- data/lib/rng/not_allowed.rb +23 -0
- data/lib/rng/ns_name.rb +31 -0
- data/lib/rng/one_or_more.rb +58 -0
- data/lib/rng/optional.rb +58 -0
- data/lib/rng/param.rb +30 -0
- data/lib/rng/parent_ref.rb +28 -0
- data/lib/rng/parse_rnc.rb +26 -0
- data/lib/rng/pattern.rb +24 -0
- data/lib/rng/ref.rb +28 -0
- data/lib/rng/rnc_parser.rb +351 -94
- data/lib/rng/start.rb +54 -5
- data/lib/rng/text.rb +26 -0
- data/lib/rng/to_rnc.rb +55 -0
- data/lib/rng/value.rb +29 -0
- data/lib/rng/version.rb +1 -1
- data/lib/rng/zero_or_more.rb +58 -0
- data/lib/rng.rb +29 -5
- data/rng.gemspec +3 -2
- data/spec/fixtures/rnc/address_book.rnc +10 -0
- data/spec/fixtures/rnc/complex_example.rnc +61 -0
- data/spec/fixtures/rng/address_book.rng +20 -0
- data/spec/fixtures/rng/relaxng.rng +335 -0
- data/spec/fixtures/rng/testSuite.rng +163 -0
- data/spec/fixtures/spectest.xml +6845 -0
- data/spec/rng/rnc_parser_spec.rb +6 -4
- data/spec/rng/rnc_roundtrip_spec.rb +121 -0
- data/spec/rng/schema_spec.rb +115 -166
- data/spec/rng/spectest_spec.rb +195 -0
- data/spec/spec_helper.rb +33 -0
- metadata +54 -7
- data/lib/rng/builder.rb +0 -158
- data/lib/rng/rng_parser.rb +0 -107
- data/lib/rng/schema.rb +0 -18
- data/spec/rng/rng_parser_spec.rb +0 -102
data/lib/rng/rnc_parser.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "parslet"
|
2
|
-
|
4
|
+
require "nokogiri"
|
5
|
+
require_relative "grammar"
|
3
6
|
|
4
7
|
module Rng
|
5
8
|
class RncParser < Parslet::Parser
|
@@ -12,125 +15,379 @@ module Rng
|
|
12
15
|
rule(:comma?) { (whitespace >> comma >> whitespace).maybe }
|
13
16
|
|
14
17
|
rule(:identifier) { match("[a-zA-Z0-9_]").repeat(1).as(:identifier) }
|
18
|
+
rule(:namespace_prefix) { identifier.as(:prefix) >> str(":") }
|
19
|
+
rule(:namespace_prefix?) { namespace_prefix.maybe }
|
20
|
+
rule(:qualified_name) { namespace_prefix? >> identifier.as(:local_name) }
|
21
|
+
|
22
|
+
rule(:datatype_library) { str("datatypes") >> space >> identifier.as(:prefix) >> space >> string_literal.as(:uri) }
|
15
23
|
|
16
|
-
rule(:
|
24
|
+
rule(:string_literal) { str('"') >> match('[^"]').repeat.as(:string) >> str('"') }
|
25
|
+
|
26
|
+
rule(:element_def) do
|
17
27
|
str("element") >> space >>
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
28
|
+
qualified_name.as(:name) >>
|
29
|
+
whitespace >>
|
30
|
+
str("{") >>
|
31
|
+
whitespace >>
|
32
|
+
content.maybe.as(:content) >>
|
33
|
+
whitespace >>
|
34
|
+
str("}") >>
|
35
|
+
(str("*") | str("+") | str("?")).maybe.as(:occurrence)
|
36
|
+
end
|
27
37
|
|
28
|
-
rule(:attribute_def)
|
38
|
+
rule(:attribute_def) do
|
29
39
|
str("attribute") >> space >>
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
40
|
+
qualified_name.as(:name) >>
|
41
|
+
whitespace >>
|
42
|
+
str("{") >>
|
43
|
+
whitespace >>
|
44
|
+
(datatype_ref | str("text")).as(:type) >>
|
45
|
+
whitespace >>
|
46
|
+
str("}")
|
47
|
+
end
|
48
|
+
|
49
|
+
rule(:datatype_ref) do
|
50
|
+
identifier.as(:prefix) >> str(":") >> identifier.as(:type)
|
51
|
+
end
|
38
52
|
|
39
53
|
rule(:text_def) { str("text").as(:text) }
|
54
|
+
rule(:empty_def) { str("empty").as(:empty) }
|
40
55
|
|
41
|
-
rule(:
|
42
|
-
((
|
43
|
-
|
56
|
+
rule(:group_def) do
|
57
|
+
str("(") >>
|
58
|
+
whitespace >>
|
59
|
+
content.as(:group) >>
|
60
|
+
whitespace >>
|
61
|
+
str(")") >>
|
62
|
+
(str("*") | str("+") | str("?")).maybe.as(:occurrence)
|
63
|
+
end
|
64
|
+
|
65
|
+
rule(:choice_def) do
|
66
|
+
content_item.as(:first) >>
|
67
|
+
(whitespace >> str("|") >> whitespace >> content_item.as(:second)).repeat(1).as(:rest)
|
68
|
+
end
|
44
69
|
|
45
|
-
rule(:
|
70
|
+
rule(:named_pattern) do
|
71
|
+
identifier.as(:name) >> whitespace >> str("=") >> whitespace >> content_item.as(:pattern)
|
72
|
+
end
|
46
73
|
|
47
|
-
rule(:
|
74
|
+
rule(:content_item) do
|
75
|
+
element_def | attribute_def | text_def | empty_def | group_def | choice_def | identifier.as(:ref)
|
76
|
+
end
|
77
|
+
|
78
|
+
rule(:content) do
|
79
|
+
(content_item >> (comma? >> content_item).repeat).as(:items)
|
80
|
+
end
|
81
|
+
|
82
|
+
rule(:start_def) do
|
83
|
+
str("start") >> whitespace >> str("=") >> whitespace >> content_item.as(:start)
|
84
|
+
end
|
85
|
+
|
86
|
+
rule(:grammar) do
|
87
|
+
whitespace >>
|
88
|
+
datatype_library.maybe.as(:datatype_library) >>
|
89
|
+
whitespace >>
|
90
|
+
(start_def | named_pattern | element_def).as(:root) >>
|
91
|
+
(whitespace >> (named_pattern | element_def)).repeat.as(:definitions) >>
|
92
|
+
whitespace
|
93
|
+
end
|
48
94
|
|
49
95
|
root(:grammar)
|
50
96
|
|
51
|
-
def parse(input)
|
52
|
-
|
53
|
-
|
97
|
+
def self.parse(input)
|
98
|
+
parser = new
|
99
|
+
tree = parser.parse(input.strip)
|
100
|
+
convert_to_rng(tree)
|
54
101
|
end
|
55
102
|
|
56
|
-
|
103
|
+
def self.to_rnc(schema)
|
104
|
+
# Convert RNG schema to RNC
|
105
|
+
builder = RncBuilder.new
|
106
|
+
builder.build(schema)
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.convert_to_rng(tree)
|
110
|
+
builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
|
111
|
+
if tree[:root].key?(:start)
|
112
|
+
# This is a grammar with named patterns
|
113
|
+
xml.grammar(xmlns: "http://relaxng.org/ns/structure/1.0") do
|
114
|
+
# Add datatype library if present
|
115
|
+
xml.datatypeLibrary tree[:datatype_library][:uri][:string].to_s if tree[:datatype_library]
|
116
|
+
|
117
|
+
# Process start pattern
|
118
|
+
xml.start do
|
119
|
+
process_content_item(xml, tree[:root][:start])
|
120
|
+
end
|
121
|
+
|
122
|
+
# Process named patterns
|
123
|
+
tree[:definitions]&.each do |def_item|
|
124
|
+
next unless def_item.key?(:name)
|
125
|
+
|
126
|
+
xml.define(name: def_item[:name][:identifier].to_s) do
|
127
|
+
process_content_item(xml, def_item[:pattern])
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
else
|
132
|
+
# This is a simple element pattern
|
133
|
+
process_content_item(xml, tree[:root])
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
builder.to_xml
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.process_content_item(xml, item)
|
141
|
+
if item.key?(:name)
|
142
|
+
# Element definition
|
143
|
+
attrs = {}
|
144
|
+
attrs[:name] = item[:name][:local_name][:identifier].to_s
|
145
|
+
|
146
|
+
attrs[:ns] = item[:name][:prefix][:identifier].to_s if item[:name][:prefix]
|
147
|
+
|
148
|
+
xml.element(attrs) do
|
149
|
+
if item[:content]
|
150
|
+
item[:content][:items].each do |content_item|
|
151
|
+
process_content_item(xml, content_item)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Handle occurrence
|
157
|
+
if item[:occurrence]
|
158
|
+
case item[:occurrence].to_s
|
159
|
+
when "*"
|
160
|
+
xml.parent.name = "zeroOrMore"
|
161
|
+
when "+"
|
162
|
+
xml.parent.name = "oneOrMore"
|
163
|
+
when "?"
|
164
|
+
xml.parent.name = "optional"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
elsif item.key?(:attr_name)
|
168
|
+
# Attribute definition
|
169
|
+
attrs = {}
|
170
|
+
attrs[:name] = item[:attr_name][:local_name][:identifier].to_s
|
171
|
+
|
172
|
+
attrs[:ns] = item[:attr_name][:prefix][:identifier].to_s if item[:attr_name][:prefix]
|
173
|
+
|
174
|
+
xml.attribute(attrs) do
|
175
|
+
if item[:type] == "text"
|
176
|
+
xml.text
|
177
|
+
elsif item[:type].key?(:prefix)
|
178
|
+
xml.data(type: item[:type][:type][:identifier].to_s,
|
179
|
+
datatypeLibrary: "http://www.w3.org/2001/XMLSchema-datatypes")
|
180
|
+
end
|
181
|
+
end
|
182
|
+
elsif item.key?(:text)
|
183
|
+
xml.text
|
184
|
+
elsif item.key?(:empty)
|
185
|
+
xml.empty
|
186
|
+
elsif item.key?(:group)
|
187
|
+
xml.group do
|
188
|
+
item[:group][:items].each do |group_item|
|
189
|
+
process_content_item(xml, group_item)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Handle occurrence
|
194
|
+
if item[:occurrence]
|
195
|
+
case item[:occurrence].to_s
|
196
|
+
when "*"
|
197
|
+
xml.parent.name = "zeroOrMore"
|
198
|
+
when "+"
|
199
|
+
xml.parent.name = "oneOrMore"
|
200
|
+
when "?"
|
201
|
+
xml.parent.name = "optional"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
elsif item.key?(:first) && item.key?(:rest)
|
205
|
+
# Choice definition
|
206
|
+
xml.choice do
|
207
|
+
process_content_item(xml, item[:first])
|
208
|
+
item[:rest].each do |choice_item|
|
209
|
+
process_content_item(xml, choice_item[:second])
|
210
|
+
end
|
211
|
+
end
|
212
|
+
elsif item.key?(:ref)
|
213
|
+
# Reference to a named pattern
|
214
|
+
xml.ref(name: item[:ref][:identifier].to_s)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
class RncBuilder
|
220
|
+
def build(schema)
|
221
|
+
if schema.element
|
222
|
+
# Simple element pattern
|
223
|
+
build_element(schema.element)
|
224
|
+
else
|
225
|
+
# Grammar with named patterns
|
226
|
+
result = []
|
227
|
+
|
228
|
+
# Add datatype library if present
|
229
|
+
if schema.datatypeLibrary
|
230
|
+
result << "datatypes xsd = \"#{schema.datatypeLibrary}\""
|
231
|
+
result << ""
|
232
|
+
end
|
233
|
+
|
234
|
+
# Process start pattern
|
235
|
+
if schema.start
|
236
|
+
result << "start = #{build_pattern(schema.start)}"
|
237
|
+
result << ""
|
238
|
+
end
|
239
|
+
|
240
|
+
# Process named patterns
|
241
|
+
if schema.define && !schema.define.empty?
|
242
|
+
schema.define.each do |define|
|
243
|
+
result << "#{define.name} = #{build_pattern(define)}"
|
244
|
+
result << ""
|
245
|
+
end
|
246
|
+
end
|
57
247
|
|
58
|
-
|
59
|
-
|
60
|
-
Schema.new(
|
61
|
-
start: Start.new(
|
62
|
-
elements: [build_element(element)],
|
63
|
-
),
|
64
|
-
)
|
248
|
+
result.join("\n")
|
249
|
+
end
|
65
250
|
end
|
66
251
|
|
252
|
+
private
|
253
|
+
|
67
254
|
def build_element(element)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
current_attributes = []
|
83
|
-
|
84
|
-
content.each do |item|
|
85
|
-
case
|
86
|
-
when item[:item][:name] || (item[:item][:identifier] && item[:item][:type])
|
87
|
-
attr_name = item[:item][:name] || item[:item][:identifier]
|
88
|
-
attr = Attribute.new(
|
89
|
-
name: attr_name.to_s,
|
90
|
-
type: ["string"],
|
91
|
-
)
|
92
|
-
current_attributes << attr
|
93
|
-
when item[:item][:identifier]
|
94
|
-
current_elements << build_element(item[:item])
|
95
|
-
when item[:item][:text]
|
96
|
-
el.text = true
|
255
|
+
result = "element #{element.name} {\n"
|
256
|
+
result += " #{build_content(element)}\n"
|
257
|
+
result += "}"
|
258
|
+
result
|
259
|
+
end
|
260
|
+
|
261
|
+
def build_content(node)
|
262
|
+
content_parts = []
|
263
|
+
|
264
|
+
# Process attributes
|
265
|
+
if node.attribute
|
266
|
+
if node.attribute.is_a?(Array)
|
267
|
+
node.attribute.each do |attr|
|
268
|
+
content_parts << build_attribute(attr)
|
97
269
|
end
|
270
|
+
else
|
271
|
+
content_parts << build_attribute(node.attribute)
|
98
272
|
end
|
273
|
+
end
|
99
274
|
|
100
|
-
|
101
|
-
|
275
|
+
# Process child elements
|
276
|
+
if node.element
|
277
|
+
if node.element.is_a?(Array)
|
278
|
+
node.element.each do |elem|
|
279
|
+
content_parts << build_element(elem)
|
280
|
+
end
|
281
|
+
else
|
282
|
+
content_parts << build_element(node.element)
|
283
|
+
end
|
102
284
|
end
|
103
285
|
|
104
|
-
#
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
)
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
286
|
+
# Process text
|
287
|
+
content_parts << "text" if node.text
|
288
|
+
|
289
|
+
# Process empty
|
290
|
+
content_parts << "empty" if node.empty
|
291
|
+
|
292
|
+
# Process choice
|
293
|
+
if node.choice
|
294
|
+
choice_parts = []
|
295
|
+
if node.choice.is_a?(Array)
|
296
|
+
node.choice.each do |choice|
|
297
|
+
choice_parts << build_pattern(choice)
|
298
|
+
end
|
299
|
+
else
|
300
|
+
choice_parts << build_pattern(node.choice)
|
301
|
+
end
|
302
|
+
content_parts << choice_parts.join(" | ")
|
303
|
+
end
|
304
|
+
|
305
|
+
# Process group
|
306
|
+
if node.group
|
307
|
+
group_parts = []
|
308
|
+
if node.group.is_a?(Array)
|
309
|
+
node.group.each do |group|
|
310
|
+
group_parts << build_pattern(group)
|
311
|
+
end
|
312
|
+
else
|
313
|
+
group_parts << build_pattern(node.group)
|
314
|
+
end
|
315
|
+
content_parts << "(#{group_parts.join(", ")})"
|
131
316
|
end
|
132
317
|
|
318
|
+
# Process ref
|
319
|
+
if node.ref
|
320
|
+
if node.ref.is_a?(Array)
|
321
|
+
node.ref.each do |ref|
|
322
|
+
content_parts << ref.name
|
323
|
+
end
|
324
|
+
else
|
325
|
+
content_parts << node.ref.name
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
# Process zeroOrMore
|
330
|
+
content_parts << "#{build_pattern(node.zeroOrMore)}*" if node.zeroOrMore
|
331
|
+
|
332
|
+
# Process oneOrMore
|
333
|
+
content_parts << "#{build_pattern(node.oneOrMore)}+" if node.oneOrMore
|
334
|
+
|
335
|
+
# Process optional
|
336
|
+
content_parts << "#{build_pattern(node.optional)}?" if node.optional
|
337
|
+
|
338
|
+
content_parts.join(",\n ")
|
339
|
+
end
|
340
|
+
|
341
|
+
def build_attribute(attr)
|
342
|
+
result = "attribute #{attr.name} { "
|
343
|
+
|
344
|
+
result += if attr.data
|
345
|
+
if attr.data.type
|
346
|
+
"xsd:#{attr.data.type}"
|
347
|
+
else
|
348
|
+
"text"
|
349
|
+
end
|
350
|
+
else
|
351
|
+
"text"
|
352
|
+
end
|
353
|
+
|
354
|
+
result += " }"
|
133
355
|
result
|
134
356
|
end
|
357
|
+
|
358
|
+
def build_pattern(node)
|
359
|
+
if node.element
|
360
|
+
build_element(node.element)
|
361
|
+
elsif node.ref
|
362
|
+
node.ref.name
|
363
|
+
elsif node.choice
|
364
|
+
choice_parts = []
|
365
|
+
if node.choice.is_a?(Array)
|
366
|
+
node.choice.each do |choice|
|
367
|
+
choice_parts << build_pattern(choice)
|
368
|
+
end
|
369
|
+
else
|
370
|
+
choice_parts << build_pattern(node.choice)
|
371
|
+
end
|
372
|
+
choice_parts.join(" | ")
|
373
|
+
elsif node.group
|
374
|
+
group_parts = []
|
375
|
+
if node.group.is_a?(Array)
|
376
|
+
node.group.each do |group|
|
377
|
+
group_parts << build_pattern(group)
|
378
|
+
end
|
379
|
+
else
|
380
|
+
group_parts << build_pattern(node.group)
|
381
|
+
end
|
382
|
+
"(#{group_parts.join(", ")})"
|
383
|
+
elsif node.text
|
384
|
+
"text"
|
385
|
+
elsif node.empty
|
386
|
+
"empty"
|
387
|
+
else
|
388
|
+
# Default case
|
389
|
+
""
|
390
|
+
end
|
391
|
+
end
|
135
392
|
end
|
136
393
|
end
|
data/lib/rng/start.rb
CHANGED
@@ -1,14 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "lutaml/model"
|
2
|
-
require_relative "element"
|
3
4
|
|
4
5
|
module Rng
|
5
6
|
class Start < Lutaml::Model::Serializable
|
6
|
-
attribute :
|
7
|
-
attribute :
|
7
|
+
attribute :id, :string
|
8
|
+
attribute :ns, :string
|
9
|
+
attribute :datatypeLibrary, :string
|
10
|
+
attribute :combine, :string
|
11
|
+
attribute :ref, Ref
|
12
|
+
attribute :element, Element
|
13
|
+
attribute :choice, Choice
|
14
|
+
attribute :group, Group
|
15
|
+
attribute :interleave, Interleave
|
16
|
+
attribute :mixed, Mixed
|
17
|
+
attribute :optional, Optional
|
18
|
+
attribute :zeroOrMore, ZeroOrMore
|
19
|
+
attribute :oneOrMore, OneOrMore
|
20
|
+
attribute :text, Text
|
21
|
+
attribute :empty, Empty
|
22
|
+
attribute :value, Value
|
23
|
+
attribute :data, Data
|
24
|
+
attribute :list, List
|
25
|
+
attribute :parentRef, ParentRef
|
26
|
+
attribute :notAllowed, NotAllowed
|
27
|
+
attribute :grammar, Grammar
|
8
28
|
|
9
29
|
xml do
|
10
|
-
|
11
|
-
|
30
|
+
root "start", ordered: true
|
31
|
+
namespace "http://relaxng.org/ns/structure/1.0"
|
32
|
+
|
33
|
+
map_attribute "id", to: :id
|
34
|
+
map_attribute "ns", to: :ns, value_map: {
|
35
|
+
from: { empty: :empty, omitted: :omitted, nil: :nil },
|
36
|
+
to: { empty: :empty, omitted: :omitted, nil: :nil }
|
37
|
+
}
|
38
|
+
map_attribute "datatypeLibrary", to: :datatypeLibrary, value_map: {
|
39
|
+
from: { empty: :empty, omitted: :omitted, nil: :nil },
|
40
|
+
to: { empty: :empty, omitted: :omitted, nil: :nil }
|
41
|
+
}
|
42
|
+
map_attribute "combine", to: :combine
|
43
|
+
|
44
|
+
map_element "ref", to: :ref
|
45
|
+
map_element "element", to: :element
|
46
|
+
map_element "choice", to: :choice
|
47
|
+
map_element "group", to: :group
|
48
|
+
map_element "interleave", to: :interleave
|
49
|
+
map_element "mixed", to: :mixed
|
50
|
+
map_element "optional", to: :optional
|
51
|
+
map_element "zeroOrMore", to: :zeroOrMore
|
52
|
+
map_element "oneOrMore", to: :oneOrMore
|
53
|
+
map_element "text", to: :text
|
54
|
+
map_element "empty", to: :empty
|
55
|
+
map_element "value", to: :value
|
56
|
+
map_element "data", to: :data
|
57
|
+
map_element "list", to: :list
|
58
|
+
map_element "parentRef", to: :parentRef
|
59
|
+
map_element "notAllowed", to: :notAllowed
|
60
|
+
map_element "grammar", to: :grammar
|
12
61
|
end
|
13
62
|
end
|
14
63
|
end
|
data/lib/rng/text.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "lutaml/model"
|
4
|
+
|
5
|
+
module Rng
|
6
|
+
class Text < Lutaml::Model::Serializable
|
7
|
+
attribute :id, :string
|
8
|
+
attribute :ns, :string
|
9
|
+
attribute :datatypeLibrary, :string
|
10
|
+
attribute :value, :string, default: ""
|
11
|
+
|
12
|
+
xml do
|
13
|
+
root "text"
|
14
|
+
map_attribute "id", to: :id
|
15
|
+
map_attribute "ns", to: :ns, value_map: {
|
16
|
+
from: { empty: :empty, omitted: :omitted, nil: :nil },
|
17
|
+
to: { empty: :empty, omitted: :omitted, nil: :nil }
|
18
|
+
}
|
19
|
+
map_attribute "datatypeLibrary", to: :datatypeLibrary, value_map: {
|
20
|
+
from: { empty: :empty, omitted: :omitted, nil: :nil },
|
21
|
+
to: { empty: :empty, omitted: :omitted, nil: :nil }
|
22
|
+
}
|
23
|
+
map_content to: :value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/rng/to_rnc.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rng
|
4
|
+
# RNG to RNC converter module
|
5
|
+
# Provides functionality to convert RELAX NG XML Schema (RNG) to RELAX NG Compact Syntax (RNC)
|
6
|
+
module ToRnc
|
7
|
+
class << self
|
8
|
+
# Convert an RNG schema object to RNC syntax
|
9
|
+
# @param schema [Rng::Grammar] The schema to convert
|
10
|
+
# @return [String] The RNC representation of the schema
|
11
|
+
def convert(schema)
|
12
|
+
# This is a placeholder implementation
|
13
|
+
# The actual conversion logic would need to be implemented
|
14
|
+
|
15
|
+
# Return a simple template indicating this is a stub
|
16
|
+
"# RELAX NG Compact Syntax (RNC) - STUB IMPLEMENTATION\n" \
|
17
|
+
"# This is a placeholder for the actual RNG to RNC conversion\n\n" \
|
18
|
+
"start = element #{element_name(schema)} {\n" \
|
19
|
+
" # Conversion not yet implemented\n" \
|
20
|
+
" text\n" \
|
21
|
+
"}\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# Extract a sensible element name from the schema
|
27
|
+
# @param schema [Rng::Grammar] The schema to extract element name from
|
28
|
+
# @return [String] The element name or a default
|
29
|
+
def element_name(schema)
|
30
|
+
# Try to determine the root element name from the schema
|
31
|
+
if schema.respond_to?(:start) &&
|
32
|
+
schema.start.respond_to?(:element) &&
|
33
|
+
schema.start.element.any? &&
|
34
|
+
schema.start.element.first.respond_to?(:name)
|
35
|
+
return schema.start.element.first.name
|
36
|
+
end
|
37
|
+
|
38
|
+
# Try other options if available
|
39
|
+
if schema.respond_to?(:element) &&
|
40
|
+
schema.element.respond_to?(:name) &&
|
41
|
+
schema.element.name.is_a?(String)
|
42
|
+
return schema.element.name
|
43
|
+
end
|
44
|
+
|
45
|
+
# Use default name if we can't determine from schema
|
46
|
+
"root"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Add class-level conversion method
|
52
|
+
def self.to_rnc(schema)
|
53
|
+
ToRnc.convert(schema)
|
54
|
+
end
|
55
|
+
end
|
data/lib/rng/value.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "lutaml/model"
|
4
|
+
|
5
|
+
module Rng
|
6
|
+
class Value < Lutaml::Model::Serializable
|
7
|
+
attribute :id, :string
|
8
|
+
attribute :ns, :string
|
9
|
+
attribute :datatypeLibrary, :string
|
10
|
+
attribute :type, :string
|
11
|
+
attribute :value, :string
|
12
|
+
|
13
|
+
xml do
|
14
|
+
root "value"
|
15
|
+
|
16
|
+
map_attribute "type", to: :type
|
17
|
+
map_attribute "id", to: :id
|
18
|
+
map_attribute "ns", to: :ns, value_map: {
|
19
|
+
from: { empty: :empty, omitted: :omitted, nil: :nil },
|
20
|
+
to: { empty: :empty, omitted: :omitted, nil: :nil }
|
21
|
+
}
|
22
|
+
map_attribute "datatypeLibrary", to: :datatypeLibrary, value_map: {
|
23
|
+
from: { empty: :empty, omitted: :omitted, nil: :nil },
|
24
|
+
to: { empty: :empty, omitted: :omitted, nil: :nil }
|
25
|
+
}
|
26
|
+
map_content to: :value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/rng/version.rb
CHANGED