expressir 2.4.18 → 2.4.20

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: 71c43838ba6c2853f84bfa1faca26760abe4919d35b98895ef224866bd49da62
4
- data.tar.gz: 715a55be95f4ead656b98e0df59ccf06c2036eb532d6990a8ea036ee5f0f78c4
3
+ metadata.gz: e6cda48af3f6a3d844c896622b39a79c31631c9b164f5f727d2841d85decb1ec
4
+ data.tar.gz: 375c96471483ed06f43272785520d5f6c763687a03f104dd1d5049636c010948
5
5
  SHA512:
6
- metadata.gz: dac93ad5688fc8e2d3814fd6933b352ea35d78b89f157e99f30114ddafb07b53aaeb80aaa45fd63540a9ee400ac1aacc3724800446f868472499d9d4cf501629
7
- data.tar.gz: 12cd7ef87afa0510215d0c1315e68ba5bbaed5a910255ebfa40e18b328b25f87ef3a24f93ce1073a798ce0e748e6344861f05900f863377ed801d9f1f66e7bab
6
+ metadata.gz: f64857ddcef1a8bd7896ee1c5c8ed6050ba263e2ab8aa2df2bc8a02e98f3e55c8cf2e6b186760dbda9a37e513882292fe3569eb5ded9eee471978e3ef4580a9e
7
+ data.tar.gz: 8e6f94bc7c441f0fd1394c7cff8d512e20247c5f07291b2d969bdfcdef1af5cadf6230b4b2512cc273ca08d9de3ef906e4762fad6cbbcaa273f3667429355dea
@@ -15,6 +15,10 @@ module Expressir
15
15
  # link := "->" | "<-" | "<=" | "=>" | "=" NAME
16
16
  # (relation to the previous node)
17
17
  # node := NAME ["." ATTRIBUTE]
18
+ # | LINK (<<express:SCHEMA.ITEM,ITEM>>,
19
+ # #460: a link may stand
20
+ # wherever a declaration
21
+ # is referenced)
18
22
  # index := "[" ("i" | DIGITS) "]"
19
23
  # MARKER := "{" | "}" | "[" | "]" | "(" | ")" | ":" | "|" | "!"
20
24
  # | "," | "/" | "*" (grouping/annotation; no
@@ -42,6 +46,8 @@ module Expressir
42
46
  attribute :attribute, :string
43
47
  attribute :index, :string
44
48
  attribute :literal, :string
49
+ # annotated EXPRESS link form: <<express:SCHEMA.ITEM,ITEM>>
50
+ attribute :link, :string
45
51
 
46
52
  key_value do
47
53
  map "operator", to: :operator
@@ -49,6 +55,7 @@ module Expressir
49
55
  map "attribute", to: :attribute
50
56
  map "index", to: :index
51
57
  map "literal", to: :literal
58
+ map "link", to: :link
52
59
  end
53
60
  end
54
61
 
@@ -62,9 +69,12 @@ module Expressir
62
69
  end
63
70
  end
64
71
 
65
- TOKEN = /->|<-|<=|=>|[{}\[\]()=:|!,*\/]|'(?:''|[^'])*'|\w+(?:\.\w+)?/
72
+ EXPRESS_LINK_PATH = /\A<<express:([^,>]+)(?:,[^>]+)?>>\z/
73
+ private_constant :EXPRESS_LINK_PATH
74
+
75
+ TOKEN = /<<express:[^,>]+(?:,[^>]+)?>>|->|<-|<=|=>|[{}\[\]()=:|!,*\/]|'(?:''|[^'])*'|\w+(?:\.\w+)?/
66
76
  MARKERS = ["{", "}", "[", "]", "(", ")", ":", "|", "!", ",", "/",
67
- "*"].freeze
77
+ "*", "MAPPING_OF"].freeze
68
78
  LINKS = ["->", "<-", "<=", "=>"].freeze
69
79
  DECLARATION_COLLECTIONS = %i[types entities].freeze
70
80
  private_constant :TOKEN, :MARKERS, :LINKS, :DECLARATION_COLLECTIONS
@@ -109,19 +119,17 @@ module Expressir
109
119
  end
110
120
  when *MARKERS
111
121
  steps << Step.new(operator: token)
122
+ when /\A<<express:/
123
+ steps << Step.new(operator: pending_link, link: token)
124
+ pending_link = nil
125
+ pending_type_assign = false
112
126
  else
113
127
  name, _, attribute = token.partition(".")
114
- if name.casecmp("MAPPING_OF").zero? && tokens[i + 1] == "("
115
- # notation wrapper: /MAPPING_OF(X)/ refers to X; the
116
- # wrapper itself carries no node semantics
117
- steps << Step.new(operator: token)
118
- else
119
- operator = pending_link || ("=" if pending_type_assign)
120
- steps << Step.new(operator: operator, name: name,
121
- attribute: attribute.empty? ? nil : attribute)
122
- pending_link = nil
123
- pending_type_assign = false
124
- end
128
+ operator = pending_link || ("=" if pending_type_assign)
129
+ steps << Step.new(operator: operator, name: name,
130
+ attribute: attribute.empty? ? nil : attribute)
131
+ pending_link = nil
132
+ pending_type_assign = false
125
133
  end
126
134
  i += 1
127
135
  end
@@ -153,34 +161,66 @@ module Expressir
153
161
  by_name = name_index(repository)
154
162
  issues = []
155
163
  prev = nil
164
+ last_qualified = nil
156
165
  path.steps.each_with_index do |step, pos|
157
- if MARKERS.include?(step.operator)
158
- prev = nil
159
- next
160
- end
166
+ # markers never break the running link, mirroring parse
167
+ next if MARKERS.include?(step.operator)
161
168
 
162
- issues.concat(check_step(step, pos, prev, by_name))
163
- prev = step if step.name
169
+ if step.link
170
+ issues.concat(check_link_step(step, pos, repository))
171
+ else
172
+ issues.concat(check_step(step, pos, prev, last_qualified, by_name))
173
+ end
174
+ prev = step if step.name || step.link
175
+ last_qualified = step if step.name && step.attribute
164
176
  end
165
177
  issues
166
178
  end
167
179
 
180
+ EXPRESS_BUILTINS = %w[NUMBER INTEGER REAL STRING BOOLEAN LOGICAL
181
+ BINARY].freeze
182
+ private_constant :EXPRESS_BUILTINS
183
+
168
184
  def name_index(repository)
185
+ index = EXPRESS_BUILTINS.to_h { |b| [b.downcase, [nil, nil]] }
169
186
  repository.schemas.flat_map do |schema|
170
187
  DECLARATION_COLLECTIONS.flat_map do |coll|
171
188
  Array(schema.public_send(coll))
172
189
  .select { |d| d.respond_to?(:id) && d.id }
173
190
  .map { |d| [d.id.safe_downcase, [schema, d]] }
174
191
  end
175
- end.to_h
192
+ end.each { |pair| index[pair[0]] = pair[1] }
193
+ index
194
+ end
195
+
196
+ # Annotated EXPRESS link node: <<express:SCHEMA.ITEM,ITEM>>
197
+ # (item part optional) resolves against the repository (#460).
198
+ def check_link_step(step, pos, repository)
199
+ parts = step.link.match(EXPRESS_LINK_PATH)
200
+ return [Issue.new(step: pos, message: "malformed link '#{step.link}'")] unless parts
201
+
202
+ schema_id, item_id = parts[1].split(".", 2).compact
203
+ schema = repository.schemas
204
+ .find { |s| s.id&.safe_downcase == schema_id&.safe_downcase }
205
+ unless schema
206
+ return [Issue.new(step: pos,
207
+ message: "unknown schema '#{schema_id}' in #{step.link}")]
208
+ end
209
+
210
+ return [] if item_id.nil?
211
+ return [] if Expressir::Mapping.item_declared?(schema, item_id)
212
+
213
+ [Issue.new(step: pos,
214
+ message: "schema '#{schema_id}' does not declare " \
215
+ "'#{item_id}' (#{step.link})")]
176
216
  end
177
217
 
178
- def check_step(step, pos, prev, by_name)
218
+ def check_step(step, pos, prev, last_qualified, by_name)
179
219
  found = by_name[step.name&.safe_downcase]
180
220
  return [Issue.new(step: pos, message: "unknown type '#{step.name}'")] unless found
181
221
 
182
222
  issues = attribute_issues(step, pos, found[1], by_name)
183
- issues.concat(link_issues(step, pos, prev, by_name))
223
+ issues.concat(link_issues(step, pos, prev, last_qualified, by_name))
184
224
  issues
185
225
  end
186
226
 
@@ -239,7 +279,7 @@ module Expressir
239
279
  "aggregate but carries [#{step.index}]")]
240
280
  end
241
281
 
242
- def link_issues(step, pos, prev, by_name)
282
+ def link_issues(step, pos, prev, last_qualified, by_name)
243
283
  return [] unless LINKS.include?(step.operator)
244
284
  unless prev && step.name
245
285
  return [Issue.new(step: pos,
@@ -247,20 +287,39 @@ module Expressir
247
287
  end
248
288
 
249
289
  case step.operator
250
- when "->", "<-" then attribute_link_issues(step, pos, prev)
290
+ when "->", "<-"
291
+ attribute_link_issues(step, pos, prev, last_qualified)
251
292
  when "<=" then subtype_link_issues(step, pos, prev, by_name)
252
293
  when "=>" then supertype_link_issues(step, pos, prev, by_name)
253
294
  end
254
295
  end
255
296
 
256
- def attribute_link_issues(_step, pos, prev)
257
- unless prev.attribute
258
- return [Issue.new(step: pos,
259
- message: "'->' requires an attribute-qualified " \
260
- "node before it, got '#{prev.name}'")]
297
+ # `->`: the attribute-qualified node precedes the operator;
298
+ # `<-`: it follows (the entity before <- is referenced BY the
299
+ # attribute after it). An aggregate dereference `[i]` keeps the
300
+ # running attribute owner qualified for the next link
301
+ # (`attr -> list_type[i] -> element_type`).
302
+ def attribute_link_issues(step, pos, prev, last_qualified)
303
+ if step.operator == "->"
304
+ qualified = if prev&.attribute
305
+ prev
306
+ else
307
+ (prev&.index ? last_qualified : nil)
308
+ end
309
+ unless qualified&.attribute
310
+ return [Issue.new(step: pos,
311
+ message: "'->' requires an attribute-qualified " \
312
+ "node before it, got '#{prev&.name}'")]
313
+ end
314
+
315
+ return []
261
316
  end
262
317
 
263
- []
318
+ return [] if step.attribute
319
+
320
+ [Issue.new(step: pos,
321
+ message: "'<-' requires an attribute-qualified node " \
322
+ "after it, got '#{step.name}'")]
264
323
  end
265
324
 
266
325
  # `a <= b`: a is a subtype of b.
@@ -136,8 +136,16 @@ module Expressir
136
136
  def values(document)
137
137
  document.ae.to_a.flat_map do |element|
138
138
  [element.entity, element.aimelt] +
139
- element.aa.to_a.flat_map { |aa| [aa.attribute, aa.aimelt] }
140
- end.compact
139
+ element.aa.to_a.flat_map do |aa|
140
+ [aa.attribute, aa.aimelt, aa.assertion_to]
141
+ end
142
+ end.compact + subtype_constraint_values(document)
143
+ end
144
+
145
+ # Every declaration reference in the sc entries (#460): the
146
+ # constraint name and the entity it constrains.
147
+ def subtype_constraint_values(document)
148
+ document.sc.to_a.flat_map { |sc| [sc.constraint, sc.entity] }.compact
141
149
  end
142
150
 
143
151
  # Every reference path in the document as [location, content]
@@ -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.18".freeze
6
+ VERSION = "2.4.20".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.18
4
+ version: 2.4.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.