expressir 2.4.31-aarch64-linux → 2.4.36-aarch64-linux

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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/conformance.yml +34 -0
  3. data/.gitignore +3 -0
  4. data/Gemfile +1 -1
  5. data/TODO.eengine/00-overview.md +39 -0
  6. data/TODO.eengine/01-resolution-diagnostics.md +30 -0
  7. data/TODO.eengine/02-check-catalog-completion.md +53 -0
  8. data/TODO.eengine/03-entity-computations.md +22 -0
  9. data/TODO.eengine/04-shtolo-rule-audit.md +32 -0
  10. data/TODO.eengine/05-shtolo-longform-flatten.md +12 -0
  11. data/TODO.eengine/06-population-p21.md +34 -0
  12. data/TODO.eengine/07-schema-compare-patch.md +25 -0
  13. data/TODO.eengine/08-official-corpus-gate.md +7 -0
  14. data/TODO.eengine/09-mim-mapping-parser.md +9 -0
  15. data/TODO.parity-ee/00-overview.md +1 -1
  16. data/TODO.sota/00-overview.md +57 -0
  17. data/TODO.sota/01-type-checking.md +115 -0
  18. data/TODO.sota/02-evaluator.md +86 -0
  19. data/TODO.sota/03-conformance.md +53 -0
  20. data/TODO.sota/04-lsp.md +70 -0
  21. data/TODO.sota/05-interop.md +55 -0
  22. data/TODO.sota/06-dialects.md +57 -0
  23. data/TODO.sota/07-reference-artifacts.md +55 -0
  24. data/TODO.suma-improvements/07-part28-xml.md +10 -13
  25. data/docs/_guides/interop/index.adoc +25 -0
  26. data/docs/_guides/interop/json-schema.adoc +88 -0
  27. data/docs/_guides/interop/owl-rdf.adoc +87 -0
  28. data/docs/_guides/reference/checker-notes.adoc +55 -0
  29. data/docs/_guides/reference/checker.adoc +36 -0
  30. data/docs/_guides/reference/evaluator.adoc +40 -0
  31. data/docs/_guides/reference/grammar-crosswalk.adoc +299 -0
  32. data/docs/_guides/reference/index.adoc +22 -0
  33. data/docs/_guides/reference/lsp.adoc +41 -0
  34. data/docs/_guides/reference/p21.adoc +30 -0
  35. data/docs/_guides/reference/shtolo.adoc +36 -0
  36. data/exe/expressir-lsp +6 -0
  37. data/expressir.gemspec +2 -2
  38. data/lib/expressir/3.2/expressir_core.so +0 -0
  39. data/lib/expressir/3.3/expressir_core.so +0 -0
  40. data/lib/expressir/3.4/expressir_core.so +0 -0
  41. data/lib/expressir/4.0/expressir_core.so +0 -0
  42. data/lib/expressir/cli.rb +25 -0
  43. data/lib/expressir/commands/check.rb +3 -1
  44. data/lib/expressir/commands/export_json_schema.rb +29 -0
  45. data/lib/expressir/commands/export_owl.rb +26 -0
  46. data/lib/expressir/commands/smrl_xml_index.rb +25 -0
  47. data/lib/expressir/commands/validate.rb +3 -0
  48. data/lib/expressir/commands.rb +3 -0
  49. data/lib/expressir/conformance/trend.rb +100 -0
  50. data/lib/expressir/conformance.rb +8 -0
  51. data/lib/expressir/eval/environment.rb +83 -0
  52. data/lib/expressir/eval/expression.rb +610 -0
  53. data/lib/expressir/eval/interpreter.rb +316 -0
  54. data/lib/expressir/eval/rules.rb +88 -0
  55. data/lib/expressir/eval/values.rb +74 -0
  56. data/lib/expressir/eval.rb +17 -0
  57. data/lib/expressir/express/builder.rb +17 -5
  58. data/lib/expressir/express/builders/entity_decl_builder.rb +17 -5
  59. data/lib/expressir/express/builders/expression_builder.rb +9 -1
  60. data/lib/expressir/express/checker.rb +970 -5
  61. data/lib/expressir/express/dialect.rb +44 -0
  62. data/lib/expressir/express/formatters/declarations_formatter.rb +21 -14
  63. data/lib/expressir/express/formatters/expressions_formatter.rb +4 -1
  64. data/lib/expressir/express/parser.rb +9 -4
  65. data/lib/expressir/express/pretty_formatter.rb +9 -14
  66. data/lib/expressir/express/schema_compare.rb +194 -0
  67. data/lib/expressir/express/shtolo.rb +194 -63
  68. data/lib/expressir/express.rb +3 -0
  69. data/lib/expressir/interop/json_schema.rb +195 -0
  70. data/lib/expressir/interop/owl.rb +216 -0
  71. data/lib/expressir/interop.rb +13 -0
  72. data/lib/expressir/lsp/document.rb +226 -0
  73. data/lib/expressir/lsp/server.rb +184 -0
  74. data/lib/expressir/lsp/workspace.rb +402 -0
  75. data/lib/expressir/lsp.rb +11 -0
  76. data/lib/expressir/model/declarations/entity.rb +132 -0
  77. data/lib/expressir/model/declarations/schema.rb +37 -6
  78. data/lib/expressir/p21/builder.rb +92 -0
  79. data/lib/expressir/p21/grammar.rb +107 -0
  80. data/lib/expressir/p21/parser.rb +16 -0
  81. data/lib/expressir/p21/population.rb +35 -0
  82. data/lib/expressir/p21/validator.rb +213 -0
  83. data/lib/expressir/p21.rb +17 -0
  84. data/lib/expressir/reference/grammar_crosswalk.rb +122 -0
  85. data/lib/expressir/reference/grammar_crosswalk.yml +800 -0
  86. data/lib/expressir/reference/note_catalog.rb +54 -0
  87. data/lib/expressir/reference.rb +10 -0
  88. data/lib/expressir/version.rb +1 -1
  89. data/lib/expressir.rb +5 -0
  90. data/lib/tasks/conformance.rake +27 -0
  91. data/lib/tasks/reference.rake +25 -0
  92. metadata +68 -4
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Express
5
+ # Edition awareness for ISO 10303-11. The grammar accepts the union
6
+ # of both editions' text syntax; edition gating is reported by the
7
+ # Checker so a 1994 consumer sees exactly which declarations use
8
+ # 2004-only constructs, and a 2004 consumer sees the edition-1
9
+ # syntax the 2004 edition deprecates (Annex K).
10
+ module Dialect
11
+ EDITION_1994 = :"10303-11:1994"
12
+ EDITION_2004 = :"10303-11:2004"
13
+ DIALECTS = [EDITION_1994, EDITION_2004].freeze
14
+ DEFAULT = EDITION_2004
15
+
16
+ # The audited change list lives in
17
+ # spec/fixtures/dialects/iso-10303-11-2004-changes.yaml; every
18
+ # construct there maps to a note id below.
19
+ NOTE_IDS = %i[
20
+ check_dialect_2004_only
21
+ check_dialect_deprecated
22
+ check_dialect_mixed
23
+ ].freeze
24
+
25
+ module_function
26
+
27
+ def normalize(value)
28
+ return DEFAULT if value.nil?
29
+
30
+ candidate = value.to_s
31
+ dialect = DIALECTS.find do |entry|
32
+ entry.to_s == candidate || entry.to_s.end_with?(":#{candidate}")
33
+ end
34
+ return dialect if dialect
35
+
36
+ raise Expressir::InvalidOptionError.new(
37
+ "dialect",
38
+ value,
39
+ valid_options: DIALECTS.map(&:to_s),
40
+ )
41
+ end
42
+ end
43
+ end
44
+ end
@@ -110,20 +110,15 @@ module Expressir
110
110
  end
111
111
 
112
112
  def format_declarations_entity(node)
113
- if node.attributes.nil?
114
- explicit_attributes = []
115
- derived_attributes = []
116
- inverse_attributes = []
117
- else
118
- explicit_attributes = node.attributes.select do |x|
119
- x.kind == Model::Declarations::Attribute::EXPLICIT
120
- end
121
- derived_attributes = node.attributes.select do |x|
122
- x.kind == Model::Declarations::Attribute::DERIVED
123
- end
124
- inverse_attributes = node.attributes.select do |x|
125
- x.kind == Model::Declarations::Attribute::INVERSE
126
- end
113
+ attrs = entity_attribute_items(node)
114
+ explicit_attributes = attrs.select do |x|
115
+ x.kind == Model::Declarations::Attribute::EXPLICIT
116
+ end
117
+ derived_attributes = attrs.select do |x|
118
+ x.kind == Model::Declarations::Attribute::DERIVED
119
+ end
120
+ inverse_attributes = attrs.select do |x|
121
+ x.kind == Model::Declarations::Attribute::INVERSE
127
122
  end
128
123
 
129
124
  indent_const = self.class.const_get(:INDENT)
@@ -231,6 +226,18 @@ module Expressir
231
226
  ].join("\n")
232
227
  end
233
228
 
229
+ # Entity#attributes is the Ruby-parse layout (every attribute kind
230
+ # in one list). The core path parks derived/inverse in their own
231
+ # collections instead. Both may also arrive as Array-of-Array when
232
+ # a builder nested list productions. Normalize to a flat list of
233
+ # Attribute-like nodes before selecting on `kind`.
234
+ def entity_attribute_items(node)
235
+ items = Array(node.attributes)
236
+ items += Array(node.derived_attributes) if node.respond_to?(:derived_attributes)
237
+ items += Array(node.inverse_attributes) if node.respond_to?(:inverse_attributes)
238
+ items.flatten.compact.uniq(&:object_id)
239
+ end
240
+
234
241
  def format_declarations_function(node)
235
242
  indent_char = self.class.const_get(:INDENT_CHAR)
236
243
  [
@@ -154,7 +154,10 @@ module Expressir
154
154
 
155
155
  def format_expressions_unary_expression(node)
156
156
  needs_parens = node.operand.is_a?(Model::Expressions::BinaryExpression) ||
157
- node.operand.is_a?(Model::Expressions::Interval)
157
+ node.operand.is_a?(Model::Expressions::Interval) ||
158
+ # Nested unary must stay parenthesized: "- -x" is not a
159
+ # simple factor, and "--x" opens a comment.
160
+ node.operand.is_a?(Model::Expressions::UnaryExpression)
158
161
 
159
162
  [
160
163
  case node.operator
@@ -56,8 +56,11 @@ module Expressir
56
56
  # @raise [Error::SchemaParseFailure] if the schema file fails to parse
57
57
  def self.from_file(file, skip_references: nil, include_source: nil,
58
58
  root_path: nil, use_native: nil,
59
- use_core: nil) # rubocop:disable Metrics/AbcSize
60
- use_core = Core::NATIVE_AVAILABLE if use_core.nil?
59
+ use_core: nil, dialect: nil) # rubocop:disable Metrics/AbcSize
60
+ Dialect.normalize(dialect)
61
+ # use_native: false pins the Ruby grammar path; the core path
62
+ # must not silently take over that request.
63
+ use_core = Core::NATIVE_AVAILABLE if use_core.nil? && use_native != false
61
64
  if use_core && Core::NATIVE_AVAILABLE
62
65
  return from_file_core(file, skip_references: skip_references,
63
66
  include_source: include_source,
@@ -115,7 +118,8 @@ module Expressir
115
118
  # @return [Model::Repository] Repository containing all parsed ExpFiles
116
119
  def self.from_files(files, skip_references: nil, include_source: nil,
117
120
  root_path: nil, use_native: nil, max_processes: nil,
118
- compiled_set: nil, &progress)
121
+ compiled_set: nil, dialect: nil, &progress)
122
+ Dialect.normalize(dialect)
119
123
  set_path = compiled_set || ENV.fetch("EXPRESSIR_COMPILED_SET", nil)
120
124
  if set_path && core_set_available?
121
125
  repo = from_compiled_set(set_path, files,
@@ -457,7 +461,8 @@ include_source: nil, root_path: nil, use_native: nil, &block)
457
461
  # @return [Model::ExpFile] Parsed ExpFile
458
462
  # @raise [Error::SchemaParseFailure] if the content fails to parse
459
463
  def self.from_exp(content, skip_references: nil, include_source: nil,
460
- use_native: nil, use_streaming: false)
464
+ use_native: nil, use_streaming: false, dialect: nil)
465
+ Dialect.normalize(dialect)
461
466
  content = strip_bom(content)
462
467
  if use_streaming
463
468
  raise Error::StreamingUnsupportedError
@@ -112,20 +112,15 @@ module Expressir
112
112
  # @param node [Model::Declarations::Entity] Entity node
113
113
  # @return [String] Formatted entity
114
114
  def format_declarations_entity(node)
115
- if node.attributes.nil?
116
- explicit_attributes = []
117
- derived_attributes = []
118
- inverse_attributes = []
119
- else
120
- explicit_attributes = node.attributes.select do |x|
121
- x.kind == Model::Declarations::Attribute::EXPLICIT
122
- end
123
- derived_attributes = node.attributes.select do |x|
124
- x.kind == Model::Declarations::Attribute::DERIVED
125
- end
126
- inverse_attributes = node.attributes.select do |x|
127
- x.kind == Model::Declarations::Attribute::INVERSE
128
- end
115
+ attrs = entity_attribute_items(node)
116
+ explicit_attributes = attrs.select do |x|
117
+ x.kind == Model::Declarations::Attribute::EXPLICIT
118
+ end
119
+ derived_attributes = attrs.select do |x|
120
+ x.kind == Model::Declarations::Attribute::DERIVED
121
+ end
122
+ inverse_attributes = attrs.select do |x|
123
+ x.kind == Model::Declarations::Attribute::INVERSE
129
124
  end
130
125
 
131
126
  indent_str = " " * @config.indent
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Express
5
+ # Structural diff of two schemata with a machine-applicable patch
6
+ # (TODO.eengine/07; eeng --compare/--patch semantics, plugin/p11
7
+ # compare entry). Declarations match by canonical path
8
+ # `schema.item`; entity attributes get their own sub-deltas at
9
+ # `schema.item.attribute` carrying types, optionality and
10
+ # aggregates inside the signature.
11
+ #
12
+ # Interfaces are out of scope: the patch target is a flattened
13
+ # (longform) schema, which has none.
14
+ module SchemaCompare
15
+ # `node` carries the revised declaration for :added and :changed
16
+ # (nil for :removed) so {SchemaPatch.apply} can rebuild.
17
+ Delta = Struct.new(:op, :path, :node, keyword_init: true)
18
+
19
+ # Resolution artifacts and presentation metadata are not part of
20
+ # a declaration's structure.
21
+ SIGNATURE_IGNORE = %w[source include_source remarks remark_items
22
+ ref base_path file].freeze
23
+
24
+ COLLECTIONS = %i[constants types entities subtype_constraints
25
+ functions rules procedures].freeze
26
+
27
+ NODE_COLLECTION = {
28
+ "Expressir::Model::Declarations::Constant" => :constants,
29
+ "Expressir::Model::Declarations::Type" => :types,
30
+ "Expressir::Model::Declarations::Entity" => :entities,
31
+ "Expressir::Model::Declarations::SubtypeConstraint" => :subtype_constraints,
32
+ "Expressir::Model::Declarations::Function" => :functions,
33
+ "Expressir::Model::Declarations::Rule" => :rules,
34
+ "Expressir::Model::Declarations::Procedure" => :procedures,
35
+ }.freeze
36
+
37
+ module_function
38
+
39
+ def compare(base, revised)
40
+ COLLECTIONS.flat_map { |collection| collection_deltas(base, revised, collection) }
41
+ end
42
+
43
+ def collection_deltas(base, revised, collection)
44
+ base_items = index(base, collection)
45
+ revised_items = index(revised, collection)
46
+ deltas = (revised_items.keys - base_items.keys).map do |path|
47
+ Delta.new(op: :added, path: path, node: revised_items[path])
48
+ end
49
+ (base_items.keys - revised_items.keys).each do |path|
50
+ deltas << Delta.new(op: :removed, path: path, node: nil)
51
+ end
52
+ (base_items.keys & revised_items.keys).each do |path|
53
+ old_item = base_items[path]
54
+ new_item = revised_items[path]
55
+ next if signature(old_item) == signature(new_item)
56
+
57
+ deltas << Delta.new(op: :changed, path: path, node: new_item)
58
+ next unless new_item.is_a?(Model::Declarations::Entity)
59
+
60
+ deltas.concat(attribute_deltas(old_item, new_item, path))
61
+ end
62
+ deltas
63
+ end
64
+
65
+ def attribute_deltas(old_item, new_item, path)
66
+ old_attrs = index_by_id(Array(old_item.attributes))
67
+ new_attrs = index_by_id(Array(new_item.attributes))
68
+ deltas = (new_attrs.keys - old_attrs.keys).map do |name|
69
+ Delta.new(op: :added, path: "#{path}.#{name}",
70
+ node: new_attrs[name])
71
+ end
72
+ (old_attrs.keys - new_attrs.keys).each do |name|
73
+ deltas << Delta.new(op: :removed, path: "#{path}.#{name}", node: nil)
74
+ end
75
+ (old_attrs.keys & new_attrs.keys).each do |name|
76
+ next if signature(old_attrs[name]) == signature(new_attrs[name])
77
+
78
+ deltas << Delta.new(op: :changed, path: "#{path}.#{name}",
79
+ node: new_attrs[name])
80
+ end
81
+ deltas
82
+ end
83
+
84
+ def index_by_id(items)
85
+ items.filter_map { |item| [item.id&.downcase, item] if item.id }.to_h
86
+ end
87
+
88
+ def index(schema, collection)
89
+ index_by_id(Array(schema.send(collection)))
90
+ .transform_keys { |key| "#{schema.id.downcase}.#{key}" }
91
+ end
92
+
93
+ def signature(node)
94
+ strip(node.to_hash)
95
+ end
96
+
97
+ def strip(value)
98
+ case value
99
+ when Hash
100
+ value.each_with_object({}) do |(key, inner), out|
101
+ next if SIGNATURE_IGNORE.include?(key.to_s)
102
+
103
+ out[key] = strip(inner)
104
+ end
105
+ when Array then value.map { |inner| strip(inner) }
106
+ else value
107
+ end
108
+ end
109
+ end
110
+
111
+ # Applies {SchemaCompare} deltas to a schema model, returning the
112
+ # revised schema. The base schema is deep-copied through the
113
+ # lutaml-model round trip; the input schema is never mutated.
114
+ module SchemaPatch
115
+ module_function
116
+
117
+ def apply(base, deltas)
118
+ revised = base.class.from_hash(base.to_hash)
119
+ item_deltas, attribute_deltas = deltas.partition do |delta|
120
+ delta.path.split(".").size == 2
121
+ end
122
+ item_deltas.each { |delta| apply_item(revised, delta) }
123
+ # An item-level :added/:changed node already carries the final
124
+ # attribute list; its sub-deltas are informational only.
125
+ replaced_ops = %i[added changed].freeze
126
+ replaced = item_deltas
127
+ .select { |delta| replaced_ops.include?(delta.op) }
128
+ .map(&:path)
129
+ attribute_deltas
130
+ .reject { |delta| replaced.include?(delta.path.split(".")[0..1].join(".")) }
131
+ .each { |delta| apply_attribute(revised, delta) }
132
+ revised
133
+ end
134
+
135
+ def apply_item(schema, delta)
136
+ item_key = delta.path.split(".").last.downcase
137
+ collection = if delta.op == :added
138
+ schema.send(SchemaCompare::NODE_COLLECTION.fetch(delta.node.class.name))
139
+ else
140
+ live_collection(schema, item_key)
141
+ end
142
+ return unless collection
143
+
144
+ case delta.op
145
+ when :added then collection << delta.node
146
+ when :removed then collection.delete_if { |item| key(item) == item_key }
147
+ when :changed
148
+ index = collection.index { |item| key(item) == item_key }
149
+ return unless index
150
+
151
+ collection.delete_at(index)
152
+ collection.insert(index, delta.node)
153
+ end
154
+ end
155
+
156
+ def apply_attribute(schema, delta)
157
+ segments = delta.path.split(".")
158
+ item = SchemaCompare.index_by_id(all_declarations(schema))[segments[1].downcase]
159
+ return unless item
160
+
161
+ attr_key = segments[2]
162
+ attrs = Array(item.attributes)
163
+ case delta.op
164
+ when :added
165
+ item.attributes = [*attrs, delta.node]
166
+ when :removed
167
+ item.attributes = attrs.reject { |attr| key(attr) == attr_key }
168
+ when :changed
169
+ index = attrs.index { |attr| key(attr) == attr_key }
170
+ return unless index
171
+
172
+ item.attributes = attrs
173
+ item.attributes.delete_at(index)
174
+ item.attributes.insert(index, delta.node)
175
+ end
176
+ end
177
+
178
+ def live_collection(schema, item_key)
179
+ SchemaCompare::COLLECTIONS.map { |name| schema.send(name) }
180
+ .find do |collection|
181
+ Array(collection).any? { |item| key(item) == item_key }
182
+ end
183
+ end
184
+
185
+ def all_declarations(schema)
186
+ SchemaCompare::COLLECTIONS.flat_map { |name| Array(schema.send(name)) }
187
+ end
188
+
189
+ def key(item)
190
+ item.id&.downcase
191
+ end
192
+ end
193
+ end
194
+ end