expressir 2.4.33 → 2.4.34

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 791c09b1ddc6f0ed583b4608c59ec327b7126c7c5b9d12c3a02acbaf236ae4c6
4
- data.tar.gz: edb97cb53754e74511c9c28926ce4d9235afcd42d3d56fb4cd127017bff58590
3
+ metadata.gz: 11ae98e1866bac67ff62c5a8f27abd2f06d94c62f8f7468c43d67554e1d4cd48
4
+ data.tar.gz: a4ee98f12efb95ca5d3302aa8835120b2bad67ca37181430c9c4fcc1bbc8c561
5
5
  SHA512:
6
- metadata.gz: ed1e988f03f3e4f5af1f77872495088c87c49a5247b055850b498b235f5fd1693055fb734441ad9d357cd71548a2692b5a17dceacea10fe9d77d21bb580766a4
7
- data.tar.gz: f63d8a48e336d00996b90efcc7797877c48a74a049aafcf52ae4e7f417dddbbf189a3d7452fd1daba527d38b17ae8d7a25b054338b64e41efb4ebd484aef669d
6
+ metadata.gz: 0ec3c593547ace542482f7b5371d446dc7810e5e52f250281f0b5a6bca742d05c31abf15a8dc25456845314c98bdc673351a458a88384ea5e2d06b499904a411
7
+ data.tar.gz: b2f7b4a8c851a61024baa8601c826c649bcff17393d91934a4faf5d75ee2ecf0936697bd5178a92dd36d3c850a809ac9c6ed39cf70fda3fb4aba3646698b65b8
@@ -130,16 +130,20 @@ module Expressir
130
130
  schema.interfaces.each do |iface|
131
131
  next unless iface.kind == Model::Declarations::Interface::REFERENCE
132
132
 
133
+ iface_schema = iface.schema.is_a?(String) ? iface.schema : iface.schema&.id
134
+ target = closure[iface_schema&.safe_downcase]
133
135
  iface.items.each do |item|
134
136
  original = original_name(item, schema)
135
- next unless closure[original]&.entities&.find { |e| e.id == original }
137
+ next unless target&.entities&.find do |e|
138
+ e.id.safe_downcase == original.safe_downcase
139
+ end
136
140
 
137
141
  @reference_entities << original.safe_downcase
138
142
  end
139
143
  end
140
144
  end
141
145
 
142
- declarations << build_dependent_rule if @reference_entities.any?
146
+ declarations.concat(build_dependent_declarations) if @reference_entities.any?
143
147
 
144
148
  Model::Declarations::Schema.new(
145
149
  id: @longform_name,
@@ -166,7 +170,18 @@ module Expressir
166
170
  # OWN schema's id, and the global map redirects the bare name so
167
171
  # declarations from third schemas resolve to the first occurrence.
168
172
  def plan_ids(schemas)
169
- seen_first = {} # downcase id => owning schema (first occurrence)
173
+ # G.NM.1: EVERY clashing declaration is prefixed (the annex
174
+ # example munges farming_dot_dog AND pet_shows_dot_dog), so the
175
+ # clash set is computed over the whole population first.
176
+ counts = Hash.new(0)
177
+ schemas.each do |schema|
178
+ all_decls(schema).each do |decl|
179
+ next unless decl.respond_to?(:id) && decl.id
180
+
181
+ counts[decl.id.safe_downcase] += 1
182
+ end
183
+ end
184
+
170
185
  global = {} # downcase id => munged final
171
186
  own_by_schema = {} # schema => {downcase id => final id}
172
187
  schemas.each do |schema|
@@ -175,14 +190,11 @@ module Expressir
175
190
  next unless decl.respond_to?(:id) && decl.id
176
191
 
177
192
  key = decl.id.safe_downcase
178
- if seen_first.key?(key)
179
- final = "#{schema.id.safe_downcase}_dot_#{decl.id}"
180
- own[key] = final
181
- global[key] = final
182
- else
183
- seen_first[key] = schema
184
- own[key] = decl.id
185
- end
193
+ own[key] = if counts[key] > 1
194
+ global[key] = "#{schema.id.safe_downcase}_dot_#{decl.id}"
195
+ else
196
+ decl.id
197
+ end
186
198
  end
187
199
  own_by_schema[schema] = own
188
200
  end
@@ -253,11 +265,20 @@ module Expressir
253
265
  current == current.upcase ? new.upcase : new
254
266
  end
255
267
 
256
- # G.1.6: parse-templates keep the annex rule/function verbatim.
257
- def build_dependent_rule
268
+ # G.1.6: parse-templates keep the annex rule/function verbatim;
269
+ # the pair is emitted together (the rule's WHERE calls the
270
+ # function, and the prune pass would drop an uncalled function).
271
+ def build_dependent_declarations
272
+ snippet = parse_schema_snippet(dependent_template_src)
273
+ rule = snippet.schemas.first.rules.first
274
+ fn = snippet.schemas.first.functions.first
275
+ [rule, fn].compact
276
+ end
277
+
278
+ def dependent_template_src
258
279
  list = @reference_entities.map(&:upcase).join(", ")
259
280
  union = @reference_entities.join("+")
260
- src = <<~EXP
281
+ <<~EXP
261
282
  SCHEMA __dependent__;
262
283
  RULE #{DEPENDENT_RULE_NAME} FOR (#{list});
263
284
  LOCAL
@@ -270,9 +291,62 @@ module Expressir
270
291
  WHERE
271
292
  wr1: dependently_instantiated(all_instances, set_of_input_types, previous_in_chain);
272
293
  END_RULE;
294
+
295
+ FUNCTION dependently_instantiated(
296
+ set_of_input_instances : SET OF GENERIC:igen;
297
+ set_of_input_types : SET OF STRING;
298
+ previous_in_chain : LIST OF GENERIC:cgen): BOOLEAN;
299
+ LOCAL
300
+ number_of_input_instances : INTEGER;
301
+ number_of_referring_instances : INTEGER;
302
+ bag_of_referring_instances : BAG OF GENERIC:igen := [];
303
+ dependently_instantiated_flag : BOOLEAN;
304
+ previous_in_chain_plus : LIST OF GENERIC:cgen := [];
305
+ result : BOOLEAN := true;
306
+ set_of_types : SET OF STRING := [];
307
+ END_LOCAL;
308
+ IF EXISTS(set_of_input_instances) THEN
309
+ number_of_input_instances := SIZEOF(set_of_input_instances);
310
+ set_of_input_types := set_of_input_types + 'GENERIC';
311
+ REPEAT i := 1 TO number_of_input_instances;
312
+ bag_of_referring_instances := USEDIN(set_of_input_instances[i], '');
313
+ IF EXISTS(bag_of_referring_instances) THEN
314
+ number_of_referring_instances := SIZEOF(bag_of_referring_instances);
315
+ dependently_instantiated_flag := false;
316
+ REPEAT j := 1 TO number_of_referring_instances;
317
+ set_of_types := TYPEOF(bag_of_referring_instances[j]);
318
+ IF set_of_types <= set_of_input_types THEN
319
+ IF NOT (bag_of_referring_instances[j] IN previous_in_chain) THEN
320
+ previous_in_chain_plus := previous_in_chain +
321
+ set_of_input_instances[i];
322
+ IF dependently_instantiated([bag_of_referring_instances[j]],
323
+ set_of_input_types,
324
+ previous_in_chain_plus) THEN
325
+ dependently_instantiated_flag := true;
326
+ ESCAPE;
327
+ ELSE
328
+ SKIP;
329
+ END_IF;
330
+ END_IF;
331
+ ELSE
332
+ dependently_instantiated_flag := true;
333
+ ESCAPE;
334
+ END_IF;
335
+ END_REPEAT;
336
+ IF NOT dependently_instantiated_flag THEN
337
+ RETURN(false);
338
+ END_IF;
339
+ ELSE
340
+ RETURN(false);
341
+ END_IF;
342
+ END_REPEAT;
343
+ ELSE
344
+ RETURN(false);
345
+ END_IF;
346
+ RETURN(true);
347
+ END_FUNCTION;
273
348
  END_SCHEMA;
274
349
  EXP
275
- parse_schema_snippet(src).schemas.first.rules.first
276
350
  end
277
351
 
278
352
  def parse_schema_snippet(src)
@@ -499,57 +573,95 @@ module Expressir
499
573
  end
500
574
  end
501
575
 
576
+ # G.2.2/G.2.4: every extensible/extending select becomes a
577
+ # non-extensible select whose items are the full domain in
578
+ # longform scope. Chains fold in dependency order (a node whose
579
+ # own BASED_ON is still pending is deferred), so an extending
580
+ # type that is itself extensible resolves against its base's
581
+ # folded domain.
582
+ # G.2.2/G.2.4: every extensible/extending select becomes a
583
+ # non-extensible select whose items are the full domain in
584
+ # longform scope. Chains fold in dependency order (a node whose
585
+ # own BASED_ON is still pending is deferred), so an extending
586
+ # type that is itself extensible resolves against its base's
587
+ # folded domain.
502
588
  def resolve_extensible_selects!(schema)
503
- schema.types.select do |t|
504
- t.underlying_type.is_a?(Model::DataTypes::Select) && t.underlying_type.extensible
505
- end.each do |base|
506
- sel = base.underlying_type
507
-
508
- # all-extenders for GENERIC_ENTITY selects (WG12 requirement,
509
- # #32): the extenders are the entity types themselves — fold in
510
- # every entity the artifact carries, so the longform select is
511
- # complete against the closure. Flags stay as declared: the
512
- # extensible/generic-entity markers are part of the shortform's
513
- # semantics and are not expressir's to drop.
514
- if sel.generic_entity
515
- sel.items = schema.entities.map do |e|
516
- Model::References::SimpleReference.new(id: e.id)
517
- end
518
- next
519
- end
589
+ select_types = schema.types.select do |ty|
590
+ ty.underlying_type.is_a?(Model::DataTypes::Select)
591
+ end
592
+ select_types.select { |ty| sel_pending?(ty) }.each do |base|
593
+ fold_select_node(schema, select_types, base)
594
+ end
595
+ end
520
596
 
521
- extensions = schema.types.select do |t|
522
- t.underlying_type.is_a?(Model::DataTypes::Select) &&
523
- !t.underlying_type.extensible &&
524
- t.underlying_type.based_on&.id&.safe_downcase == base.id.safe_downcase
597
+ def sel_pending?(select_type)
598
+ sel = select_type.underlying_type
599
+ sel.extensible || sel.based_on
600
+ end
601
+
602
+ def fold_select_node(schema, select_types, base_type)
603
+ sel = base_type.underlying_type
604
+ return unless sel_pending?(base_type)
605
+
606
+ # all-extenders for GENERIC_ENTITY selects (WG12 requirement,
607
+ # #32): the extenders are the entity types themselves — fold in
608
+ # every entity the artifact carries, so the longform select is
609
+ # complete against the closure. Flags stay as declared: the
610
+ # extensible/generic-entity markers are part of the shortform's
611
+ # semantics and are not expressir's to drop.
612
+ if sel.generic_entity && !sel.based_on
613
+ sel.items = schema.entities.map do |e|
614
+ Model::References::SimpleReference.new(id: e.id)
525
615
  end
526
- merged_items = sel.items +
527
- extensions.flat_map { |x| x.underlying_type.items }
528
- seen_ids = {}
529
- uniq = merged_items.select do |item|
530
- key = item.id.safe_downcase
531
- next false if seen_ids.key?(key) || key.empty?
532
-
533
- seen_ids[key] = true
616
+ return
617
+ end
618
+
619
+ own_items = Array(sel.items)
620
+ inherited = []
621
+ if sel.based_on
622
+ base_ty = select_types.find do |ty|
623
+ ty.id&.safe_downcase == sel.based_on&.id&.safe_downcase
534
624
  end
535
- sel.items = uniq
536
- sel.extensible = false
537
- sel.based_on = nil
625
+ fold_select_node(schema, select_types, base_ty) if base_ty && sel_pending?(base_ty)
626
+ inherited = base_ty ? Array(base_ty.underlying_type.items) : []
627
+ end
538
628
 
539
- extensions.each do |ext|
540
- keep = ext.underlying_type.items.map { |i| i.id.upcase }
541
- exclusions = uniq.reject { |i| keep.include?(i.id.upcase) }
542
- base_ref = "#{@longform_name.upcase}.#{base.id.upcase}"
543
- ext.underlying_type = Model::DataTypes::Select.new(
544
- items: sel.items.map { |i| Model::References::SimpleReference.new(id: i.id) },
629
+ extenders = select_types.select do |ty|
630
+ !ty.equal?(base_type) &&
631
+ ty.underlying_type.based_on&.id&.safe_downcase == base_type.id&.safe_downcase
632
+ end
633
+
634
+ merged = inherited + own_items +
635
+ extenders.flat_map { |ty| Array(ty.underlying_type.items) }
636
+ seen = {}
637
+ uniq = merged.select do |item|
638
+ key = item.id.safe_downcase
639
+ next false if key.empty? || seen.key?(key)
640
+
641
+ seen[key] = true
642
+ end
643
+ sel.items = uniq
644
+ sel.extensible = false
645
+ sel.based_on = nil
646
+
647
+ extenders.each do |ty|
648
+ ext_sel = ty.underlying_type
649
+ keep = Array(ext_sel.items).map { |i| i.id.upcase }
650
+ base_ref = "#{@longform_name.upcase}.#{base_type.id.upcase}"
651
+ ty.underlying_type = Model::DataTypes::Select.new(
652
+ items: uniq.map { |i| Model::References::SimpleReference.new(id: i.id) },
653
+ )
654
+ ext_sel_items = ty.underlying_type.items
655
+ exclusions = uniq.reject { |i| keep.include?(i.id.upcase) }
656
+ ty.where_rules = exclusions.each_with_index.map do |item, i|
657
+ Model::Declarations::WhereRule.new(
658
+ id: "wr#{i + 1}",
659
+ expression: parse_expression(
660
+ "NOT ('#{base_ref}.#{item.id.upcase}' IN TYPEOF(SELF));",
661
+ ),
545
662
  )
546
- ext.where_rules = exclusions.each_with_index.map do |item, i|
547
- Model::Declarations::WhereRule.new(
548
- id: "wr#{i + 1}",
549
- expression: parse_expression("NOT ('#{base_ref}.#{item.id.upcase}' IN TYPEOF(SELF));"),
550
- )
551
- end
552
663
  end
664
+ ext_sel_items
553
665
  end
554
666
  end
555
667
 
@@ -565,10 +677,7 @@ module Expressir
565
677
  entity.supertype_expression =
566
678
  entity.supertype_expression ? combine_andor(entity.supertype_expression, expr) : expr
567
679
  end
568
- if entity.abstract
569
- # G.2.6: abstract entity ⇒ ABSTRACT SUPERTYPE (formatter
570
- # renders from abstract + supertype status).
571
- end
680
+ entity.abstract = true if sc.abstract
572
681
  next if Array(sc.total_over).empty?
573
682
 
574
683
  rule_name = "total_over_#{sc.id}"
@@ -3,6 +3,6 @@ module Expressir
3
3
  # autoload it (Ruby autoload only works for module/class constants, not
4
4
  # for string constants like `Expressir::VERSION`).
5
5
  module Version
6
- VERSION = "2.4.33".freeze
6
+ VERSION = "2.4.34".freeze
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: expressir
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.33
4
+ version: 2.4.34
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-15 00:00:00.000000000 Z
11
+ date: 2026-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rb_sys