expressir 2.3.7 → 2.4.1

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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/TODO.bugs/01-stale-transformer-autoload.md +39 -0
  3. data/TODO.bugs/02-parser-class-instance-vars.md +36 -0
  4. data/TODO.bugs/03-builder-mutable-state.md +43 -0
  5. data/TODO.bugs/04-formatter-public-send-dispatch.md +53 -0
  6. data/TODO.bugs/05-anonymous-formatter-subclass.md +45 -0
  7. data/TODO.bugs/06-collection-registry-single-source.md +53 -0
  8. data/TODO.bugs/07-require-relative-cleanup.md +42 -0
  9. data/TODO.bugs/08-require-expressir-in-commands.md +34 -0
  10. data/TODO.bugs/09-parser-split.md +53 -0
  11. data/TODO.bugs/10-to-s-override.md +42 -0
  12. data/TODO.bugs/11-parser-class-variables.md +39 -0
  13. data/TODO.bugs/12-marker-modules-vs-registry.md +64 -0
  14. data/TODO.bugs/13-string-literal-scanner-limitation.md +52 -0
  15. data/TODO.bugs/14-model-formatting-leak.md +30 -0
  16. data/TODO.bugs/15-expression-children-macro.md +27 -0
  17. data/TODO.bugs/16-pretty-formatter-duplication.md +28 -0
  18. data/TODO.bugs/17-snake-case-cache-mutable-constant.md +28 -0
  19. data/TODO.bugs/18-const-get-private-constants.md +30 -0
  20. data/TODO.bugs/19-format-methods-public.md +22 -0
  21. data/TODO.bugs/20-coverage-nested-entities-dedup.md +20 -0
  22. data/TODO.bugs/21-operator-tokens-secondary-dispatch.md +21 -0
  23. data/TODO.bugs/22-builder-fast-path-wrappers.md +32 -0
  24. data/TODO.bugs/23-coverage-inverse-maps.md +21 -0
  25. data/TODO.bugs/24-streaming-builder-complexity.md +19 -0
  26. data/TODO.bugs/25-debug-puts-in-production.md +21 -0
  27. data/TODO.bugs/26-generic-entity-children-misplaced.md +21 -0
  28. data/TODO.bugs/27-package-build-god-method.md +19 -0
  29. data/TODO.bugs/28-package-god-class.md +30 -0
  30. data/TODO.bugs/29-validate-ascii-god-class.md +24 -0
  31. data/TODO.bugs/30-unicode-map-extraction.md +19 -0
  32. data/TODO.bugs/README.md +43 -0
  33. data/docs/_tutorials/formatting-schemas.adoc +89 -0
  34. data/docs/_tutorials/index.adoc +10 -0
  35. data/docs/lychee.toml +3 -0
  36. data/expressir.gemspec +4 -2
  37. data/lib/expressir/commands/changes_import_eengine.rb +0 -6
  38. data/lib/expressir/commands/changes_validate.rb +0 -2
  39. data/lib/expressir/commands/clean.rb +1 -1
  40. data/lib/expressir/commands/file_violations.rb +70 -0
  41. data/lib/expressir/commands/format.rb +1 -1
  42. data/lib/expressir/commands/non_ascii_character.rb +49 -0
  43. data/lib/expressir/commands/non_ascii_violation_collection.rb +301 -0
  44. data/lib/expressir/commands/package.rb +138 -185
  45. data/lib/expressir/commands/validate_ascii.rb +0 -510
  46. data/lib/expressir/commands/version.rb +1 -1
  47. data/lib/expressir/commands.rb +3 -0
  48. data/lib/expressir/coverage.rb +49 -117
  49. data/lib/expressir/express/builder.rb +74 -86
  50. data/lib/expressir/express/builder_context.rb +22 -0
  51. data/lib/expressir/express/builders/expression_builder.rb +16 -16
  52. data/lib/expressir/express/cache.rb +35 -8
  53. data/lib/expressir/express/error.rb +7 -0
  54. data/lib/expressir/express/formatter.rb +33 -18
  55. data/lib/expressir/express/formatters/declarations_formatter.rb +24 -2
  56. data/lib/expressir/express/formatters/remark_formatter.rb +75 -2
  57. data/lib/expressir/express/formatters/statements_formatter.rb +20 -3
  58. data/lib/expressir/express/grammar/parser.rb +705 -0
  59. data/lib/expressir/express/grammar.rb +11 -0
  60. data/lib/expressir/express/line_map.rb +48 -0
  61. data/lib/expressir/express/node_position_index.rb +215 -0
  62. data/lib/expressir/express/parser.rb +114 -884
  63. data/lib/expressir/express/pretty_formatter.rb +23 -5
  64. data/lib/expressir/express/remark_attacher.rb +483 -669
  65. data/lib/expressir/express/remark_scanner.rb +245 -0
  66. data/lib/expressir/express/schema_block_scanner.rb +136 -0
  67. data/lib/expressir/express/schema_source_formatter.rb +15 -0
  68. data/lib/expressir/express/scope_resolver.rb +194 -0
  69. data/lib/expressir/express/source_formatter.rb +15 -0
  70. data/lib/expressir/express/streaming_builder.rb +147 -176
  71. data/lib/expressir/express.rb +10 -6
  72. data/lib/expressir/model/concerns.rb +3 -0
  73. data/lib/expressir/model/data_types/generic_entity.rb +6 -6
  74. data/lib/expressir/model/declarations/entity.rb +5 -0
  75. data/lib/expressir/model/declarations/function.rb +5 -0
  76. data/lib/expressir/model/declarations/procedure.rb +5 -0
  77. data/lib/expressir/model/declarations/rule.rb +6 -0
  78. data/lib/expressir/model/declarations/schema.rb +21 -8
  79. data/lib/expressir/model/declarations/type.rb +3 -0
  80. data/lib/expressir/model/exp_file.rb +2 -0
  81. data/lib/expressir/model/expressions/aggregate_initializer.rb +1 -0
  82. data/lib/expressir/model/expressions/aggregate_initializer_item.rb +1 -0
  83. data/lib/expressir/model/expressions/binary_expression.rb +1 -0
  84. data/lib/expressir/model/expressions/entity_constructor.rb +1 -0
  85. data/lib/expressir/model/expressions/function_call.rb +1 -0
  86. data/lib/expressir/model/expressions/interval.rb +1 -0
  87. data/lib/expressir/model/expressions/query_expression.rb +2 -0
  88. data/lib/expressir/model/expressions/unary_expression.rb +1 -0
  89. data/lib/expressir/model/model_element.rb +128 -14
  90. data/lib/expressir/model/remark_format.rb +17 -0
  91. data/lib/expressir/model/remark_info.rb +31 -3
  92. data/lib/expressir/model/remark_placement.rb +29 -0
  93. data/lib/expressir/model/statements/alias.rb +2 -0
  94. data/lib/expressir/model/statements/assignment.rb +4 -0
  95. data/lib/expressir/model/statements/case.rb +21 -0
  96. data/lib/expressir/model/statements/case_action.rb +1 -0
  97. data/lib/expressir/model/statements/compound.rb +4 -0
  98. data/lib/expressir/model/statements/escape.rb +3 -0
  99. data/lib/expressir/model/statements/if.rb +5 -0
  100. data/lib/expressir/model/statements/null.rb +3 -0
  101. data/lib/expressir/model/statements/procedure_call.rb +3 -0
  102. data/lib/expressir/model/statements/repeat.rb +3 -0
  103. data/lib/expressir/model/statements/return.rb +3 -0
  104. data/lib/expressir/model/statements/skip.rb +3 -0
  105. data/lib/expressir/model.rb +12 -3
  106. data/lib/expressir/package/builder.rb +2 -2
  107. data/lib/expressir/version.rb +6 -1
  108. data/lib/expressir.rb +38 -3
  109. metadata +65 -5
  110. data/lib/expressir/express/transformer/remark_handling.rb +0 -194
@@ -4,6 +4,63 @@ module Expressir
4
4
  module Model
5
5
  # Base model element
6
6
  class ModelElement < Lutaml::Model::Serializable
7
+ # ---- Collection-attributes macro (TODO.bugs/12) ----
8
+ #
9
+ # Each model class declares which attributes hold child collections
10
+ # via `collection_attributes :foo, :bar, ...`. The declarations
11
+ # auto-register into the global registry, eliminating the need for
12
+ # a hand-maintained hash in NodePositionIndex.
13
+
14
+ # Returns the global registry: class → array of attr symbols.
15
+ def self.collection_registry
16
+ @collection_registry ||= {}
17
+ end
18
+
19
+ # Returns the collection attributes declared on this class (empty
20
+ # if none declared).
21
+ def self.collection_attributes_list
22
+ @collection_attributes || []
23
+ end
24
+
25
+ # Declare which attributes on this model class hold child collections
26
+ # that should be traversed by tree-walkers (NodePositionIndex,
27
+ # RemarkAttacher, ScopeResolver). Also registers this class in the
28
+ # global collection registry so NodePositionIndex can derive its
29
+ # COLLECTION_REGISTRY from the model instead of duplicating it.
30
+ def self.collection_attributes(*attrs)
31
+ @collection_attributes = attrs.freeze
32
+ ModelElement.collection_registry[self] = attrs
33
+ end
34
+
35
+ # ---- End collection-attributes macro ----
36
+
37
+ # ---- Child-attributes macro (TODO.bugs/15) ----
38
+ #
39
+ # Similar to collection_attributes, but for single-value ModelElement
40
+ # children (non-collection attributes that hold a ModelElement, like
41
+ # BinaryExpression#operand1). Used by RemarkAttacher for query
42
+ # traversal so the EXPRESSION_CHILDREN hash lives on the model.
43
+
44
+ # Returns the global registry: class → array of attr symbols.
45
+ def self.child_attributes_registry
46
+ @child_attributes_registry ||= {}
47
+ end
48
+
49
+ # Returns the child attributes declared on this class (empty if none).
50
+ def self.child_attributes_list
51
+ @child_attributes || []
52
+ end
53
+
54
+ # Declare which attributes on this model class hold single-value
55
+ # ModelElement children that should be traversed by the remark
56
+ # attacher's query-search.
57
+ def self.child_attributes(*attrs)
58
+ @child_attributes = attrs.freeze
59
+ ModelElement.child_attributes_registry[self] = attrs
60
+ end
61
+
62
+ # ---- End child-attributes macro ----
63
+
7
64
  SKIP_ATTRIBUTES = %i[parent _class].freeze
8
65
  # :parent is a special attribute that is used to store the parent of the current element
9
66
  # It is not a real attribute
@@ -98,10 +155,7 @@ module Expressir
98
155
  end
99
156
 
100
157
  def source
101
- formatter = Class.new(Expressir::Express::Formatter) do
102
- include Expressir::Express::HyperlinkFormatter
103
- end
104
- formatter.format(self)
158
+ Expressir::Express::SourceFormatter.format(self)
105
159
  end
106
160
 
107
161
  # @param [Hash] options
@@ -149,16 +203,9 @@ module Expressir
149
203
  current_scope = self
150
204
  target_node = nil
151
205
  loop do
152
- # find in current scope
153
- current_node = current_scope
154
- path_parts.each do |current_path|
155
- current_node = current_node.children_by_id[current_path]
156
- break unless current_node
157
- end
158
- target_node = current_node
206
+ target_node = resolve_path_in_scope(current_scope, path_parts)
159
207
  break if target_node
160
208
 
161
- # retry search in parent scope
162
209
  current_scope = current_scope.parent
163
210
  break unless current_scope
164
211
  end
@@ -170,6 +217,62 @@ module Expressir
170
217
  target_node
171
218
  end
172
219
 
220
+ # Walk `path_parts` through `scope`, resolving one part at a time.
221
+ # Handles `SELF\<supertype>.<attr>` qualifiers inline: when a part
222
+ # starts with `self\`, the next part is matched against the scope's
223
+ # redeclared attributes and disambiguated by the supertype name
224
+ # (issues #130 and #267).
225
+ def resolve_path_in_scope(scope, path_parts)
226
+ current_node = scope
227
+ pending_supertype = nil
228
+
229
+ path_parts.each do |current_path|
230
+ if current_path.start_with?("self\\")
231
+ pending_supertype = current_path.split("\\", 2).last
232
+ next
233
+ end
234
+
235
+ if pending_supertype
236
+ child = find_redeclared_by_supertype(current_node, current_path,
237
+ pending_supertype)
238
+ return nil unless child
239
+
240
+ current_node = child
241
+ pending_supertype = nil
242
+ else
243
+ current_node = current_node.children_by_id[current_path]
244
+ return nil unless current_node
245
+ end
246
+ end
247
+
248
+ # If the path ended on a SELF qualifier (no attr name followed),
249
+ # resolution is incomplete.
250
+ pending_supertype ? nil : current_node
251
+ end
252
+
253
+ # Find a redeclared attribute among `scope`'s children whose id matches
254
+ # `attr_name` and whose supertype_attribute references the given
255
+ # `supertype` name. Returns nil if `scope` is not an Attribute container
256
+ # or no such child exists.
257
+ def find_redeclared_by_supertype(scope, attr_name, supertype)
258
+ attrs = scope.respond_to?(:attributes) ? scope.attributes : nil
259
+ return nil unless attrs
260
+
261
+ attrs.find do |child|
262
+ next false unless child.is_a?(Model::Declarations::Attribute)
263
+ next false unless child.id&.safe_downcase == attr_name
264
+
265
+ supertype_attr = child.supertype_attribute
266
+ next false unless supertype_attr.respond_to?(:ref)
267
+
268
+ group_ref = supertype_attr.ref
269
+ next false unless group_ref.respond_to?(:entity)
270
+
271
+ entity_ref = group_ref.entity
272
+ entity_ref&.id&.safe_downcase == supertype
273
+ end
274
+ end
275
+
173
276
  # @return [Array<Declaration>]
174
277
  def children
175
278
  []
@@ -188,12 +291,23 @@ module Expressir
188
291
  nil
189
292
  end
190
293
 
191
- def to_s(no_remarks: false, formatter: nil)
294
+ # Format this element back into EXPRESS source text.
295
+ # @param no_remarks [Boolean] omit remarks from the output
296
+ # @param formatter [Express::Formatter, nil] use a specific formatter
297
+ # @return [String]
298
+ def format(no_remarks: false, formatter: nil)
192
299
  f = formatter || Express::Formatter.new(no_remarks: no_remarks)
193
- f.no_remarks = no_remarks
194
300
  f.format(self)
195
301
  end
196
302
 
303
+ # No-arg delegator so string interpolation ("#{element}") still
304
+ # produces EXPRESS source rather than the default Object#to_s.
305
+ # Remark-free output is requested explicitly through
306
+ # #format(no_remarks: true); it is not this method's default.
307
+ def to_s
308
+ format
309
+ end
310
+
197
311
  # Add a remark to this element
198
312
  # @param remark_info [RemarkInfo] Remark to add
199
313
  def add_remark(remark_info)
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Model
5
+ # Single source of truth for the remark-format vocabulary.
6
+ #
7
+ # Reference: ISO 10303-11 §7.1.6 defines exactly two remark forms:
8
+ # the tail remark (`-- ... \n`) and the embedded remark (`(* ... *)`).
9
+ # Both the model (RemarkInfo) and the express-side scanner/attacher
10
+ # reference these constants instead of bare string literals, so a
11
+ # rename or extension lands in one place.
12
+ module RemarkFormat
13
+ TAIL = "tail"
14
+ EMBEDDED = "embedded"
15
+ end
16
+ end
17
+ end
@@ -5,19 +5,24 @@ module Expressir
5
5
  # Can include optional tags for associating remarks with specific items
6
6
  class RemarkInfo < Lutaml::Model::Serializable
7
7
  attribute :text, :string
8
- attribute :format, :string, default: "embedded" # 'tail' or 'embedded'
8
+ attribute :format, :string, default: RemarkFormat::EMBEDDED
9
9
  attribute :tag, :string # optional remark tag like "entity.attr"
10
+ # See RemarkPlacement. nil = legacy placement.
11
+ attribute :placement, :string
12
+ # For TRAILING remarks, which body of the owner they close.
13
+ attribute :region, :string
14
+ attribute :source_offset, :integer # byte offset in source text
10
15
 
11
16
  # Check if this is a tail remark
12
17
  # @return [Boolean] True if format is 'tail'
13
18
  def tail?
14
- format == "tail"
19
+ format == RemarkFormat::TAIL
15
20
  end
16
21
 
17
22
  # Check if this is an embedded remark
18
23
  # @return [Boolean] True if format is 'embedded'
19
24
  def embedded?
20
- format == "embedded"
25
+ format == RemarkFormat::EMBEDDED
21
26
  end
22
27
 
23
28
  # Check if this remark has a tag
@@ -26,11 +31,32 @@ module Expressir
26
31
  !tag.nil? && !tag.empty?
27
32
  end
28
33
 
34
+ # Check if this remark should be emitted above its owning statement
35
+ # @return [Boolean] True if placement is 'leading'
36
+ def leading?
37
+ placement == RemarkPlacement::LEADING
38
+ end
39
+
40
+ # Check if this remark trails its owning statement on the same line
41
+ # @return [Boolean] True if placement is 'inline'
42
+ def inline?
43
+ placement == RemarkPlacement::INLINE
44
+ end
45
+
46
+ # Check if this remark closes the given body of its owning node
47
+ # @param name [Symbol, String] region attribute name
48
+ # @return [Boolean]
49
+ def trailing_region?(name)
50
+ placement == RemarkPlacement::TRAILING && region == name.to_s
51
+ end
52
+
29
53
  # YAML serialization
30
54
  yaml do
31
55
  map "text", to: :text
32
56
  map "format", to: :format
33
57
  map "tag", to: :tag
58
+ map "placement", to: :placement
59
+ map "region", to: :region
34
60
  end
35
61
 
36
62
  # XML serialization
@@ -39,6 +65,8 @@ module Expressir
39
65
  map_element "text", to: :text
40
66
  map_element "format", to: :format
41
67
  map_element "tag", to: :tag
68
+ map_element "placement", to: :placement
69
+ map_element "region", to: :region
42
70
  end
43
71
  end
44
72
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Model
5
+ # Single source of truth for where a remark sits relative to the model
6
+ # element that owns it.
7
+ #
8
+ # The parser discards comments, so attachment reconstructs their position
9
+ # from source lines. Placement records that reconstruction, letting the
10
+ # formatter put the remark back where it was written:
11
+ #
12
+ # - LEADING — on its own line(s) directly above the owning statement.
13
+ # - TRAILING — on its own line(s) at the end of a body, before the closing
14
+ # keyword. The owner is the body's owner, and RemarkInfo#region names
15
+ # which body: an IF owns two, and they close at different keywords.
16
+ # - INLINE — on the same line as the owning statement, after its text.
17
+ # Only single-line statements take these: appending to a multi-line
18
+ # statement would move the remark down to its closing keyword.
19
+ #
20
+ # `nil` means the remark was attached by a legacy path, or predates
21
+ # placement tracking. Those keep their historical emission and must not
22
+ # be rendered by placement-aware emitters.
23
+ module RemarkPlacement
24
+ LEADING = "leading"
25
+ TRAILING = "trailing"
26
+ INLINE = "inline"
27
+ end
28
+ end
29
+ end
@@ -4,8 +4,10 @@ module Expressir
4
4
  # Specified in ISO 10303-11:2004
5
5
  # - section 13.2 Alias statement
6
6
  class Alias < ModelElement
7
+ include Statement
7
8
  include Identifier
8
9
 
10
+ collection_attributes :statements
9
11
  attribute :expression, ModelElement
10
12
  attribute :statements, ModelElement, collection: true
11
13
  attribute :_class, :string, default: -> { self.class.name }
@@ -4,12 +4,16 @@ module Expressir
4
4
  # Specified in ISO 10303-11:2004
5
5
  # - section 13.3 Assignment
6
6
  class Assignment < ModelElement
7
+ include Statement
8
+
9
+ child_attributes :expression
7
10
  attribute :ref, ModelElement
8
11
  attribute :expression, ModelElement
9
12
  attribute :_class, :string, default: -> { self.class.name }
10
13
 
11
14
  key_value do
12
15
  map "_class", to: :_class, render_default: true
16
+ map "untagged_remarks", to: :untagged_remarks
13
17
  map "ref", to: :ref
14
18
  map "expression", to: :expression
15
19
  end
@@ -4,13 +4,34 @@ module Expressir
4
4
  # Specified in ISO 10303-11:2004
5
5
  # - section 13.4 Case statement
6
6
  class Case < ModelElement
7
+ include Statement
8
+
9
+ # The executable bodies of a CASE, exposed as ordered statement
10
+ # lists. Tree-walkers traverse these rather than `actions`, because
11
+ # a CaseAction is a label wrapper the parser gives no usable source
12
+ # position — the statement inside it carries the position that
13
+ # matters for placing comments.
14
+ collection_attributes :action_statements, :otherwise_statements
15
+
16
+ child_attributes :expression
7
17
  attribute :expression, ModelElement
8
18
  attribute :actions, CaseAction, collection: true
9
19
  attribute :otherwise_statement, ModelElement
10
20
  attribute :_class, :string, default: -> { self.class.name }
11
21
 
22
+ # @return [Array<ModelElement>] each action's statement, in order
23
+ def action_statements
24
+ Array(actions).filter_map(&:statement)
25
+ end
26
+
27
+ # @return [Array<ModelElement>] the OTHERWISE body, if present
28
+ def otherwise_statements
29
+ [otherwise_statement].compact
30
+ end
31
+
12
32
  key_value do
13
33
  map "_class", to: :_class, render_default: true
34
+ map "untagged_remarks", to: :untagged_remarks
14
35
  map "expression", to: :expression
15
36
  map "actions", to: :actions
16
37
  map "otherwise_statement", to: :otherwise_statement
@@ -4,6 +4,7 @@ module Expressir
4
4
  # Specified in ISO 10303-11:2004
5
5
  # - section 13.4 Case statement
6
6
  class CaseAction < ModelElement
7
+ child_attributes :expression
7
8
  attribute :labels, ModelElement, collection: true
8
9
  attribute :statement, ModelElement
9
10
  attribute :_class, :string, default: -> { self.class.name }
@@ -4,11 +4,15 @@ module Expressir
4
4
  # Specified in ISO 10303-11:2004
5
5
  # - section 13.5 Compound statement
6
6
  class Compound < ModelElement
7
+ include Statement
8
+
9
+ collection_attributes :statements
7
10
  attribute :statements, ModelElement, collection: true
8
11
  attribute :_class, :string, default: -> { self.class.name }
9
12
 
10
13
  key_value do
11
14
  map "_class", to: :_class, render_default: true
15
+ map "untagged_remarks", to: :untagged_remarks
12
16
  map "statements", to: :statements
13
17
  end
14
18
  end
@@ -4,10 +4,13 @@ module Expressir
4
4
  # Specified in ISO 10303-11:2004
5
5
  # - section 13.6 Escape statement
6
6
  class Escape < ModelElement
7
+ include Statement
8
+
7
9
  attribute :_class, :string, default: -> { self.class.name }
8
10
 
9
11
  key_value do
10
12
  map "_class", to: :_class, render_default: true
13
+ map "untagged_remarks", to: :untagged_remarks
11
14
  end
12
15
  end
13
16
  end
@@ -4,6 +4,10 @@ module Expressir
4
4
  # Specified in ISO 10303-11:2004
5
5
  # - section 13.7 If ... Then ... Else statement
6
6
  class If < ModelElement
7
+ include Statement
8
+
9
+ collection_attributes :statements, :else_statements
10
+ child_attributes :expression
7
11
  attribute :expression, ModelElement
8
12
  attribute :statements, ModelElement, collection: true
9
13
  attribute :else_statements, ModelElement, collection: true
@@ -11,6 +15,7 @@ module Expressir
11
15
 
12
16
  key_value do
13
17
  map "_class", to: :_class, render_default: true
18
+ map "untagged_remarks", to: :untagged_remarks
14
19
  map "expression", to: :expression
15
20
  map "statements", to: :statements
16
21
  map "else_statements", to: :else_statements
@@ -4,10 +4,13 @@ module Expressir
4
4
  # Specified in ISO 10303-11:2004
5
5
  # - section 13.1 Null (statement)
6
6
  class Null < ModelElement
7
+ include Statement
8
+
7
9
  attribute :_class, :string, default: -> { self.class.name }
8
10
 
9
11
  key_value do
10
12
  map "_class", to: :_class, render_default: true
13
+ map "untagged_remarks", to: :untagged_remarks
11
14
  end
12
15
  end
13
16
  end
@@ -4,12 +4,15 @@ module Expressir
4
4
  # Specified in ISO 10303-11:2004
5
5
  # - section 13.8 Procedure call statement
6
6
  class ProcedureCall < ModelElement
7
+ include Statement
8
+
7
9
  attribute :procedure, ModelElement
8
10
  attribute :parameters, ModelElement, collection: true
9
11
  attribute :_class, :string, default: -> { self.class.name }
10
12
 
11
13
  key_value do
12
14
  map "_class", to: :_class, render_default: true
15
+ map "untagged_remarks", to: :untagged_remarks
13
16
  map "procedure", to: :procedure
14
17
  map "parameters", to: :parameters
15
18
  end
@@ -4,8 +4,11 @@ module Expressir
4
4
  # Specified in ISO 10303-11:2004
5
5
  # - section 13.9 Repeat statement
6
6
  class Repeat < ModelElement
7
+ include Statement
7
8
  include Identifier
8
9
 
10
+ collection_attributes :statements
11
+ child_attributes :while_expression, :until_expression
9
12
  attribute :bound1, ModelElement
10
13
  attribute :bound2, ModelElement
11
14
  attribute :increment, ModelElement
@@ -4,11 +4,14 @@ module Expressir
4
4
  # Specified in ISO 10303-11:2004
5
5
  # - section 13.10 Return statement
6
6
  class Return < ModelElement
7
+ include Statement
8
+
7
9
  attribute :expression, ModelElement
8
10
  attribute :_class, :string, default: -> { self.class.name }
9
11
 
10
12
  key_value do
11
13
  map "_class", to: :_class, render_default: true
14
+ map "untagged_remarks", to: :untagged_remarks
12
15
  map "expression", to: :expression
13
16
  end
14
17
  end
@@ -4,10 +4,13 @@ module Expressir
4
4
  # Specified in ISO 10303-11:2004
5
5
  # - section 13.11 Skip statement
6
6
  class Skip < ModelElement
7
+ include Statement
8
+
7
9
  attribute :_class, :string, default: -> { self.class.name }
8
10
 
9
11
  key_value do
10
12
  map "_class", to: :_class, render_default: true
13
+ map "untagged_remarks", to: :untagged_remarks
11
14
  end
12
15
  end
13
16
  end
@@ -1,10 +1,17 @@
1
1
  # lib/expressir/model.rb
2
2
 
3
- # Load marker modules for type checking
4
- require_relative "model/concerns"
5
-
6
3
  module Expressir
7
4
  module Model
5
+ # Marker modules — autoload the same concerns.rb file from each name.
6
+ # The first reference triggers the load; the file defines all seven markers.
7
+ autoload :HasId, "#{__dir__}/model/concerns"
8
+ autoload :HasRemarkItems, "#{__dir__}/model/concerns"
9
+ autoload :HasRemarks, "#{__dir__}/model/concerns"
10
+ autoload :ScopeContainer, "#{__dir__}/model/concerns"
11
+ autoload :HasInformalPropositions, "#{__dir__}/model/concerns"
12
+ autoload :HasWhereRules, "#{__dir__}/model/concerns"
13
+ autoload :Statement, "#{__dir__}/model/concerns"
14
+
8
15
  autoload :ModelElement, "#{__dir__}/model/model_element"
9
16
  autoload :Cache, "#{__dir__}/model/cache"
10
17
  autoload :ExpFile, "#{__dir__}/model/exp_file"
@@ -12,6 +19,8 @@ module Expressir
12
19
  autoload :Repository, "#{__dir__}/model/repository"
13
20
  autoload :RepositoryValidator, "#{__dir__}/model/repository_validator"
14
21
  autoload :DependencyResolver, "#{__dir__}/model/dependency_resolver"
22
+ autoload :RemarkFormat, "#{__dir__}/model/remark_format"
23
+ autoload :RemarkPlacement, "#{__dir__}/model/remark_placement"
15
24
  autoload :RemarkInfo, "#{__dir__}/model/remark_info"
16
25
  autoload :InterfaceValidator, "#{__dir__}/model/interface_validator"
17
26
  autoload :SearchEngine, "#{__dir__}/model/search_engine"
@@ -31,7 +31,7 @@ module Expressir
31
31
  end
32
32
 
33
33
  # Create ZIP package
34
- Zip::File.open(output_path, Zip::File::CREATE) do |zip|
34
+ Zip::File.open(output_path, create: true) do |zip|
35
35
  add_metadata(zip, metadata)
36
36
  add_schemas(zip, repository, options)
37
37
  add_indexes(zip, repository)
@@ -68,7 +68,7 @@ module Expressir
68
68
  description: options[:description],
69
69
  created_at: Time.now.utc.iso8601,
70
70
  created_by: "expressir",
71
- expressir_version: Expressir::VERSION,
71
+ expressir_version: Expressir::Version::VERSION,
72
72
  express_mode: options[:express_mode],
73
73
  resolution_mode: options[:resolution_mode],
74
74
  serialization_format: options[:serialization_format],
@@ -1,3 +1,8 @@
1
1
  module Expressir
2
- VERSION = "2.3.7".freeze
2
+ # Gem version. Wrapped in a Version module so that lib/expressir.rb can
3
+ # autoload it (Ruby autoload only works for module/class constants, not
4
+ # for string constants like `Expressir::VERSION`).
5
+ module Version
6
+ VERSION = "2.4.1".freeze
7
+ end
3
8
  end
data/lib/expressir.rb CHANGED
@@ -1,6 +1,3 @@
1
- require_relative "expressir/version"
2
- require_relative "expressir/errors"
3
-
4
1
  require "lutaml/model"
5
2
  require "liquid" # To enable Lutaml::Model::Liquefiable
6
3
 
@@ -29,6 +26,44 @@ module Expressir
29
26
  autoload :Config, "#{__dir__}/expressir/config"
30
27
  extend Config
31
28
 
29
+ autoload :Version, "#{__dir__}/expressir/version"
30
+
31
+ # Error classes — autoload the same errors.rb file from each name.
32
+ # The first reference triggers the load; the file defines all 21 classes.
33
+ autoload :Error, "#{__dir__}/expressir/errors"
34
+ autoload :FileNotFoundError, "#{__dir__}/expressir/errors"
35
+ autoload :ManifestNotFoundError, "#{__dir__}/expressir/errors"
36
+ autoload :PackageNotFoundError, "#{__dir__}/expressir/errors"
37
+ autoload :SchemaNotFoundError, "#{__dir__}/expressir/errors"
38
+ autoload :UsageError, "#{__dir__}/expressir/errors"
39
+ autoload :MissingRequiredArgumentError, "#{__dir__}/expressir/errors"
40
+ autoload :InvalidOptionError, "#{__dir__}/expressir/errors"
41
+ autoload :ValidationError, "#{__dir__}/expressir/errors"
42
+ autoload :ManifestValidationError, "#{__dir__}/expressir/errors"
43
+ autoload :SchemaValidationError, "#{__dir__}/expressir/errors"
44
+ autoload :ReferentialIntegrityError, "#{__dir__}/expressir/errors"
45
+ autoload :NoValidSchemaPathsError, "#{__dir__}/expressir/errors"
46
+ autoload :CommandError, "#{__dir__}/expressir/errors"
47
+ autoload :PackageBuildError, "#{__dir__}/expressir/errors"
48
+ autoload :PackageReadError, "#{__dir__}/expressir/errors"
49
+ autoload :PackageExtractError, "#{__dir__}/expressir/errors"
50
+ autoload :PackageValidationError, "#{__dir__}/expressir/errors"
51
+ autoload :PackageListError, "#{__dir__}/expressir/errors"
52
+ autoload :PackageSearchError, "#{__dir__}/expressir/errors"
53
+ autoload :PackageTreeError, "#{__dir__}/expressir/errors"
54
+
55
+ # Backward-compatible top-level VERSION constant.
56
+ # Expressir::VERSION is a string, which Ruby autoload cannot lazy-load
57
+ # (autoload only works for module/class constants). The actual value
58
+ # lives in Expressir::Version::VERSION, autoloaded on first reference.
59
+ # This const_missing triggers the autoload when Expressir::VERSION is
60
+ # referenced, then caches the result.
61
+ def self.const_missing(name)
62
+ return Version::VERSION if name == :VERSION
63
+
64
+ super
65
+ end
66
+
32
67
  autoload :Benchmark, "#{__dir__}/expressir/benchmark"
33
68
  autoload :Coverage, "#{__dir__}/expressir/coverage"
34
69