ldpath 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +17 -2
- data/ldpath.gemspec +1 -0
- data/lib/ldpath.rb +13 -0
- data/lib/ldpath/field_mapping.rb +2 -0
- data/lib/ldpath/functions.rb +144 -0
- data/lib/ldpath/parser.rb +358 -0
- data/lib/ldpath/program.rb +63 -252
- data/lib/ldpath/selectors.rb +124 -0
- data/lib/ldpath/tests.rb +102 -0
- data/lib/ldpath/transform.rb +143 -0
- data/lib/ldpath/version.rb +1 -1
- data/spec/fixtures/foaf_example.program +9 -0
- data/spec/fixtures/namespaces.ldpath +21 -0
- data/spec/fixtures/program.ldpath +41 -0
- data/spec/ldpath_parser_spec.rb +162 -0
- data/spec/ldpath_program_spec.rb +115 -0
- data/spec/ldpath_transform_spec.rb +62 -0
- metadata +34 -4
- data/spec/ldpath_program_parser_spec.rb +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 154cc858a4b03022c48012acdb9e897c105a7c3f
|
4
|
+
data.tar.gz: 5c99156666f615486d5b8134bcd869e0f9b44590
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a9879ef3a8345d8c0a5c82ab79b048c9143cd1da9c33b5d53e8940d54187666723c9c67f5114efa9d7e2485aa4c28e8d63c2d43919571f8f955169485106fb2
|
7
|
+
data.tar.gz: 81f9a84ff7d8a761327c833b8acd6a1ff96403c3e512c02fa6c060755817aab69d9382bcaa3782048a2d1d6c7c983528a155b2554a961c71fe4bb711d0fb7d6b
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Ldpath
|
2
2
|
|
3
|
-
|
3
|
+
This is a ruby implementation of [LDPath](http://marmotta.apache.org/ldpath/language.html), a language for selecting values linked data resources.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -18,8 +18,23 @@ Or install it yourself as:
|
|
18
18
|
|
19
19
|
## Usage
|
20
20
|
|
21
|
-
|
21
|
+
```ruby
|
22
|
+
require 'ldpath'
|
22
23
|
|
24
|
+
my_program = <<-EOF
|
25
|
+
@prefix dcterms : <http://purl.org/dc/terms/> ;
|
26
|
+
title = dcterms:title :: xsd:string ;
|
27
|
+
EOF
|
28
|
+
|
29
|
+
uri = RDF::URI.new "info:a"
|
30
|
+
|
31
|
+
context = RDF::Graph.new << [uri, RDF::DC.title, "Some Title"]
|
32
|
+
|
33
|
+
program = Ldpath::Program.parse my_program
|
34
|
+
output = program.evaluate uri, context
|
35
|
+
# => { ... }
|
36
|
+
```
|
37
|
+
|
23
38
|
## Contributing
|
24
39
|
|
25
40
|
1. Fork it ( http://github.com/<my-github-username>/ldpath/fork )
|
data/ldpath.gemspec
CHANGED
data/lib/ldpath.rb
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
require "ldpath/version"
|
2
|
+
require 'linkeddata'
|
2
3
|
|
3
4
|
module Ldpath
|
5
|
+
require 'ldpath/field_mapping'
|
6
|
+
require 'ldpath/selectors'
|
7
|
+
require 'ldpath/tests'
|
8
|
+
require 'ldpath/parser'
|
9
|
+
require 'ldpath/transform'
|
10
|
+
require 'ldpath/functions'
|
4
11
|
require 'ldpath/program'
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def evaluate program, uri, context
|
15
|
+
Ldpath::Program.parse(program).evaluate(uri, context)
|
16
|
+
end
|
17
|
+
end
|
5
18
|
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module Ldpath
|
2
|
+
module Functions
|
3
|
+
def concat uri, context, *args
|
4
|
+
args.join
|
5
|
+
end
|
6
|
+
|
7
|
+
def first uri, context, *args
|
8
|
+
args.flatten.compact.first
|
9
|
+
end
|
10
|
+
|
11
|
+
def last uri, context, *args
|
12
|
+
args.flatten.compact.last
|
13
|
+
end
|
14
|
+
|
15
|
+
def eq uri, context, *args
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
def ne uri, context, *args
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
def lt uri, context, *args
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
def le uri, context, *args
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
def gt uri, context, *args
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def ge uri, context, *args
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
# collections
|
40
|
+
def flatten uri, context, *args
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
def get uri, context, *args
|
45
|
+
end
|
46
|
+
|
47
|
+
def subList uri, context, *args
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
# dates
|
52
|
+
|
53
|
+
def earliest uri, context, *args
|
54
|
+
args.flatten.min
|
55
|
+
end
|
56
|
+
|
57
|
+
def latest uri, context, *args
|
58
|
+
args.flatten.max
|
59
|
+
end
|
60
|
+
|
61
|
+
# math
|
62
|
+
|
63
|
+
def min uri, context, *args
|
64
|
+
args.flatten.min
|
65
|
+
end
|
66
|
+
|
67
|
+
def max uri, context, *args
|
68
|
+
args.flatten.max
|
69
|
+
end
|
70
|
+
|
71
|
+
def round uri, context, *args
|
72
|
+
args.map do |n|
|
73
|
+
Array(n).map do |i|
|
74
|
+
i.respond_to? :round ? i.round : i
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def sum uri, context, *args
|
80
|
+
args.inject(0) { |sum, n| sum + n }
|
81
|
+
end
|
82
|
+
|
83
|
+
# text
|
84
|
+
|
85
|
+
def replace uri, context, str, pattern, replacement
|
86
|
+
regex = Regexp.parse(pattern)
|
87
|
+
Array(str).map do |x|
|
88
|
+
x.gsub(regex, replacement)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def strlen uri, context, str
|
93
|
+
Array(str).map { |x| x.length }
|
94
|
+
end
|
95
|
+
|
96
|
+
def wc uri, context, str
|
97
|
+
Array(str).map { |x| x.split.length }
|
98
|
+
end
|
99
|
+
|
100
|
+
def strLeft uri, context, str, left
|
101
|
+
Array(str).map { |x| x[0..left.to_i] }
|
102
|
+
end
|
103
|
+
|
104
|
+
def strRight uri, context, str, right
|
105
|
+
Array(str).map { |x| x[right.to_i..x.length] }
|
106
|
+
end
|
107
|
+
|
108
|
+
def substr uri, context, str, left, right
|
109
|
+
Array(str).map { |x| x[left.to_i..right.to_i] }
|
110
|
+
end
|
111
|
+
|
112
|
+
def strJoin uri, context, str, sep = "", prefix = "", suffix = ""
|
113
|
+
prefix + Array(str).join(sep) + suffix
|
114
|
+
end
|
115
|
+
|
116
|
+
def equals uri, context, str, other
|
117
|
+
Array(str).map { |x| x == other }
|
118
|
+
end
|
119
|
+
|
120
|
+
def equalsIgnoreCase uri, context, str, other
|
121
|
+
Array(str).map { |x| x.downcase == other.downcase }
|
122
|
+
end
|
123
|
+
|
124
|
+
def contains uri, context, str, substr
|
125
|
+
Array(str).map { |x| x.include? substr }
|
126
|
+
end
|
127
|
+
|
128
|
+
def startsWith uri, context, str, suffix
|
129
|
+
|
130
|
+
Array(str).map { |x| x.start_with? suffix }
|
131
|
+
end
|
132
|
+
|
133
|
+
def endsWith uri, context, str, suffix
|
134
|
+
|
135
|
+
Array(str).map { |x| x.end_with? suffix }
|
136
|
+
end
|
137
|
+
|
138
|
+
def isEmpty uri, context, str
|
139
|
+
|
140
|
+
Array(str).map(&:empty?)
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,358 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
|
3
|
+
module Ldpath
|
4
|
+
class Parser < Parslet::Parser
|
5
|
+
root :lines
|
6
|
+
rule(:lines) { line.repeat }
|
7
|
+
rule(:line) { expression >> wsp? >> (newline | eof) }
|
8
|
+
|
9
|
+
rule(:newline) { (str("\n") >> str("\r").maybe).repeat(1) }
|
10
|
+
rule(:eof) { any.absent? }
|
11
|
+
rule(:wsp) { (match["\\t "] | multiline_comment).repeat(1) }
|
12
|
+
rule(:wsp?) { wsp.maybe }
|
13
|
+
rule(:multiline_comment) { (str('/*') >> (str('*/').absent? >> any).repeat >> str('*/') ) }
|
14
|
+
|
15
|
+
rule(:expression) { wsp | namespace | mapping | graph }
|
16
|
+
|
17
|
+
rule(:int) { match("\\d+") }
|
18
|
+
|
19
|
+
rule(:comma) { str(",") }
|
20
|
+
rule(:scolon) { str(";") }
|
21
|
+
rule(:colon) { str(":") }
|
22
|
+
rule(:dcolon) { str("::") }
|
23
|
+
rule(:assign) { str("=") }
|
24
|
+
rule(:k_prefix) { str("@prefix")}
|
25
|
+
rule(:k_graph) { str("@graph")}
|
26
|
+
|
27
|
+
rule(:self_op) { str(".") }
|
28
|
+
rule(:and_op) { str("&") }
|
29
|
+
rule(:or_op) { str("|") }
|
30
|
+
rule(:p_sep) { str("/") }
|
31
|
+
rule(:plus) { str("+") }
|
32
|
+
rule(:star) { str("*") }
|
33
|
+
rule(:not_op) { str("!") }
|
34
|
+
rule(:inverse) { str("^") }
|
35
|
+
rule(:is) { str "is" }
|
36
|
+
rule(:is_a) { str "is-a" }
|
37
|
+
rule(:func) { str "fn:"}
|
38
|
+
rule(:type) { str "^^" }
|
39
|
+
rule(:lang) { str "@" }
|
40
|
+
|
41
|
+
# todo: fixme
|
42
|
+
rule(:uri) do
|
43
|
+
uri_in_brackets |
|
44
|
+
prefix_and_localname
|
45
|
+
end
|
46
|
+
|
47
|
+
rule(:uri_in_brackets) do
|
48
|
+
str("<") >> (str(">").absent? >> any).repeat.as(:uri) >> str(">")
|
49
|
+
end
|
50
|
+
|
51
|
+
rule(:prefix_and_localname) do
|
52
|
+
(identifier.as(:prefix) >> str(":") >> identifier.as(:localName)).as(:uri)
|
53
|
+
end
|
54
|
+
|
55
|
+
rule(:identifier) { match["a-zA-Z0-9_"] >> (match["a-zA-Z0-9_'\\.-"]).repeat }
|
56
|
+
|
57
|
+
rule(:strlit) {
|
58
|
+
wsp? >>
|
59
|
+
str('"') >> (str("\\") >> str("\"") | (str('"').absent? >> any)).repeat.as(:literal) >> str('"') >>
|
60
|
+
wsp?
|
61
|
+
}
|
62
|
+
|
63
|
+
rule(:node) {
|
64
|
+
uri.as(:uri) | strlit.as(:literal)
|
65
|
+
}
|
66
|
+
|
67
|
+
# @prefix id = uri ;
|
68
|
+
rule(:namespace) {
|
69
|
+
(
|
70
|
+
wsp? >>
|
71
|
+
k_prefix >> wsp? >>
|
72
|
+
identifier.as(:id) >> wsp? >>
|
73
|
+
colon >> wsp? >>
|
74
|
+
uri.as(:uri) >> wsp? >>
|
75
|
+
scolon.maybe >> wsp?
|
76
|
+
).as(:namespace)
|
77
|
+
}
|
78
|
+
|
79
|
+
# @graph uri, uri, uri ;
|
80
|
+
rule(:graph) {
|
81
|
+
k_graph >> wsp? >>
|
82
|
+
uri_list.as(:graphs) >> wsp? >>
|
83
|
+
scolon
|
84
|
+
}
|
85
|
+
|
86
|
+
rule(:uri_list) {
|
87
|
+
wsp? >>
|
88
|
+
uri.as(:uri) >>
|
89
|
+
(
|
90
|
+
wsp? >>
|
91
|
+
comma >> wsp? >>
|
92
|
+
uri_list.as(:rest)
|
93
|
+
).repeat
|
94
|
+
}
|
95
|
+
|
96
|
+
# id = . ;
|
97
|
+
rule(:mapping) {
|
98
|
+
(
|
99
|
+
identifier.as(:name) >> wsp? >>
|
100
|
+
assign >> wsp? >>
|
101
|
+
selector.as(:selector) >> wsp? >>
|
102
|
+
(
|
103
|
+
dcolon >> wsp? >>
|
104
|
+
uri.as(:field_type)
|
105
|
+
).maybe >> wsp? >>
|
106
|
+
scolon
|
107
|
+
).as(:mapping)
|
108
|
+
}
|
109
|
+
|
110
|
+
|
111
|
+
# selector groups
|
112
|
+
rule(:selector) {
|
113
|
+
(
|
114
|
+
compound_selector |
|
115
|
+
testing_selector |
|
116
|
+
atomic_selector
|
117
|
+
)
|
118
|
+
}
|
119
|
+
|
120
|
+
rule(:compound_selector) {
|
121
|
+
(
|
122
|
+
union_selector |
|
123
|
+
intersection_selector |
|
124
|
+
path_selector
|
125
|
+
)
|
126
|
+
}
|
127
|
+
|
128
|
+
rule(:testing_selector) {
|
129
|
+
wsp? >>
|
130
|
+
atomic_selector.as(:delegate) >>
|
131
|
+
str("[") >> wsp? >>
|
132
|
+
node_test.as(:test) >> wsp? >>
|
133
|
+
str("]") >> wsp?
|
134
|
+
}
|
135
|
+
|
136
|
+
rule(:atomic_selector) {
|
137
|
+
(
|
138
|
+
self_selector |
|
139
|
+
function_selector |
|
140
|
+
property_selector |
|
141
|
+
wildcard_selector |
|
142
|
+
reverse_property_selector |
|
143
|
+
string_constant_selector |
|
144
|
+
recursive_path_selector |
|
145
|
+
grouped_selector
|
146
|
+
)
|
147
|
+
}
|
148
|
+
|
149
|
+
rule(:atomic_or_testing_selector) {
|
150
|
+
(testing_selector | atomic_selector)
|
151
|
+
}
|
152
|
+
|
153
|
+
rule(:atomic_or_testing_or_path_selector) {
|
154
|
+
(path_selector | atomic_or_testing_selector)
|
155
|
+
}
|
156
|
+
|
157
|
+
# Compound selectors
|
158
|
+
## x / y
|
159
|
+
rule(:path_selector) {
|
160
|
+
(
|
161
|
+
wsp? >>
|
162
|
+
atomic_or_testing_selector.as(:left) >> wsp? >>
|
163
|
+
p_sep >> wsp? >>
|
164
|
+
atomic_or_testing_or_path_selector.as(:right) >> wsp?
|
165
|
+
).as(:path)
|
166
|
+
}
|
167
|
+
|
168
|
+
## x & y
|
169
|
+
rule(:intersection_selector) {
|
170
|
+
(
|
171
|
+
wsp? >>
|
172
|
+
atomic_or_testing_or_path_selector.as(:left) >> wsp? >>
|
173
|
+
and_op >> wsp? >>
|
174
|
+
selector.as(:right) >> wsp?
|
175
|
+
).as(:intersection)
|
176
|
+
}
|
177
|
+
|
178
|
+
## x | y
|
179
|
+
rule(:union_selector) {
|
180
|
+
(
|
181
|
+
wsp? >>
|
182
|
+
atomic_or_testing_or_path_selector.as(:left) >> wsp? >>
|
183
|
+
or_op >> wsp? >>
|
184
|
+
selector.as(:right) >> wsp?
|
185
|
+
).as(:union)
|
186
|
+
}
|
187
|
+
|
188
|
+
# Atomic Selectors
|
189
|
+
rule(:self_selector) {
|
190
|
+
wsp? >>
|
191
|
+
self_op.as(:self) >> wsp?
|
192
|
+
}
|
193
|
+
|
194
|
+
# fn:x() or fn:x(1,2,3)
|
195
|
+
rule(:function_selector) {
|
196
|
+
func >> identifier.as(:fname) >> str("()") |
|
197
|
+
func >> identifier.as(:fname) >> str("(") >> arglist.as(:arglist) >> str(")")
|
198
|
+
}
|
199
|
+
|
200
|
+
rule(:arglist) {
|
201
|
+
wsp? >>
|
202
|
+
selector >>
|
203
|
+
(
|
204
|
+
wsp? >>
|
205
|
+
comma >> wsp? >>
|
206
|
+
selector
|
207
|
+
).repeat >>
|
208
|
+
wsp?
|
209
|
+
}
|
210
|
+
|
211
|
+
# xyz
|
212
|
+
rule(:property_selector) {
|
213
|
+
wsp? >>
|
214
|
+
uri.as(:property) >> wsp?
|
215
|
+
}
|
216
|
+
|
217
|
+
# *
|
218
|
+
rule(:wildcard_selector) {
|
219
|
+
wsp? >>
|
220
|
+
star.as(:wildcard) >> wsp?
|
221
|
+
}
|
222
|
+
|
223
|
+
# ^xyz
|
224
|
+
rule(:reverse_property_selector) {
|
225
|
+
wsp? >>
|
226
|
+
inverse >> uri.as(:reverse_property) >> wsp?
|
227
|
+
}
|
228
|
+
|
229
|
+
rule(:string_constant_selector) {
|
230
|
+
strlit
|
231
|
+
}
|
232
|
+
|
233
|
+
# (x)*
|
234
|
+
rule(:recursive_path_selector) {
|
235
|
+
(
|
236
|
+
wsp? >>
|
237
|
+
str("(") >> wsp? >>
|
238
|
+
selector.as(:delegate) >> wsp? >>
|
239
|
+
str(")") >>
|
240
|
+
(
|
241
|
+
star |
|
242
|
+
plus |
|
243
|
+
(str("{") >> wsp? >> int.as(:min).maybe >> wsp? >>str(",") >> wsp? >> int.as(:max).maybe >> wsp? >>str("}") ).as(:range)
|
244
|
+
).as(:repeat) >> wsp?
|
245
|
+
).as(:recursive)
|
246
|
+
}
|
247
|
+
|
248
|
+
rule(:grouped_selector) {
|
249
|
+
wsp? >>
|
250
|
+
str("(") >> wsp? >>
|
251
|
+
selector >> wsp? >>
|
252
|
+
str(")") >> wsp?
|
253
|
+
}
|
254
|
+
|
255
|
+
# Testing Selectors
|
256
|
+
|
257
|
+
rule(:node_test) {
|
258
|
+
grouped_test |
|
259
|
+
not_test |
|
260
|
+
and_test |
|
261
|
+
or_test |
|
262
|
+
atomic_node_test
|
263
|
+
}
|
264
|
+
|
265
|
+
rule(:atomic_node_test) {
|
266
|
+
literal_language_test |
|
267
|
+
literal_type_test |
|
268
|
+
is_a_test |
|
269
|
+
path_equality_test |
|
270
|
+
function_test |
|
271
|
+
path_test
|
272
|
+
}
|
273
|
+
|
274
|
+
rule(:grouped_test) {
|
275
|
+
wsp? >>
|
276
|
+
str("(") >> wsp? >>
|
277
|
+
node_test >> wsp? >>
|
278
|
+
str(")") >> wsp?
|
279
|
+
}
|
280
|
+
|
281
|
+
rule(:not_test) {
|
282
|
+
(
|
283
|
+
wsp? >>
|
284
|
+
not_op >> node_test.as(:delegate) >>
|
285
|
+
wsp?
|
286
|
+
).as(:not)
|
287
|
+
}
|
288
|
+
|
289
|
+
rule(:and_test) {
|
290
|
+
(
|
291
|
+
wsp? >>
|
292
|
+
atomic_node_test.as(:left) >> wsp? >>
|
293
|
+
and_op >> wsp? >>
|
294
|
+
node_test.as(:right) >> wsp?
|
295
|
+
).as(:and)
|
296
|
+
}
|
297
|
+
|
298
|
+
rule(:or_test) {
|
299
|
+
(
|
300
|
+
wsp? >>
|
301
|
+
atomic_node_test.as(:left) >> wsp? >>
|
302
|
+
or_op >> wsp? >>
|
303
|
+
node_test.as(:right) >> wsp?
|
304
|
+
).as(:or)
|
305
|
+
}
|
306
|
+
|
307
|
+
# @en
|
308
|
+
rule(:literal_language_test) {
|
309
|
+
wsp? >>
|
310
|
+
lang >> identifier.as(:lang) >>
|
311
|
+
wsp?
|
312
|
+
}
|
313
|
+
|
314
|
+
# ^^xyz
|
315
|
+
rule(:literal_type_test) {
|
316
|
+
wsp? >>
|
317
|
+
type >> uri.as(:type) >>
|
318
|
+
wsp?
|
319
|
+
}
|
320
|
+
|
321
|
+
rule(:is_a_test) {
|
322
|
+
(
|
323
|
+
wsp? >>
|
324
|
+
is_a >> wsp? >>
|
325
|
+
node.as(:right) >>
|
326
|
+
wsp?
|
327
|
+
).as(:is_a)
|
328
|
+
}
|
329
|
+
|
330
|
+
rule(:path_equality_test) {
|
331
|
+
(
|
332
|
+
wsp? >>
|
333
|
+
selector >> wsp? >>
|
334
|
+
is >> wsp? >>
|
335
|
+
node.as(:right) >> wsp?
|
336
|
+
).as(:is)
|
337
|
+
}
|
338
|
+
|
339
|
+
rule(:function_test) {
|
340
|
+
wsp? >>
|
341
|
+
(
|
342
|
+
func >> identifier.as(:fname) >> str("()") |
|
343
|
+
func >> identifier.as(:fname) >> str("(") >>
|
344
|
+
arglist.as(:arglist) >>
|
345
|
+
str(")")
|
346
|
+
) >> wsp?
|
347
|
+
}
|
348
|
+
|
349
|
+
rule(:path_test) {
|
350
|
+
(
|
351
|
+
path_selector |
|
352
|
+
testing_selector |
|
353
|
+
atomic_selector
|
354
|
+
)
|
355
|
+
}
|
356
|
+
|
357
|
+
end
|
358
|
+
end
|