bel 0.1.0

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.
@@ -0,0 +1,576 @@
1
+ # vim: ts=2 sw=2:
2
+
3
+ module BEL
4
+ module Language
5
+ class Signature
6
+ attr_reader :fx, :arguments
7
+ def initialize(fx, *arguments)
8
+ @fx = fx
9
+ @arguments = arguments
10
+
11
+ dup_hash = {}
12
+ @arguments.each_with_index { |arg, i| (dup_hash[arg] ||= []) << i }
13
+ dup_hash.keep_if { |k, v| v.length > 1 }.values.each do |v|
14
+ first_arg = @arguments[v[0]]
15
+ if F === first_arg
16
+ replace_range = (v.first..v.last)
17
+ @arguments[replace_range] = F.new(first_arg.func_return, true)
18
+ end
19
+ end
20
+ end
21
+
22
+ def ==(other)
23
+ return false if @fx != other.fx
24
+ @arguments == other.arguments
25
+ end
26
+
27
+ def <=>(other)
28
+ return 1 if other.nil?
29
+ return -1 if @fx != other.fx
30
+ return -1 if @arguments.nil? and not other.nil?
31
+ return 1 if not @arguments.nil? and other.nil?
32
+ @arguments <=> other.arguments
33
+ end
34
+
35
+ def to_s
36
+ return_type = FUNCTIONS[@fx][:return_type]
37
+ "#{@fx}(#{@arguments.map(&:to_s) * ','})#{return_type}"
38
+ end
39
+ end
40
+
41
+ class F
42
+ attr_reader :func_return, :var
43
+ def initialize(func_return, var=false)
44
+ @func_return = func_return
45
+ @var = var
46
+ end
47
+
48
+ def ==(other)
49
+ return false if not other.respond_to? :func_return
50
+
51
+ @func_return == other.func_return and @var == other.var
52
+ end
53
+
54
+ alias_method :eql?, :==
55
+
56
+ def hash
57
+ [@func_return, @var].hash
58
+ end
59
+
60
+ def <=>(other)
61
+ return 1 if @var ^ other.var
62
+
63
+ tree = FUNCTION_TYPES[@func_return]
64
+ return -1 if not tree.include?(other.func_return)
65
+ -(tree.index(@func_return) <=> tree.index(other.func_return))
66
+ end
67
+
68
+ def to_s
69
+ "F:#{@func_return}#{'...' if @var}"
70
+ end
71
+ end
72
+
73
+ class E
74
+ attr_reader :encoding, :var
75
+ def initialize(encoding, var=false)
76
+ @encoding = encoding
77
+ @var = var
78
+ end
79
+
80
+ def <=>(other)
81
+ return 1 if @var ^ other.var
82
+
83
+ # compare for equals and wildcard case
84
+ cmp = @encoding <=> other.encoding
85
+ return cmp if cmp.zero? or (@encoding == :* or other.encoding == :*)
86
+
87
+ # compare encoding for assignability; based on array index
88
+ @encoding.to_s.each_char do |enc_char|
89
+ enc_sym = enc_char.to_sym
90
+ tree = PARAMETER_ENCODING[enc_sym]
91
+ next if not tree.include?(other.encoding)
92
+
93
+ match = -(tree.index(enc_sym) <=> tree.index(other.encoding))
94
+ return match if match >= 0
95
+ end
96
+ -1
97
+ end
98
+
99
+ def to_s
100
+ "E:#{@encoding}"
101
+ end
102
+ end
103
+
104
+ class NullE
105
+ def <=>(other)
106
+ return 0 if NullE === other
107
+ -1
108
+ end
109
+
110
+ def to_s
111
+ "E:nil"
112
+ end
113
+ end
114
+
115
+ class Parameter
116
+ include Comparable
117
+ attr_reader :ns_def, :value, :enc, :signature
118
+
119
+ def initialize(ns_def, value, enc)
120
+ @ns_def = ns_def
121
+ @value = value
122
+ @enc = enc || ''
123
+ @signature = E.new(@enc)
124
+ end
125
+
126
+ def <=>(other)
127
+ ns_compare = ns_def <=> other.ns_def
128
+ if ns_compare == 0
129
+ value <=> other.value
130
+ else
131
+ ns_compare
132
+ end
133
+ end
134
+
135
+ def to_s
136
+ value = @value
137
+ if value =~ %r{\W}
138
+ value = %Q{"#{value}"}
139
+ end
140
+ "#{@ns_def}:#{value}"
141
+ end
142
+ end
143
+
144
+ class Statement
145
+ attr_accessor :subject, :relationship, :object
146
+
147
+ def initialize(subject, relationship=nil, object=nil)
148
+ raise ArgumentError, 'subject must not be nil' unless subject
149
+ @subject = subject
150
+ @relationship = relationship
151
+ @object = object
152
+ end
153
+ end
154
+
155
+ class Function
156
+ attr_reader :short_form, :long_form, :return_type,
157
+ :description, :signatures
158
+
159
+ def initialize args
160
+ args.each do |k,v|
161
+ instance_variable_set("@#{k}", v) unless v.nil?
162
+ end
163
+ end
164
+ end
165
+
166
+ class Term
167
+ include Comparable
168
+ attr_reader :fx, :arguments, :signature
169
+
170
+ def initialize(fx, *arguments)
171
+ @fx = fx
172
+ @arguments = arguments ||= []
173
+ @signature = Signature.new(
174
+ @fx.short_form,
175
+ *@arguments.map { |arg|
176
+ case arg
177
+ when Term
178
+ F.new(arg.fx.return_type)
179
+ when Parameter
180
+ E.new(arg.enc)
181
+ when nil
182
+ NullE.new
183
+ end
184
+ })
185
+ end
186
+
187
+ def validate_signature
188
+ invalids = []
189
+ @arguments.select { |arg| Term === arg }.each do |term|
190
+ invalids << term if not term.validate_signature
191
+ end
192
+
193
+ sigs = fx.signatures
194
+ match = sigs.any? do |sig| (@signature <=> sig) >= 0 end
195
+ invalids << self if not match
196
+ if block_given? and not invalids.empty?
197
+ invalids.each do |term|
198
+ yield term, term.fx.signatures
199
+ end
200
+ end
201
+ invalids.empty?
202
+ end
203
+
204
+ def to_s
205
+ "#{@fx.short_form}(#{@arguments.map(&:to_s).join(',')})"
206
+ end
207
+ end
208
+
209
+ FUNCTIONS = {
210
+ a: {
211
+ short_form: :a,
212
+ long_form: :abundance,
213
+ description: 'Denotes the abundance of an entity',
214
+ return_type: :a,
215
+ signatures: [
216
+ Signature.new(:a, E.new(:A))
217
+ ]
218
+ },
219
+ bp: {
220
+ short_form: :bp,
221
+ long_form: :biologicalProcess,
222
+ description: 'Denotes a process or population of events',
223
+ return_type: :bp,
224
+ signatures: [
225
+ Signature.new(:bp, E.new(:B))
226
+ ]
227
+ },
228
+ cat: {
229
+ short_form: :cat,
230
+ long_form: :catalyticActivity,
231
+ description: 'Denotes the frequency or abundance of events where a member acts as an enzymatic catalyst of biochecmial reactions',
232
+ return_type: :a,
233
+ signatures: [
234
+ Signature.new(:cat, F.new(:complex)),
235
+ Signature.new(:cat, F.new(:p))
236
+ ]
237
+ },
238
+ sec: {
239
+ short_form: :sec,
240
+ long_form: :cellSecretion,
241
+ description: 'Denotes the frequency or abundance of events in which members of an abundance move from cells to regions outside of the cells',
242
+ return_type: :a,
243
+ signatures: [
244
+ Signature.new(:sec, F.new(:a))
245
+ ]
246
+ },
247
+ surf: {
248
+ short_form: :surf,
249
+ long_form: :cellSurfaceExpression,
250
+ description: 'Denotes the frequency or abundance of events in which members of an abundance move to the surface of cells',
251
+ return_type: :a,
252
+ signatures: [
253
+ Signature.new(:surf, F.new(:a))
254
+ ]
255
+ },
256
+ chap: {
257
+ short_form: :chap,
258
+ long_form: :chaperoneActivity,
259
+ description: 'Denotes the frequency or abundance of events in which a member binds to some substrate and acts as a chaperone for the substrate',
260
+ return_type: :a,
261
+ signatures: [
262
+ Signature.new(:chap, F.new(:complex)),
263
+ Signature.new(:chap, F.new(:p))
264
+ ]
265
+ },
266
+ complex: {
267
+ short_form: :complex,
268
+ long_form: :complexAbundance,
269
+ description: 'Denotes the abundance of a molecular complex',
270
+ return_type: :complex,
271
+ signatures: [
272
+ Signature.new(:complex, E.new(:A)),
273
+ Signature.new(:complex, F.new(:a, true))
274
+ ]
275
+ },
276
+ deg: {
277
+ short_form: :deg,
278
+ long_form: :degradation,
279
+ description: 'Denotes the frequency or abundance of events in which a member is degraded in some way such that it is no longer a member',
280
+ return_type: :a,
281
+ signatures: [
282
+ Signature.new(:deg, F.new(:a))
283
+ ]
284
+ },
285
+ fus: {
286
+ short_form: :fus,
287
+ long_form: :fusion,
288
+ description: 'Specifies the abundance of a protein translated from the fusion of a gene',
289
+ return_type: :fus,
290
+ signatures: [
291
+ Signature.new(:fus, E.new(:G)),
292
+ Signature.new(:fus, E.new(:G), E.new(:*), E.new(:*)),
293
+ Signature.new(:fus, E.new(:P)),
294
+ Signature.new(:fus, E.new(:P), E.new(:*), E.new(:*)),
295
+ Signature.new(:fus, E.new(:R)),
296
+ Signature.new(:fus, E.new(:R), E.new(:*), E.new(:*))
297
+ ]
298
+ },
299
+ g: {
300
+ short_form: :g,
301
+ long_form: :geneAbundance,
302
+ description: 'Denotes the abundance of a gene',
303
+ return_type: :g,
304
+ signatures: [
305
+ Signature.new(:g, E.new(:G)),
306
+ Signature.new(:g, E.new(:G), F.new(:fus))
307
+ ]
308
+ },
309
+ gtp: {
310
+ short_form: :gtp,
311
+ long_form: :gtpBoundActivity,
312
+ description: 'Denotes the frequency or abundance of events in which a member of a G-protein abundance is GTP-bound',
313
+ return_type: :a,
314
+ signatures: [
315
+ Signature.new(:gtp, F.new(:complex)),
316
+ Signature.new(:gtp, F.new(:p))
317
+ ]
318
+ },
319
+ kin: {
320
+ short_form: :kin,
321
+ long_form: :kinaseActivity,
322
+ description: 'Denotes the frequency or abundance of events in which a member acts as a kinase, performing enzymatic phosphorylation of a substrate',
323
+ return_type: :a,
324
+ signatures: [
325
+ Signature.new(:kin, F.new(:complex)),
326
+ Signature.new(:kin, F.new(:p))
327
+ ]
328
+ },
329
+ list: {
330
+ short_form: :list,
331
+ long_form: :list,
332
+ description: 'Groups a list of terms together',
333
+ return_type: :list,
334
+ signatures: [
335
+ Signature.new(:list, E.new(:A, true)),
336
+ Signature.new(:list, F.new(:a, true))
337
+ ]
338
+ },
339
+ m: {
340
+ short_form: :m,
341
+ long_form: :microRNAAbundance,
342
+ description: 'Denotes the abundance of a processed, functional microRNA',
343
+ return_type: :m,
344
+ signatures: [
345
+ Signature.new(:m, E.new(:M))
346
+ ]
347
+ },
348
+ act: {
349
+ short_form: :act,
350
+ long_form: :molecularActivity,
351
+ description: 'Denotes the frequency or abundance of events in which a member acts as a causal agent at the molecular scale',
352
+ return_type: :a,
353
+ signatures: [
354
+ Signature.new(:act, F.new(:a))
355
+ ]
356
+ },
357
+ path: {
358
+ short_form: :path,
359
+ long_form: :pathology,
360
+ description: 'Denotes a disease or pathology process',
361
+ return_type: :path,
362
+ signatures: [
363
+ Signature.new(:path, E.new(:O))
364
+ ]
365
+ },
366
+ pep: {
367
+ short_form: :pep,
368
+ long_form: :peptidaseActivity,
369
+ description: 'Denotes the frequency or abundance of events in which a member acts to cleave a protein',
370
+ return_type: :a,
371
+ signatures: [
372
+ Signature.new(:pep, F.new(:complex)),
373
+ Signature.new(:pep, F.new(:p))
374
+ ]
375
+ },
376
+ phos: {
377
+ short_form: :phos,
378
+ long_form: :phosphataseActivity,
379
+ description: 'Denotes the frequency or abundance of events in which a member acts as a phosphatase',
380
+ return_type: :a,
381
+ signatures: [
382
+ Signature.new(:phos, F.new(:complex)),
383
+ Signature.new(:phos, F.new(:p))
384
+ ]
385
+ },
386
+ products: {
387
+ short_form: :products,
388
+ long_form: :products,
389
+ description: 'Denotes the products of a reaction',
390
+ return_type: :products,
391
+ signatures: [
392
+ Signature.new(:products, F.new(:a))
393
+ ]
394
+ },
395
+ p: {
396
+ short_form: :p,
397
+ long_form: :proteinAbundance,
398
+ description: 'Denotes the abundance of a protein',
399
+ return_type: :p,
400
+ signatures: [
401
+ Signature.new(:p, E.new(:P)),
402
+ Signature.new(:p, E.new(:P), F.new(:pmod)),
403
+ Signature.new(:p, E.new(:P), F.new(:sub)),
404
+ Signature.new(:p, E.new(:P), F.new(:fus)),
405
+ Signature.new(:p, E.new(:P), F.new(:trunc))
406
+ ]
407
+ },
408
+ pmod: {
409
+ short_form: :pmod,
410
+ long_form: :proteinModification,
411
+ description: 'Denotes a covalently modified protein abundance',
412
+ return_type: :pmod,
413
+ signatures: [
414
+ Signature.new(:pmod, E.new(:*)),
415
+ Signature.new(:pmod, E.new(:*), E.new(:*)),
416
+ Signature.new(:pmod, E.new(:*), E.new(:*), E.new(:*))
417
+ ]
418
+ },
419
+ reactants: {
420
+ short_form: :reactants,
421
+ long_form: :reactants,
422
+ description: 'Denotes the reactants of a reaction',
423
+ return_type: :reactants,
424
+ signatures: [
425
+ Signature.new(:reactants, F.new(:a))
426
+ ]
427
+ },
428
+ rxn: {
429
+ short_form: :rxn,
430
+ long_form: :reaction,
431
+ description: 'Denotes the frequency or abundance of events in a reaction',
432
+ return_type: :a,
433
+ signatures: [
434
+ Signature.new(:rxn, F.new(:reactants), F.new(:products))
435
+ ]
436
+ },
437
+ ribo: {
438
+ short_form: :ribo,
439
+ long_form: :ribosylationActivity,
440
+ description: 'Denotes the frequency or abundance of events in which a member acts to perform post-translational modification of proteins',
441
+ return_type: :a,
442
+ signatures: [
443
+ Signature.new(:ribo, F.new(:complex)),
444
+ Signature.new(:ribo, F.new(:p))
445
+ ]
446
+ },
447
+ r: {
448
+ short_form: :r,
449
+ long_form: :rnaAbundance,
450
+ description: 'Denotes the abundance of a gene',
451
+ return_type: :g,
452
+ signatures: [
453
+ Signature.new(:r, E.new(:R)),
454
+ Signature.new(:r, E.new(:R), F.new(:fus))
455
+ ]
456
+ },
457
+ sub: {
458
+ short_form: :sub,
459
+ long_form: :substitution,
460
+ description: 'Indicates the abundance of proteins with amino acid substitution sequence',
461
+ return_type: :sub,
462
+ signatures: [
463
+ Signature.new(:sub, E.new(:*), E.new(:*), E.new(:*))
464
+ ]
465
+ },
466
+ tscript: {
467
+ short_form: :tscript,
468
+ long_form: :transcriptionalActivity,
469
+ description: 'Denotes the frequency or abundance of events in which a member directly acts to control transcription of genes',
470
+ return_type: :a,
471
+ signatures: [
472
+ Signature.new(:tscript, F.new(:complex)),
473
+ Signature.new(:tscript, F.new(:p))
474
+ ]
475
+ },
476
+ tloc: {
477
+ short_form: :tloc,
478
+ long_form: :translocation,
479
+ description: 'Denotes the frequency or abundance of events in which members move between locations',
480
+ return_type: :a,
481
+ signatures: [
482
+ Signature.new(:tloc, F.new(:a), E.new(:A), E.new(:A))
483
+ ]
484
+ },
485
+ tport: {
486
+ short_form: :tport,
487
+ long_form: :transportActivity,
488
+ description: 'Denotes the frequency or abundance of events in which a member directs acts to enable the directed movement of substances into, out of, within, or between cells',
489
+ return_type: :a,
490
+ signatures: [
491
+ Signature.new(:tport, F.new(:complex)),
492
+ Signature.new(:tport, F.new(:p))
493
+ ]
494
+ },
495
+ trunc: {
496
+ short_form: :trunc,
497
+ long_form: :truncation,
498
+ description: 'Indicates an abundance of proteins with truncation sequence variants',
499
+ return_type: :trunc,
500
+ signatures: [
501
+ Signature.new(:trunc, E.new(:*))
502
+ ]
503
+ }
504
+ }
505
+ FUNCTIONS.merge!(Hash[*FUNCTIONS.map {|_,v| [v[:long_form], v]}.flatten])
506
+
507
+ PARAMETER_ENCODING = {
508
+ B: [:B],
509
+ O: [:O, :B],
510
+ R: [:R, :A],
511
+ M: [:M, :R, :A],
512
+ P: [:P, :A],
513
+ G: [:G, :A],
514
+ A: [:A],
515
+ C: [:C, :A]
516
+ }
517
+
518
+ FUNCTION_TYPES = {
519
+ a: [:a],
520
+ bp: [:bp],
521
+ complex: [:complex, :a],
522
+ g: [:g, :a],
523
+ m: [:m, :r, :a],
524
+ path: [:path, :bp],
525
+ p: [:p, :a],
526
+ r: [:r, :a]
527
+ }
528
+
529
+ RELATIONSHIPS = [
530
+ :actsIn,
531
+ :analogous,
532
+ :association,
533
+ :biomarkerFor,
534
+ :causesNoChange,
535
+ :decreases,
536
+ :directlyDecreases,
537
+ :directlyIncreases,
538
+ :hasComponent, :hasComponents,
539
+ :hasMember, :hasMembers,
540
+ :hasModification,
541
+ :hasProduct,
542
+ :hasVariant,
543
+ :includes,
544
+ :increases,
545
+ :isA,
546
+ :negativeCorrelation,
547
+ :orthologous,
548
+ :positiveCorrelation,
549
+ :prognosticBiomarkerFor,
550
+ :rateLimitingStepOf,
551
+ :reactantIn,
552
+ :subProcessOf,
553
+ :transcribedTo,
554
+ :translatedTo,
555
+ :translocates
556
+ ]
557
+
558
+ RELATIONSHIPS.each do |rel|
559
+ Term.send(:define_method, rel) do |another|
560
+ s = Statement.new self
561
+ s.relationship = rel
562
+ s.object = another
563
+ s
564
+ end
565
+ end
566
+ FUNCTIONS.each do |fx, metadata|
567
+ func = Function.new(metadata)
568
+ Language.send(:define_method, fx) do |*args|
569
+ Term.new(func, *args)
570
+ end
571
+ Language.send(:define_method, metadata[:long_form]) do |*args|
572
+ Term.new(func, *args)
573
+ end
574
+ end
575
+ end
576
+ end