lutaml-uml 0.1.0 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/macos.yml +38 -0
  3. data/.github/workflows/ubuntu.yml +40 -0
  4. data/.github/workflows/windows.yml +51 -0
  5. data/.gitignore +1 -0
  6. data/Gemfile +2 -1
  7. data/LUTAML.adoc +339 -0
  8. data/{README.md → README.adoc} +15 -16
  9. data/Rakefile +3 -1
  10. data/bin/console +1 -0
  11. data/bin/folder_yaml2lutaml.sh +6 -0
  12. data/bin/plantuml2lutaml +58 -0
  13. data/bin/yaml2lutaml +144 -0
  14. data/exe/lutaml-uml +4 -3
  15. data/lib/lutaml/layout/engine.rb +15 -0
  16. data/lib/lutaml/layout/graph_viz_engine.rb +19 -0
  17. data/lib/lutaml/uml.rb +4 -0
  18. data/lib/lutaml/uml/abstraction.rb +7 -5
  19. data/lib/lutaml/uml/activity.rb +7 -5
  20. data/lib/lutaml/uml/actor.rb +14 -12
  21. data/lib/lutaml/uml/association.rb +40 -14
  22. data/lib/lutaml/uml/behavior.rb +7 -5
  23. data/lib/lutaml/uml/class.rb +61 -16
  24. data/lib/lutaml/uml/classifier.rb +9 -6
  25. data/lib/lutaml/uml/connector.rb +16 -12
  26. data/lib/lutaml/uml/constraint.rb +8 -7
  27. data/lib/lutaml/uml/constructor_end.rb +11 -8
  28. data/lib/lutaml/uml/data_type.rb +12 -4
  29. data/lib/lutaml/uml/dependency.rb +16 -13
  30. data/lib/lutaml/uml/document.rb +71 -0
  31. data/lib/lutaml/uml/enum.rb +40 -0
  32. data/lib/lutaml/uml/event.rb +7 -5
  33. data/lib/lutaml/uml/final_state.rb +7 -5
  34. data/lib/lutaml/uml/formatter.rb +21 -0
  35. data/lib/lutaml/uml/formatter/base.rb +67 -0
  36. data/lib/lutaml/uml/formatter/graphviz.rb +334 -0
  37. data/lib/lutaml/uml/has_attributes.rb +14 -0
  38. data/lib/lutaml/uml/has_members.rb +30 -0
  39. data/lib/lutaml/uml/instance.rb +15 -10
  40. data/lib/lutaml/uml/interface/base.rb +28 -0
  41. data/lib/lutaml/uml/interface/command_line.rb +265 -0
  42. data/lib/lutaml/uml/lutaml_path/document_wrapper.rb +15 -0
  43. data/lib/lutaml/uml/model.rb +11 -8
  44. data/lib/lutaml/uml/node/base.rb +21 -0
  45. data/lib/lutaml/uml/node/class_node.rb +57 -0
  46. data/lib/lutaml/uml/node/class_relationship.rb +14 -0
  47. data/lib/lutaml/uml/node/document.rb +18 -0
  48. data/lib/lutaml/uml/node/field.rb +34 -0
  49. data/lib/lutaml/uml/node/has_name.rb +15 -0
  50. data/lib/lutaml/uml/node/has_type.rb +15 -0
  51. data/lib/lutaml/uml/node/method.rb +29 -0
  52. data/lib/lutaml/uml/node/method_argument.rb +16 -0
  53. data/lib/lutaml/uml/node/relationship.rb +28 -0
  54. data/lib/lutaml/uml/opaque_behavior.rb +7 -6
  55. data/lib/lutaml/uml/package.rb +16 -13
  56. data/lib/lutaml/uml/parsers/attribute.rb +70 -0
  57. data/lib/lutaml/uml/parsers/dsl.rb +399 -0
  58. data/lib/lutaml/uml/parsers/dsl_preprocessor.rb +44 -0
  59. data/lib/lutaml/uml/parsers/dsl_transform.rb +27 -0
  60. data/lib/lutaml/uml/parsers/yaml.rb +46 -0
  61. data/lib/lutaml/uml/port.rb +6 -4
  62. data/lib/lutaml/uml/primitive_type.rb +11 -3
  63. data/lib/lutaml/uml/property.rb +25 -15
  64. data/lib/lutaml/uml/pseudostate.rb +7 -6
  65. data/lib/lutaml/uml/realization.rb +7 -5
  66. data/lib/lutaml/uml/region.rb +7 -6
  67. data/lib/lutaml/uml/serializers/association.rb +58 -0
  68. data/lib/lutaml/uml/serializers/base.rb +16 -0
  69. data/lib/lutaml/uml/serializers/class.rb +29 -0
  70. data/lib/lutaml/uml/serializers/top_element_attribute.rb +14 -0
  71. data/lib/lutaml/uml/serializers/yaml_view.rb +18 -0
  72. data/lib/lutaml/uml/state.rb +8 -6
  73. data/lib/lutaml/uml/state_machine.rb +7 -5
  74. data/lib/lutaml/uml/top_element.rb +45 -35
  75. data/lib/lutaml/uml/top_element_attribute.rb +26 -0
  76. data/lib/lutaml/uml/transition.rb +8 -6
  77. data/lib/lutaml/uml/trigger.rb +8 -6
  78. data/lib/lutaml/uml/version.rb +3 -1
  79. data/lib/lutaml/uml/vertex.rb +7 -5
  80. data/lutaml-uml.gemspec +11 -3
  81. data/spec/fixtures/datamodel/models/AddressClassProfile.yml +90 -0
  82. data/spec/fixtures/datamodel/models/AddressComponentProfile.yml +63 -0
  83. data/spec/fixtures/datamodel/models/AddressComponentSpecification.yml +15 -0
  84. data/spec/fixtures/datamodel/models/AddressProfile.yml +36 -0
  85. data/spec/fixtures/datamodel/models/AttributeProfile.yml +32 -0
  86. data/spec/fixtures/datamodel/models/InterchangeAddressClassProfile.yml +79 -0
  87. data/spec/fixtures/datamodel/models/Localization copy.yml +23 -0
  88. data/spec/fixtures/datamodel/models/Localization.yml +23 -0
  89. data/spec/fixtures/datamodel/models/ProfileCompliantAddress.yml +36 -0
  90. data/spec/fixtures/datamodel/models/ProfileCompliantAddressComponent.yml +15 -0
  91. data/spec/fixtures/datamodel/models/Signature.yml +20 -0
  92. data/spec/fixtures/datamodel/models/SignatureBlankDefinition.yml +20 -0
  93. data/spec/fixtures/datamodel/models/TextDirectionCode copy.yml +16 -0
  94. data/spec/fixtures/datamodel/models/TextDirectionCode.yml +16 -0
  95. data/spec/fixtures/datamodel/models/Validity.yml +14 -0
  96. data/spec/fixtures/datamodel/models/iso19160-1/Address.yml +22 -0
  97. data/spec/fixtures/datamodel/models/iso19160-1/AddressComponent.yml +2 -0
  98. data/spec/fixtures/datamodel/style.uml.inc +37 -0
  99. data/spec/fixtures/datamodel/views/AddressClassProfile.yml +12 -0
  100. data/spec/fixtures/datamodel/views/AddressProfile.yml +3 -0
  101. data/spec/fixtures/datamodel/views/CommonModels.yml +9 -0
  102. data/spec/fixtures/datamodel/views/TopDown.yml +62 -0
  103. data/spec/fixtures/dsl/diagram.lutaml +3 -0
  104. data/spec/fixtures/dsl/diagram_attributes.lutaml +5 -0
  105. data/spec/fixtures/dsl/diagram_class_assocation.lutaml +29 -0
  106. data/spec/fixtures/dsl/diagram_class_fields.lutaml +19 -0
  107. data/spec/fixtures/dsl/diagram_comments.lutaml +28 -0
  108. data/spec/fixtures/dsl/diagram_concept_model.lutaml +132 -0
  109. data/spec/fixtures/dsl/diagram_data_types.lutaml +24 -0
  110. data/spec/fixtures/dsl/diagram_definitions.lutaml +20 -0
  111. data/spec/fixtures/dsl/diagram_includes.lutaml +6 -0
  112. data/spec/fixtures/dsl/diagram_multiply_classes.lutaml +7 -0
  113. data/spec/fixtures/dsl/shared.lutaml +3 -0
  114. data/spec/fixtures/dsl/shared1.lutaml +4 -0
  115. data/spec/fixtures/generated_dot/AddressClassProfile.dot +170 -0
  116. data/spec/fixtures/generated_dot/AddressProfile.dot +34 -0
  117. data/spec/lutaml/layout/graph_viz_engine_spec.rb +31 -0
  118. data/spec/lutaml/uml/formatter/graphviz_spec.rb +41 -0
  119. data/spec/lutaml/uml/parsers/dsl_spec.rb +276 -0
  120. data/spec/lutaml/uml/parsers/yaml_spec.rb +18 -0
  121. data/spec/lutaml/uml/serializers/yaml_view_spec.rb +20 -0
  122. data/spec/lutaml/uml_spec.rb +2 -4
  123. data/spec/spec_helper.rb +11 -0
  124. metadata +182 -17
@@ -0,0 +1,15 @@
1
+ require "lutaml/lutaml_path/document_wrapper"
2
+
3
+ module Lutaml
4
+ module Uml
5
+ module LutamlPath
6
+ class DocumentWrapper < ::Lutaml::LutamlPath::DocumentWrapper
7
+ protected
8
+
9
+ def serialize_document(document)
10
+ serialize_to_hash(document)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,10 +1,13 @@
1
- module Lutaml::Uml
1
+ # frozen_string_literal: true
2
2
 
3
- class Model < Package
4
- attr_accessor :viewpoint
5
- def initialize
6
- @contents = []
7
- end
8
- end
3
+ module Lutaml
4
+ module Uml
5
+ class Model < Package
6
+ attr_accessor :viewpoint
9
7
 
10
- end
8
+ def initialize
9
+ @contents = []
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/uml/has_attributes"
4
+
5
+ module Lutaml
6
+ module Uml
7
+ module Node
8
+ class Base
9
+ include HasAttributes
10
+
11
+ # rubocop:disable Rails/ActiveRecordAliases
12
+ def initialize(attributes = {})
13
+ update_attributes(attributes)
14
+ end
15
+ # rubocop:enable Rails/ActiveRecordAliases
16
+
17
+ attr_accessor :parent
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/uml/node/base"
4
+ require "lutaml/uml/node/field"
5
+ require "lutaml/uml/node/method"
6
+ require "lutaml/uml/node/relationship"
7
+ require "lutaml/uml/node/class_relationship"
8
+ require "lutaml/uml/node/has_name"
9
+
10
+ module Lutaml
11
+ module Uml
12
+ module Node
13
+ class ClassNode < Base
14
+ include HasName
15
+
16
+ attr_reader :modifier
17
+
18
+ def modifier=(value)
19
+ @modifier = value.to_s # TODO: Validate?
20
+ end
21
+
22
+ attr_reader :members
23
+
24
+ def members=(value)
25
+ @members = value.to_a.map do |member|
26
+ type = member.to_a[0][0] # TODO: This is dumb
27
+ attributes = member.to_a[0][1]
28
+ attributes[:parent] = self
29
+
30
+ case type
31
+ when :field then Field.new(attributes)
32
+ when :method then Method.new(attributes)
33
+ when :relationship then Relationship.new(attributes)
34
+ when :class_relationship then ClassRelationship.new(attributes)
35
+ end
36
+ end
37
+ end
38
+
39
+ def fields
40
+ @members.select { |member| member.class == Field }
41
+ end
42
+
43
+ def methods
44
+ @members.select { |member| member.class == Method }
45
+ end
46
+
47
+ def relationships
48
+ @members.select { |member| member.class == Relationship }
49
+ end
50
+
51
+ def class_relationships
52
+ @members.select { |member| member.class == ClassRelationship }
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/uml/node/relationship"
4
+ require "lutaml/uml/node/has_name"
5
+
6
+ module Lutaml
7
+ module Uml
8
+ module Node
9
+ class ClassRelationship < Relationship
10
+ include HasName
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/uml/node/base"
4
+ require "lutaml/uml/node/class_node"
5
+
6
+ module Lutaml
7
+ module Uml
8
+ module Node
9
+ class Document < Base
10
+ attr_reader :classes
11
+
12
+ def classes=(value)
13
+ @classes = value.to_a.map { |attributes| ClassNode.new(attributes) }
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/uml/node/base"
4
+ require "lutaml/uml/node/has_name"
5
+ require "lutaml/uml/node/has_type"
6
+
7
+ module Lutaml
8
+ module Uml
9
+ module Node
10
+ class Field < Base
11
+ include HasName
12
+ include HasType
13
+
14
+ def initialize(attributes = {})
15
+ @access = "public"
16
+
17
+ super
18
+ end
19
+
20
+ attr_reader :static
21
+
22
+ def static=(value)
23
+ @static = !!value
24
+ end
25
+
26
+ attr_reader :access
27
+
28
+ def access=(value)
29
+ @access = value.to_s # TODO: Validate?
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Uml
5
+ module Node
6
+ module HasName
7
+ attr_reader :name
8
+
9
+ def name=(value)
10
+ @name = value.to_s
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Uml
5
+ module Node
6
+ module HasType
7
+ attr_reader :type
8
+
9
+ def type=(value)
10
+ @type = value.to_s
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/uml/node/field"
4
+ require "lutaml/uml/node/method_argument"
5
+ require "lutaml/uml/node/has_name"
6
+
7
+ module Lutaml
8
+ module Uml
9
+ module Node
10
+ class Method < Field
11
+ include HasName
12
+
13
+ attr_reader :abstract
14
+
15
+ def abstract=(value)
16
+ @abstract = !!value
17
+ end
18
+
19
+ attr_reader :arguments
20
+
21
+ def arguments=(value)
22
+ @arguments = value.to_a.map do |attributes|
23
+ MethodArgument.new(attributes)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/uml/node/base"
4
+ require "lutaml/uml/node/has_name"
5
+ require "lutaml/uml/node/has_type"
6
+
7
+ module Lutaml
8
+ module Uml
9
+ module Node
10
+ class MethodArgument < Base
11
+ include HasName
12
+ include HasType
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/uml/node/base"
4
+ require "lutaml/uml/node/has_name"
5
+ require "lutaml/uml/node/has_type"
6
+
7
+ module Lutaml
8
+ module Uml
9
+ module Node
10
+ class Relationship < Base
11
+ include HasName
12
+ include HasType
13
+
14
+ attr_reader :from
15
+
16
+ def from=(value)
17
+ @from = value.to_s
18
+ end
19
+
20
+ attr_reader :to
21
+
22
+ def to=(value)
23
+ @to = value.to_s
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  ##
2
4
  ## Behaviour metamodel
3
5
  ##
4
- module Lutaml::Uml
5
-
6
- class OpaqueBehavior < Behavior
7
- end
8
-
9
-
6
+ module Lutaml
7
+ module Uml
8
+ class OpaqueBehavior < Behavior
9
+ end
10
+ end
10
11
  end
@@ -1,16 +1,19 @@
1
- module Lutaml::Uml
1
+ # frozen_string_literal: true
2
2
 
3
- class Package < TopElement
4
- attr_accessor :imports, :contents
5
- def initialize
6
- @imports = []
7
- @contents = []
8
- @name = nil
9
- @xmi_id = nil
10
- @xmi_uuid = nil
11
- @namespace = nil
12
- @href = nil
13
- end
14
- end
3
+ module Lutaml
4
+ module Uml
5
+ class Package < TopElement
6
+ attr_accessor :imports, :contents
15
7
 
8
+ def initialize
9
+ @imports = []
10
+ @contents = []
11
+ @name = nil
12
+ @xmi_id = nil
13
+ @xmi_uuid = nil
14
+ @namespace = nil
15
+ @href = nil
16
+ end
17
+ end
18
+ end
16
19
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parslet"
4
+
5
+ module Lutaml
6
+ module Uml
7
+ module Parsers
8
+ class Attribute < Parslet::Parser
9
+ class Transform < Parslet::Transform
10
+ rule(integer: simple(:x)) { Integer(x) }
11
+ rule(float: simple(:x)) { Float(x) }
12
+ rule(string: simple(:x)) { String(x) }
13
+ end
14
+
15
+ def self.parse(io, options = {})
16
+ new.parse(io, options)
17
+ end
18
+
19
+ def parse(io, options = {})
20
+ tree = Transform.new.apply(super)
21
+ tree = tree[:assignments].each_with_object({}) do |assignment, memo|
22
+ memo[assignment[:name].to_s] = assignment[:value]
23
+ end
24
+
25
+ tree
26
+ end
27
+
28
+ rule(:spaces) { match("\s").repeat(1) }
29
+ rule(:spaces?) { spaces.maybe }
30
+
31
+ rule(:digits) { match["0-9"].repeat(1) }
32
+
33
+ rule(:integer) do
34
+ (str("-").maybe >> digits >> str(".").absent?).as(:integer)
35
+ end
36
+ rule(:float) do
37
+ (str("-").maybe >> digits >> str(".") >> digits).as(:float)
38
+ end
39
+
40
+ rule(:string_single_quoted) do
41
+ str("'") >>
42
+ (str("'").absent? >> any).repeat.as(:string) >>
43
+ str("'")
44
+ end
45
+ rule(:string_double_quoted) do
46
+ str('"') >>
47
+ (str('"').absent? >> any).repeat.as(:string) >>
48
+ str('"')
49
+ end
50
+
51
+ rule(:string) { string_single_quoted | string_double_quoted }
52
+
53
+ rule(:assignment_name) do
54
+ (match["=\s"].absent? >> any).repeat.as(:name)
55
+ end
56
+ rule(:assignment_value) { (integer | float | string).as(:value) }
57
+ rule(:assignment) do
58
+ assignment_name >> spaces? >> str("=") >> spaces? >> assignment_value
59
+ end
60
+
61
+ rule(:attribute) { spaces? >> assignment >> spaces? }
62
+ rule(:attributes) do
63
+ (attribute >> (str(",") >> attribute).repeat).repeat.as(:assignments)
64
+ end
65
+
66
+ root(:attributes)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,399 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parslet"
4
+ require "lutaml/uml/parsers/dsl_preprocessor"
5
+ require "lutaml/uml/parsers/dsl_transform"
6
+ require "lutaml/uml/node/document"
7
+
8
+ module Lutaml
9
+ module Uml
10
+ module Parsers
11
+ # Class for parsing LutaML dsl into Lutaml::Uml::Document
12
+ class Dsl < Parslet::Parser
13
+ # @param [String] io - LutaML string representation
14
+ # [Hash] options - options for parsing
15
+ #
16
+ # @return [Lutaml::Uml::Document]
17
+ def self.parse(io, options = {})
18
+ new.parse(io, options)
19
+ end
20
+
21
+ def parse(input_file, _options = {})
22
+ data = Lutaml::Uml::Parsers::DslPreprocessor.call(input_file)
23
+ ::Lutaml::Uml::Document.new(DslTransform.new.apply(super(data)))
24
+ end
25
+
26
+ KEYWORDS = %w[
27
+ abstract
28
+ aggregation
29
+ association
30
+ association
31
+ attribute
32
+ bidirectional
33
+ class
34
+ composition
35
+ data_type
36
+ dependency
37
+ diagram
38
+ directional
39
+ enum
40
+ fontname
41
+ generalizes
42
+ include
43
+ interface
44
+ member
45
+ member_type
46
+ method
47
+ owner
48
+ owner_type
49
+ primitive
50
+ private
51
+ protected
52
+ public
53
+ realizes
54
+ static
55
+ title
56
+ caption
57
+ ].freeze
58
+
59
+ KEYWORDS.each do |keyword|
60
+ rule("kw_#{keyword}") { str(keyword) }
61
+ end
62
+
63
+ rule(:spaces) { match("\s").repeat(1) }
64
+ rule(:spaces?) { spaces.maybe }
65
+ rule(:whitespace) do
66
+ (match("\s") | match("\r?\n") | match("\r") | str(";")).repeat(1)
67
+ end
68
+ rule(:whitespace?) { whitespace.maybe }
69
+ rule(:name) { match["a-zA-Z0-9 _-"].repeat(1) }
70
+ rule(:newline) { str("\n") >> str("\r").maybe }
71
+ rule(:comment_definition) do
72
+ spaces? >> str("**") >> (newline.absent? >> any).repeat.as(:comments)
73
+ end
74
+ rule(:comment_multiline_definition) do
75
+ spaces? >> str("*|") >> (str("|*").absent? >> any).repeat.as(:comments) >> whitespace? >> str("|*")
76
+ end
77
+ rule(:class_name_chars) { match('(?:[a-zA-Z0-9 _-]|\:|\.)').repeat(1) }
78
+ rule(:class_name) do
79
+ class_name_chars >>
80
+ (str("(") >>
81
+ class_name_chars >>
82
+ str(")")).maybe
83
+ end
84
+ rule(:cardinality_body_definition) do
85
+ match['0-9\*'].as(:min) >>
86
+ str("..").maybe >>
87
+ match['0-9\*'].as(:max).maybe
88
+ end
89
+ rule(:cardinality) do
90
+ str("[") >>
91
+ cardinality_body_definition.as(:cardinality) >>
92
+ str("]")
93
+ end
94
+ rule(:cardinality?) { cardinality.maybe }
95
+
96
+ # -- attribute/Method
97
+ rule(:kw_visibility_modifier) do
98
+ str("+") | str("-") | str("#") | str("~")
99
+ end
100
+
101
+ rule(:member_static) { (kw_static.as(:static) >> spaces).maybe }
102
+ rule(:visibility) do
103
+ kw_visibility_modifier.as(:visibility_modifier)
104
+ end
105
+ rule(:visibility?) { visibility.maybe }
106
+
107
+ rule(:method_abstract) { (kw_abstract.as(:abstract) >> spaces).maybe }
108
+ rule(:attribute_keyword) do
109
+ str("<<") >>
110
+ match['a-zA-Z0-9_\-\/'].repeat(1).as(:keyword) >>
111
+ str(">>")
112
+ end
113
+ rule(:attribute_keyword?) { attribute_keyword.maybe }
114
+ rule(:attribute_type) do
115
+ (str(":") >>
116
+ spaces? >>
117
+ attribute_keyword? >>
118
+ spaces? >>
119
+ match['"\''].maybe >>
120
+ match['a-zA-Z0-9_\- '].repeat(1).as(:type) >>
121
+ match['"\''].maybe >>
122
+ spaces?
123
+ )
124
+ end
125
+ rule(:attribute_type?) do
126
+ attribute_type.maybe
127
+ end
128
+
129
+ rule(:attribute_name) { name.as(:name) }
130
+ rule(:attribute_definition) do
131
+ (visibility?.as(:visibility) >>
132
+ match['"\''].maybe >>
133
+ attribute_name >>
134
+ match['"\''].maybe >>
135
+ attribute_type? >>
136
+ cardinality? >>
137
+ class_body?)
138
+ .as(:attributes)
139
+ end
140
+
141
+ rule(:title_keyword) { kw_title >> spaces }
142
+ rule(:title_text) do
143
+ match['"\''].maybe >>
144
+ match['a-zA-Z0-9_\- '].repeat(1).as(:title) >>
145
+ match['"\''].maybe
146
+ end
147
+ rule(:title_definition) { title_keyword >> title_text }
148
+ rule(:caption_keyword) { kw_caption >> spaces }
149
+ rule(:caption_text) do
150
+ match['"\''].maybe >>
151
+ match['a-zA-Z0-9_\- '].repeat(1).as(:caption) >>
152
+ match['"\''].maybe
153
+ end
154
+ rule(:caption_definition) { caption_keyword >> caption_text }
155
+
156
+ rule(:fontname_keyword) { kw_fontname >> spaces }
157
+ rule(:fontname_text) do
158
+ match['"\''].maybe >>
159
+ match['a-zA-Z0-9_\- '].repeat(1).as(:fontname) >>
160
+ match['"\''].maybe
161
+ end
162
+ rule(:fontname_definition) { fontname_keyword >> fontname_text }
163
+
164
+ # Method
165
+ # rule(:method_keyword) { kw_method >> spaces }
166
+ # rule(:method_argument) { name.as(:name) >> member_type }
167
+ # rule(:method_arguments_inner) do
168
+ # (method_argument >>
169
+ # (spaces? >> str(",") >> spaces? >> method_argument).repeat)
170
+ # .repeat.as(:arguments)
171
+ # end
172
+ # rule(:method_arguments) do
173
+ # (str("(") >>
174
+ # spaces? >>
175
+ # method_arguments_inner >>
176
+ # spaces? >>
177
+ # str(")"))
178
+ # .maybe
179
+ # end
180
+
181
+ # rule(:method_name) { name.as(:name) }
182
+ # rule(:method_return_type) { member_type.maybe }
183
+ # rule(:method_definition) do
184
+ # (method_abstract >>
185
+ # member_static >>
186
+ # visibility >>
187
+ # method_keyword >>
188
+ # method_name >>
189
+ # method_arguments >>
190
+ # method_return_type)
191
+ # .as(:method)
192
+ # end
193
+
194
+ # -- Association
195
+
196
+ rule(:association_keyword) { kw_association >> spaces }
197
+
198
+ %w[owner member].each do |association_end_type|
199
+ rule("#{association_end_type}_cardinality") do
200
+ spaces? >>
201
+ str("[") >>
202
+ cardinality_body_definition
203
+ .as("#{association_end_type}_end_cardinality") >>
204
+ str("]")
205
+ end
206
+ rule("#{association_end_type}_cardinality?") do
207
+ send("#{association_end_type}_cardinality").maybe
208
+ end
209
+ rule("#{association_end_type}_attribute_name") do
210
+ str("#") >>
211
+ visibility? >>
212
+ name.as("#{association_end_type}_end_attribute_name")
213
+ end
214
+ rule("#{association_end_type}_attribute_name?") do
215
+ send("#{association_end_type}_attribute_name").maybe
216
+ end
217
+ rule("#{association_end_type}_definition") do
218
+ send("kw_#{association_end_type}") >>
219
+ spaces >>
220
+ name.as("#{association_end_type}_end") >>
221
+ send("#{association_end_type}_attribute_name?") >>
222
+ send("#{association_end_type}_cardinality?")
223
+ end
224
+ rule("#{association_end_type}_type") do
225
+ send("kw_#{association_end_type}_type") >>
226
+ spaces >>
227
+ name.as("#{association_end_type}_end_type")
228
+ end
229
+ end
230
+
231
+ rule(:association_inner_definitions) do
232
+ owner_type |
233
+ member_type |
234
+ owner_definition |
235
+ member_definition |
236
+ comment_definition |
237
+ comment_multiline_definition
238
+ end
239
+ rule(:association_inner_definition) do
240
+ association_inner_definitions >> whitespace?
241
+ end
242
+ rule(:association_body) do
243
+ spaces? >>
244
+ str("{") >>
245
+ whitespace? >>
246
+ association_inner_definition.repeat.as(:members) >>
247
+ str("}")
248
+ end
249
+ rule(:association_definition) do
250
+ association_keyword >>
251
+ name.as(:name).maybe >>
252
+ association_body
253
+ end
254
+
255
+ # -- Class
256
+
257
+ rule(:kw_class_modifier) { kw_abstract | kw_interface }
258
+
259
+ rule(:class_modifier) do
260
+ (kw_class_modifier.as(:modifier) >> spaces).maybe
261
+ end
262
+ rule(:class_keyword) { kw_class >> spaces }
263
+ rule(:class_inner_definitions) do
264
+ definition_body |
265
+ attribute_definition |
266
+ comment_definition |
267
+ comment_multiline_definition
268
+ end
269
+ rule(:class_inner_definition) do
270
+ class_inner_definitions >> whitespace?
271
+ end
272
+ rule(:class_body) do
273
+ spaces? >>
274
+ str("{") >>
275
+ whitespace? >>
276
+ class_inner_definition.repeat.as(:members) >>
277
+ str("}")
278
+ end
279
+ rule(:class_body?) { class_body.maybe }
280
+ rule(:class_definition) do
281
+ class_modifier >>
282
+ class_keyword >>
283
+ class_name.as(:name) >>
284
+ spaces? >>
285
+ attribute_keyword? >>
286
+ class_body?
287
+ end
288
+
289
+ # -- Definition
290
+ rule(:definition_body) do
291
+ spaces? >>
292
+ str("definition") >>
293
+ whitespace? >>
294
+ (str("end definition").absent? >> any).repeat.as(:definition) >>
295
+ whitespace? >>
296
+ str("end definition")
297
+ end
298
+
299
+ # -- Enum
300
+ rule(:enum_keyword) { kw_enum >> spaces }
301
+ rule(:enum_inner_definitions) do
302
+ definition_body |
303
+ attribute_definition |
304
+ comment_definition |
305
+ comment_multiline_definition
306
+ end
307
+ rule(:enum_inner_definition) do
308
+ enum_inner_definitions >> whitespace?
309
+ end
310
+ rule(:enum_body) do
311
+ spaces? >>
312
+ str("{") >>
313
+ whitespace? >>
314
+ enum_inner_definition.repeat.as(:members) >>
315
+ str("}")
316
+ end
317
+ rule(:enum_body?) { enum_body.maybe }
318
+ rule(:enum_definition) do
319
+ enum_keyword >>
320
+ match['"\''].maybe >>
321
+ class_name.as(:name) >>
322
+ match['"\''].maybe >>
323
+ enum_body?
324
+ end
325
+
326
+ # -- data_type
327
+ rule(:data_type_keyword) { kw_data_type >> spaces }
328
+ rule(:data_type_inner_definitions) do
329
+ attribute_definition
330
+ end
331
+ rule(:data_type_inner_definition) do
332
+ data_type_inner_definitions >> whitespace?
333
+ end
334
+ rule(:data_type_body) do
335
+ spaces? >>
336
+ str("{") >>
337
+ whitespace? >>
338
+ data_type_inner_definition.repeat.as(:members) >>
339
+ str("}")
340
+ end
341
+ rule(:data_type_body?) { data_type_body.maybe }
342
+ rule(:data_type_definition) do
343
+ data_type_keyword >>
344
+ match['"\''].maybe >>
345
+ class_name.as(:name) >>
346
+ match['"\''].maybe >>
347
+ data_type_body?
348
+ end
349
+
350
+ # -- primitive
351
+ rule(:primitive_keyword) { kw_primitive >> spaces }
352
+ rule(:primitive_definition) do
353
+ primitive_keyword >>
354
+ match['"\''].maybe >>
355
+ class_name.as(:name) >>
356
+ match['"\''].maybe
357
+ end
358
+
359
+ # -- Diagram
360
+ rule(:diagram_keyword) { kw_diagram >> spaces? }
361
+ rule(:diagram_inner_definitions) do
362
+ title_definition |
363
+ caption_definition |
364
+ fontname_definition |
365
+ class_definition.as(:classes) |
366
+ enum_definition.as(:enums) |
367
+ primitive_definition.as(:primitives) |
368
+ data_type_definition.as(:data_types) |
369
+ association_definition.as(:associations) |
370
+ comment_definition |
371
+ comment_multiline_definition
372
+ end
373
+ rule(:diagram_inner_definition) do
374
+ diagram_inner_definitions >> whitespace?
375
+ end
376
+ rule(:diagram_body) do
377
+ spaces? >>
378
+ str("{") >>
379
+ whitespace? >>
380
+ diagram_inner_definition.repeat.as(:members) >>
381
+ str("}")
382
+ end
383
+ rule(:diagram_body?) { diagram_body.maybe }
384
+ rule(:diagram_definition) do
385
+ diagram_keyword >>
386
+ spaces? >>
387
+ class_name.as(:name) >>
388
+ diagram_body? >>
389
+ whitespace?
390
+ end
391
+ rule(:diagram_definitions) { diagram_definition >> whitespace? }
392
+ rule(:diagram) { whitespace? >> diagram_definition }
393
+ # -- Root
394
+
395
+ root(:diagram)
396
+ end
397
+ end
398
+ end
399
+ end