bel 0.3.0.beta1-x64-mingw32
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.
- data/INSTALL.md +19 -0
- data/INSTALL_RUBY.md +107 -0
- data/LICENSE +191 -0
- data/README.md +319 -0
- data/bel.gemspec +67 -0
- data/bin/bel2rdf +134 -0
- data/bin/bel_compare +177 -0
- data/bin/bel_parse +60 -0
- data/bin/bel_rdfschema +72 -0
- data/bin/bel_summarize +86 -0
- data/bin/bel_upgrade +175 -0
- data/bin/bel_upgrade_term +163 -0
- data/ext/mri/bel-ast.c +221 -0
- data/ext/mri/bel-ast.h +82 -0
- data/ext/mri/bel-node-stack.c +83 -0
- data/ext/mri/bel-node-stack.h +26 -0
- data/ext/mri/bel-parse-statement.c +122296 -0
- data/ext/mri/bel-parse-term.c +117670 -0
- data/ext/mri/bel-parser.c +91 -0
- data/ext/mri/bel-parser.h +13 -0
- data/ext/mri/bel-token.c +161 -0
- data/ext/mri/bel-token.h +58 -0
- data/ext/mri/bel-tokenize-term.c +391 -0
- data/ext/mri/extconf.rb +8 -0
- data/ext/mri/libbel.c +5 -0
- data/ext/mri/libbel.def +26 -0
- data/lib/bel.rb +17 -0
- data/lib/bel/completion.rb +53 -0
- data/lib/bel/completion_rule.rb +236 -0
- data/lib/bel/language.rb +1052 -0
- data/lib/bel/namespace.rb +323 -0
- data/lib/bel/quoting.rb +29 -0
- data/lib/bel/rdf.rb +314 -0
- data/lib/bel/script.rb +239632 -0
- data/lib/features.rb +21 -0
- data/lib/libbel.rb +201 -0
- data/lib/libbel.so +0 -0
- data/lib/util.rb +125 -0
- metadata +269 -0
data/ext/mri/extconf.rb
ADDED
data/ext/mri/libbel.c
ADDED
data/ext/mri/libbel.def
ADDED
@@ -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
ADDED
@@ -0,0 +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
|
+
|
9
|
+
module BEL
|
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'
|
15
|
+
end
|
16
|
+
# vim: ts=2 sw=2:
|
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
ADDED
@@ -0,0 +1,1052 @@
|
|
1
|
+
require_relative '../features'
|
2
|
+
require_relative 'quoting'
|
3
|
+
module BEL
|
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
|
+
|
303
|
+
class Signature
|
304
|
+
attr_reader :fx, :arguments
|
305
|
+
def initialize(fx, *arguments)
|
306
|
+
@fx = fx
|
307
|
+
@arguments = arguments
|
308
|
+
|
309
|
+
dup_hash = {}
|
310
|
+
@arguments.each_with_index { |arg, i| (dup_hash[arg] ||= []) << i }
|
311
|
+
dup_hash.keep_if { |k, v| v.length > 1 }.values.each do |v|
|
312
|
+
first_arg = @arguments[v[0]]
|
313
|
+
if F === first_arg
|
314
|
+
replace_range = (v.first..v.last)
|
315
|
+
@arguments[replace_range] = F.new(first_arg.func_return, true)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def ==(other)
|
321
|
+
return false if other == nil
|
322
|
+
@fx == other.fx && @arguments == other.arguments
|
323
|
+
end
|
324
|
+
|
325
|
+
def <=>(other)
|
326
|
+
return 1 if other.nil?
|
327
|
+
return -1 if @fx != other.fx
|
328
|
+
return -1 if @arguments.nil? and not other.nil?
|
329
|
+
return 1 if not @arguments.nil? and other.nil?
|
330
|
+
@arguments <=> other.arguments
|
331
|
+
end
|
332
|
+
|
333
|
+
def to_s
|
334
|
+
return_type = FUNCTIONS[@fx][:return_type]
|
335
|
+
"#{@fx}(#{@arguments.map(&:to_s) * ','})#{return_type}"
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
class F
|
340
|
+
attr_reader :func_return, :var
|
341
|
+
def initialize(func_return, var=false)
|
342
|
+
@func_return = func_return
|
343
|
+
@var = var
|
344
|
+
end
|
345
|
+
|
346
|
+
def ==(other)
|
347
|
+
return false if other == nil
|
348
|
+
return false if not other.respond_to? :func_return
|
349
|
+
@func_return == other.func_return and @var == other.var
|
350
|
+
end
|
351
|
+
|
352
|
+
alias_method :eql?, :==
|
353
|
+
|
354
|
+
def hash
|
355
|
+
[@func_return, @var].hash
|
356
|
+
end
|
357
|
+
|
358
|
+
def <=>(other)
|
359
|
+
return 1 if @var ^ other.var
|
360
|
+
|
361
|
+
tree = FUNCTION_TYPES[@func_return]
|
362
|
+
return -1 if not tree.include?(other.func_return)
|
363
|
+
-(tree.index(@func_return) <=> tree.index(other.func_return))
|
364
|
+
end
|
365
|
+
|
366
|
+
def to_s
|
367
|
+
"F:#{@func_return}#{'...' if @var}"
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
class E
|
372
|
+
attr_reader :encoding, :var
|
373
|
+
def initialize(encoding, var=false)
|
374
|
+
@encoding = encoding
|
375
|
+
@var = var
|
376
|
+
end
|
377
|
+
|
378
|
+
def <=>(other)
|
379
|
+
return 1 if @var ^ other.var
|
380
|
+
|
381
|
+
# compare for equals and wildcard case
|
382
|
+
cmp = @encoding <=> other.encoding
|
383
|
+
return cmp if cmp.zero? or (@encoding == :* or other.encoding == :*)
|
384
|
+
|
385
|
+
# compare encoding for assignability; based on array index
|
386
|
+
@encoding.to_s.each_char do |enc_char|
|
387
|
+
enc_sym = enc_char.to_sym
|
388
|
+
tree = PARAMETER_ENCODING[enc_sym]
|
389
|
+
next if not tree.include?(other.encoding)
|
390
|
+
|
391
|
+
match = -(tree.index(enc_sym) <=> tree.index(other.encoding))
|
392
|
+
return match if match >= 0
|
393
|
+
end
|
394
|
+
-1
|
395
|
+
end
|
396
|
+
|
397
|
+
def to_s
|
398
|
+
"E:#{@encoding}"
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
class NullE
|
403
|
+
def <=>(other)
|
404
|
+
return 0 if NullE === other
|
405
|
+
-1
|
406
|
+
end
|
407
|
+
|
408
|
+
def to_s
|
409
|
+
"E:nil"
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
FUNCTIONS = {
|
414
|
+
a: {
|
415
|
+
short_form: :a,
|
416
|
+
long_form: :abundance,
|
417
|
+
description: 'Denotes the abundance of an entity',
|
418
|
+
return_type: :a,
|
419
|
+
signatures: [
|
420
|
+
Signature.new(:a, E.new(:A))
|
421
|
+
]
|
422
|
+
},
|
423
|
+
bp: {
|
424
|
+
short_form: :bp,
|
425
|
+
long_form: :biologicalProcess,
|
426
|
+
description: 'Denotes a process or population of events',
|
427
|
+
return_type: :bp,
|
428
|
+
signatures: [
|
429
|
+
Signature.new(:bp, E.new(:B))
|
430
|
+
]
|
431
|
+
},
|
432
|
+
cat: {
|
433
|
+
short_form: :cat,
|
434
|
+
long_form: :catalyticActivity,
|
435
|
+
description: 'Denotes the frequency or abundance of events where a member acts as an enzymatic catalyst of biochecmial reactions',
|
436
|
+
return_type: :a,
|
437
|
+
signatures: [
|
438
|
+
Signature.new(:cat, F.new(:complex)),
|
439
|
+
Signature.new(:cat, F.new(:p))
|
440
|
+
]
|
441
|
+
},
|
442
|
+
sec: {
|
443
|
+
short_form: :sec,
|
444
|
+
long_form: :cellSecretion,
|
445
|
+
description: 'Denotes the frequency or abundance of events in which members of an abundance move from cells to regions outside of the cells',
|
446
|
+
return_type: :a,
|
447
|
+
signatures: [
|
448
|
+
Signature.new(:sec, F.new(:a))
|
449
|
+
]
|
450
|
+
},
|
451
|
+
surf: {
|
452
|
+
short_form: :surf,
|
453
|
+
long_form: :cellSurfaceExpression,
|
454
|
+
description: 'Denotes the frequency or abundance of events in which members of an abundance move to the surface of cells',
|
455
|
+
return_type: :a,
|
456
|
+
signatures: [
|
457
|
+
Signature.new(:surf, F.new(:a))
|
458
|
+
]
|
459
|
+
},
|
460
|
+
chap: {
|
461
|
+
short_form: :chap,
|
462
|
+
long_form: :chaperoneActivity,
|
463
|
+
description: 'Denotes the frequency or abundance of events in which a member binds to some substrate and acts as a chaperone for the substrate',
|
464
|
+
return_type: :a,
|
465
|
+
signatures: [
|
466
|
+
Signature.new(:chap, F.new(:complex)),
|
467
|
+
Signature.new(:chap, F.new(:p))
|
468
|
+
]
|
469
|
+
},
|
470
|
+
complex: {
|
471
|
+
short_form: :complex,
|
472
|
+
long_form: :complexAbundance,
|
473
|
+
description: 'Denotes the abundance of a molecular complex',
|
474
|
+
return_type: :complex,
|
475
|
+
signatures: [
|
476
|
+
Signature.new(:complex, E.new(:A)),
|
477
|
+
Signature.new(:complex, F.new(:a, true))
|
478
|
+
]
|
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
|
+
},
|
489
|
+
deg: {
|
490
|
+
short_form: :deg,
|
491
|
+
long_form: :degradation,
|
492
|
+
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',
|
493
|
+
return_type: :a,
|
494
|
+
signatures: [
|
495
|
+
Signature.new(:deg, F.new(:a))
|
496
|
+
]
|
497
|
+
},
|
498
|
+
fus: {
|
499
|
+
short_form: :fus,
|
500
|
+
long_form: :fusion,
|
501
|
+
description: 'Specifies the abundance of a protein translated from the fusion of a gene',
|
502
|
+
return_type: :fus,
|
503
|
+
signatures: [
|
504
|
+
Signature.new(:fus, E.new(:G)),
|
505
|
+
Signature.new(:fus, E.new(:G), E.new(:*), E.new(:*)),
|
506
|
+
Signature.new(:fus, E.new(:P)),
|
507
|
+
Signature.new(:fus, E.new(:P), E.new(:*), E.new(:*)),
|
508
|
+
Signature.new(:fus, E.new(:R)),
|
509
|
+
Signature.new(:fus, E.new(:R), E.new(:*), E.new(:*))
|
510
|
+
]
|
511
|
+
},
|
512
|
+
g: {
|
513
|
+
short_form: :g,
|
514
|
+
long_form: :geneAbundance,
|
515
|
+
description: 'Denotes the abundance of a gene',
|
516
|
+
return_type: :g,
|
517
|
+
signatures: [
|
518
|
+
Signature.new(:g, E.new(:G)),
|
519
|
+
Signature.new(:g, E.new(:G), F.new(:fus))
|
520
|
+
]
|
521
|
+
},
|
522
|
+
gtp: {
|
523
|
+
short_form: :gtp,
|
524
|
+
long_form: :gtpBoundActivity,
|
525
|
+
description: 'Denotes the frequency or abundance of events in which a member of a G-protein abundance is GTP-bound',
|
526
|
+
return_type: :a,
|
527
|
+
signatures: [
|
528
|
+
Signature.new(:gtp, F.new(:complex)),
|
529
|
+
Signature.new(:gtp, F.new(:p))
|
530
|
+
]
|
531
|
+
},
|
532
|
+
kin: {
|
533
|
+
short_form: :kin,
|
534
|
+
long_form: :kinaseActivity,
|
535
|
+
description: 'Denotes the frequency or abundance of events in which a member acts as a kinase, performing enzymatic phosphorylation of a substrate',
|
536
|
+
return_type: :a,
|
537
|
+
signatures: [
|
538
|
+
Signature.new(:kin, F.new(:complex)),
|
539
|
+
Signature.new(:kin, F.new(:p))
|
540
|
+
]
|
541
|
+
},
|
542
|
+
list: {
|
543
|
+
short_form: :list,
|
544
|
+
long_form: :list,
|
545
|
+
description: 'Groups a list of terms together',
|
546
|
+
return_type: :list,
|
547
|
+
signatures: [
|
548
|
+
Signature.new(:list, E.new(:A, true)),
|
549
|
+
Signature.new(:list, F.new(:a, true))
|
550
|
+
]
|
551
|
+
},
|
552
|
+
m: {
|
553
|
+
short_form: :m,
|
554
|
+
long_form: :microRNAAbundance,
|
555
|
+
description: 'Denotes the abundance of a processed, functional microRNA',
|
556
|
+
return_type: :m,
|
557
|
+
signatures: [
|
558
|
+
Signature.new(:m, E.new(:M))
|
559
|
+
]
|
560
|
+
},
|
561
|
+
act: {
|
562
|
+
short_form: :act,
|
563
|
+
long_form: :molecularActivity,
|
564
|
+
description: 'Denotes the frequency or abundance of events in which a member acts as a causal agent at the molecular scale',
|
565
|
+
return_type: :a,
|
566
|
+
signatures: [
|
567
|
+
Signature.new(:act, F.new(:a))
|
568
|
+
]
|
569
|
+
},
|
570
|
+
path: {
|
571
|
+
short_form: :path,
|
572
|
+
long_form: :pathology,
|
573
|
+
description: 'Denotes a disease or pathology process',
|
574
|
+
return_type: :path,
|
575
|
+
signatures: [
|
576
|
+
Signature.new(:path, E.new(:O))
|
577
|
+
]
|
578
|
+
},
|
579
|
+
pep: {
|
580
|
+
short_form: :pep,
|
581
|
+
long_form: :peptidaseActivity,
|
582
|
+
description: 'Denotes the frequency or abundance of events in which a member acts to cleave a protein',
|
583
|
+
return_type: :a,
|
584
|
+
signatures: [
|
585
|
+
Signature.new(:pep, F.new(:complex)),
|
586
|
+
Signature.new(:pep, F.new(:p))
|
587
|
+
]
|
588
|
+
},
|
589
|
+
phos: {
|
590
|
+
short_form: :phos,
|
591
|
+
long_form: :phosphataseActivity,
|
592
|
+
description: 'Denotes the frequency or abundance of events in which a member acts as a phosphatase',
|
593
|
+
return_type: :a,
|
594
|
+
signatures: [
|
595
|
+
Signature.new(:phos, F.new(:complex)),
|
596
|
+
Signature.new(:phos, F.new(:p))
|
597
|
+
]
|
598
|
+
},
|
599
|
+
products: {
|
600
|
+
short_form: :products,
|
601
|
+
long_form: :products,
|
602
|
+
description: 'Denotes the products of a reaction',
|
603
|
+
return_type: :products,
|
604
|
+
signatures: [
|
605
|
+
Signature.new(:products, F.new(:a))
|
606
|
+
]
|
607
|
+
},
|
608
|
+
p: {
|
609
|
+
short_form: :p,
|
610
|
+
long_form: :proteinAbundance,
|
611
|
+
description: 'Denotes the abundance of a protein',
|
612
|
+
return_type: :p,
|
613
|
+
signatures: [
|
614
|
+
Signature.new(:p, E.new(:P)),
|
615
|
+
Signature.new(:p, E.new(:P), F.new(:pmod)),
|
616
|
+
Signature.new(:p, E.new(:P), F.new(:sub)),
|
617
|
+
Signature.new(:p, E.new(:P), F.new(:fus)),
|
618
|
+
Signature.new(:p, E.new(:P), F.new(:trunc))
|
619
|
+
]
|
620
|
+
},
|
621
|
+
pmod: {
|
622
|
+
short_form: :pmod,
|
623
|
+
long_form: :proteinModification,
|
624
|
+
description: 'Denotes a covalently modified protein abundance',
|
625
|
+
return_type: :pmod,
|
626
|
+
signatures: [
|
627
|
+
Signature.new(:pmod, E.new(:*)),
|
628
|
+
Signature.new(:pmod, E.new(:*), E.new(:*)),
|
629
|
+
Signature.new(:pmod, E.new(:*), E.new(:*), E.new(:*))
|
630
|
+
]
|
631
|
+
},
|
632
|
+
reactants: {
|
633
|
+
short_form: :reactants,
|
634
|
+
long_form: :reactants,
|
635
|
+
description: 'Denotes the reactants of a reaction',
|
636
|
+
return_type: :reactants,
|
637
|
+
signatures: [
|
638
|
+
Signature.new(:reactants, F.new(:a))
|
639
|
+
]
|
640
|
+
},
|
641
|
+
rxn: {
|
642
|
+
short_form: :rxn,
|
643
|
+
long_form: :reaction,
|
644
|
+
description: 'Denotes the frequency or abundance of events in a reaction',
|
645
|
+
return_type: :a,
|
646
|
+
signatures: [
|
647
|
+
Signature.new(:rxn, F.new(:reactants), F.new(:products))
|
648
|
+
]
|
649
|
+
},
|
650
|
+
ribo: {
|
651
|
+
short_form: :ribo,
|
652
|
+
long_form: :ribosylationActivity,
|
653
|
+
description: 'Denotes the frequency or abundance of events in which a member acts to perform post-translational modification of proteins',
|
654
|
+
return_type: :a,
|
655
|
+
signatures: [
|
656
|
+
Signature.new(:ribo, F.new(:complex)),
|
657
|
+
Signature.new(:ribo, F.new(:p))
|
658
|
+
]
|
659
|
+
},
|
660
|
+
r: {
|
661
|
+
short_form: :r,
|
662
|
+
long_form: :rnaAbundance,
|
663
|
+
description: 'Denotes the abundance of a gene',
|
664
|
+
return_type: :g,
|
665
|
+
signatures: [
|
666
|
+
Signature.new(:r, E.new(:R)),
|
667
|
+
Signature.new(:r, E.new(:R), F.new(:fus))
|
668
|
+
]
|
669
|
+
},
|
670
|
+
sub: {
|
671
|
+
short_form: :sub,
|
672
|
+
long_form: :substitution,
|
673
|
+
description: 'Indicates the abundance of proteins with amino acid substitution sequence',
|
674
|
+
return_type: :sub,
|
675
|
+
signatures: [
|
676
|
+
Signature.new(:sub, E.new(:*), E.new(:*), E.new(:*))
|
677
|
+
]
|
678
|
+
},
|
679
|
+
tscript: {
|
680
|
+
short_form: :tscript,
|
681
|
+
long_form: :transcriptionalActivity,
|
682
|
+
description: 'Denotes the frequency or abundance of events in which a member directly acts to control transcription of genes',
|
683
|
+
return_type: :a,
|
684
|
+
signatures: [
|
685
|
+
Signature.new(:tscript, F.new(:complex)),
|
686
|
+
Signature.new(:tscript, F.new(:p))
|
687
|
+
]
|
688
|
+
},
|
689
|
+
tloc: {
|
690
|
+
short_form: :tloc,
|
691
|
+
long_form: :translocation,
|
692
|
+
description: 'Denotes the frequency or abundance of events in which members move between locations',
|
693
|
+
return_type: :a,
|
694
|
+
signatures: [
|
695
|
+
Signature.new(:tloc, F.new(:a), E.new(:A), E.new(:A))
|
696
|
+
]
|
697
|
+
},
|
698
|
+
tport: {
|
699
|
+
short_form: :tport,
|
700
|
+
long_form: :transportActivity,
|
701
|
+
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',
|
702
|
+
return_type: :a,
|
703
|
+
signatures: [
|
704
|
+
Signature.new(:tport, F.new(:complex)),
|
705
|
+
Signature.new(:tport, F.new(:p))
|
706
|
+
]
|
707
|
+
},
|
708
|
+
trunc: {
|
709
|
+
short_form: :trunc,
|
710
|
+
long_form: :truncation,
|
711
|
+
description: 'Indicates an abundance of proteins with truncation sequence variants',
|
712
|
+
return_type: :trunc,
|
713
|
+
signatures: [
|
714
|
+
Signature.new(:trunc, E.new(:*))
|
715
|
+
]
|
716
|
+
}
|
717
|
+
}
|
718
|
+
FUNCTIONS.merge!(Hash[*FUNCTIONS.map {|_,v| [v[:long_form], v]}.flatten])
|
719
|
+
|
720
|
+
PARAMETER_ENCODING = {
|
721
|
+
B: [:B],
|
722
|
+
O: [:O, :B],
|
723
|
+
R: [:R, :A],
|
724
|
+
M: [:M, :R, :A],
|
725
|
+
P: [:P, :A],
|
726
|
+
G: [:G, :A],
|
727
|
+
A: [:A],
|
728
|
+
C: [:C, :A]
|
729
|
+
}
|
730
|
+
|
731
|
+
FUNCTION_TYPES = {
|
732
|
+
a: [:a],
|
733
|
+
bp: [:bp],
|
734
|
+
complex: [:complex, :a],
|
735
|
+
g: [:g, :a],
|
736
|
+
m: [:m, :r, :a],
|
737
|
+
path: [:path, :bp],
|
738
|
+
p: [:p, :a],
|
739
|
+
r: [:r, :a]
|
740
|
+
}
|
741
|
+
|
742
|
+
RELATIONSHIPS = [
|
743
|
+
:actsIn,
|
744
|
+
:analogous,
|
745
|
+
:association,
|
746
|
+
:biomarkerFor,
|
747
|
+
:causesNoChange,
|
748
|
+
:decreases,
|
749
|
+
:directlyDecreases,
|
750
|
+
:directlyIncreases,
|
751
|
+
:hasComponent, :hasComponents,
|
752
|
+
:hasMember, :hasMembers,
|
753
|
+
:hasModification,
|
754
|
+
:hasProduct,
|
755
|
+
:hasVariant,
|
756
|
+
:includes,
|
757
|
+
:increases,
|
758
|
+
:isA,
|
759
|
+
:negativeCorrelation,
|
760
|
+
:orthologous,
|
761
|
+
:positiveCorrelation,
|
762
|
+
:prognosticBiomarkerFor,
|
763
|
+
:rateLimitingStepOf,
|
764
|
+
:reactantIn,
|
765
|
+
:subProcessOf,
|
766
|
+
:transcribedTo,
|
767
|
+
:translatedTo,
|
768
|
+
:translocates
|
769
|
+
]
|
770
|
+
|
771
|
+
RELATIONSHIPS.each do |rel|
|
772
|
+
Term.send(:define_method, rel) do |another|
|
773
|
+
s = Statement.new self
|
774
|
+
s.relationship = rel
|
775
|
+
s.object = another
|
776
|
+
s
|
777
|
+
end
|
778
|
+
end
|
779
|
+
FUNCTIONS.each do |fx, metadata|
|
780
|
+
func = Function.new(metadata)
|
781
|
+
Language.send(:define_method, fx) do |*args|
|
782
|
+
Term.new(func, *args)
|
783
|
+
end
|
784
|
+
Language.send(:define_method, metadata[:long_form]) do |*args|
|
785
|
+
Term.new(func, *args)
|
786
|
+
end
|
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
|
1049
|
+
end
|
1050
|
+
end
|
1051
|
+
# vim: ts=2 sw=2:
|
1052
|
+
# encoding: utf-8
|