bel_parser 1.0.8-java → 1.1.1-java

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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/{.gemspec-java → .gemspec} +11 -4
  3. data/VERSION +1 -1
  4. data/bin/bel2_compatibility +12 -9
  5. data/bin/bel2_upgrade +18 -6
  6. data/bin/bel2_validator +6 -3
  7. data/bin/bel_script_reader +1 -0
  8. data/lib/bel_parser/completion.rb +984 -0
  9. data/lib/bel_parser/language/apply_namespace_encoding.rb +14 -3
  10. data/lib/bel_parser/language/expression_validator.rb +1 -2
  11. data/lib/bel_parser/language/function.rb +4 -0
  12. data/lib/bel_parser/language/relationship.rb +4 -0
  13. data/lib/bel_parser/language/semantics/function_deprecation.rb +1 -0
  14. data/lib/bel_parser/language/semantics/list_function_subject.rb +2 -0
  15. data/lib/bel_parser/language/semantics/multiple_subject_object.rb +4 -1
  16. data/lib/bel_parser/language/semantics/nested_statement_without_object.rb +43 -0
  17. data/lib/bel_parser/language/semantics/non_object_list.rb +3 -0
  18. data/lib/bel_parser/language/semantics/relationship_not_listable.rb +2 -0
  19. data/lib/bel_parser/language/semantics/signature_mapping.rb +2 -0
  20. data/lib/bel_parser/language/semantics_ast.rb +0 -7
  21. data/lib/bel_parser/language/syntax/invalid_function.rb +6 -1
  22. data/lib/bel_parser/language/syntax/invalid_relationship.rb +1 -0
  23. data/lib/bel_parser/language/version1_0/relationships/acts_in.rb +9 -9
  24. data/lib/bel_parser/language/version1_0/relationships/analogous.rb +6 -4
  25. data/lib/bel_parser/language/version1_0/relationships/association.rb +8 -7
  26. data/lib/bel_parser/language/version1_0/relationships/biomarker_for.rb +6 -5
  27. data/lib/bel_parser/language/version1_0/relationships/causes_no_change.rb +8 -7
  28. data/lib/bel_parser/language/version1_0/relationships/decreases.rb +13 -12
  29. data/lib/bel_parser/language/version1_0/relationships/directly_decreases.rb +8 -6
  30. data/lib/bel_parser/language/version1_0/relationships/directly_increases.rb +8 -6
  31. data/lib/bel_parser/language/version1_0/relationships/has_component.rb +14 -14
  32. data/lib/bel_parser/language/version1_0/relationships/has_components.rb +12 -11
  33. data/lib/bel_parser/language/version1_0/relationships/has_member.rb +10 -8
  34. data/lib/bel_parser/language/version1_0/relationships/has_members.rb +12 -11
  35. data/lib/bel_parser/language/version1_0/relationships/has_modification.rb +9 -7
  36. data/lib/bel_parser/language/version1_0/relationships/has_product.rb +10 -8
  37. data/lib/bel_parser/language/version1_0/relationships/has_variant.rb +9 -7
  38. data/lib/bel_parser/language/version1_0/relationships/includes.rb +11 -10
  39. data/lib/bel_parser/language/version1_0/relationships/increases.rb +12 -13
  40. data/lib/bel_parser/language/version1_0/relationships/is_a.rb +10 -8
  41. data/lib/bel_parser/language/version1_0/relationships/negative_correlation.rb +9 -7
  42. data/lib/bel_parser/language/version1_0/relationships/orthologous.rb +9 -8
  43. data/lib/bel_parser/language/version1_0/relationships/positive_correlation.rb +8 -5
  44. data/lib/bel_parser/language/version1_0/relationships/prognostic_biomarker_for.rb +8 -6
  45. data/lib/bel_parser/language/version1_0/relationships/rate_limiting_step_of.rb +10 -9
  46. data/lib/bel_parser/language/version1_0/relationships/reactant_in.rb +10 -8
  47. data/lib/bel_parser/language/version1_0/relationships/sub_process_of.rb +13 -12
  48. data/lib/bel_parser/language/version1_0/relationships/transcribed_to.rb +8 -7
  49. data/lib/bel_parser/language/version1_0/relationships/translated_to.rb +9 -7
  50. data/lib/bel_parser/language/version1_0/relationships/translocates.rb +10 -9
  51. data/lib/bel_parser/language/version2_0/functions/protein_modification.rb +1 -2
  52. data/lib/bel_parser/language/version2_0/functions/translocation.rb +1 -2
  53. data/lib/bel_parser/language/version2_0/functions/variant.rb +1 -2
  54. data/lib/bel_parser/language/version2_0/relationships/acts_in.rb +10 -9
  55. data/lib/bel_parser/language/version2_0/relationships/biomarker_for.rb +7 -5
  56. data/lib/bel_parser/language/version2_0/relationships/causes_no_change.rb +9 -7
  57. data/lib/bel_parser/language/version2_0/relationships/decreases.rb +13 -12
  58. data/lib/bel_parser/language/version2_0/relationships/directly_decreases.rb +8 -6
  59. data/lib/bel_parser/language/version2_0/relationships/directly_increases.rb +8 -6
  60. data/lib/bel_parser/language/version2_0/relationships/has_component.rb +15 -14
  61. data/lib/bel_parser/language/version2_0/relationships/has_components.rb +12 -11
  62. data/lib/bel_parser/language/version2_0/relationships/has_member.rb +9 -8
  63. data/lib/bel_parser/language/version2_0/relationships/has_members.rb +12 -11
  64. data/lib/bel_parser/language/version2_0/relationships/has_modification.rb +9 -7
  65. data/lib/bel_parser/language/version2_0/relationships/has_product.rb +10 -8
  66. data/lib/bel_parser/language/version2_0/relationships/has_variant.rb +9 -7
  67. data/lib/bel_parser/language/version2_0/relationships/includes.rb +11 -10
  68. data/lib/bel_parser/language/version2_0/relationships/increases.rb +13 -12
  69. data/lib/bel_parser/language/version2_0/relationships/is_a.rb +10 -8
  70. data/lib/bel_parser/language/version2_0/relationships/negative_correlation.rb +9 -7
  71. data/lib/bel_parser/language/version2_0/relationships/orthologous.rb +10 -8
  72. data/lib/bel_parser/language/version2_0/relationships/positive_correlation.rb +7 -5
  73. data/lib/bel_parser/language/version2_0/relationships/prognostic_biomarker_for.rb +8 -6
  74. data/lib/bel_parser/language/version2_0/relationships/rate_limiting_step_of.rb +10 -9
  75. data/lib/bel_parser/language/version2_0/relationships/reactant_in.rb +10 -8
  76. data/lib/bel_parser/language/version2_0/relationships/regulates.rb +9 -8
  77. data/lib/bel_parser/language/version2_0/relationships/sub_process_of.rb +13 -12
  78. data/lib/bel_parser/language/version2_0/relationships/transcribed_to.rb +8 -7
  79. data/lib/bel_parser/language/version2_0/relationships/translated_to.rb +9 -7
  80. data/lib/bel_parser/language/version2_0/relationships/translocates.rb +10 -9
  81. data/lib/bel_parser/mixin/levenshtein.rb +20 -0
  82. data/lib/bel_parser/parsers/ast/node.rb +49 -2
  83. data/lib/bel_parser/parsers/bel_script/define_annotation.rb +156 -156
  84. data/lib/bel_parser/parsers/bel_script/define_namespace.rb +60 -60
  85. data/lib/bel_parser/parsers/bel_script/set.rb +200 -200
  86. data/lib/bel_parser/parsers/bel_script/set_document.rb +188 -188
  87. data/lib/bel_parser/parsers/bel_script/unset.rb +14 -14
  88. data/lib/bel_parser/parsers/common/common.rl +1 -0
  89. data/lib/bel_parser/parsers/common/function.rb +8 -8
  90. data/lib/bel_parser/parsers/common/function.rl +6 -6
  91. data/lib/bel_parser/parsers/common/identifier.rb +3 -3
  92. data/lib/bel_parser/parsers/common/identifier.rl +4 -4
  93. data/lib/bel_parser/parsers/common/list.rb +78 -78
  94. data/lib/bel_parser/parsers/common/multi_identifier.rb +275 -0
  95. data/lib/bel_parser/parsers/common/multi_identifier.rl +141 -0
  96. data/lib/bel_parser/parsers/common/string.rb +6 -6
  97. data/lib/bel_parser/parsers/common/string.rl +4 -4
  98. data/lib/bel_parser/parsers/common.rb +1 -0
  99. data/lib/bel_parser/parsers/expression/nested_statement.rb +30180 -29055
  100. data/lib/bel_parser/parsers/expression/observed_term.rb +1456 -1099
  101. data/lib/bel_parser/parsers/expression/parameter.rb +192 -111
  102. data/lib/bel_parser/parsers/expression/parameter.rl +6 -3
  103. data/lib/bel_parser/parsers/expression/relationship.rb +43 -19
  104. data/lib/bel_parser/parsers/expression/relationship.rl +1 -0
  105. data/lib/bel_parser/parsers/expression/simple_statement.rb +17805 -17093
  106. data/lib/bel_parser/parsers/expression/statement_autocomplete.rb +1035 -0
  107. data/lib/bel_parser/parsers/expression/statement_autocomplete.rl +736 -0
  108. data/lib/bel_parser/parsers/expression/term.rb +960 -705
  109. data/lib/bel_parser/parsers/serializer.rb +8 -4
  110. data/lib/bel_parser/version.rb +33 -0
  111. data/lib/bel_parser.rb +3 -0
  112. metadata +26 -4
@@ -0,0 +1,984 @@
1
+ require 'bel_parser/language/expression_validator'
2
+ require 'bel_parser/language/semantics'
3
+ require 'bel_parser/resource'
4
+ require 'bel_parser/parsers/ast/node'
5
+ require 'bel_parser/parsers/expression/statement_autocomplete'
6
+ require 'bel_parser/parsers/serializer'
7
+ require_relative 'mixin/levenshtein'
8
+
9
+ module BELParser
10
+ module Completion
11
+ extend BELParser::Parsers::AST::Sexp
12
+ extend BELParser::Parsers
13
+
14
+ def self.complete(input, spec, search, namespaces, caret_position = input.length)
15
+ # Algorithm
16
+ # 1. Parse AST using statement_autocomplete ragel FSM.
17
+ # 2. Given cursor find node to complete.
18
+ # 3. Determine completers that should run given node type and surrounding nodes in the AST.
19
+ # 4. Compute completion AST for each suggestion.
20
+ # 5. For each suggestion, transform original AST into full completion.
21
+ # 6. Run semantic validation on each completion AST.
22
+ # 7. Return combined completion AST and semantic details.
23
+
24
+ ast, caret_position = BELParser::Parsers::Expression::StatementAutocomplete.parse(input, caret_position)
25
+ completing_node = find_node(ast, caret_position)
26
+ return [] unless completing_node
27
+
28
+ completions =
29
+ case completing_node.type
30
+ when :parameter
31
+ complete_parameter(completing_node, caret_position, ast, spec, search, namespaces)
32
+ when :function
33
+ complete_function(completing_node, caret_position, ast, spec, search, namespaces)
34
+ when :argument
35
+ complete_argument(completing_node, caret_position, ast, spec, search, namespaces)
36
+ when :relationship
37
+ complete_relationship(completing_node, caret_position, ast, spec, search, namespaces)
38
+ else
39
+ []
40
+ end
41
+
42
+ urir = BELParser::Resource.default_uri_reader
43
+ urlr = BELParser::Resource.default_url_reader
44
+ validator = BELParser::Language::ExpressionValidator.new(spec, namespaces, urir, urlr)
45
+
46
+ validated_completions =
47
+ completions
48
+ .map { |(completion_ast, completion_result)|
49
+ message = ''
50
+ terms = completion_ast.traverse.select { |node| node.type == :term }.to_a
51
+ semantics_functions =
52
+ BELParser::Language::Semantics.semantics_functions.reject { |fun|
53
+ fun == BELParser::Language::Semantics::SignatureMapping
54
+ }
55
+
56
+ semantic_warnings =
57
+ completion_ast
58
+ .traverse
59
+ .flat_map { |node|
60
+ semantics_functions.flat_map { |func|
61
+ func.map(node, spec, namespaces)
62
+ }
63
+ }
64
+ .compact
65
+
66
+ if semantic_warnings.empty?
67
+ valid = true
68
+ else
69
+ valid = false
70
+ message =
71
+ semantic_warnings.reduce('') { |msg, warning|
72
+ msg << "#{warning}\n"
73
+ }
74
+ message << "\n"
75
+ end
76
+
77
+ term_semantics =
78
+ terms.map { |term|
79
+ term_result = validator.validate(term)
80
+ valid &= term_result.valid_semantics?
81
+ bel_term = serialize(term)
82
+
83
+ unless valid
84
+ message << "Term: #{bel_term}\n"
85
+ term_result.invalid_signature_mappings.map { |m|
86
+ message << " #{m}\n"
87
+ }
88
+ message << "\n"
89
+ end
90
+
91
+ {
92
+ term: bel_term,
93
+ valid_signatures: term_result.valid_signature_mappings.map(&:to_s),
94
+ invalid_signatures: term_result.invalid_signature_mappings.map(&:to_s)
95
+ }
96
+ }
97
+
98
+ completion_result[:validation] = {
99
+ expression: completion_result[:value],
100
+ valid_syntax: true,
101
+ valid_semantics: valid,
102
+ message: valid ? 'Valid semantics' : message,
103
+ warnings: semantic_warnings.map(&:to_s),
104
+ term_signatures: term_semantics
105
+ }
106
+ completion_result
107
+ }
108
+ .group_by { |completion_result|
109
+ completion_result[:validation][:valid_semantics]
110
+ }
111
+
112
+ (validated_completions[true] || []) + (validated_completions[false] || [])
113
+ end
114
+
115
+ def self.complete_function(
116
+ completing_node, caret_position, ast, spec, search, namespaces
117
+ )
118
+ string_literal =
119
+ if completing_node.identifier.nil?
120
+ ''
121
+ else
122
+ completing_node.identifier.string_literal
123
+ end
124
+
125
+ FunctionCompleter
126
+ .new(spec, search, namespaces)
127
+ .complete(string_literal, caret_position)
128
+ .map { |(function, completion_ast)|
129
+ short = function.short.to_s
130
+ long = function.long.to_s
131
+
132
+ completion_ast.character_range = [
133
+ completing_node.range_start,
134
+ completing_node.range_start + short.length
135
+ ]
136
+
137
+ completion_ast = MergeCompletion.new(completion_ast).process(ast)
138
+ completion = serialize(completion_ast)
139
+
140
+ [
141
+ completion_ast,
142
+ {
143
+ type: :function,
144
+ id: long,
145
+ label: long,
146
+ value: completion,
147
+ caret_position: short.length + 1
148
+ }
149
+ ]
150
+ }
151
+ end
152
+
153
+ def self.complete_relationship(
154
+ completing_node, caret_position, ast, spec, search, namespaces
155
+ )
156
+ string_literal = completing_node.string_literal
157
+
158
+ completer =
159
+ if string_literal.nil?
160
+ AllRelationshipCompleter.new(spec, search, namespaces)
161
+ else
162
+ RelationshipCompleter.new(spec, search, namespaces)
163
+ end
164
+
165
+ completer
166
+ .complete(string_literal, caret_position)
167
+ .map { |(relationship, completion_ast)|
168
+ short = relationship.short.to_s
169
+ long = relationship.long.to_s
170
+
171
+ completion_ast.character_range = [
172
+ completing_node.range_start,
173
+ completing_node.range_start + short.length
174
+ ]
175
+
176
+ completion_ast = MergeCompletion.new(completion_ast).process(ast)
177
+ completion = serialize(completion_ast)
178
+
179
+ [
180
+ completion_ast,
181
+ {
182
+ type: :relationship,
183
+ id: long,
184
+ label: long,
185
+ value: completion,
186
+ caret_position: short.length + 1
187
+ }
188
+ ]
189
+ }
190
+ end
191
+
192
+ def self.complete_parameter(
193
+ completing_node, caret_position, ast, spec, search, namespaces
194
+ )
195
+ prefix, value = completing_node.children
196
+
197
+ # Completing prefix
198
+ if Range.new(*prefix.character_range, false).include?(caret_position)
199
+ if prefix.identifier.nil?
200
+ # Provide all namespace prefix completions.
201
+ all_prefix_completions = AllNamespacePrefixCompleter
202
+ .new(spec, search, namespaces)
203
+ .complete(nil, nil)
204
+ .map { |(bel_prefix, completion_ast)|
205
+ completion_ast.character_range = [
206
+ prefix.range_start,
207
+ prefix.range_start + bel_prefix.length + 1
208
+ ]
209
+
210
+ completion_ast = MergeCompletion.new(completion_ast).process(ast)
211
+ completion = serialize(completion_ast)
212
+
213
+ [
214
+ completion_ast,
215
+ {
216
+ type: :namespace_prefix,
217
+ id: bel_prefix,
218
+ label: bel_prefix,
219
+ value: completion,
220
+ caret_position: completing_node.range_start + bel_prefix.length + 1
221
+ }
222
+ ]
223
+ }
224
+
225
+ all_prefix_completions
226
+ else
227
+ # Match provided namespace prefix.
228
+ string_literal = prefix.identifier.string_literal
229
+
230
+ prefix_completions = NamespacePrefixCompleter
231
+ .new(spec, search, namespaces)
232
+ .complete(string_literal, caret_position)
233
+ .map { |(bel_prefix, completion_ast)|
234
+ completion_ast.character_range = [
235
+ prefix.range_start,
236
+ prefix.range_start + bel_prefix.length + 1
237
+ ]
238
+
239
+ completion_ast = MergeCompletion.new(completion_ast).process(ast)
240
+ completion = serialize(completion_ast)
241
+
242
+ [
243
+ completion_ast,
244
+ {
245
+ type: :namespace_prefix,
246
+ id: bel_prefix,
247
+ label: bel_prefix,
248
+ value: completion,
249
+ caret_position: completing_node.range_start + bel_prefix.length + 1
250
+ }
251
+ ]
252
+ }
253
+
254
+ prefix_completions
255
+ end
256
+ else
257
+ string_literal =
258
+ case value.first_child.type
259
+ when :identifier
260
+ value.first_child.string_literal
261
+ when :string
262
+ value.first_child.string_value
263
+ end
264
+
265
+ prefix_str =
266
+ if prefix && prefix.identifier
267
+ prefix.identifier.string_literal
268
+ else
269
+ nil
270
+ end
271
+
272
+ function_completions = FunctionTermCompleter
273
+ .new(spec, search, namespaces)
274
+ .complete(string_literal, caret_position)
275
+ .map { |(function, completion_ast)|
276
+ short = function.short.to_s
277
+ long = function.long.to_s
278
+ completion = serialize(completion_ast)
279
+
280
+ [
281
+ completion_ast,
282
+ {
283
+ type: :function,
284
+ id: long,
285
+ label: long,
286
+ value: completion,
287
+ caret_position: short.length + 1
288
+ }
289
+ ]
290
+ }
291
+
292
+ prefix_completions = NamespacePrefixArgumentCompleter
293
+ .new(spec, search, namespaces)
294
+ .complete(string_literal, nil)
295
+ .map { |(bel_prefix, completion_ast)|
296
+ completion = serialize(completion_ast)
297
+
298
+ [
299
+ completion_ast,
300
+ {
301
+ type: :namespace_prefix,
302
+ id: bel_prefix,
303
+ label: bel_prefix,
304
+ value: completion,
305
+ caret_position: completing_node.range_start + bel_prefix.length + 1
306
+ }
307
+ ]
308
+ }
309
+
310
+ exact_match_completions = ExactMatchParameterCompleter
311
+ .new(spec, search, namespaces)
312
+ .complete(string_literal, caret_position - value.range_start, prefix: prefix_str)
313
+ .map { |(ns_value, completion_ast)|
314
+ completion = "(#{serialize(completion_ast)})"
315
+
316
+ [
317
+ completion_ast,
318
+ {
319
+ type: :namespace_value,
320
+ id: ns_value,
321
+ label: ns_value,
322
+ value: completion,
323
+ caret_position: 0
324
+ }
325
+ ]
326
+ }
327
+
328
+ wildcard_completions = WildcardMatchParameterCompleter
329
+ .new(spec, search, namespaces)
330
+ .complete(string_literal, caret_position - value.range_start, prefix: prefix_str)
331
+ .map { |(ns_value, completion_ast)|
332
+ completion = "(#{serialize(completion_ast)})"
333
+
334
+ [
335
+ completion_ast,
336
+ {
337
+ type: :namespace_value,
338
+ id: ns_value,
339
+ label: ns_value,
340
+ value: completion,
341
+ caret_position: 0
342
+ }
343
+ ]
344
+ }
345
+
346
+ function_completions + prefix_completions + (exact_match_completions + wildcard_completions).uniq
347
+ end
348
+ end
349
+
350
+ def self.complete_argument(
351
+ completing_node, caret_position, ast, spec, search, namespaces
352
+ )
353
+ if completing_node.child.nil?
354
+ all_prefix_completions = AllNamespacePrefixArgumentCompleter
355
+ .new(spec, search, namespaces)
356
+ .complete(nil, nil)
357
+ .map { |(bel_prefix, completion_ast)|
358
+ completion_ast.character_range = completing_node.character_range
359
+
360
+ completion_ast = MergeCompletion.new(completion_ast).process(ast)
361
+ completion = serialize(completion_ast)
362
+
363
+ [
364
+ completion_ast,
365
+ {
366
+ type: :namespace_prefix,
367
+ id: bel_prefix,
368
+ label: bel_prefix,
369
+ value: completion,
370
+ caret_position: completing_node.range_start + bel_prefix.length + 1
371
+ }
372
+ ]
373
+ }
374
+
375
+ all_function_completions = AllFunctionArgumentCompleter
376
+ .new(spec, search, namespaces)
377
+ .complete(nil, nil)
378
+ .map { |(function, completion_ast)|
379
+ short = function.short.to_s
380
+ long = function.long.to_s
381
+
382
+ completion_ast.character_range = [
383
+ completing_node.range_start,
384
+ completing_node.range_start + short.length
385
+ ]
386
+
387
+ completion_ast = MergeCompletion.new(completion_ast).process(ast)
388
+ completion = serialize(completion_ast)
389
+
390
+ [
391
+ completion_ast,
392
+ {
393
+ type: :function,
394
+ id: long,
395
+ label: long,
396
+ value: completion,
397
+ caret_position: short.length + 1
398
+ }
399
+ ]
400
+ }
401
+
402
+ all_prefix_completions + all_function_completions
403
+ elsif completing_node.parameter?
404
+ parameter = completing_node.child
405
+ prefix, value = parameter.children
406
+
407
+ if prefix && Range.new(*prefix.character_range, false).include?(caret_position)
408
+ prefix_str = prefix.identifier.string_literal
409
+
410
+ prefix_completions = NamespacePrefixArgumentCompleter
411
+ .new(spec, search, namespaces)
412
+ .complete(prefix_str, nil)
413
+ .map { |(bel_prefix, completion_ast)|
414
+ completion_ast.character_range = completing_node.character_range
415
+
416
+ completion_ast = MergeCompletion.new(completion_ast).process(ast)
417
+ completion = serialize(completion_ast)
418
+
419
+ [
420
+ completion_ast,
421
+ {
422
+ type: :namespace_prefix,
423
+ id: bel_prefix,
424
+ label: bel_prefix,
425
+ value: completion,
426
+ caret_position: completing_node.range_start + bel_prefix.length + 1
427
+ }
428
+ ]
429
+ }
430
+
431
+ prefix_completions
432
+ else
433
+ # completing value of parameter
434
+ value_str =
435
+ case value.first_child.type
436
+ when :identifier
437
+ value.first_child.string_literal
438
+ when :string
439
+ value.first_child.string_value
440
+ end
441
+
442
+ prefix_string = nil
443
+ prefix_completions =
444
+ if prefix && prefix.identifier
445
+ # ... prefix exists, store it for later value lookup
446
+ prefix_string = prefix.identifier.string_literal
447
+ []
448
+ else
449
+ # ... prefix is nil, try to complete it, lookup values later without prefix
450
+ prefix_string = nil
451
+
452
+ NamespacePrefixArgumentCompleter
453
+ .new(spec, search, namespaces)
454
+ .complete(value_str, nil)
455
+ .map { |(bel_prefix, completion_ast)|
456
+ completion_ast.character_range = completing_node.character_range
457
+
458
+ completion_ast = MergeCompletion.new(completion_ast).process(ast)
459
+ completion = serialize(completion_ast)
460
+
461
+ [
462
+ completion_ast,
463
+ {
464
+ type: :namespace_prefix,
465
+ id: bel_prefix,
466
+ label: bel_prefix,
467
+ value: completion,
468
+ caret_position: completion_ast.range_start + bel_prefix.length + 1
469
+ }
470
+ ]
471
+ }
472
+ end
473
+
474
+ function_completions = []
475
+ if prefix_string.nil?
476
+ completer =
477
+ if ast.subject.term.function.nil? || (!ast.object.nil? && ast.object.term? && ast.object.child.function.nil?)
478
+ FunctionTermCompleter
479
+ else
480
+ FunctionArgumentCompleter
481
+ end
482
+ function_completions = completer
483
+ .new(spec, search, namespaces)
484
+ .complete(value_str, caret_position)
485
+ .map { |(function, completion_ast)|
486
+ short = function.short.to_s
487
+ long = function.long.to_s
488
+
489
+ completion_ast.character_range = [
490
+ completing_node.range_start,
491
+ completing_node.range_start + short.length
492
+ ]
493
+
494
+ completion_ast = MergeCompletion.new(completion_ast).process(ast)
495
+ completion = serialize(completion_ast)
496
+
497
+ [
498
+ completion_ast,
499
+ {
500
+ type: :function,
501
+ id: long,
502
+ label: long,
503
+ value: completion,
504
+ caret_position: completing_node.range_start + short.length + 1
505
+ }
506
+ ]
507
+ }
508
+ end
509
+
510
+ exact_match_completions = ExactMatchParameterCompleter
511
+ .new(spec, search, namespaces)
512
+ .complete(value_str, caret_position - value.range_start, prefix: prefix_string)
513
+ .map { |(ns_value, completion_ast)|
514
+ completion_ast.character_range = completing_node.character_range
515
+
516
+ completion_ast = MergeCompletion.new(completion_ast).process(ast)
517
+ completion = serialize(completion_ast)
518
+
519
+ [
520
+ completion_ast,
521
+ {
522
+ type: :namespace_value,
523
+ id: ns_value,
524
+ label: ns_value,
525
+ value: completion,
526
+ caret_position: value.range_start + ns_value.length
527
+ }
528
+ ]
529
+ }
530
+
531
+ wildcard_completions = WildcardMatchParameterCompleter
532
+ .new(spec, search, namespaces)
533
+ .complete(value_str, caret_position - value.range_start, prefix: prefix_string)
534
+ .map { |(ns_value, completion_ast)|
535
+ completion_ast.character_range = completing_node.character_range
536
+
537
+ completion_ast = MergeCompletion.new(completion_ast).process(ast)
538
+ completion = serialize(completion_ast)
539
+
540
+ [
541
+ completion_ast,
542
+ {
543
+ type: :namespace_value,
544
+ id: ns_value,
545
+ label: ns_value,
546
+ value: completion,
547
+ caret_position: value.range_start + ns_value.length
548
+ }
549
+ ]
550
+ }
551
+
552
+ prefix_completions + function_completions + (exact_match_completions + wildcard_completions).uniq
553
+ end
554
+ else
555
+ # TODO Completing term argument, will we ever get here?
556
+ puts "#{completing_node.type}: child is a term, how do we proceed?"
557
+ []
558
+ end
559
+ end
560
+
561
+ def self.find_node(ast, caret_position)
562
+ ast.traverse do |node|
563
+ next if
564
+ node.type == :term ||
565
+ caret_position < node.range_start ||
566
+ caret_position > node.range_end
567
+
568
+ case node.type
569
+ when :argument
570
+ return node if node.child.nil? || node.parameter?
571
+ when :parameter, :function, :relationship
572
+ return node
573
+ end
574
+ end
575
+
576
+ nil
577
+ end
578
+
579
+ class BaseCompleter
580
+ include BELParser::Parsers::AST::Sexp
581
+
582
+ def initialize(spec, search, namespaces)
583
+ @spec = spec
584
+ @search = search
585
+ @namespaces = namespaces
586
+ end
587
+
588
+ def complete(string_literal, caret_position, options = {})
589
+ raise NotImplementedError, "#{__method__} is not implemented."
590
+ end
591
+ end
592
+
593
+ class FunctionCompleter < BaseCompleter
594
+
595
+ def complete(string_literal, caret_position)
596
+ pattern = /.*#{Regexp.quote(string_literal)}.*/i
597
+ @spec.functions
598
+ .select { |function| function =~ pattern }
599
+ .sort_by { |function| function.long }
600
+ .map { |function|
601
+ make_completion(function)
602
+ }
603
+ end
604
+
605
+ protected
606
+
607
+ def make_completion(function)
608
+ [
609
+ function,
610
+ function(
611
+ identifier(
612
+ function.short.to_s))
613
+ ]
614
+ end
615
+ end
616
+
617
+ class FunctionTermCompleter < FunctionCompleter
618
+
619
+ def make_completion(function)
620
+ [
621
+ function,
622
+ term(
623
+ function(
624
+ identifier(
625
+ function.short.to_s)))
626
+ ]
627
+ end
628
+ end
629
+
630
+ class FunctionArgumentCompleter < FunctionCompleter
631
+
632
+ def make_completion(function)
633
+ [
634
+ function,
635
+ argument(
636
+ term(
637
+ function(
638
+ identifier(
639
+ function.short.to_s))))
640
+ ]
641
+ end
642
+ end
643
+
644
+ class AllFunctionCompleter < BaseCompleter
645
+
646
+ def complete(_, _)
647
+ @spec.functions
648
+ .sort_by { |function| function.long }
649
+ .map { |function|
650
+ make_completion(function)
651
+ }
652
+ end
653
+
654
+ protected
655
+
656
+ def make_completion(function)
657
+ [
658
+ function,
659
+ function(
660
+ identifier(
661
+ function.short.to_s))
662
+ ]
663
+ end
664
+ end
665
+
666
+ class AllFunctionTermCompleter < AllFunctionCompleter
667
+
668
+ def make_completion(function)
669
+ [
670
+ function,
671
+ term(
672
+ function(
673
+ identifier(
674
+ function.short.to_s)))
675
+ ]
676
+ end
677
+ end
678
+
679
+ class AllFunctionArgumentCompleter < AllFunctionCompleter
680
+
681
+ def make_completion(function)
682
+ [
683
+ function,
684
+ argument(
685
+ term(
686
+ function(
687
+ identifier(
688
+ function.short.to_s))))
689
+ ]
690
+ end
691
+ end
692
+
693
+ class NamespacePrefixCompleter < BaseCompleter
694
+
695
+ def complete(string_literal, _)
696
+ lowercase_substring = string_literal.upcase
697
+ @namespaces.keys
698
+ .select { |px| px.include?(lowercase_substring) }
699
+ .sort
700
+ .map { |px|
701
+ make_completion(px.upcase)
702
+ }
703
+ end
704
+
705
+ protected
706
+
707
+ def make_completion(bel_prefix)
708
+ [
709
+ bel_prefix,
710
+ prefix(
711
+ identifier(
712
+ bel_prefix))
713
+ ]
714
+ end
715
+ end
716
+
717
+ class NamespacePrefixArgumentCompleter < NamespacePrefixCompleter
718
+
719
+ def make_completion(prefix)
720
+ [
721
+ prefix,
722
+ argument(
723
+ parameter(
724
+ prefix(
725
+ identifier(
726
+ prefix)),
727
+ value(
728
+ identifier(
729
+ ""))))
730
+ ]
731
+ end
732
+ end
733
+
734
+ class AllNamespacePrefixCompleter < NamespacePrefixCompleter
735
+
736
+ def complete(_, _)
737
+ @namespaces.keys
738
+ .sort
739
+ .map { |px|
740
+ make_completion(px)
741
+ }
742
+ end
743
+ end
744
+
745
+ class AllNamespacePrefixArgumentCompleter < NamespacePrefixArgumentCompleter
746
+
747
+ def complete(_, _)
748
+ @namespaces.keys
749
+ .sort
750
+ .map { |px|
751
+ make_completion(px)
752
+ }
753
+ end
754
+ end
755
+
756
+ module QuotedValue
757
+
758
+ def map_value(prefix, pref_label)
759
+ if !pref_label.scan(/[^\w]/).empty?
760
+ [
761
+ %Q{#{prefix}:"#{pref_label}"},
762
+ value(
763
+ string(
764
+ pref_label))
765
+ ]
766
+ else
767
+ [
768
+ %Q{#{prefix}:#{pref_label}},
769
+ value(
770
+ identifier(
771
+ pref_label))
772
+ ]
773
+ end
774
+ end
775
+ end
776
+
777
+ class WildcardMatchParameterCompleter < BaseCompleter
778
+ include QuotedValue
779
+
780
+ L = BELParser::Levenshtein
781
+
782
+ def complete(string_literal, caret_position, options = {})
783
+ return [] if string_literal.length < 3
784
+
785
+ query =
786
+ case
787
+ when caret_position == string_literal.length
788
+ "#{string_literal}*"
789
+ when caret_position == 0
790
+ "*#{string_literal}"
791
+ else
792
+ ante = string_literal.slice(0...caret_position)
793
+ post = string_literal.slice(caret_position..-1)
794
+ "#{ante}*#{post}"
795
+ end
796
+
797
+ # find namespace URI if prefix was provided
798
+ prefix = options[:prefix]
799
+ if prefix
800
+ specified_prefix = prefix.to_s.upcase
801
+ matched_namespace = @namespaces[specified_prefix]
802
+ uri = matched_namespace ? matched_namespace.uri : nil
803
+ else
804
+ uri = nil
805
+ end
806
+
807
+ @search
808
+ .search(query, :namespace_concept, uri, nil, size: 100)
809
+ .sort { |match1, match2|
810
+ L.distance(string_literal.downcase, match1.pref_label.downcase) <=>
811
+ L.distance(string_literal.downcase, match2.pref_label.downcase)
812
+ }
813
+ .map { |match|
814
+ match_namespace = @namespaces.values.find { |ns| ns.uri == match.scheme_uri }
815
+ if match_namespace
816
+ [match_namespace.keyword, match.pref_label]
817
+ else
818
+ nil
819
+ end
820
+ }
821
+ .compact
822
+ .take(20)
823
+ .sort_by { |(_, v)| v }
824
+ .uniq
825
+ .map { |(ns, v)|
826
+ ns_value, value_ast = map_value(ns, v)
827
+
828
+ [
829
+ ns_value,
830
+ argument(
831
+ parameter(
832
+ prefix(
833
+ identifier(
834
+ ns)),
835
+ value_ast))
836
+ ]
837
+ }
838
+ end
839
+ end
840
+
841
+ class ExactMatchParameterCompleter < BaseCompleter
842
+ include QuotedValue
843
+
844
+ def complete(string_literal, caret_position, options = {})
845
+ # find namespace URI if prefix was provided
846
+ prefix = options[:prefix]
847
+ if prefix
848
+ specified_prefix = prefix.to_s.upcase
849
+ matched_namespace = @namespaces[specified_prefix]
850
+ uri = matched_namespace ? matched_namespace.uri : nil
851
+ else
852
+ uri = nil
853
+ end
854
+
855
+ @search
856
+ .search(string_literal, :namespace_concept, uri, nil, size: 100, exact_match: true)
857
+ .map { |match|
858
+ match_namespace = @namespaces.values.find { |ns| ns.uri == match.scheme_uri }
859
+ next unless match_namespace
860
+
861
+ prefix = match_namespace.keyword
862
+ ns_value, value_ast = map_value(prefix, match.pref_label)
863
+
864
+ [
865
+ ns_value,
866
+ argument(
867
+ parameter(
868
+ prefix(
869
+ identifier(
870
+ prefix)),
871
+ value_ast))
872
+ ]
873
+ }
874
+ .to_a
875
+ .compact
876
+ end
877
+ end
878
+
879
+ class RelationshipCompleter < BaseCompleter
880
+
881
+ def complete(string_literal, caret_position)
882
+ @spec.relationships
883
+ .select { |relationship|
884
+ relationship =~ /.*#{Regexp.quote(string_literal)}.*/i
885
+ }
886
+ .sort_by { |relationship| relationship.long }
887
+ .map { |relationship|
888
+ make_completion(relationship)
889
+ }
890
+ end
891
+
892
+ def make_completion(relationship)
893
+ short = relationship.short.to_s
894
+ [
895
+ relationship,
896
+ relationship(
897
+ short)
898
+ ]
899
+ end
900
+ end
901
+
902
+ class AllRelationshipCompleter < RelationshipCompleter
903
+
904
+ def complete(string_literal, caret_position)
905
+ @spec.relationships
906
+ .sort_by { |relationship| relationship.long }
907
+ .map { |relationship|
908
+ make_completion(relationship)
909
+ }
910
+ end
911
+ end
912
+
913
+ class MergeCompletion
914
+ include ::AST::Processor::Mixin
915
+
916
+ def initialize(completion_node)
917
+ @completion_node = completion_node
918
+ @target_type = completion_node.type
919
+ @range_start = completion_node.range_start
920
+ end
921
+
922
+ def handler_missing(node)
923
+ if node.type == @target_type && node.range_start == @range_start
924
+ node = @completion_node
925
+ end
926
+
927
+ node.updated(
928
+ node.children.map { |n|
929
+ if n.respond_to?(:type)
930
+ process(n)
931
+ else
932
+ n
933
+ end
934
+ }
935
+ )
936
+ end
937
+ end
938
+ end
939
+ end
940
+
941
+ if RUBY_ENGINE =~ /jruby/ && __FILE__ == $0
942
+ require 'bel_parser'
943
+ require 'bel_parser/resource/jena_tdb_reader'
944
+ require 'bel'
945
+
946
+ # RdfRepository using Jena.
947
+ tdb = ARGV.shift
948
+ rr = BEL::RdfRepository.plugins[:jena].create_repository(:tdb_directory => tdb)
949
+ namespaces = BEL::Resource::Namespaces.new(rr)
950
+
951
+ BELParser::Resource.default_uri_reader = BELParser::Resource::JenaTDBReader.new(tdb)
952
+
953
+ ns_hash = Hash[
954
+ namespaces.each.map { |ns|
955
+ prefix = ns.prefix.first.upcase
956
+
957
+ [
958
+ prefix,
959
+ BELParser::Expression::Model::Namespace.new(
960
+ prefix,
961
+ ns.uri
962
+ )
963
+ ]
964
+ }
965
+ ]
966
+
967
+
968
+ puts "Available namespaces:"
969
+ ns_hash.each do |_, ns|
970
+ puts " #{ns.keyword}: #{ns.uri}"
971
+ end
972
+
973
+ spec = BELParser::Language.specification('2.0')
974
+ search = BEL::Resource::Search.plugins[:sqlite].create_search(
975
+ :database_file => '/home/tony/projects/openbel/openbel-api/data/rdf_resources.db'
976
+ )
977
+
978
+ $stdin.each_line do |line|
979
+ line.strip!
980
+ puts BELParser::Completion.complete(line, spec, search, ns_hash)
981
+ end
982
+ end
983
+ # vim: ft=ruby ts=2 sw=2:
984
+ # encoding: utf-8