lutaml-lml 0.1.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 (79) hide show
  1. checksums.yaml +7 -0
  2. data/lib/lutaml/lml/association_label_resolver.rb +52 -0
  3. data/lib/lutaml/lml/cli.rb +262 -0
  4. data/lib/lutaml/lml/data_processor/attribute_processing.rb +81 -0
  5. data/lib/lutaml/lml/data_processor/collection_processing.rb +37 -0
  6. data/lib/lutaml/lml/data_processor/instance_processing.rb +63 -0
  7. data/lib/lutaml/lml/data_processor/value_processing.rb +98 -0
  8. data/lib/lutaml/lml/data_processor/view_processing.rb +25 -0
  9. data/lib/lutaml/lml/data_processor.rb +49 -0
  10. data/lib/lutaml/lml/document_builder.rb +139 -0
  11. data/lib/lutaml/lml/executor/adapter_helpers.rb +45 -0
  12. data/lib/lutaml/lml/executor/condition_evaluator.rb +169 -0
  13. data/lib/lutaml/lml/executor/csv_adapter.rb +88 -0
  14. data/lib/lutaml/lml/executor/format_adapter.rb +54 -0
  15. data/lib/lutaml/lml/executor/xml_adapter.rb +102 -0
  16. data/lib/lutaml/lml/executor.rb +89 -0
  17. data/lib/lutaml/lml/format/adapter/document.rb +11 -0
  18. data/lib/lutaml/lml/format/adapter/mapping.rb +19 -0
  19. data/lib/lutaml/lml/format/adapter/standard_adapter.rb +127 -0
  20. data/lib/lutaml/lml/format/adapter/transform.rb +11 -0
  21. data/lib/lutaml/lml/format.rb +29 -0
  22. data/lib/lutaml/lml/formatter/base.rb +79 -0
  23. data/lib/lutaml/lml/formatter/graphviz/document_formatter.rb +89 -0
  24. data/lib/lutaml/lml/formatter/graphviz/html_builder.rb +72 -0
  25. data/lib/lutaml/lml/formatter/graphviz/node_formatter.rb +74 -0
  26. data/lib/lutaml/lml/formatter/graphviz/relationship_formatter.rb +130 -0
  27. data/lib/lutaml/lml/formatter/graphviz.rb +90 -0
  28. data/lib/lutaml/lml/formatter.rb +8 -0
  29. data/lib/lutaml/lml/grammar/concerns/associations.rb +76 -0
  30. data/lib/lutaml/lml/grammar/concerns/attributes.rb +126 -0
  31. data/lib/lutaml/lml/grammar/concerns/data_structures.rb +84 -0
  32. data/lib/lutaml/lml/grammar/concerns/definitions.rb +222 -0
  33. data/lib/lutaml/lml/grammar/concerns/instance_rules.rb +59 -0
  34. data/lib/lutaml/lml/grammar/concerns/primitives.rb +89 -0
  35. data/lib/lutaml/lml/grammar/concerns/view_rules.rb +34 -0
  36. data/lib/lutaml/lml/grammar/concerns.rb +17 -0
  37. data/lib/lutaml/lml/grammar/core.rb +71 -0
  38. data/lib/lutaml/lml/grammar/full.rb +12 -0
  39. data/lib/lutaml/lml/grammar/instances.rb +38 -0
  40. data/lib/lutaml/lml/grammar.rb +12 -0
  41. data/lib/lutaml/lml/has_attributes.rb +14 -0
  42. data/lib/lutaml/lml/import_resolver.rb +89 -0
  43. data/lib/lutaml/lml/layout/engine.rb +17 -0
  44. data/lib/lutaml/lml/layout/graph_viz_engine.rb +19 -0
  45. data/lib/lutaml/lml/layout.rb +8 -0
  46. data/lib/lutaml/lml/model_compiler.rb +325 -0
  47. data/lib/lutaml/lml/models/action.rb +10 -0
  48. data/lib/lutaml/lml/models/association.rb +26 -0
  49. data/lib/lutaml/lml/models/cardinality.rb +10 -0
  50. data/lib/lutaml/lml/models/collection.rb +11 -0
  51. data/lib/lutaml/lml/models/constraint.rb +20 -0
  52. data/lib/lutaml/lml/models/data_type.rb +31 -0
  53. data/lib/lutaml/lml/models/diagram.rb +17 -0
  54. data/lib/lutaml/lml/models/document.rb +45 -0
  55. data/lib/lutaml/lml/models/enum.rb +28 -0
  56. data/lib/lutaml/lml/models/fidelity.rb +10 -0
  57. data/lib/lutaml/lml/models/group.rb +11 -0
  58. data/lib/lutaml/lml/models/instance.rb +13 -0
  59. data/lib/lutaml/lml/models/instance_collection.rb +12 -0
  60. data/lib/lutaml/lml/models/instances_export.rb +10 -0
  61. data/lib/lutaml/lml/models/instances_import.rb +11 -0
  62. data/lib/lutaml/lml/models/operation.rb +23 -0
  63. data/lib/lutaml/lml/models/operation_parameter.rb +11 -0
  64. data/lib/lutaml/lml/models/package.rb +22 -0
  65. data/lib/lutaml/lml/models/primitive_type.rb +26 -0
  66. data/lib/lutaml/lml/models/top_element_attribute.rb +31 -0
  67. data/lib/lutaml/lml/models/uml_class.rb +84 -0
  68. data/lib/lutaml/lml/models/value.rb +12 -0
  69. data/lib/lutaml/lml/models/view_filter.rb +11 -0
  70. data/lib/lutaml/lml/models/view_import.rb +11 -0
  71. data/lib/lutaml/lml/parser.rb +22 -0
  72. data/lib/lutaml/lml/pipeline.rb +64 -0
  73. data/lib/lutaml/lml/preprocessor.rb +56 -0
  74. data/lib/lutaml/lml/transform.rb +20 -0
  75. data/lib/lutaml/lml/version.rb +7 -0
  76. data/lib/lutaml/lml/view_resolver.rb +48 -0
  77. data/lib/lutaml/lml/yaml_parser.rb +17 -0
  78. data/lib/lutaml/lml.rb +67 -0
  79. metadata +178 -0
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Lml
5
+ module Grammar
6
+ module Concerns
7
+ module Primitives
8
+ include Parslet
9
+
10
+ rule(:quotes) { match['"\''] }
11
+ rule(:quotes?) { quotes.maybe }
12
+ rule(:space) { match("\s") }
13
+ rule(:spaces) { space.repeat(1) }
14
+ rule(:spaces?) { spaces.maybe }
15
+ rule(:whitespace) do
16
+ (space | match(" ") | match("\r?\n") | match("\r") | str(";"))
17
+ .repeat(1)
18
+ end
19
+ rule(:whitespace?) { whitespace.maybe }
20
+ rule(:newline) { match('[\r\n]') }
21
+
22
+ rule(:quoted_string) do
23
+ str('"') >> (str('"').absent? >> any).repeat.as(:string) >> str('"')
24
+ end
25
+ rule(:boolean) { (str("true") | str("false")).as(:boolean) }
26
+ rule(:number) { (match("[0-9]").repeat(1) >> str(".") >> match("[0-9]").repeat(1)).as(:float) | match("[0-9]").repeat(1).as(:number) }
27
+ rule(:variable) { (quoted_string | match("[a-zA-Z0-9_]").repeat(1)) }
28
+ rule(:reference) do
29
+ str("reference:(") >>
30
+ (variable >> (str(".") >> variable).repeat).as(:reference) >>
31
+ str(")")
32
+ end
33
+ rule(:range) do
34
+ (variable.as(:start) >> str("..") >> variable.as(:end)).as(:range)
35
+ end
36
+ rule(:namespaced_identifier) do
37
+ variable >> (str("::") >> variable).repeat
38
+ end
39
+ rule(:comment_definition) do
40
+ spaces? >> (str("**") | str("#")) >> (newline.absent? >> any).repeat.as(:comments)
41
+ end
42
+ rule(:comment_multiline_definition) do
43
+ spaces? >> str("*|") >> (str("|*").absent? >> any)
44
+ .repeat.as(:comments) >> whitespace? >> str("|*")
45
+ end
46
+ rule(:class_name_chars) { match('(?:[a-zA-Z0-9 _-]|\:|\.)').repeat(1) }
47
+ rule(:class_name) do
48
+ class_name_chars >>
49
+ (str("(") >>
50
+ class_name_chars >>
51
+ str(")")).maybe
52
+ end
53
+ rule(:cardinality_body_definition) do
54
+ match['0-9a-z\*'].as("min") >>
55
+ str("..").maybe >>
56
+ match['0-9a-z\*'].as("max").maybe
57
+ end
58
+ rule(:cardinality) do
59
+ str("[") >>
60
+ cardinality_body_definition.as(:cardinality) >>
61
+ str("]")
62
+ end
63
+ rule(:cardinality?) { cardinality.maybe }
64
+
65
+ rule(:value) do
66
+ boolean |
67
+ reference |
68
+ range |
69
+ number |
70
+ quoted_string
71
+ end
72
+
73
+ rule(:key_value_pair) do
74
+ variable.as(:key) >> spaces >> str("=").maybe >> spaces? >> value.as(:value)
75
+ end
76
+ rule(:key_value_map) do
77
+ str("{") >> whitespace? >>
78
+ (key_value_pair >> whitespace).repeat.as(:key_value_map) >>
79
+ str("}")
80
+ end
81
+
82
+ rule(:kw_visibility_modifier) do
83
+ str("+") | str("-") | str("#") | str("~")
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Lml
5
+ module Grammar
6
+ module Concerns
7
+ module ViewRules
8
+ include Parslet
9
+
10
+ rule(:view_import_keyword) { str("import") >> spaces }
11
+
12
+ rule(:view_import) do
13
+ view_import_keyword >>
14
+ str('"') >> (str('"').absent? >> any).repeat.as(:path) >> str('"') >>
15
+ whitespace?
16
+ end
17
+
18
+ rule(:entity_name_list) do
19
+ class_name.as(:entity_name) >>
20
+ (spaces? >> str(",") >> spaces? >> class_name.as(:entity_name)).repeat
21
+ end
22
+
23
+ rule(:show_directive) do
24
+ str("show") >> spaces >> entity_name_list.as(:show_list) >> whitespace?
25
+ end
26
+
27
+ rule(:hide_directive) do
28
+ str("hide") >> spaces >> entity_name_list.as(:hide_list) >> whitespace?
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Lml
5
+ module Grammar
6
+ module Concerns
7
+ autoload :Primitives, "lutaml/lml/grammar/concerns/primitives"
8
+ autoload :Attributes, "lutaml/lml/grammar/concerns/attributes"
9
+ autoload :Associations, "lutaml/lml/grammar/concerns/associations"
10
+ autoload :Definitions, "lutaml/lml/grammar/concerns/definitions"
11
+ autoload :ViewRules, "lutaml/lml/grammar/concerns/view_rules"
12
+ autoload :InstanceRules, "lutaml/lml/grammar/concerns/instance_rules"
13
+ autoload :DataStructures, "lutaml/lml/grammar/concerns/data_structures"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parslet"
4
+
5
+ module Lutaml
6
+ module Lml
7
+ module Grammar
8
+ module Core
9
+ include Parslet
10
+ include Concerns::Primitives
11
+ include Concerns::Attributes
12
+ include Concerns::Associations
13
+ include Concerns::Definitions
14
+ include Concerns::ViewRules
15
+
16
+ CORE_KEYWORDS = %w[
17
+ abstract
18
+ aggregation
19
+ association
20
+ attribute
21
+ bidirectional
22
+ caption
23
+ class
24
+ composition
25
+ data_type
26
+ dependency
27
+ diagram
28
+ directional
29
+ enum
30
+ fontname
31
+ generalizes
32
+ include
33
+ interface
34
+ member
35
+ member_type
36
+ method
37
+ owner
38
+ owner_type
39
+ primitive
40
+ private
41
+ protected
42
+ public
43
+ realizes
44
+ static
45
+ title
46
+ view
47
+ ].freeze
48
+
49
+ CORE_KEYWORDS.each do |keyword|
50
+ rule("kw_#{keyword}") { whitespace? >> str(keyword) }
51
+ end
52
+
53
+ # === Require statements ===
54
+ rule(:require_stmt) do
55
+ kw_require >> spaces >> quoted_string.as(:require) >> whitespace?
56
+ end
57
+
58
+ rule(:require_block) do
59
+ (require_stmt >> whitespace?).repeat.as(:requires)
60
+ end
61
+
62
+ rule(:require_block?) do
63
+ require_block.maybe
64
+ end
65
+
66
+ # -- Root (Core: diagram only)
67
+ rule(:diagram) { require_block? >> diagram_definitions }
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Lml
5
+ module Grammar
6
+ module Full
7
+ include Core
8
+ include Instances
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parslet"
4
+
5
+ module Lutaml
6
+ module Lml
7
+ module Grammar
8
+ module Instances
9
+ include Parslet
10
+ include Concerns::InstanceRules
11
+ include Concerns::DataStructures
12
+
13
+ INSTANCE_KEYWORDS = %w[
14
+ collection
15
+ condition
16
+ export
17
+ extends
18
+ format
19
+ import
20
+ includes
21
+ instance
22
+ instances
23
+ models
24
+ require
25
+ template
26
+ validation
27
+ ].freeze
28
+
29
+ INSTANCE_KEYWORDS.each do |keyword|
30
+ rule("kw_#{keyword}") { whitespace? >> str(keyword) }
31
+ end
32
+
33
+ # -- Root (Full: diagram + models + instances)
34
+ rule(:diagram) { require_block? >> (models | diagram_definitions | view_definitions | instances | instance) }
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Lml
5
+ module Grammar
6
+ autoload :Core, "lutaml/lml/grammar/core"
7
+ autoload :Instances, "lutaml/lml/grammar/instances"
8
+ autoload :Full, "lutaml/lml/grammar/full"
9
+ autoload :Concerns, "lutaml/lml/grammar/concerns"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Lml
5
+ module HasAttributes
6
+ def update_attributes(attributes = {})
7
+ attributes.to_h.each do |name, value|
8
+ value = value.str if value.is_a?(Parslet::Slice)
9
+ public_send(:"#{name}=", value)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module Lutaml
6
+ module Lml
7
+ class ImportResolver
8
+ def initialize(base_path)
9
+ @base_path = base_path
10
+ end
11
+
12
+ def resolve(document)
13
+ entities = {}
14
+ associations = []
15
+ visited = Set.new
16
+
17
+ document.view_imports.each do |import|
18
+ resolve_import(import.path, entities, associations, visited, @base_path)
19
+ end
20
+
21
+ collect_local_entities(document, entities, associations)
22
+
23
+ [entities.values, associations]
24
+ end
25
+
26
+ private
27
+
28
+ def resolve_import(path, entities, associations, visited, base_path)
29
+ base_dir = base_path ? File.dirname(base_path) : Dir.pwd
30
+ abs_pattern = File.expand_path(path, base_dir)
31
+
32
+ Dir.glob(abs_pattern).each do |file_path|
33
+ next if visited.include?(file_path)
34
+ visited.add(file_path)
35
+
36
+ if view_file?(file_path)
37
+ resolve_view_file(file_path, entities, associations, visited)
38
+ else
39
+ resolve_model_file(file_path, entities, associations)
40
+ end
41
+ end
42
+ end
43
+
44
+ def resolve_view_file(file_path, entities, associations, visited)
45
+ doc = Parser.parse_document(File.new(file_path))
46
+
47
+ collect_local_entities(doc, entities, associations)
48
+
49
+ doc.view_imports&.each do |import|
50
+ resolve_import(import.path, entities, associations, visited, file_path)
51
+ end
52
+ rescue Errno::ENOENT, Errno::EACCES => e
53
+ warn "Skipping #{file_path}: #{e.message}"
54
+ []
55
+ end
56
+
57
+ def resolve_model_file(file_path, entities, associations)
58
+ content = File.read(file_path)
59
+ wrapped = "diagram Fragment {\n#{content}\n}"
60
+ doc = Parser.parse_document(StringIO.new(wrapped))
61
+
62
+ collect_local_entities(doc, entities, associations)
63
+ rescue Errno::ENOENT, Errno::EACCES => e
64
+ warn "Skipping #{file_path}: #{e.message}"
65
+ []
66
+ end
67
+
68
+ def view_file?(file_path)
69
+ head = File.read(file_path, 200, encoding: "UTF-8")
70
+ head.match?(/\b(view|diagram)\s+\w/)
71
+ rescue Errno::ENOENT, Errno::EACCES
72
+ false
73
+ end
74
+
75
+ def collect_local_entities(doc, entities, associations)
76
+ merge_entities(doc.classes, entities)
77
+ merge_entities(doc.enums, entities)
78
+ merge_entities(doc.data_types, entities)
79
+ associations.concat(doc.associations.to_a)
80
+ end
81
+
82
+ def merge_entities(collection, entities)
83
+ collection.each do |entity|
84
+ entities[entity.name] ||= entity
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Layout
5
+ class Engine
6
+ attr_accessor :input
7
+
8
+ def initialize(input:)
9
+ @input = input
10
+ end
11
+
12
+ def render(_type)
13
+ raise ArgumentError, "Implement render method"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ruby-graphviz"
4
+ require "open3"
5
+
6
+ module Lutaml
7
+ module Layout
8
+ class GraphVizEngine < Engine
9
+ def render(type)
10
+ Open3.popen3("dot -T#{type}") do |stdin, stdout, _stderr, _wait|
11
+ stdout.binmode
12
+ stdin.puts(input)
13
+ stdin.close
14
+ stdout.read
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Layout
5
+ autoload :Engine, "lutaml/lml/layout/engine"
6
+ autoload :GraphVizEngine, "lutaml/lml/layout/graph_viz_engine"
7
+ end
8
+ end