bel 0.3.0.beta1-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- 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
|