lutaml-lml 0.2.0 → 0.3.0
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 +84 -18
- data/lib/lutaml/lml/data_processor/value_processing.rb +6 -1
- data/lib/lutaml/lml/document_builder.rb +72 -3
- data/lib/lutaml/lml/format/adapter/mapping.rb +12 -0
- data/lib/lutaml/lml/format/adapter/standard_adapter.rb +41 -12
- data/lib/lutaml/lml/formatter/elk.rb +45 -0
- data/lib/lutaml/lml/formatter/graphviz/node_formatter.rb +2 -1
- data/lib/lutaml/lml/formatter/svg_pdf/path_parser.rb +134 -0
- data/lib/lutaml/lml/formatter/svg_pdf.rb +281 -0
- data/lib/lutaml/lml/formatter.rb +2 -0
- data/lib/lutaml/lml/grammar/lml.artifact.json +7265 -4550
- data/lib/lutaml/lml/grammar/lml.parg +230 -115
- data/lib/lutaml/lml/layout/elk_engine.rb +15 -0
- data/lib/lutaml/lml/layout/elk_graph_builder.rb +129 -0
- data/lib/lutaml/lml/layout/elk_svg_markers.rb +45 -0
- data/lib/lutaml/lml/layout/elk_svg_renderer.rb +119 -0
- data/lib/lutaml/lml/layout/graph_viz_engine.rb +1 -1
- data/lib/lutaml/lml/layout.rb +4 -0
- data/lib/lutaml/lml/model_compiler/enums.rb +172 -0
- data/lib/lutaml/lml/model_compiler/hydration.rb +123 -0
- data/lib/lutaml/lml/model_compiler/serialization_mappings.rb +162 -0
- data/lib/lutaml/lml/model_compiler/string_formats.rb +42 -0
- data/lib/lutaml/lml/model_compiler/validation.rb +65 -0
- data/lib/lutaml/lml/model_compiler.rb +46 -453
- data/lib/lutaml/lml/models/document.rb +29 -0
- data/lib/lutaml/lml/models/operation_parameter.rb +1 -0
- data/lib/lutaml/lml/models/reference.rb +23 -0
- data/lib/lutaml/lml/models/top_element_attribute.rb +12 -0
- data/lib/lutaml/lml/models/uml_class.rb +1 -1
- data/lib/lutaml/lml/preprocessor.rb +44 -79
- data/lib/lutaml/lml/transform.rb +54 -6
- data/lib/lutaml/lml/validator.rb +88 -19
- data/lib/lutaml/lml/version.rb +1 -1
- data/lib/lutaml/lml.rb +19 -3
- metadata +17 -4
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Lml
|
|
5
|
+
# RS 3001 par. Value type: a reference to another instance
|
|
6
|
+
# (`reference:(path)` / `ref:(path)`). The attribute's value slot keeps
|
|
7
|
+
# its primitive wire shape (a `reference` hash); this is the typed
|
|
8
|
+
# domain view of it, built at one canonical point
|
|
9
|
+
# (TopElementAttribute#reference) so validation and serialization
|
|
10
|
+
# consume structured references instead of re-sniffing hashes.
|
|
11
|
+
class Reference < Lutaml::Model::Serializable
|
|
12
|
+
attribute :path, :string
|
|
13
|
+
|
|
14
|
+
def segments
|
|
15
|
+
@segments ||= path.to_s.split('.')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_s
|
|
19
|
+
path.to_s
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -83,6 +83,18 @@ module Lutaml
|
|
|
83
83
|
model.value = value
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
+
# RS 3001 par. Value type: the typed view of a reference value
|
|
87
|
+
# (`reference:(...)` / `ref:(...)`). Nil for every other value shape;
|
|
88
|
+
# for a list of references, the first reference.
|
|
89
|
+
def reference
|
|
90
|
+
literal = value.respond_to?(:value) ? value.value : value
|
|
91
|
+
cargo = literal.is_a?(::Array) ? literal.first : literal
|
|
92
|
+
cargo = cargo.value if cargo.is_a?(LiteralValue)
|
|
93
|
+
return nil unless cargo.is_a?(::Hash) && cargo[:reference]
|
|
94
|
+
|
|
95
|
+
Reference.new(path: cargo[:reference].to_s)
|
|
96
|
+
end
|
|
97
|
+
|
|
86
98
|
private
|
|
87
99
|
|
|
88
100
|
# A LiteralValue sits at the top level for a map literal, and inside the
|
|
@@ -20,7 +20,7 @@ module Lutaml
|
|
|
20
20
|
attribute :attributes, "Lutaml::Lml::TopElementAttribute", collection: true
|
|
21
21
|
attribute :modifier, :string
|
|
22
22
|
attribute :constraints, "Lutaml::Lml::Constraint", collection: true
|
|
23
|
-
attribute :operations, "Lutaml::Lml::Operation", collection: true
|
|
23
|
+
attribute :operations, "Lutaml::Lml::Operation", collection: true, default: -> { [] }
|
|
24
24
|
attribute :data_types, "Lutaml::Lml::DataType", collection: true
|
|
25
25
|
attribute :associations, "Lutaml::Lml::Association", collection: true
|
|
26
26
|
attribute :wire_mappings, "Lutaml::Lml::Mapping", collection: true, default: -> { [] }
|
|
@@ -21,11 +21,12 @@ module Lutaml
|
|
|
21
21
|
|
|
22
22
|
private
|
|
23
23
|
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
24
|
+
# Comments (`//`, `/* */`) are consumed by the grammar's trivia
|
|
25
|
+
# skip (parsanol-ruby#134); only include directives are expanded
|
|
26
|
+
# here. Line structure is preserved so parse-error positions
|
|
27
|
+
# match the source.
|
|
27
28
|
def expand_lines(text, base_dir, chain)
|
|
28
|
-
|
|
29
|
+
validate_block_comments(text)
|
|
29
30
|
text.split(/\r?\n/)
|
|
30
31
|
.flat_map { |line| expand_line(line, base_dir, chain) }
|
|
31
32
|
.join("\n")
|
|
@@ -41,93 +42,57 @@ module Lutaml
|
|
|
41
42
|
end
|
|
42
43
|
|
|
43
44
|
def include_path(line, base_dir)
|
|
44
|
-
match = line.match(/^\s*include\s+(.+)
|
|
45
|
+
match = line.match(/^\s*include\s+(.+)$/)
|
|
45
46
|
return nil unless match
|
|
46
47
|
|
|
47
|
-
File.expand_path(match[1].strip, base_dir)
|
|
48
|
+
File.expand_path(trim_trailing_comment(match[1].strip), base_dir)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def trim_trailing_comment(path)
|
|
52
|
+
path.sub(%r{\s+(//|/\*).*}, '')
|
|
48
53
|
end
|
|
49
54
|
|
|
50
55
|
def read_included(path)
|
|
51
|
-
File.read(path, encoding:
|
|
56
|
+
File.read(path, encoding: 'UTF-8')
|
|
52
57
|
rescue Errno::ENOENT, Errno::EACCES => e
|
|
53
58
|
raise Error, "cannot read include #{path}: #{e.message}"
|
|
54
59
|
end
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
out << @text[@pos]
|
|
85
|
-
@pos += 1
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
private
|
|
89
|
-
|
|
90
|
-
def string_delimiter?(ch)
|
|
91
|
-
ch == '"' || ch == "'"
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def emit_delimiter(out)
|
|
95
|
-
@quote = @text[@pos]
|
|
96
|
-
out << @text[@pos]
|
|
97
|
-
@pos += 1
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def emit_quoted(out)
|
|
101
|
-
ch = @text[@pos]
|
|
102
|
-
out << ch
|
|
103
|
-
if ch == "\\"
|
|
104
|
-
out << @text[@pos + 1].to_s
|
|
105
|
-
@pos += 2
|
|
106
|
-
else
|
|
107
|
-
@quote = nil if ch == @quote
|
|
108
|
-
@pos += 1
|
|
61
|
+
# Quote-aware scan: a `/*` opened outside a string literal or a
|
|
62
|
+
# line comment must close before end of input. The grammar's
|
|
63
|
+
# trivia would otherwise reject the text with an unrelated
|
|
64
|
+
# expected-token error.
|
|
65
|
+
def validate_block_comments(text)
|
|
66
|
+
quote = nil
|
|
67
|
+
pos = 0
|
|
68
|
+
open_index = nil
|
|
69
|
+
while pos < text.length
|
|
70
|
+
ch = text[pos]
|
|
71
|
+
if quote
|
|
72
|
+
if ch == '\\'
|
|
73
|
+
pos += 2
|
|
74
|
+
next
|
|
75
|
+
end
|
|
76
|
+
quote = nil if ch == quote
|
|
77
|
+
elsif open_index
|
|
78
|
+
if text[pos, 2] == '*/'
|
|
79
|
+
open_index = nil
|
|
80
|
+
pos += 1
|
|
81
|
+
end
|
|
82
|
+
elsif ['"', "'"].include?(ch)
|
|
83
|
+
quote = ch
|
|
84
|
+
elsif text[pos, 2] == '//'
|
|
85
|
+
pos = text.index("\n", pos) || text.length
|
|
86
|
+
next
|
|
87
|
+
elsif text[pos, 2] == '/*'
|
|
88
|
+
open_index = pos
|
|
109
89
|
end
|
|
90
|
+
pos += 1
|
|
110
91
|
end
|
|
92
|
+
return unless open_index
|
|
111
93
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def emit_block_comment(out)
|
|
117
|
-
close = @text.index("*/", @pos + 2)
|
|
118
|
-
raise Lutaml::Lml::Error, "unterminated block comment" unless close
|
|
119
|
-
|
|
120
|
-
out << "\n" * @text[@pos...close].count("\n") << " "
|
|
121
|
-
@pos = close + 2
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def line_comment_start?
|
|
125
|
-
@text[@pos, 2] == "//"
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def skip_line_comment
|
|
129
|
-
@pos = @text.index("\n", @pos) || @text.length
|
|
130
|
-
end
|
|
94
|
+
line = text[0...open_index].count("\n") + 1
|
|
95
|
+
raise Error, "unterminated block comment at line #{line}"
|
|
131
96
|
end
|
|
132
97
|
end
|
|
133
98
|
end
|
data/lib/lutaml/lml/transform.rb
CHANGED
|
@@ -8,17 +8,65 @@ module Lutaml
|
|
|
8
8
|
VISIBILITY_MAP = {
|
|
9
9
|
'-' => 'private',
|
|
10
10
|
'#' => 'protected',
|
|
11
|
-
'~' => '
|
|
11
|
+
'~' => 'package'
|
|
12
12
|
}.freeze
|
|
13
13
|
|
|
14
14
|
rule(visibility_modifier: simple(:visibility_value)) do
|
|
15
15
|
VISIBILITY_MAP.fetch(visibility_value.to_s, 'public')
|
|
16
16
|
end
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
|
|
17
|
+
# An empty `until` capture is an empty sequence, not an empty
|
|
18
|
+
# string: `""` parses the dq_string body as [] (parsanol
|
|
19
|
+
# repetition semantics). A present, zero-length string value is
|
|
20
|
+
# distinct from an absent one (RS 3001 §String values); only the
|
|
21
|
+
# :string capture is normalized, other empty sequences stay [].
|
|
22
|
+
rule(string: sequence(:empty_string)) { { string: '' } }
|
|
23
|
+
|
|
24
|
+
# RS 3001 §String values: the only two escapes are backslash-quote
|
|
25
|
+
# and backslash-backslash (the grammar rejects every other
|
|
26
|
+
# backslash), so unescaping is removing the backslash before
|
|
27
|
+
# those two characters.
|
|
28
|
+
rule(string: simple(:escaped)) { { string: Lutaml::Lml::Transform.unescape(escaped.to_s) } }
|
|
29
|
+
|
|
30
|
+
# Rule blocks evaluate in a Parsanol::Context, so this helper is
|
|
31
|
+
# a class method the block reaches through the class.
|
|
32
|
+
# @param body [String] raw string body as captured between quotes
|
|
33
|
+
# @return [String] the value with \" and \\ resolved
|
|
34
|
+
def self.unescape(body)
|
|
35
|
+
body.gsub(/\\(["'\\])/) { Regexp.last_match(1) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Global scalar normalizer: every leaf becomes a string (or stays
|
|
39
|
+
# nil). No stripping — name and type tokens are interior-bounded
|
|
40
|
+
# in the grammar (class_run/type_run/quoted_name_run), so quoted
|
|
41
|
+
# string values keep their padding verbatim (RS 3001 §String
|
|
42
|
+
# values).
|
|
43
|
+
rule(simple(:value)) { value.nil? ? value : value.to_s }
|
|
44
|
+
|
|
45
|
+
# Block captures (definition bodies, block comments) read their
|
|
46
|
+
# surrounding layout as content — a block's first line break and
|
|
47
|
+
# closing indentation are layout, not prose. parsanol only fires
|
|
48
|
+
# hash-keyed rules at selected tree positions, so the strip is a
|
|
49
|
+
# post-pass walk, not a rule.
|
|
50
|
+
BLOCK_KEYS = %i[definition comments].freeze
|
|
51
|
+
|
|
52
|
+
def apply(tree, context = nil)
|
|
53
|
+
strip_block_layout(super)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def strip_block_layout(node)
|
|
57
|
+
case node
|
|
58
|
+
when Hash
|
|
59
|
+
node.each_with_object({}) do |(key, value), result|
|
|
60
|
+
result[key] = if BLOCK_KEYS.include?(key) && value.is_a?(String)
|
|
61
|
+
value.strip
|
|
62
|
+
else
|
|
63
|
+
strip_block_layout(value)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
when Array then node.map { |item| strip_block_layout(item) }
|
|
67
|
+
else node
|
|
68
|
+
end
|
|
69
|
+
end
|
|
22
70
|
end
|
|
23
71
|
end
|
|
24
72
|
end
|
data/lib/lutaml/lml/validator.rb
CHANGED
|
@@ -14,7 +14,8 @@ module Lutaml
|
|
|
14
14
|
'Integer' => /\A-?\d+\z/,
|
|
15
15
|
'Number' => /\A-?\d+\z/,
|
|
16
16
|
'Float' => /\A-?\d+\.\d+\z/,
|
|
17
|
-
'Boolean' => /\A(true|false)\z
|
|
17
|
+
'Boolean' => /\A(true|false)\z/,
|
|
18
|
+
'Null' => /\Anull\z/
|
|
18
19
|
}.freeze
|
|
19
20
|
|
|
20
21
|
COLLECTION_KEYS = %i[properties attributes instances].freeze
|
|
@@ -34,6 +35,28 @@ module Lutaml
|
|
|
34
35
|
circular_reference_violations
|
|
35
36
|
end
|
|
36
37
|
|
|
38
|
+
# RS 3001 scopes definitions to packages: uniqueness is per scope
|
|
39
|
+
# (the document root and each package, recursively), not global.
|
|
40
|
+
def definition_scopes
|
|
41
|
+
scopes = [[nil, document_definitions]]
|
|
42
|
+
Array(@document.packages).each { |pkg| collect_package_scopes(pkg, scopes) }
|
|
43
|
+
scopes
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def document_definitions
|
|
47
|
+
[
|
|
48
|
+
*Array(@document.classes),
|
|
49
|
+
*Array(@document.enums),
|
|
50
|
+
*Array(@document.data_types)
|
|
51
|
+
]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def collect_package_scopes(pkg, scopes, parent = nil)
|
|
55
|
+
path = parent ? "#{parent}.#{pkg.name}" : pkg.name.to_s
|
|
56
|
+
scopes << [path, [*Array(pkg.classes), *Array(pkg.enums), *Array(pkg.data_types)]]
|
|
57
|
+
Array(pkg.packages).each { |nested| collect_package_scopes(nested, scopes, path) }
|
|
58
|
+
end
|
|
59
|
+
|
|
37
60
|
private
|
|
38
61
|
|
|
39
62
|
def all_definitions
|
|
@@ -59,14 +82,18 @@ module Lutaml
|
|
|
59
82
|
end
|
|
60
83
|
|
|
61
84
|
def unique_name_violations
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
85
|
+
definition_scopes.flat_map do |scope, definitions|
|
|
86
|
+
scope_label = scope ? "package '#{scope}'" : 'the document root'
|
|
87
|
+
definitions.map { |d| d.name.to_s }
|
|
88
|
+
.tally
|
|
89
|
+
.filter_map do |name, count|
|
|
90
|
+
next if count == 1
|
|
91
|
+
|
|
92
|
+
Violation.new(
|
|
93
|
+
rule: 'unique_names',
|
|
94
|
+
message: "'#{name}' is defined #{count} times in #{scope_label}"
|
|
95
|
+
)
|
|
96
|
+
end
|
|
70
97
|
end
|
|
71
98
|
end
|
|
72
99
|
|
|
@@ -91,10 +118,49 @@ module Lutaml
|
|
|
91
118
|
instances.flat_map do |instance|
|
|
92
119
|
mandatory_violations(instance) +
|
|
93
120
|
type_conformity_violations(instance) +
|
|
121
|
+
values_set_violations(instance) +
|
|
122
|
+
unknown_attribute_violations(instance) +
|
|
94
123
|
reference_violations(instance)
|
|
95
124
|
end.compact
|
|
96
125
|
end
|
|
97
126
|
|
|
127
|
+
# RS 3001 par. Attribute: a `values` declaration restricts an
|
|
128
|
+
# attribute to its declared set.
|
|
129
|
+
def values_set_violations(instance)
|
|
130
|
+
definition = type_definitions[definition_type(instance)]
|
|
131
|
+
return [] unless definition.is_a?(UmlClass) || definition.is_a?(DataType)
|
|
132
|
+
|
|
133
|
+
instance_attribute_pairs(instance).filter_map do |name, value|
|
|
134
|
+
attr = Array(definition.attributes).find { |a| a.name.to_s == name }
|
|
135
|
+
next unless attr && !attr.value_set.empty?
|
|
136
|
+
|
|
137
|
+
literal = value.respond_to?(:value) ? value.value : value
|
|
138
|
+
next if attr.value_set.map(&:to_s).include?(literal.to_s)
|
|
139
|
+
|
|
140
|
+
Violation.new(
|
|
141
|
+
rule: 'values_set_membership',
|
|
142
|
+
message: "instance '#{instance_name(instance)}': attribute '#{name}' " \
|
|
143
|
+
"must be one of #{attr.value_set.inspect}, got #{literal.to_s.inspect}"
|
|
144
|
+
)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def unknown_attribute_violations(instance)
|
|
149
|
+
definition = type_definitions[definition_type(instance)]
|
|
150
|
+
return [] unless definition.is_a?(UmlClass) || definition.is_a?(DataType)
|
|
151
|
+
|
|
152
|
+
declared = attribute_names(definition)
|
|
153
|
+
instance_attribute_pairs(instance)
|
|
154
|
+
.reject { |name, _value| declared.include?(name) }
|
|
155
|
+
.map do |name, _value|
|
|
156
|
+
Violation.new(
|
|
157
|
+
rule: 'unknown_attributes',
|
|
158
|
+
message: "instance '#{instance_name(instance)}' (#{definition_type(instance)}): " \
|
|
159
|
+
"unknown attribute '#{name}'"
|
|
160
|
+
)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
98
164
|
def instances
|
|
99
165
|
Array(@document.instances&.instances)
|
|
100
166
|
end
|
|
@@ -119,13 +185,7 @@ module Lutaml
|
|
|
119
185
|
attr.cardinality && attr.cardinality.min == '1'
|
|
120
186
|
end
|
|
121
187
|
|
|
122
|
-
def populated?(instance, attr_name)
|
|
123
|
-
pair = instance_attribute(instance, attr_name)
|
|
124
|
-
return false unless pair
|
|
125
188
|
|
|
126
|
-
value = pair[1]
|
|
127
|
-
value.respond_to?(:value) ? !value.to_s.empty? : !value.to_s.empty?
|
|
128
|
-
end
|
|
129
189
|
|
|
130
190
|
def type_conformity_violations(instance)
|
|
131
191
|
definition = type_definitions[definition_type(instance)]
|
|
@@ -213,12 +273,21 @@ module Lutaml
|
|
|
213
273
|
literal
|
|
214
274
|
end
|
|
215
275
|
|
|
276
|
+
def populated?(instance, attr_name)
|
|
277
|
+
pair = instance_attribute(instance, attr_name)
|
|
278
|
+
return false unless pair
|
|
279
|
+
|
|
280
|
+
value = pair[1]
|
|
281
|
+
literal = value.respond_to?(:value) ? value.value : value
|
|
282
|
+
!literal.to_s.empty?
|
|
283
|
+
end
|
|
284
|
+
|
|
216
285
|
def reference_values(instance)
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
next unless
|
|
286
|
+
Array(instance.attributes).filter_map do |attr|
|
|
287
|
+
ref = attr.reference
|
|
288
|
+
next unless ref
|
|
220
289
|
|
|
221
|
-
[name,
|
|
290
|
+
[attr.name.to_s, ref.path]
|
|
222
291
|
end
|
|
223
292
|
end
|
|
224
293
|
|
data/lib/lutaml/lml/version.rb
CHANGED
data/lib/lutaml/lml.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "lutaml/model"
|
|
4
4
|
|
|
5
|
+
# Root namespace for the LutaML libraries.
|
|
5
6
|
module Lutaml
|
|
6
7
|
class Error < StandardError; end
|
|
7
8
|
|
|
@@ -11,6 +12,10 @@ module Lutaml
|
|
|
11
12
|
autoload :Formatter, "lutaml/lml/formatter"
|
|
12
13
|
autoload :Layout, "lutaml/lml/layout"
|
|
13
14
|
|
|
15
|
+
# LML — the LutaML Model Language: parses the text DSL into domain
|
|
16
|
+
# documents, compiles model definitions into Serializable classes,
|
|
17
|
+
# and renders diagrams. See the entry-point contract under .parse /
|
|
18
|
+
# .parse_document below.
|
|
14
19
|
module Lml
|
|
15
20
|
class Error < Lutaml::Error; end
|
|
16
21
|
class ParsingError < Error; end
|
|
@@ -20,15 +25,25 @@ module Lutaml
|
|
|
20
25
|
ModelCompiler.new(namespace: namespace).compile(input)
|
|
21
26
|
end
|
|
22
27
|
|
|
23
|
-
#
|
|
24
|
-
#
|
|
28
|
+
# Entry-point contract:
|
|
29
|
+
#
|
|
30
|
+
# parse — Pipeline (preprocess, parse, transform, build)
|
|
31
|
+
# followed by ViewResolution: view-import
|
|
32
|
+
# expansion, show/hide filtering, and
|
|
33
|
+
# association-label enrichment. Use for
|
|
34
|
+
# documents consumed as diagrams or views.
|
|
35
|
+
# parse_document — Pipeline only; the document keeps raw
|
|
36
|
+
# collections and unresolved imports. Use for
|
|
37
|
+
# machine processing (ModelCompiler, Validator)
|
|
38
|
+
# where view semantics do not apply.
|
|
39
|
+
#
|
|
40
|
+
# Both return a Lutaml::Lml::Document.
|
|
25
41
|
def self.parse(input)
|
|
26
42
|
source = Source.wrap(input)
|
|
27
43
|
document = Pipeline.call(source)
|
|
28
44
|
ViewResolution.call(document, source.base_dir)
|
|
29
45
|
end
|
|
30
46
|
|
|
31
|
-
# Parse only — no import resolution, no label enrichment.
|
|
32
47
|
def self.parse_document(input)
|
|
33
48
|
Pipeline.call(input)
|
|
34
49
|
end
|
|
@@ -80,6 +95,7 @@ module Lutaml
|
|
|
80
95
|
autoload :OperationParameter, "lutaml/lml/models/operation_parameter"
|
|
81
96
|
autoload :Package, "lutaml/lml/models/package"
|
|
82
97
|
autoload :PrimitiveType, "lutaml/lml/models/primitive_type"
|
|
98
|
+
autoload :Reference, "lutaml/lml/models/reference"
|
|
83
99
|
autoload :TopElementAttribute, "lutaml/lml/models/top_element_attribute"
|
|
84
100
|
autoload :UmlClass, "lutaml/lml/models/uml_class"
|
|
85
101
|
autoload :Value, "lutaml/lml/models/value"
|
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.
|
|
4
|
+
version: 0.3.0
|
|
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-10-
|
|
11
|
+
date: 2026-10-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: lutaml-model
|
|
@@ -30,14 +30,14 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 1.3.
|
|
33
|
+
version: 1.3.68
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: 1.3.
|
|
40
|
+
version: 1.3.68
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: moxml
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -102,21 +102,33 @@ files:
|
|
|
102
102
|
- lib/lutaml/lml/format/adapter/transform.rb
|
|
103
103
|
- lib/lutaml/lml/formatter.rb
|
|
104
104
|
- lib/lutaml/lml/formatter/base.rb
|
|
105
|
+
- lib/lutaml/lml/formatter/elk.rb
|
|
105
106
|
- lib/lutaml/lml/formatter/graphviz.rb
|
|
106
107
|
- lib/lutaml/lml/formatter/graphviz/document_formatter.rb
|
|
107
108
|
- lib/lutaml/lml/formatter/graphviz/html_builder.rb
|
|
108
109
|
- lib/lutaml/lml/formatter/graphviz/node_formatter.rb
|
|
109
110
|
- lib/lutaml/lml/formatter/graphviz/relationship_formatter.rb
|
|
111
|
+
- lib/lutaml/lml/formatter/svg_pdf.rb
|
|
112
|
+
- lib/lutaml/lml/formatter/svg_pdf/path_parser.rb
|
|
110
113
|
- lib/lutaml/lml/grammar.rb
|
|
111
114
|
- lib/lutaml/lml/grammar/lml.artifact.json
|
|
112
115
|
- lib/lutaml/lml/grammar/lml.parg
|
|
113
116
|
- lib/lutaml/lml/has_attributes.rb
|
|
114
117
|
- lib/lutaml/lml/import_resolver.rb
|
|
115
118
|
- lib/lutaml/lml/layout.rb
|
|
119
|
+
- lib/lutaml/lml/layout/elk_engine.rb
|
|
120
|
+
- lib/lutaml/lml/layout/elk_graph_builder.rb
|
|
121
|
+
- lib/lutaml/lml/layout/elk_svg_markers.rb
|
|
122
|
+
- lib/lutaml/lml/layout/elk_svg_renderer.rb
|
|
116
123
|
- lib/lutaml/lml/layout/engine.rb
|
|
117
124
|
- lib/lutaml/lml/layout/graph_viz_engine.rb
|
|
118
125
|
- lib/lutaml/lml/literal_value.rb
|
|
119
126
|
- lib/lutaml/lml/model_compiler.rb
|
|
127
|
+
- lib/lutaml/lml/model_compiler/enums.rb
|
|
128
|
+
- lib/lutaml/lml/model_compiler/hydration.rb
|
|
129
|
+
- lib/lutaml/lml/model_compiler/serialization_mappings.rb
|
|
130
|
+
- lib/lutaml/lml/model_compiler/string_formats.rb
|
|
131
|
+
- lib/lutaml/lml/model_compiler/validation.rb
|
|
120
132
|
- lib/lutaml/lml/models/action.rb
|
|
121
133
|
- lib/lutaml/lml/models/association.rb
|
|
122
134
|
- lib/lutaml/lml/models/cardinality.rb
|
|
@@ -141,6 +153,7 @@ files:
|
|
|
141
153
|
- lib/lutaml/lml/models/operation_parameter.rb
|
|
142
154
|
- lib/lutaml/lml/models/package.rb
|
|
143
155
|
- lib/lutaml/lml/models/primitive_type.rb
|
|
156
|
+
- lib/lutaml/lml/models/reference.rb
|
|
144
157
|
- lib/lutaml/lml/models/serialization_mapping.rb
|
|
145
158
|
- lib/lutaml/lml/models/string_format.rb
|
|
146
159
|
- lib/lutaml/lml/models/top_element_attribute.rb
|