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