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 +4 -4
- data/lib/expressir/commands/parity_inputs.rb +86 -45
- data/lib/expressir/express/checker.rb +18 -6
- data/lib/expressir/express/shtolo.rb +38 -15
- data/lib/expressir/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e3cf38ff1a09f95805cc5a6dc8c074832e47eeb8ceb7e62c79f503d521d775ac
|
|
4
|
+
data.tar.gz: a308605c1d7c97d33f766d08e8262e6e2039104ff7362e36a0fa5bd8e3839ccf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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+.
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
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
|
-
|
|
26
|
+
missing = []
|
|
27
|
+
queue = [root_path]
|
|
19
28
|
until queue.empty?
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
68
|
+
rescue StandardError => e
|
|
69
|
+
warn "expressir: could not load schema manifest #{manifest_path}: #{e.message}"
|
|
70
|
+
nil
|
|
71
|
+
end
|
|
47
72
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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.
|
|
71
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
|
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}
|
|
315
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
355
|
-
#
|
|
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
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
|
|
396
|
-
|
|
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
|
|
405
|
+
return nil if reduced.empty?
|
|
400
406
|
|
|
401
|
-
|
|
407
|
+
reduced.one? ? reduced.first : reset_oneof(expr, reduced)
|
|
402
408
|
when Model::SupertypeExpressions::BinarySupertypeExpression
|
|
403
|
-
left = reduce_supertype_expression(expr.
|
|
404
|
-
right = reduce_supertype_expression(expr.
|
|
405
|
-
|
|
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
|
-
|
|
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
|
data/lib/expressir/version.rb
CHANGED