bel_parser 1.0.8 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/.gemspec +12 -2
  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 +25 -3
@@ -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