bel 0.2.1 → 0.3.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ require 'mkmf'
2
+
3
+ # if set in environment, set compiler for make
4
+ # example:
5
+ # CC=gcc-4.8 gem install bel.rb
6
+ RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
7
+
8
+ create_makefile('libbel')
data/ext/mri/libbel.c ADDED
@@ -0,0 +1,5 @@
1
+ void Init_libbel() {
2
+ // initialization for bel_ext extension
3
+ // empty; loaded using ffi
4
+ }
5
+
@@ -0,0 +1,26 @@
1
+ EXPORTS
2
+ Init_libbel
3
+ bel_ast_as_string
4
+ bel_copy_ast_node
5
+ bel_free_ast
6
+ bel_free_ast_node
7
+ bel_new_ast
8
+ bel_new_ast_node_token
9
+ bel_new_ast_node_value
10
+ bel_new_token
11
+ bel_new_token_iterator
12
+ bel_new_token_list
13
+ bel_parse_statement
14
+ bel_parse_term
15
+ bel_print_ast
16
+ bel_print_ast_node
17
+ bel_print_token
18
+ bel_print_token_list
19
+ bel_set_value
20
+ bel_token_iterator_end
21
+ bel_token_iterator_get
22
+ bel_token_iterator_next
23
+ bel_tokenize_term
24
+ free_bel_token
25
+ free_bel_token_iterator
26
+ free_bel_token_list
data/lib/bel.rb CHANGED
@@ -1,7 +1,17 @@
1
+ # Load core objects
2
+ require_relative 'libbel'
3
+ require_relative 'bel/completion'
4
+ require_relative 'bel/language'
5
+ require_relative 'bel/namespace'
6
+ include BEL::Language
7
+ include BEL::Namespace
8
+
1
9
  module BEL
2
- autoload :Language, "#{File.dirname(__FILE__)}/bel/language"
3
- autoload :Namespace, "#{File.dirname(__FILE__)}/bel/namespace"
4
10
  autoload :Script, "#{File.dirname(__FILE__)}/bel/script"
11
+ autoload :RDF, "#{File.dirname(__FILE__)}/bel/rdf"
12
+
13
+ require_relative './features.rb'
14
+ require_relative './util.rb'
5
15
  end
6
16
  # vim: ts=2 sw=2:
7
17
  # encoding: utf-8
@@ -0,0 +1,53 @@
1
+ require_relative '../libbel'
2
+ require_relative 'completion_rule'
3
+ require_relative 'language'
4
+ require_relative 'namespace'
5
+
6
+ module BEL
7
+ module Completion
8
+
9
+ # Provides completions on BEL expressions.
10
+ #
11
+ # If +bel_expression+ is +nil+ then its assumed to be the empty string
12
+ # otherwise the +to_s+ method is called. An empty +bel_expression+ will
13
+ # return all BEL functions as possible completions.
14
+ #
15
+ # If +search+ is +nil+ then namespace values will not be provided as
16
+ # completion. +search+ is expected to implement {IdentifierSearch}.
17
+ #
18
+ # If +position+ is +nil+ then its assumed to be the last index of
19
+ # +bel_expression+ otherwise the +to_i+ method is called.
20
+ #
21
+ # If +position+ is negative or greater than the length of +bel_expression+
22
+ # an +IndexError+ is raised.
23
+ #
24
+ # @param bel_expression [responds to #to_s] the bel expression to
25
+ # complete on
26
+ # @param search [IdentifierSearch] the search object used to
27
+ # provide namespace value completions
28
+ # @param position [responds to #to_i] the position to complete from
29
+ # @return [Array<Completion>]
30
+ def self.complete(bel_expression, search = nil, position = nil)
31
+ bel_expression = (bel_expression || '').to_s
32
+ position = (position || bel_expression.length).to_i
33
+ if position < 0 or position > bel_expression.length
34
+ msg = %Q{position #{position}, bel_expression "#{bel_expression}"}
35
+ fail IndexError, msg
36
+ end
37
+
38
+ token_list = LibBEL::tokenize_term(bel_expression)
39
+ active_token, active_index = token_list.token_at(position)
40
+
41
+ # no active token indicates the position is out of
42
+ # range of all tokens in the list.
43
+ return [] unless active_token
44
+
45
+ tokens = token_list.to_a
46
+ options = {
47
+ :search => search
48
+ }
49
+ BEL::Completion::run_rules(tokens, active_index, active_token, options)
50
+ end
51
+ end
52
+ end
53
+
@@ -0,0 +1,236 @@
1
+ require_relative 'language'
2
+ require_relative 'namespace'
3
+ require_relative 'quoting'
4
+ require 'uri'
5
+
6
+ module BEL
7
+ module Completion
8
+
9
+ SORTED_FUNCTIONS = BEL::Language::FUNCTIONS.keys.sort.map(&:to_s)
10
+ SORTED_NAMESPACES = BEL::Namespace::NAMESPACE_LATEST.keys.sort.map(&:to_s)
11
+ EMPTY_MATCH = []
12
+
13
+ def self.run_rules(tokens, active_index, active_token, options = {})
14
+ self.rules.reduce([]) { |completion_results, rule|
15
+ completion_results.concat(
16
+ rule.apply(tokens, active_token, active_index, options)
17
+ )
18
+ }
19
+ end
20
+
21
+ def self.rules
22
+ [
23
+ MatchFunctionRule.new,
24
+ MatchNamespacePrefixRule.new,
25
+ MatchNamespaceValueRule.new
26
+ ]
27
+ end
28
+
29
+ module Rule
30
+
31
+ def apply(token_list, active_token, active_token_index, options = {})
32
+ matches = _apply(token_list, active_token, active_token_index, options)
33
+
34
+ matches.map { |match|
35
+ match = map_highlight(match, active_token)
36
+ match = map_actions(match, active_token)
37
+ match.delete(:offset)
38
+ match
39
+ }
40
+ end
41
+
42
+ protected
43
+
44
+ def _apply(token_list, active_token, active_token_index, options = {})
45
+ raise NotImplementedError
46
+ end
47
+
48
+ def map_highlight(match, active_token)
49
+ if active_token and not [:O_PAREN, :COMMA].include?(active_token.type)
50
+ value_start = match[:value].downcase.index(active_token.value.downcase)
51
+ if value_start
52
+ value_end = value_start + active_token.value.length
53
+ highlight = {
54
+ :start_position => value_start,
55
+ :end_position => value_end,
56
+ :range_type => :inclusive
57
+ }
58
+ else
59
+ highlight = nil
60
+ end
61
+ else
62
+ highlight = nil
63
+ end
64
+ match.merge!({:highlight => highlight})
65
+ end
66
+
67
+ def map_actions(match, active_token)
68
+ position_start = active_token ? active_token.pos_start : 0
69
+ actions = []
70
+
71
+ if active_token and not [:O_PAREN, :COLON].include?(active_token.type)
72
+ # delete from start of active token to end of a
73
+ actions.push({
74
+ :delete => {
75
+ :start_position => position_start,
76
+ :end_position => active_token.pos_end - 1,
77
+ :range_type => :inclusive
78
+ }
79
+ })
80
+ end
81
+
82
+ # add the active_token length if we do not need to delete it
83
+ if active_token and actions.empty?
84
+ position_start += active_token.value.length
85
+ end
86
+
87
+ actions.concat([
88
+ {
89
+ :insert => {
90
+ :position => position_start,
91
+ :value => match[:value]
92
+ }
93
+ },
94
+ {
95
+ :move_cursor => {
96
+ :position => position_start + match[:value].length + match[:offset]
97
+ }
98
+ }
99
+ ])
100
+ match.merge!({:actions => actions})
101
+ end
102
+ end
103
+
104
+ class MatchFunctionRule
105
+ include Rule
106
+
107
+ def _apply(token_list, active_token, active_token_index, options = {})
108
+ if token_list.empty? or active_token.type == :O_PAREN
109
+ return SORTED_FUNCTIONS.map { |fx| map_function(fx) }.uniq.sort_by { |fx|
110
+ fx[:label]
111
+ }
112
+ end
113
+
114
+ if active_token.type == :IDENT
115
+ value = active_token.value.downcase
116
+ return SORTED_FUNCTIONS.find_all { |x|
117
+ x.downcase.include? value
118
+ }.map { |fx| map_function(fx) }.uniq.sort_by { |fx| fx[:label] }
119
+ end
120
+
121
+ return EMPTY_MATCH
122
+ end
123
+
124
+ protected
125
+
126
+ def map_function(fx_name)
127
+ fx = Function.new(FUNCTIONS[fx_name.to_sym])
128
+ if fx
129
+ {
130
+ :id => fx.short_form,
131
+ :type => :function,
132
+ :label => fx.long_form,
133
+ :value => "#{fx.short_form}()",
134
+ :offset => -1
135
+ }
136
+ else
137
+ nil
138
+ end
139
+ end
140
+ end
141
+
142
+ class MatchNamespacePrefixRule
143
+ include Rule
144
+
145
+ def _apply(token_list, active_token, active_token_index, options = {})
146
+ if token_list.empty? or active_token.type == :O_PAREN
147
+ return SORTED_NAMESPACES.map { |ns_prefix|
148
+ map_namespace_prefix(ns_prefix)
149
+ }
150
+ end
151
+
152
+ # first token is always function
153
+ return [] if active_token == token_list[0]
154
+
155
+ if active_token.type == :IDENT
156
+ value = active_token.value.downcase
157
+ return SORTED_NAMESPACES.find_all { |x|
158
+ x.downcase.include? value
159
+ }.map { |ns_prefix|
160
+ map_namespace_prefix(ns_prefix)
161
+ }
162
+ end
163
+
164
+ return EMPTY_MATCH
165
+ end
166
+
167
+ private
168
+
169
+ def map_namespace_prefix(ns_prefix)
170
+ {
171
+ :id => ns_prefix,
172
+ :type => :namespace_prefix,
173
+ :label => ns_prefix,
174
+ :value => "#{ns_prefix}:",
175
+ :offset => 0
176
+ }
177
+ end
178
+ end
179
+
180
+ class MatchNamespaceValueRule
181
+ include Rule
182
+ include BEL::Quoting
183
+
184
+ def _apply(token_list, active_token, active_token_index, options = {})
185
+ search = options.delete(:search)
186
+ return EMPTY_MATCH if not search or token_list.empty?
187
+
188
+ if active_token.type == :IDENT && active_token.value.length > 1
189
+ previous_token = token_list[active_token_index - 1]
190
+ if previous_token and previous_token.type == :COLON
191
+ # search within a namespace
192
+ prefix_token = token_list[active_token_index - 2]
193
+ if prefix_token and prefix_token.type == :IDENT
194
+ namespace = BEL::Namespace::NAMESPACE_LATEST[prefix_token.value.to_sym]
195
+ if namespace
196
+ scheme_uri = namespace[1]
197
+ return search.search_namespace(
198
+ URI(scheme_uri),
199
+ "#{active_token.value}*",
200
+ :start => 0,
201
+ :size => 10
202
+ ).
203
+ map { |search_result|
204
+ map_namespace_value(search_result.pref_label)
205
+ }.to_a
206
+ end
207
+ end
208
+ else
209
+ return search.search(
210
+ active_token.value,
211
+ :start => 0,
212
+ :size => 10
213
+ ).
214
+ map { |search_result|
215
+ map_namespace_value(search_result.pref_label)
216
+ }.to_a
217
+ end
218
+ end
219
+
220
+ return EMPTY_MATCH
221
+ end
222
+
223
+ protected
224
+
225
+ def map_namespace_value(ns_value)
226
+ {
227
+ :id => ns_value,
228
+ :type => :namespace_value,
229
+ :label => ns_value,
230
+ :value => ensure_quotes(ns_value),
231
+ :offset => 0
232
+ }
233
+ end
234
+ end
235
+ end
236
+ end
data/lib/bel/language.rb CHANGED
@@ -1,5 +1,305 @@
1
+ require_relative '../features'
2
+ require_relative 'quoting'
1
3
  module BEL
2
4
  module Language
5
+
6
+ class Newline
7
+ def to_bel
8
+ ""
9
+ end
10
+ alias_method :to_s, :to_bel
11
+ end
12
+ NEW_LINE = Newline.new
13
+
14
+ Comment = Struct.new(:text) do
15
+ def to_bel
16
+ %Q{##{self.text}}
17
+ end
18
+ alias_method :to_s, :to_bel
19
+ end
20
+
21
+ DocumentProperty = Struct.new(:name, :value) do
22
+ include BEL::Quoting
23
+
24
+ def to_bel
25
+ %Q{SET DOCUMENT #{self.name} = #{ensure_quotes(self.value)}}
26
+ end
27
+ alias_method :to_s, :to_bel
28
+ end
29
+
30
+ AnnotationDefinition = Struct.new(:type, :prefix, :value) do
31
+ def to_bel
32
+ case self.type
33
+ when :list
34
+ %Q{DEFINE ANNOTATION #{self.prefix} AS LIST {#{self.value.join(',')}}}
35
+ when :pattern
36
+ %Q{DEFINE ANNOTATION #{self.prefix} AS PATTERN "#{self.value}"}
37
+ when :url
38
+ %Q{DEFINE ANNOTATION #{self.prefix} AS URL "#{self.value}"}
39
+ end
40
+ end
41
+ alias_method :to_s, :to_bel
42
+ end
43
+
44
+ class Parameter
45
+ include BEL::Quoting
46
+ include Comparable
47
+ attr_accessor :ns, :value, :enc, :signature
48
+
49
+ def initialize(ns, value, enc=nil)
50
+ @ns = ns
51
+ @value = value
52
+ @enc = enc || ''
53
+ @signature = E.new(@enc)
54
+ end
55
+
56
+ def <=>(other)
57
+ ns_compare = @ns <=> other.ns
58
+ if ns_compare == 0
59
+ @value <=> other.value
60
+ else
61
+ ns_compare
62
+ end
63
+ end
64
+
65
+ def valid?
66
+ return false unless value
67
+ return true unless @ns
68
+ @ns.respond_to?(:values) && ns.values.include?(value)
69
+ end
70
+
71
+ def hash
72
+ [@ns, @value].hash
73
+ end
74
+
75
+ def ==(other)
76
+ return false if other == nil
77
+ @ns == other.ns && @value == other.value
78
+ end
79
+
80
+ alias_method :eql?, :'=='
81
+
82
+ def to_bel
83
+ %Q{#{@ns ? @ns.prefix.to_s + ':' : ''}#{ensure_quotes(@value)}}
84
+ end
85
+
86
+ alias_method :to_s, :to_bel
87
+ end
88
+
89
+ class Function
90
+ attr_reader :short_form, :long_form, :return_type,
91
+ :description, :signatures
92
+
93
+ def initialize args
94
+ args.each do |k,v|
95
+ instance_variable_set("@#{k}", v) unless v.nil?
96
+ end
97
+ end
98
+
99
+ def [](key)
100
+ instance_variable_get("@#{key}")
101
+ end
102
+
103
+ def hash
104
+ [@short_form, @long_form, @return_type, @description, @signatures].hash
105
+ end
106
+
107
+ def ==(other)
108
+ return false if other == nil
109
+ @short_form == other.short_form &&
110
+ @long_form == other.long_form &&
111
+ @return_type == other.return_type &&
112
+ @description == other.description &&
113
+ @signatures == other.signatures
114
+ end
115
+
116
+ alias_method :eql?, :'=='
117
+
118
+ def to_sym
119
+ @short_form
120
+ end
121
+
122
+ def to_s
123
+ @long_form.to_s
124
+ end
125
+ end
126
+
127
+ class Term
128
+ include Comparable
129
+ attr_accessor :fx, :arguments, :signature
130
+
131
+ def initialize(fx, *arguments)
132
+ @fx = case fx
133
+ when String
134
+ when Symbol
135
+ Function.new(FUNCTIONS[fx.to_sym])
136
+ when Function
137
+ fx
138
+ when nil
139
+ raise ArgumentError, 'fx must not be nil'
140
+ end
141
+ @arguments = (arguments ||= []).flatten
142
+ @signature = Signature.new(
143
+ @fx[:short_form],
144
+ *@arguments.map { |arg|
145
+ case arg
146
+ when Term
147
+ F.new(arg.fx.return_type)
148
+ when Parameter
149
+ E.new(arg.enc)
150
+ when nil
151
+ NullE.new
152
+ end
153
+ })
154
+ end
155
+
156
+ def <<(item)
157
+ @arguments << item
158
+ end
159
+
160
+ def valid?
161
+ invalid_signatures = @arguments.find_all { |arg|
162
+ arg.is_a? Term
163
+ }.find_all { |term|
164
+ not term.valid?
165
+ }
166
+ return false if not invalid_signatures.empty?
167
+
168
+ sigs = @fx.signatures
169
+ sigs.any? do |sig| (@signature <=> sig) >= 0 end
170
+ end
171
+
172
+ def valid_signatures
173
+ @fx.signatures.find_all { |sig| (@signature <=> sig) >= 0 }
174
+ end
175
+
176
+ def invalid_signatures
177
+ @fx.signatures.find_all { |sig| (@signature <=> sig) < 0 }
178
+ end
179
+
180
+ def hash
181
+ [@fx, @arguments].hash
182
+ end
183
+
184
+ def ==(other)
185
+ return false if other == nil
186
+ @fx == other.fx && @arguments == other.arguments
187
+ end
188
+
189
+ alias_method :eql?, :'=='
190
+
191
+ def to_bel
192
+ "#{@fx[:short_form]}(#{[@arguments].flatten.map(&:to_bel).join(',')})"
193
+ end
194
+
195
+ alias_method :to_s, :to_bel
196
+ end
197
+
198
+ Annotation = Struct.new(:name, :value) do
199
+ include BEL::Quoting
200
+
201
+ def to_bel
202
+ if self.value.respond_to? :each
203
+ value = self.value.map {|v| always_quote(v)}
204
+ value = "{#{value.join(',')}}"
205
+ else
206
+ value = ensure_quotes(self.value)
207
+ end
208
+ "SET #{self.name} = #{value}"
209
+ end
210
+
211
+ alias_method :to_s, :to_bel
212
+ end
213
+
214
+ UnsetAnnotation = Struct.new(:name) do
215
+ def to_bel
216
+ %Q{UNSET #{self.name}}
217
+ end
218
+
219
+ alias_method :to_s, :to_bel
220
+ end
221
+
222
+ class Statement
223
+ attr_accessor :subject, :relationship, :object, :annotations, :comment
224
+
225
+ def initialize(subject=nil, relationship=nil, object=nil, annotations=[], comment=nil)
226
+ @subject = subject
227
+ @relationship = relationship
228
+ @object = object
229
+ @annotations = annotations
230
+ @comment = comment
231
+ end
232
+
233
+ def subject_only?
234
+ !@relationship
235
+ end
236
+
237
+ def simple?
238
+ @object and @object.is_a? Term
239
+ end
240
+
241
+ def nested?
242
+ @object and @object.is_a? Statement
243
+ end
244
+
245
+ def hash
246
+ [@subject, @relationship, @object, @annotations, @comment].hash
247
+ end
248
+
249
+ def ==(other)
250
+ return false if other == nil
251
+ @subject == other.subject &&
252
+ @relationship == other.relationship &&
253
+ @object == other.object &&
254
+ @annotations == other.annotations &&
255
+ @comment == comment
256
+ end
257
+
258
+ alias_method :eql?, :'=='
259
+
260
+ def to_bel
261
+ lbl = case
262
+ when subject_only?
263
+ @subject.to_s
264
+ when simple?
265
+ "#{@subject.to_s} #{@relationship} #{@object.to_s}"
266
+ when nested?
267
+ "#{@subject.to_s} #{@relationship} (#{@object.to_s})"
268
+ else
269
+ ''
270
+ end
271
+ comment ? lbl + ' //' + comment : lbl
272
+ end
273
+
274
+ alias_method :to_s, :to_bel
275
+ end
276
+
277
+ StatementGroup = Struct.new(:name, :statements, :annotations) do
278
+ include BEL::Quoting
279
+
280
+ def <=>(other_group)
281
+ if not other_group || other_group.is_a?
282
+ 1
283
+ else
284
+ (statements || []) <=> (other_group.statements || [])
285
+ end
286
+ end
287
+
288
+ def to_bel
289
+ %Q{SET STATEMENT_GROUP = #{ensure_quotes(self.name)}}
290
+ end
291
+
292
+ alias_method :to_s, :to_bel
293
+ end
294
+
295
+ UnsetStatementGroup = Struct.new(:name) do
296
+ def to_bel
297
+ %Q{UNSET STATEMENT_GROUP}
298
+ end
299
+
300
+ alias_method :to_s, :to_bel
301
+ end
302
+
3
303
  class Signature
4
304
  attr_reader :fx, :arguments
5
305
  def initialize(fx, *arguments)
@@ -18,8 +318,8 @@ module BEL
18
318
  end
19
319
 
20
320
  def ==(other)
21
- return false if @fx != other.fx
22
- @arguments == other.arguments
321
+ return false if other == nil
322
+ @fx == other.fx && @arguments == other.arguments
23
323
  end
24
324
 
25
325
  def <=>(other)
@@ -44,8 +344,8 @@ module BEL
44
344
  end
45
345
 
46
346
  def ==(other)
347
+ return false if other == nil
47
348
  return false if not other.respond_to? :func_return
48
-
49
349
  @func_return == other.func_return and @var == other.var
50
350
  end
51
351
 
@@ -110,100 +410,6 @@ module BEL
110
410
  end
111
411
  end
112
412
 
113
- class Parameter
114
- include Comparable
115
- attr_reader :ns_def, :value, :enc, :signature
116
-
117
- def initialize(ns_def, value, enc)
118
- @ns_def = ns_def
119
- @value = value
120
- @enc = enc || ''
121
- @signature = E.new(@enc)
122
- end
123
-
124
- def <=>(other)
125
- ns_compare = ns_def <=> other.ns_def
126
- if ns_compare == 0
127
- value <=> other.value
128
- else
129
- ns_compare
130
- end
131
- end
132
-
133
- def to_s
134
- value = @value
135
- if value =~ %r{\W}
136
- value = %Q{"#{value}"}
137
- end
138
- "#{@ns_def}:#{value}"
139
- end
140
- end
141
-
142
- class Statement
143
- attr_accessor :subject, :relationship, :object
144
-
145
- def initialize(subject, relationship=nil, object=nil)
146
- raise ArgumentError, 'subject must not be nil' unless subject
147
- @subject = subject
148
- @relationship = relationship
149
- @object = object
150
- end
151
- end
152
-
153
- class Function
154
- attr_reader :short_form, :long_form, :return_type,
155
- :description, :signatures
156
-
157
- def initialize args
158
- args.each do |k,v|
159
- instance_variable_set("@#{k}", v) unless v.nil?
160
- end
161
- end
162
- end
163
-
164
- class Term
165
- include Comparable
166
- attr_reader :fx, :arguments, :signature
167
-
168
- def initialize(fx, *arguments)
169
- @fx = fx
170
- @arguments = arguments ||= []
171
- @signature = Signature.new(
172
- @fx.short_form,
173
- *@arguments.map { |arg|
174
- case arg
175
- when Term
176
- F.new(arg.fx.return_type)
177
- when Parameter
178
- E.new(arg.enc)
179
- when nil
180
- NullE.new
181
- end
182
- })
183
- end
184
-
185
- def validate_signature
186
- invalids = []
187
- @arguments.select { |arg| Term === arg }.each do |term|
188
- invalids << term if not term.validate_signature
189
- end
190
-
191
- sigs = fx.signatures
192
- match = sigs.any? do |sig| (@signature <=> sig) >= 0 end
193
- invalids << self if not match
194
- if block_given? and not invalids.empty?
195
- invalids.each do |term|
196
- yield term, term.fx.signatures
197
- end
198
- end
199
- invalids.empty?
200
- end
201
-
202
- def to_s
203
- "#{@fx.short_form}(#{@arguments.map(&:to_s).join(',')})"
204
- end
205
- end
206
-
207
413
  FUNCTIONS = {
208
414
  a: {
209
415
  short_form: :a,
@@ -271,6 +477,15 @@ module BEL
271
477
  Signature.new(:complex, F.new(:a, true))
272
478
  ]
273
479
  },
480
+ composite: {
481
+ short_form: :composite,
482
+ long_form: :compositeAbundance,
483
+ description: 'Denotes the frequency or abundance of events in which members are present',
484
+ return_type: :a,
485
+ signatures: [
486
+ Signature.new(:composite, F.new(:a, true))
487
+ ]
488
+ },
274
489
  deg: {
275
490
  short_form: :deg,
276
491
  long_form: :degradation,
@@ -570,6 +785,267 @@ module BEL
570
785
  Term.new(func, *args)
571
786
  end
572
787
  end
788
+
789
+ if BEL::Features.rdf_support?
790
+ require_relative 'rdf'
791
+
792
+ class Parameter
793
+ def to_uri
794
+ @ns.to_rdf_vocabulary[@value]
795
+ end
796
+
797
+ def to_rdf
798
+ uri = to_uri
799
+ char_enum = @enc.to_s.each_char
800
+ if block_given?
801
+ char_enum.map {|c| concept_statement(c, uri) }.each do |stmt|
802
+ yield stmt
803
+ end
804
+ else
805
+ char_enum.map { |c| concept_statement(c, uri)}
806
+ end
807
+ end
808
+
809
+ private
810
+ def concept_statement(encoding_character, uri)
811
+ case encoding_character
812
+ when 'G'
813
+ RUBYRDF::Statement(uri, RUBYRDF.type, BEL::RDF::BELV.GeneConcept)
814
+ when 'R'
815
+ RUBYRDF::Statement(uri, RUBYRDF.type, BEL::RDF::BELV.RNAConcept)
816
+ when 'P'
817
+ RUBYRDF::Statement(uri, RUBYRDF.type, BEL::RDF::BELV.ProteinConcept)
818
+ when 'M'
819
+ RUBYRDF::Statement(uri, RUBYRDF.type, BEL::RDF::BELV.MicroRNAConcept)
820
+ when 'C'
821
+ RUBYRDF::Statement(uri, RUBYRDF.type, BEL::RDF::BELV.ComplexConcept)
822
+ when 'B'
823
+ RUBYRDF::Statement(uri, RUBYRDF.type, BEL::RDF::BELV.BiologicalProcessConcept)
824
+ when 'A'
825
+ RUBYRDF::Statement(uri, RUBYRDF.type, BEL::RDF::BELV.AbundanceConcept)
826
+ when 'O'
827
+ RUBYRDF::Statement(uri, RUBYRDF.type, BEL::RDF::BELV.PathologyConcept)
828
+ end
829
+ end
830
+ end
831
+
832
+ class Term
833
+ def to_uri
834
+ tid = to_s.squeeze(')').gsub(/[")\[\]]/, '').gsub(/[(:, ]/, '_')
835
+ BEL::RDF::BELR[tid]
836
+ end
837
+
838
+ def rdf_type
839
+ if respond_to? 'fx'
840
+ if @fx.short_form == :p and @arguments.find{|x| x.is_a? Term and x.fx.short_form == :pmod}
841
+ return BEL::RDF::BELV.ModifiedProteinAbundance
842
+ end
843
+ if @fx.short_form == :p and @arguments.find{|x| x.is_a? Term and BEL::RDF::PROTEIN_VARIANT.include? x.fx}
844
+ return BEL::RDF::BELV.ProteinVariantAbundance
845
+ end
846
+
847
+ BEL::RDF::FUNCTION_TYPE[@fx.short_form] || BEL::RDF::BELV.Abundance
848
+ end
849
+ end
850
+
851
+ def to_rdf
852
+ uri = to_uri
853
+ statements = []
854
+
855
+ # rdf:type
856
+ type = rdf_type
857
+ statements << [uri, BEL::RDF::RDF.type, BEL::RDF::BELV.Term]
858
+ statements << [uri, BEL::RDF::RDF.type, type]
859
+ if BEL::RDF::ACTIVITY_TYPE.include? @fx.short_form
860
+ statements << [uri, BEL::RDF::BELV.hasActivityType, BEL::RDF::ACTIVITY_TYPE[@fx.short_form]]
861
+ end
862
+
863
+ # rdfs:label
864
+ statements << [uri, BEL::RDF::RDFS.label, to_s]
865
+
866
+ # special proteins (does not recurse into pmod)
867
+ if @fx.short_form == :p
868
+ if @arguments.find{|x| x.is_a? Term and x.fx.short_form == :pmod}
869
+ pmod = @arguments.find{|x| x.is_a? Term and x.fx.short_form == :pmod}
870
+ mod_string = pmod.arguments.map(&:to_s).join(',')
871
+ mod_type = BEL::RDF::MODIFICATION_TYPE.find {|k,v| mod_string.start_with? k}
872
+ mod_type = (mod_type ? mod_type[1] : BEL::RDF::BELV.Modification)
873
+ statements << [uri, BEL::RDF::BELV.hasModificationType, mod_type]
874
+ last = pmod.arguments.last.to_s
875
+ if last.match(/^\d+$/)
876
+ statements << [uri, BEL::RDF::BELV.hasModificationPosition, last.to_i]
877
+ end
878
+ # link root protein abundance as hasChild
879
+ root_param = @arguments.find{|x| x.is_a? Parameter}
880
+ (root_id, root_statements) = Term.new(:p, [root_param]).to_rdf
881
+ statements << [uri, BEL::RDF::BELV.hasChild, root_id]
882
+ statements += root_statements
883
+ return [uri, statements]
884
+ elsif @arguments.find{|x| x.is_a? Term and BEL::RDF::PROTEIN_VARIANT.include? x.fx}
885
+ # link root protein abundance as hasChild
886
+ root_param = @arguments.find{|x| x.is_a? Parameter}
887
+ (root_id, root_statements) = Term.new(:p, [root_param]).to_rdf
888
+ statements << [uri, BEL::RDF::BELV.hasChild, root_id]
889
+ statements += root_statements
890
+ return [uri, statements]
891
+ end
892
+ end
893
+
894
+ # BEL::RDF::BELV.hasConcept]
895
+ @arguments.find_all{ |x|
896
+ x.is_a? Parameter and x.ns != nil
897
+ }.each do |param|
898
+ concept_uri = param.ns.to_rdf_vocabulary[param.value.to_s]
899
+ statements << [uri, BEL::RDF::BELV.hasConcept, BEL::RDF::RDF::URI(Addressable::URI.encode(concept_uri))]
900
+ end
901
+
902
+ # BEL::RDF::BELV.hasChild]
903
+ @arguments.find_all{|x| x.is_a? Term}.each do |child|
904
+ (child_id, child_statements) = child.to_rdf
905
+ statements << [uri, BEL::RDF::BELV.hasChild, child_id]
906
+ statements += child_statements
907
+ end
908
+
909
+ return [uri, statements]
910
+ end
911
+ end
912
+
913
+ class Statement
914
+ def to_uri
915
+ case
916
+ when subject_only?
917
+ tid = @subject.to_s.squeeze(')').gsub(/[")\[\]]/, '').gsub(/[(:, ]/, '_')
918
+ BEL::RDF::BELR[tid]
919
+ when simple?
920
+ sub_id = @subject.to_s.squeeze(')').gsub(/[")\[\]]/, '').gsub(/[(:, ]/, '_')
921
+ obj_id = @object.to_s.squeeze(')').gsub(/[")\[\]]/, '').gsub(/[(:, ]/, '_')
922
+ rel = BEL::RDF::RELATIONSHIP_TYPE[@relationship.to_s]
923
+ if rel
924
+ rel = rel.path.split('/')[-1]
925
+ else
926
+ rel = @relationship.to_s
927
+ end
928
+ BEL::RDF::BELR["#{sub_id}_#{rel}_#{obj_id}"]
929
+ when nested?
930
+ sub_id = @subject.to_s.squeeze(')').gsub(/[")\[\]]/, '').gsub(/[(:, ]/, '_')
931
+ nsub_id = @object.subject.to_s.squeeze(')').gsub(/[")\[\]]/, '').gsub(/[(:, ]/, '_')
932
+ nobj_id = @object.object.to_s.squeeze(')').gsub(/[")\[\]]/, '').gsub(/[(:, ]/, '_')
933
+ rel = BEL::RDF::RELATIONSHIP_TYPE[@relationship.to_s]
934
+ if rel
935
+ rel = rel.path.split('/')[-1]
936
+ else
937
+ rel = @relationship.to_s
938
+ end
939
+ nrel = BEL::RDF::RELATIONSHIP_TYPE[@object.relationship.to_s]
940
+ if nrel
941
+ nrel = nrel.path.split('/')[-1]
942
+ else
943
+ nrel = @object.relationship.to_s
944
+ end
945
+ BEL::RDF::BELR["#{sub_id}_#{rel}_#{nsub_id}_#{nrel}_#{nobj_id}"]
946
+ end
947
+ end
948
+
949
+ def to_rdf
950
+ uri = to_uri
951
+ statements = []
952
+
953
+ case
954
+ when subject_only?
955
+ (sub_uri, sub_statements) = @subject.to_rdf
956
+ statements << [uri, BEL::RDF::BELV.hasSubject, sub_uri]
957
+ statements += sub_statements
958
+ when simple?
959
+ (sub_uri, sub_statements) = @subject.to_rdf
960
+ statements += sub_statements
961
+
962
+ (obj_uri, obj_statements) = @object.to_rdf
963
+ statements += obj_statements
964
+
965
+ rel = BEL::RDF::RELATIONSHIP_TYPE[@relationship.to_s]
966
+ statements << [uri, BEL::RDF::BELV.hasSubject, sub_uri]
967
+ statements << [uri, BEL::RDF::BELV.hasObject, obj_uri]
968
+ statements << [uri, BEL::RDF::BELV.hasRelationship, rel]
969
+ when nested?
970
+ (sub_uri, sub_statements) = @subject.to_rdf
971
+ (nsub_uri, nsub_statements) = @object.subject.to_rdf
972
+ (nobj_uri, nobj_statements) = @object.object.to_rdf
973
+ statements += sub_statements
974
+ statements += nsub_statements
975
+ statements += nobj_statements
976
+ rel = BEL::RDF::RELATIONSHIP_TYPE[@relationship.to_s]
977
+ nrel = BEL::RDF::RELATIONSHIP_TYPE[@object.relationship.to_s]
978
+ nuri = BEL::RDF::BELR["#{strip_prefix(nsub_uri)}_#{nrel}_#{strip_prefix(nobj_uri)}"]
979
+
980
+ # inner
981
+ statements << [nuri, BEL::RDF::BELV.hasSubject, nsub_uri]
982
+ statements << [nuri, BEL::RDF::BELV.hasObject, nobj_uri]
983
+ statements << [nuri, BEL::RDF::BELV.hasRelationship, nrel]
984
+
985
+ # outer
986
+ statements << [uri, BEL::RDF::BELV.hasSubject, sub_uri]
987
+ statements << [uri, BEL::RDF::BELV.hasObject, nuri]
988
+ statements << [uri, BEL::RDF::BELV.hasRelationship, rel]
989
+ end
990
+
991
+ # common statement triples
992
+ statements << [uri, BEL::RDF::RDF.type, BEL::RDF::BELV.Statement]
993
+ statements << [uri, RDF::RDFS.label, to_s]
994
+
995
+ # evidence
996
+ evidence_bnode = BEL::RDF::RDF::Node.uuid
997
+ statements << [evidence_bnode, BEL::RDF::RDF.type, BEL::RDF::BELV.Evidence]
998
+ statements << [uri, BEL::RDF::BELV.hasEvidence, evidence_bnode]
999
+ statements << [evidence_bnode, BEL::RDF::BELV.hasStatement, uri]
1000
+
1001
+ # citation
1002
+ citation = @annotations.delete('Citation')
1003
+ if citation
1004
+ value = citation.value.map{|x| x.gsub('"', '')}
1005
+ if citation and value[0] == 'PubMed'
1006
+ pid = value[2]
1007
+ statements << [
1008
+ evidence_bnode,
1009
+ BEL::RDF::BELV.hasCitation,
1010
+ BEL::RDF::RDF::URI(BEL::RDF::PUBMED[pid])
1011
+ ]
1012
+ end
1013
+ end
1014
+
1015
+ # evidence
1016
+ evidence_text = @annotations.delete('Evidence')
1017
+ if evidence_text
1018
+ value = evidence_text.value.gsub('"', '')
1019
+ statements << [evidence_bnode, BEL::RDF::BELV.hasEvidenceText, value]
1020
+ end
1021
+
1022
+ # annotations
1023
+ @annotations.each do |name, anno|
1024
+ name = anno.name.gsub('"', '')
1025
+
1026
+ if BEL::RDF::const_defined? name
1027
+ annotation_scheme = BEL::RDF::const_get name
1028
+ [anno.value].flatten.map{|x| x.gsub('"', '')}.each do |val|
1029
+ value_uri = BEL::RDF::RDF::URI(Addressable::URI.encode(annotation_scheme[val.to_s]))
1030
+ statements << [evidence_bnode, BEL::RDF::BELV.hasAnnotation, value_uri]
1031
+ end
1032
+ end
1033
+ end
1034
+
1035
+ return [uri, statements]
1036
+ end
1037
+
1038
+ private
1039
+
1040
+ def strip_prefix(uri)
1041
+ if uri.to_s.start_with? 'http://www.openbel.org/bel/'
1042
+ uri.to_s[28..-1]
1043
+ else
1044
+ uri
1045
+ end
1046
+ end
1047
+ end
1048
+ end
573
1049
  end
574
1050
  end
575
1051
  # vim: ts=2 sw=2: