expressir 2.4.12 → 2.4.13

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: 76901fb66e21e9752a5ba64f7b80c23e92a7fa54fac6df80ba221d46bee358e3
4
- data.tar.gz: d02e64ac60b0b670f31f665e3834553e29dfd8abc9f7ff51c22bc33dbafcbe11
3
+ metadata.gz: e3cf38ff1a09f95805cc5a6dc8c074832e47eeb8ceb7e62c79f503d521d775ac
4
+ data.tar.gz: a308605c1d7c97d33f766d08e8262e6e2039104ff7362e36a0fa5bd8e3839ccf
5
5
  SHA512:
6
- metadata.gz: c6c20c797b67efe96bf149207c2283b6e8ba3054e17feda55630fbccf92d337bdfc3322c0a77408262bf1066a60a30b2f137c5f2d4f5a62f78bcea69f818e567
7
- data.tar.gz: 39f8f5f287a3d01df2f84cc3667afc00632de391415f9b6aad21538623f167330de9ea3b8857ebe25490d946113d7a85637b5884625ec445b52d3d4a2663b214
6
+ metadata.gz: 4c97b4d258e50604fba0bdc0365c3f8272d6944dc225755e6408c6b585324b917448134352a4ffb4da445ca21ef910858283931abef43625d0b4138c90ecf694
7
+ data.tar.gz: d268fd6aef2e11bdafe5f81845931e91742dcd33ff48f3a2f6bb0076a6f9bad440045609a501b81fcc2130fef6dcd565d972ebc6634861d0341f9efa2649b5b5
@@ -5,58 +5,92 @@ module Expressir
5
5
  # Common parser/discovery helpers shared by parity CLI commands
6
6
  # (expand, flatten, check) — they all need: list of files in the
7
7
  # interface closure of a root file, and the root schema parsed.
8
+ #
9
+ # Schema resolution order (per the ELF schema manifest directive):
10
+ # 1. `--manifest PATH` — an ELF schema manifest explicitly defines
11
+ # where each schema lives; this is the primary mode.
12
+ # 2. `--stepmod DIR` — explicit opt-in to the STEPmod directory
13
+ # convention (`schemas/**/<name>.exp`) as the fallback.
14
+ # 3. Otherwise the file's own directory is used; a lookup failure
15
+ # warns and points at the two explicit modes.
8
16
  module ParityInputs
9
17
  module_function
10
18
 
11
- # Resolve the transitive interface closure of +root_path+. Deps are
12
- # looked up by schema name: first in the file's own directory, then
13
- # across the enclosing STEPmod checkout (the nearest ancestor holding
14
- # a `schemas/` directory, e.g. resources/ and modules/ trees).
19
+ # Resolve the transitive interface closure of +root_path+. The queue
20
+ # holds paths and `seen` guards by schema name: mutual USE FROM
21
+ # cycles must terminate (this loop once span rounds forever on them,
22
+ # re-reading the same files).
15
23
  def closure_paths(root_path, manifest: nil, stepmod: nil)
16
- index = schema_index(root_path)
24
+ index = schema_index(root_path, manifest: manifest, stepmod: stepmod)
17
25
  seen = { File.basename(root_path, ".exp").downcase => root_path }
18
- queue = [File.basename(root_path, ".exp").downcase]
26
+ missing = []
27
+ queue = [root_path]
19
28
  until queue.empty?
20
- name = queue.shift
21
- path = seen[name] || index[name]
22
- next unless path
23
- next if seen.key?(name) && seen[name] != root_path
29
+ closure_names(File.read(queue.shift)).each do |dep|
30
+ name = dep.downcase
31
+ next if seen.key?(name)
24
32
 
25
- seen[name] = path
26
- closure_names(File.read(path)).each { |d| queue << d.downcase }
33
+ path = index&.[](name)
34
+ unless path
35
+ missing << name
36
+ seen[name] = nil
37
+ next
38
+ end
39
+
40
+ seen[name] = path
41
+ queue << path
42
+ end
43
+ end
44
+ unless missing.empty?
45
+ warn "expressir: schema(s) not found: #{missing.uniq.join(', ')}" \
46
+ "#{resolver_hint(manifest, stepmod)}"
27
47
  end
28
- seen.values.uniq
48
+ seen.values.compact.uniq
29
49
  end
30
50
 
31
- # Schema-name → path map for the STEPmod checkout around +root_path+,
32
- # built once per invocation. The index root is the nearest ancestor
33
- # directory NAMED `schemas` (wg12-step layout:
34
- # `schemas/modules/<module>/mim.exp`), and entries key on the schema
35
- # name declared inside each file — module files are `arm.exp` /
36
- # `mim.exp`, so basenames cannot be the key. Falls back to the file's
37
- # own directory when there is no schemas/ ancestor.
38
- def schema_index(root_path)
39
- dir = File.dirname(File.expand_path(root_path))
40
- until dir == "/" || File.basename(dir) == "schemas"
41
- parent = File.dirname(dir)
42
- break if parent == dir
51
+ def resolver_hint(manifest, stepmod)
52
+ if manifest
53
+ " (not in schema manifest #{manifest})"
54
+ elsif stepmod
55
+ " (not found under STEPmod root #{stepmod})"
56
+ else
57
+ " - provide --manifest PATH (ELF schema manifest) or " \
58
+ "--stepmod DIR to resolve them"
59
+ end
60
+ end
43
61
 
44
- dir = parent
62
+ # Schema-name → path index from the ELF schema manifest.
63
+ def manifest_index(manifest_path)
64
+ manifest = Expressir::SchemaManifest.from_file(manifest_path)
65
+ manifest.schemas.to_h do |entry|
66
+ [entry.id.downcase, entry.path]
45
67
  end
46
- return same_dir_index(root_path) unless File.basename(dir) == "schemas"
68
+ rescue StandardError => e
69
+ warn "expressir: could not load schema manifest #{manifest_path}: #{e.message}"
70
+ nil
71
+ end
47
72
 
48
- @@indexes ||= {}
49
- @@indexes[dir] ||= begin
50
- h = {}
51
- Find.find(dir) do |path|
52
- next unless path.end_with?(".exp")
73
+ # Schema-name → path index over a STEPmod checkout root (the
74
+ # directory holding `schemas/`, or that directory itself).
75
+ def stepmod_index(stepmod_dir)
76
+ root = File.join(File.expand_path(stepmod_dir), "schemas")
77
+ root = File.expand_path(stepmod_dir) unless File.directory?(root)
78
+ h = {}
79
+ Find.find(root) do |path|
80
+ next unless path.end_with?(".exp")
53
81
 
54
- declared = File.read(path)[/\bSCHEMA\s+(\w+)/i, 1]
55
- h[declared.downcase] = path if declared
56
- h[File.basename(path, ".exp").downcase] ||= path
57
- end
58
- h
82
+ declared = File.read(path)[/\bSCHEMA\s+(\w+)/i, 1]
83
+ h[declared.downcase] = path if declared
84
+ h[File.basename(path, ".exp").downcase] ||= path
59
85
  end
86
+ h
87
+ end
88
+
89
+ def schema_index(root_path, manifest: nil, stepmod: nil)
90
+ return manifest_index(manifest) if manifest
91
+ return stepmod_index(stepmod) if stepmod
92
+
93
+ same_dir_index(root_path)
60
94
  end
61
95
 
62
96
  def same_dir_index(root_path)
@@ -66,19 +100,26 @@ module Expressir
66
100
  end
67
101
  end
68
102
 
103
+ # Interface names declared by +source+. Remark text is stripped
104
+ # first: prose like "use from the main schema" must not spawn
105
+ # phantom dependencies.
69
106
  def closure_names(source)
70
- source.scan(/\bUSE\s+FROM\s+(\w+)/i).flatten.uniq +
71
- source.scan(/\bREFERENCE\s+FROM\s+(\w+)/i).flatten.uniq
107
+ code = source.gsub(/--[^\n]*/, "").gsub(/\(\*.*?\*\)/m, "")
108
+ code.scan(/\bUSE\s+FROM\s+(\w+)/i).flatten.uniq +
109
+ code.scan(/\bREFERENCE\s+FROM\s+(\w+)/i).flatten.uniq
72
110
  end
73
111
 
74
- # manifest:/stepmod: are accepted for the ELF-manifest resolver
75
- # contract; the directory-index fallback ignores them until that
76
- # resolver lands.
77
112
  def root_schema(root_path, manifest: nil, stepmod: nil)
78
113
  name = File.read(root_path)[/\bSCHEMA\s+(\w+)/, 1]
79
- cp = closure_paths(root_path)
80
- repo = Expressir::Express::Parser.from_files(cp, max_processes: 1)
81
- [repo.schemas.find { |s| s.id == name }, repo]
114
+ # skip_references: longform generation and checking run their own
115
+ # resolution; resolving every reference of a large closure here made
116
+ # flatten crawl (minutes vs seconds).
117
+ repo = Expressir::Express::Parser.from_files(
118
+ closure_paths(root_path, manifest: manifest, stepmod: stepmod),
119
+ skip_references: true,
120
+ max_processes: 1,
121
+ )
122
+ [repo.schemas.find { |s| s.id&.downcase == name&.downcase }, repo]
82
123
  end
83
124
  end
84
125
  end
@@ -307,20 +307,32 @@ module Expressir
307
307
 
308
308
  Array(type.underlying_type.items).each do |item|
309
309
  next if item.id.nil?
310
- next if find_type(schema, item.id)
311
- next if find_entity(schema, item.id).nil? # unresolved handled elsewhere
310
+ next if select_item_resolves?(schema, item.id)
312
311
 
313
312
  note!(:check_select_named_type, :error, schema,
314
- "TYPE #{type.id}: select item '#{item.id}' is an entity, " \
315
- "not a named type", type)
313
+ "In the SELECT TYPE #{type.id}, '#{item.id}' does not " \
314
+ "name an existing TYPE or ENTITY", type)
316
315
  end
317
316
  end
318
317
 
319
318
  def find_type(schema, id)
320
319
  key = id.safe_downcase
321
- return schema.types.find { |t| t.id.safe_downcase == key } if schema
320
+ schema.types.find { |t| t.id.safe_downcase == key }
321
+ end
322
+
323
+ # ISO 10303-11 rule 258: select_list items are named_types —
324
+ # entity_ref | type_ref. The item is fine when it names either,
325
+ # in this schema or any repository schema (interface-visible).
326
+ def select_item_resolves?(schema, id)
327
+ return true if find_entity(schema, id)
328
+ return true if schema.types.to_a.any? { |t| t.id.safe_downcase == id.safe_downcase }
322
329
 
323
- nil
330
+ @by_name.each_value.any? do |group|
331
+ group.any? do |other|
332
+ other.types.to_a.any? { |t| t.id.safe_downcase == id.safe_downcase } ||
333
+ other.entities.to_a.any? { |e| e.id.safe_downcase == id.safe_downcase }
334
+ end
335
+ end
324
336
  end
325
337
 
326
338
  # eeng declared-kind probe: :type when a TYPE of that name is
@@ -351,15 +351,22 @@ module Expressir
351
351
  end
352
352
  end
353
353
 
354
- # Every identifier a FunctionCall site names, across the
355
- # artifact's surviving declarations.
354
+ # Every identifier a call site names, across the artifact's
355
+ # surviving declarations: FunctionCall for functions and
356
+ # ProcedureCall statements for procedures. (A zero-argument
357
+ # function reference written bare is not represented as a
358
+ # FunctionCall by the parser and is counted via any SimpleReference
359
+ # miss — accepted v1 blind spot, see tranche notes.)
356
360
  def called_ids(schema)
357
361
  called = {}
358
362
  each_node(schema) do |node|
359
- next unless node.is_a?(Model::Expressions::FunctionCall)
360
-
361
- ref = node.function
362
- id = ref.is_a?(String) ? ref : ref&.id
363
+ id = nil
364
+ case node
365
+ when Expressir::Model::Expressions::FunctionCall
366
+ id = node.function.is_a?(String) ? node.function : node.function&.id
367
+ when Expressir::Model::Statements::ProcedureCall
368
+ id = node.procedure.is_a?(String) ? node.procedure : node.procedure&.id
369
+ end
363
370
  called[id.safe_downcase] = true if id
364
371
  end
365
372
  called
@@ -392,19 +399,28 @@ module Expressir
392
399
  def reduce_supertype_expression(expr, visible_ids)
393
400
  case expr
394
401
  when Model::SupertypeExpressions::OneofSupertypeExpression
395
- refs = Array(expr.operands).select do |ref|
396
- id = ref.is_a?(String) ? ref : ref.id
397
- id.nil? || visible_ids.key?(id.safe_downcase)
402
+ reduced = expr.operands.filter_map do |operand|
403
+ reduce_supertype_expression(operand, visible_ids)
398
404
  end
399
- return nil if refs.empty?
405
+ return nil if reduced.empty?
400
406
 
401
- refs.one? ? refs.first : reset_oneof(expr, refs)
407
+ reduced.one? ? reduced.first : reset_oneof(expr, reduced)
402
408
  when Model::SupertypeExpressions::BinarySupertypeExpression
403
- left = reduce_supertype_expression(expr.operands[0], visible_ids)
404
- right = reduce_supertype_expression(expr.operands[1], visible_ids)
405
- return nil if left.nil? && right.nil?
409
+ left = reduce_supertype_expression(expr.operand1, visible_ids)
410
+ right = reduce_supertype_expression(expr.operand2, visible_ids)
411
+ if left.nil?
412
+ return right
413
+ end
414
+ if right.nil?
415
+ return left
416
+ end
417
+
418
+ rebuild_binary(expr, left, right)
419
+ when Model::References::SimpleReference
420
+ id = expr.id
421
+ return nil if id && !visible_ids.key?(id.safe_downcase)
406
422
 
407
- left || right
423
+ expr
408
424
  else
409
425
  expr
410
426
  end
@@ -416,6 +432,13 @@ module Expressir
416
432
  copy
417
433
  end
418
434
 
435
+ def rebuild_binary(expr, left, right)
436
+ copy = deep_copy(expr)
437
+ copy.operand1 = left
438
+ copy.operand2 = right
439
+ copy
440
+ end
441
+
419
442
  def resolve_extensible_enumerations!(schema)
420
443
  schema.types.select do |t|
421
444
  t.underlying_type.is_a?(Model::DataTypes::Enumeration) && t.underlying_type.extensible
@@ -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.12".freeze
6
+ VERSION = "2.4.13".freeze
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: expressir
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.12
4
+ version: 2.4.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.