bel 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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