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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/lutaml/lml/cli.rb +84 -18
  3. data/lib/lutaml/lml/data_processor/value_processing.rb +6 -1
  4. data/lib/lutaml/lml/document_builder.rb +72 -3
  5. data/lib/lutaml/lml/format/adapter/mapping.rb +12 -0
  6. data/lib/lutaml/lml/format/adapter/standard_adapter.rb +41 -12
  7. data/lib/lutaml/lml/formatter/elk.rb +45 -0
  8. data/lib/lutaml/lml/formatter/graphviz/node_formatter.rb +2 -1
  9. data/lib/lutaml/lml/formatter/svg_pdf/path_parser.rb +134 -0
  10. data/lib/lutaml/lml/formatter/svg_pdf.rb +281 -0
  11. data/lib/lutaml/lml/formatter.rb +2 -0
  12. data/lib/lutaml/lml/grammar/lml.artifact.json +7265 -4550
  13. data/lib/lutaml/lml/grammar/lml.parg +230 -115
  14. data/lib/lutaml/lml/layout/elk_engine.rb +15 -0
  15. data/lib/lutaml/lml/layout/elk_graph_builder.rb +129 -0
  16. data/lib/lutaml/lml/layout/elk_svg_markers.rb +45 -0
  17. data/lib/lutaml/lml/layout/elk_svg_renderer.rb +119 -0
  18. data/lib/lutaml/lml/layout/graph_viz_engine.rb +1 -1
  19. data/lib/lutaml/lml/layout.rb +4 -0
  20. data/lib/lutaml/lml/model_compiler/enums.rb +172 -0
  21. data/lib/lutaml/lml/model_compiler/hydration.rb +123 -0
  22. data/lib/lutaml/lml/model_compiler/serialization_mappings.rb +162 -0
  23. data/lib/lutaml/lml/model_compiler/string_formats.rb +42 -0
  24. data/lib/lutaml/lml/model_compiler/validation.rb +65 -0
  25. data/lib/lutaml/lml/model_compiler.rb +46 -453
  26. data/lib/lutaml/lml/models/document.rb +29 -0
  27. data/lib/lutaml/lml/models/operation_parameter.rb +1 -0
  28. data/lib/lutaml/lml/models/reference.rb +23 -0
  29. data/lib/lutaml/lml/models/top_element_attribute.rb +12 -0
  30. data/lib/lutaml/lml/models/uml_class.rb +1 -1
  31. data/lib/lutaml/lml/preprocessor.rb +44 -79
  32. data/lib/lutaml/lml/transform.rb +54 -6
  33. data/lib/lutaml/lml/validator.rb +88 -19
  34. data/lib/lutaml/lml/version.rb +1 -1
  35. data/lib/lutaml/lml.rb +19 -3
  36. metadata +17 -4
@@ -6,6 +6,7 @@ module Lutaml
6
6
  attribute :name, :string
7
7
  attribute :type, :string
8
8
  attribute :direction, :string, default: "in"
9
+ attribute :default, :string
9
10
  end
10
11
  end
11
12
  end
@@ -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
- # RS 3001 §Comment: `//` to end of line and `/* ... */` block
25
- # comments are stripped quote-aware before include expansion, so
26
- # string literals (e.g. URLs) survive intact.
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
- text = strip_comments(text)
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: "UTF-8")
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
- def strip_comments(text)
57
- scanner = CommentScanner.new(text)
58
- out = +""
59
- until scanner.done?
60
- scanner.emit(out)
61
- end
62
- out
63
- end
64
-
65
- # Quote-aware scanner removing `//` and `/* */` comments while
66
- # preserving string literals and line structure.
67
- class CommentScanner
68
- def initialize(text)
69
- @text = text
70
- @pos = 0
71
- @quote = nil
72
- end
73
-
74
- def done?
75
- @pos >= @text.length
76
- end
77
-
78
- def emit(out)
79
- return emit_quoted(out) if @quote
80
- return emit_delimiter(out) if string_delimiter?(@text[@pos])
81
- return emit_block_comment(out) if block_comment_start?
82
- return skip_line_comment if line_comment_start?
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
- def block_comment_start?
113
- @text[@pos, 2] == "/*"
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
@@ -8,17 +8,65 @@ module Lutaml
8
8
  VISIBILITY_MAP = {
9
9
  '-' => 'private',
10
10
  '#' => 'protected',
11
- '~' => 'friendly'
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
- # Global scalar normalizer: applied to every leaf in the parse
18
- # tree (names may legally contain trailing spaces via
19
- # class_name_chars). Binding is named :value to make the breadth
20
- # explicit — it is not tied to any one grammar rule.
21
- rule(simple(:value)) { value.nil? ? value : value.to_s.strip }
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
@@ -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
- names = all_definitions.map(&:first)
63
- names.tally.filter_map do |name, count|
64
- next if count == 1
65
-
66
- Violation.new(
67
- rule: 'unique_names',
68
- message: "'#{name}' is defined #{count} times"
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
- instance_attribute_pairs(instance).filter_map do |name, value|
218
- literal = value.respond_to?(:value) ? value.value : value
219
- next unless literal.is_a?(Hash) && literal[:reference]
286
+ Array(instance.attributes).filter_map do |attr|
287
+ ref = attr.reference
288
+ next unless ref
220
289
 
221
- [name, literal[:reference]]
290
+ [attr.name.to_s, ref.path]
222
291
  end
223
292
  end
224
293
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Lml
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.0"
6
6
  end
7
7
  end
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
- # Full entry point: parse, then resolve view imports and enrich
24
- # association labels.
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.2.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-05 00:00:00.000000000 Z
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.57
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.57
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