lutaml-lml 0.3.4 → 0.3.5
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/lib/lutaml/lml/cli.rb +1 -0
- data/lib/lutaml/lml/document_builder.rb +10 -0
- data/lib/lutaml/lml/formatter/canonical.rb +21 -4
- data/lib/lutaml/lml/grammar/lml.artifact.json +1919 -1672
- data/lib/lutaml/lml/grammar/lml.parg +32 -5
- data/lib/lutaml/lml/model_compiler/serialization_mappings.rb +36 -10
- data/lib/lutaml/lml/model_compiler.rb +9 -1
- data/lib/lutaml/lml/models/top_element_attribute.rb +3 -0
- data/lib/lutaml/lml/models/uml_class.rb +3 -0
- data/lib/lutaml/lml/version.rb +1 -1
- metadata +4 -4
|
@@ -9,7 +9,28 @@
|
|
|
9
9
|
# capture binds the whole match (Parslet's `.repeat.as(:x)` shape);
|
|
10
10
|
# `[ x as k ]` yields k: nil when absent (Parslet omits the key —
|
|
11
11
|
# consumers treat nil and absent as equivalent).
|
|
12
|
-
grammar Lml version "1.0.0" {
|
|
12
|
+
grammar Lml version "1.0.0" edition 2 {
|
|
13
|
+
# ---- Glue (parsanol-ruby#364) ----
|
|
14
|
+
# Edition 2 names every uncaptured rule reference; these are the
|
|
15
|
+
# token mechanics the tree must not see: separators that eat `;` and
|
|
16
|
+
# comments, verbatim-scan classes, quote delimiters, identifier
|
|
17
|
+
# runs and keyword tokens. Declaring the whole chain keeps the tree
|
|
18
|
+
# the authored captures only — byte-identical to edition 1 (the
|
|
19
|
+
# corpus differential proves it). Un-glue a rule when its node
|
|
20
|
+
# should surface.
|
|
21
|
+
glue whitespace line_comment block_comment
|
|
22
|
+
glue any_char quotes
|
|
23
|
+
glue word nonspace class_run class_name_chars type_run type_token quoted_name_run
|
|
24
|
+
glue definition_content
|
|
25
|
+
glue kw_abstract_sp kw_artifact_sp kw_association_sp kw_attribute_sp kw_caption_sp
|
|
26
|
+
glue kw_class_sp kw_collection_sp kw_condition_sp kw_data_type_sp kw_definition_sp
|
|
27
|
+
glue kw_derive_sp kw_element_sp kw_enum_sp kw_fontname_sp kw_format_sp kw_hide_sp
|
|
28
|
+
glue kw_import_sp kw_instance_sp kw_isa_sp kw_lml_sp kw_map_attribute_sp kw_map_content_sp
|
|
29
|
+
glue kw_map_element_sp kw_map_key_sp kw_map_sp kw_map_value_sp kw_mapping_sp kw_member_sp
|
|
30
|
+
glue kw_member_type_sp kw_namespace_sp kw_owner_sp kw_owner_type_sp kw_package_sp
|
|
31
|
+
glue kw_pattern_sp kw_prefix_sp kw_primitive_sp kw_require_sp kw_show_sp
|
|
32
|
+
glue kw_string_format_sp kw_title_sp kw_to_instance_sp kw_uri_sp kw_value_sp
|
|
33
|
+
glue kw_values_sp kw_version_sp kw_visibility_sp
|
|
13
34
|
# ---- Trivia (parsanol-ruby#134 skip rule) ----
|
|
14
35
|
# Grammar-native trivia replaces the pre-parse comment stripping:
|
|
15
36
|
# `//` line and `/* */` block comments (RS 3001 par. Comment) plus
|
|
@@ -136,14 +157,17 @@ grammar Lml version "1.0.0" {
|
|
|
136
157
|
visibility = kw_visibility_modifier as visibility_modifier
|
|
137
158
|
attribute_stereotype = "<<" ( 1*( %x41-5A / %x61-7A / %x30-39 / "_" / "-" / "/" ) ) as stereotype ">>"
|
|
138
159
|
atomic type_run = 1*( !( "//" / "/*" ) ( %x41-5A / %x61-7A / %x30-39 / "_" / "-" / ":" / "/" / "+" ) )
|
|
139
|
-
atomic type_token = type_run *( %x20 type_run )
|
|
160
|
+
atomic type_token = type_run *( %x20 !( "as" 1*" " ":" ) type_run )
|
|
140
161
|
atomic namechar = %x41-5A / %x61-7A / %x30-39 / "_" / "-" / "/" / "+"
|
|
141
162
|
attribute_type = [ ":" ] [ attribute_stereotype ] [ quotes as type_literal ] ( type_token ) as type [ quotes ]
|
|
142
163
|
attribute_name = ( 1*namechar ) as name
|
|
143
164
|
atomic quoted_name_run = 1*( %x41-5A / %x61-7A / %x30-39 / "_" / "-" / "/" / "+" )
|
|
144
165
|
atomic quoted_name_body = quoted_name_run *( %x20 quoted_name_run )
|
|
145
166
|
attribute_definition_name = ( quotes ( quoted_name_body ) as name quotes ) / attribute_name
|
|
146
|
-
|
|
167
|
+
# Issue lutaml/lutaml#49: `as :element` / `as :attribute` picks the XML
|
|
168
|
+
# node kind of an attribute (lutaml-model map_element / map_attribute).
|
|
169
|
+
atomic xml_role_suffix = "as" 1*" " ":" ( ( "element" / "attribute" ) as xml_role )
|
|
170
|
+
attribute_definition = ( [ visibility ] as visibility attribute_definition_name [ attribute_type ] [ cardinality ] [ xml_role_suffix ] [ class_body ] ) as attributes
|
|
147
171
|
|
|
148
172
|
keyword_type_argument = "type" ( 1*nonspace ) as type [ whitespace ]
|
|
149
173
|
keyword_cardinality_argument = "cardinality" cardinality_body_definition as cardinality [ whitespace ]
|
|
@@ -154,7 +178,7 @@ grammar Lml version "1.0.0" {
|
|
|
154
178
|
value_set_body = "{" [ whitespace ] ( *( ( ( data_value / variable ) as item ) [ "," ] [ whitespace ] ) ) as items "}"
|
|
155
179
|
keyword_attribute_options = *( keyword_type_argument / keyword_cardinality_argument / keyword_values_argument / keyword_pattern_argument / ( keyword_any_argument ) as properties )
|
|
156
180
|
keyword_attribute_body = "{" [ whitespace ] keyword_attribute_options [ whitespace ] "}"
|
|
157
|
-
keyword_attribute_definition = ( kw_attribute_sp attribute_name keyword_attribute_body ) as attributes
|
|
181
|
+
keyword_attribute_definition = ( kw_attribute_sp attribute_name [ xml_role_suffix ] keyword_attribute_body ) as attributes
|
|
158
182
|
|
|
159
183
|
# ---- Associations ----
|
|
160
184
|
owner_cardinality = "[" cardinality_body_definition as owner_end_cardinality "]"
|
|
@@ -235,7 +259,10 @@ grammar Lml version "1.0.0" {
|
|
|
235
259
|
string_format_body = "{" [ whitespace ] ( *( string_format_item ) ) as items [ whitespace ] "}"
|
|
236
260
|
string_format_definition = ( [ whitespace ] kw_string_format_sp ( variable ) as format string_format_body ) as string_format
|
|
237
261
|
|
|
238
|
-
|
|
262
|
+
# Issue lutaml/lutaml#49: a quoted `namespace "uri" [prefix "p"]` line
|
|
263
|
+
# inside a class declares the class's XML namespace.
|
|
264
|
+
class_namespace_definition = ( kw_namespace_sp ( quoted_string ) as uri [ kw_prefix_sp ( quoted_string / ( 1*word ) ) as prefix ] ) as class_namespace [ whitespace ]
|
|
265
|
+
class_inner_definitions = class_namespace_definition / visibility_declaration / abstract_declaration / definition_body / definition_line / class_mapping_section / derive_definition / string_format_definition / ( operation_definition ) as operations / attribute_comma_definition / ( !( kw_attribute_sp ) attribute_definition ) / keyword_attribute_definition / comment_definition / comment_multiline_definition
|
|
239
266
|
class_inner_definition = class_inner_definitions [ whitespace ]
|
|
240
267
|
class_body = "{" [ whitespace ] ( *( class_inner_definition ) ) as members "}"
|
|
241
268
|
parent_class = "<" ( class_name_chars ) as parent_class
|
|
@@ -49,18 +49,40 @@ module Lutaml
|
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
# Default XML mapping: every attribute an element, except those
|
|
53
|
+
# declared `as :attribute`; an in-class `namespace "uri"` becomes
|
|
54
|
+
# the class's XML namespace (issue #49).
|
|
52
55
|
def apply_xml_mapping(klass, root_name)
|
|
53
56
|
attr_names = klass.attributes.keys
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
def_obj = @class_defs[root_name]
|
|
58
|
+
roles = xml_roles_of(def_obj)
|
|
59
|
+
ns_class = inline_namespace_class(def_obj)
|
|
56
60
|
klass.xml do |mapping|
|
|
57
61
|
mapping.element(root_name)
|
|
62
|
+
mapping.namespace(ns_class) if ns_class
|
|
58
63
|
attr_names.each do |attr_name|
|
|
59
|
-
|
|
64
|
+
if roles[attr_name] == 'attribute'
|
|
65
|
+
mapping.map_attribute(attr_name.to_s, to: attr_name)
|
|
66
|
+
else
|
|
67
|
+
mapping.map_element(attr_name.to_s, to: attr_name)
|
|
68
|
+
end
|
|
60
69
|
end
|
|
61
70
|
end
|
|
62
71
|
end
|
|
63
72
|
|
|
73
|
+
def xml_roles_of(def_obj)
|
|
74
|
+
Array(def_obj&.attributes).each_with_object({}) do |attr, roles|
|
|
75
|
+
roles[attr.name.to_sym] = attr.xml_role if attr.xml_role
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def inline_namespace_class(def_obj)
|
|
80
|
+
decl = def_obj.xml_namespace if def_obj.is_a?(UmlClass)
|
|
81
|
+
return nil unless decl&.uri
|
|
82
|
+
|
|
83
|
+
build_namespace_class("#{decl.uri}|#{decl.prefix}", decl.uri, decl.prefix.to_s)
|
|
84
|
+
end
|
|
85
|
+
|
|
64
86
|
# RS 3010 extension: key-value mapping blocks for the declared
|
|
65
87
|
# formats (yaml/json/toml share lutaml-model's key_value model).
|
|
66
88
|
|
|
@@ -89,7 +111,7 @@ module Lutaml
|
|
|
89
111
|
check_namespace_ambiguity(mapping)
|
|
90
112
|
ns_class = namespace_class_for(mapping.namespace_ref) if mapping.namespace_ref
|
|
91
113
|
rule_ns = mapping.rules.select { |r| r.kind == 'namespace' }
|
|
92
|
-
|
|
114
|
+
.to_h { |r| [r.field, namespace_class_for(r.field)] }
|
|
93
115
|
klass.xml do |m|
|
|
94
116
|
m.element(mapping.element_name) if mapping.element_name
|
|
95
117
|
m.namespace(ns_class) if ns_class
|
|
@@ -149,12 +171,16 @@ module Lutaml
|
|
|
149
171
|
declared = @declared_namespaces.find { |ns| ns.name.to_s == ref }
|
|
150
172
|
raise ArgumentError, "unknown namespace '#{ref}'" unless declared
|
|
151
173
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
174
|
+
build_namespace_class(ref, declared.uri, declared.prefix.to_s)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def build_namespace_class(key, uri, prefix)
|
|
178
|
+
require 'lutaml/xml/namespace' unless defined?(Lutaml::Xml::Namespace)
|
|
179
|
+
|
|
180
|
+
@namespace_classes[key] ||= Class.new(Lutaml::Xml::Namespace).tap do |ns_class|
|
|
181
|
+
ns_class.define_singleton_method(:uri) { uri }
|
|
182
|
+
ns_class.define_singleton_method(:prefix_default) { prefix }
|
|
183
|
+
end
|
|
158
184
|
end
|
|
159
185
|
end
|
|
160
186
|
end
|
|
@@ -59,6 +59,7 @@ module Lutaml
|
|
|
59
59
|
@declared_namespaces = []
|
|
60
60
|
@serialization_mappings = []
|
|
61
61
|
@namespace_classes = {}
|
|
62
|
+
@class_defs = {}
|
|
62
63
|
end
|
|
63
64
|
|
|
64
65
|
def compile(input)
|
|
@@ -140,6 +141,7 @@ module Lutaml
|
|
|
140
141
|
|
|
141
142
|
def compile_class(klass_def)
|
|
142
143
|
name = klass_def.name.to_s
|
|
144
|
+
@class_defs[name] = klass_def
|
|
143
145
|
compiled_klass = build_compiled_class(klass_def)
|
|
144
146
|
apply_declared_mappings(compiled_klass, klass_def)
|
|
145
147
|
declare_derived_fields(compiled_klass, klass_def)
|
|
@@ -293,11 +295,17 @@ module Lutaml
|
|
|
293
295
|
|
|
294
296
|
def register(name, klass)
|
|
295
297
|
@compiled[name] = klass
|
|
296
|
-
return unless @namespace
|
|
298
|
+
return name_anonymous_class(klass, name) unless @namespace
|
|
297
299
|
|
|
298
300
|
namespace_module.const_set(name, klass)
|
|
299
301
|
end
|
|
300
302
|
|
|
303
|
+
# Without a target namespace the class stays anonymous, and
|
|
304
|
+
# lutaml-model's schema generators need a class name for $defs/$ref.
|
|
305
|
+
def name_anonymous_class(klass, name)
|
|
306
|
+
klass.define_singleton_method(:name) { name }
|
|
307
|
+
end
|
|
308
|
+
|
|
301
309
|
def namespace_module
|
|
302
310
|
return @namespace if @namespace.is_a?(Module)
|
|
303
311
|
|
|
@@ -24,6 +24,8 @@ module Lutaml
|
|
|
24
24
|
attribute :default, :string
|
|
25
25
|
attribute :value_set, :string, collection: true, default: -> { [] }
|
|
26
26
|
attribute :pattern, :string
|
|
27
|
+
# `as :element` / `as :attribute`: the XML node kind (issue #49)
|
|
28
|
+
attribute :xml_role, :string
|
|
27
29
|
|
|
28
30
|
# LML-specific attributes
|
|
29
31
|
attribute :properties, "Lutaml::Lml::TopElementAttribute", collection: true, default: -> { [] }
|
|
@@ -60,6 +62,7 @@ module Lutaml
|
|
|
60
62
|
map "default", to: :default
|
|
61
63
|
map "value_set", to: :value_set
|
|
62
64
|
map "pattern", to: :pattern
|
|
65
|
+
map "xml_role", to: :xml_role
|
|
63
66
|
map "properties", to: :properties
|
|
64
67
|
map "value", to: :value, with: { to: :value_to, from: :value_from }
|
|
65
68
|
map "attributes", to: :attributes
|
|
@@ -32,6 +32,8 @@ module Lutaml
|
|
|
32
32
|
attribute :serialization_mappings, "Lutaml::Lml::SerializationMapping", collection: true, default: -> { [] }
|
|
33
33
|
|
|
34
34
|
# LML-specific
|
|
35
|
+
# In-class `namespace "uri"` declaration (issue #49)
|
|
36
|
+
attribute :xml_namespace, "Lutaml::Lml::Namespace"
|
|
35
37
|
attribute :parent_class, :string
|
|
36
38
|
|
|
37
39
|
yaml do
|
|
@@ -42,6 +44,7 @@ module Lutaml
|
|
|
42
44
|
map "stereotype", to: :stereotype
|
|
43
45
|
map "visibility", to: :visibility
|
|
44
46
|
map "type", to: :type
|
|
47
|
+
map "xml_namespace", to: :xml_namespace
|
|
45
48
|
map "attributes", to: :attributes
|
|
46
49
|
map "operations", to: :operations
|
|
47
50
|
map "constraints", to: :constraints
|
data/lib/lutaml/lml/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lutaml-lml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-11-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: lutaml-model
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.8.
|
|
19
|
+
version: 0.8.156
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.8.
|
|
26
|
+
version: 0.8.156
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: parsanol
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|