ldpath 0.0.0 → 0.0.1

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.
@@ -1,259 +1,70 @@
1
- require 'parslet'
1
+ module Ldpath
2
+ class Program
2
3
 
3
- class Ldpath::Program < Parslet::Parser
4
- root :lines
5
- rule(:lines) { line.repeat }
6
- rule(:line) { expression >> newline }
7
- rule(:newline) { any.absent? | str("\n") >> str("\r").maybe }
8
- rule(:expression) { wsp | multiline_comment | namespace | mapping }
9
-
10
- rule(:multiline_comment) { (str('/*') >> (str('*/').absent? >> any).repeat >> str('*/')) }
11
-
12
- rule(:comma) { str(",") }
13
- rule(:scolon) { str(";") }
14
- rule(:colon) { str(":") }
15
- rule(:dcolon) { str("::") }
16
- rule(:assign) { str("=") }
17
- rule(:k_prefix) { str("@prefix")}
18
- rule(:k_graph) { str("@graph")}
19
-
20
- rule(:self_op) { str(".") }
21
- rule(:and_op) { str("&") }
22
- rule(:or_op) { str("|") }
23
- rule(:p_sep) { str("/") }
24
- rule(:plus) { str("+") }
25
- rule(:star) { str("*") }
26
- rule(:not_op) { str("!") }
27
- rule(:inverse) { str("^") }
28
- rule(:is) { str "is" }
29
- rule(:is_a) { str "is-a" }
30
- rule(:func) { str "fn:"}
31
- rule(:type) { str "^^" }
32
- rule(:lang) { str "@" }
33
-
34
- rule(:wsp) { (match["\s"] | str("\n")).repeat(1) }
35
- rule(:wsp?) { wsp.maybe }
36
-
37
- # todo: fixme
38
- rule(:uri) { (str("<") >> (str(">").absent? >> any).repeat >> str(">")) | (identifier.as(:prefix) >> str(":") >> identifier.as(:localName) )}
39
- rule(:identifier) { match["a-zA-Z0-9_"] >> (match["a-zA-Z0-9_'\\.-"]).repeat }
4
+ include Ldpath::Functions
40
5
 
41
- rule(:namespace) {
42
- k_prefix >>
43
- wsp? >>
44
- identifier.as(:id) >>
45
- wsp? >>
46
- colon >>
47
- wsp? >>
48
- uri.as(:uri) >>
49
- wsp? >>
50
- scolon.maybe
51
- }
52
-
53
- rule(:mapping) {
54
- name_selector_mapping
55
- }
56
-
57
- rule(:name_selector_mapping) {
58
- identifier.as(:name) >>
59
- wsp? >>
60
- assign >>
61
- wsp? >>
62
- selector.as(:selector) >>
63
- wsp? >>
64
- (
65
- dcolon >> wsp? >>
66
- uri.as(:type)
67
- ).maybe >>
68
- wsp? >>
69
- scolon
70
- }
71
-
72
- rule(:selector) {
73
- (
74
- compound_selector |
75
- testing_selector |
76
- atomic_selector
77
- ).as(:result)
78
- }
79
-
80
- rule(:compound_selector) {
81
- (
82
- union_selector |
83
- intersection_selector |
84
- path_selector
85
- ).as(:result)
86
- }
87
-
88
- rule(:grouped_selector) {
89
- wsp? >>
90
- str("(") >> wsp? >> selector.as(:result) >> wsp? >> str(")") >> wsp?
91
- }
92
-
93
- rule(:path_selector) {
94
- wsp? >>
95
- atomic_or_testing_selector.as(:left) >>
96
- wsp? >>
97
- p_sep >>
98
- wsp? >>
99
- atomic_or_testing_or_path_selector.as(:right) >>
100
- wsp?
101
- }
102
-
103
- rule(:intersection_selector) {
104
- wsp? >>
105
- atomic_or_testing_or_path_selector.as(:left) >>
106
- wsp? >>
107
- and_op >>
108
- wsp? >>
109
- selector.as(:right) >>
110
- wsp?
111
- }
112
-
113
- rule(:union_selector) {
114
- wsp? >>
115
- atomic_or_testing_or_path_selector.as(:left) >>
6
+ class << self
7
+ def parse program, transform_context = {}
8
+ parsed = parser.parse(program)
9
+ ast = transform.apply parsed, transform_context
10
+
11
+ Ldpath::Program.new ast.compact
12
+ end
13
+
14
+ private
15
+ def transform
16
+ Ldpath::Transform.new
17
+ end
18
+
19
+ def parser
20
+ @parser ||= Ldpath::Parser.new
21
+ end
22
+ end
116
23
 
117
- wsp? >>
118
- or_op >>
24
+ attr_reader :mappings
25
+ def initialize mappings
26
+ @mappings ||= mappings
27
+ @cache = {}
28
+ end
119
29
 
120
- wsp? >>
121
- selector.as(:right) >>
30
+ def loading uri, context
31
+ if uri.to_s =~ /^http/ and !context.has_subject?(uri)
32
+ @cache[uri] ||= RDF::Graph.load(uri)
33
+ context << @cache[uri]
34
+ end
35
+ end
36
+
37
+ def evaluate uri, context = nil
38
+ h = {}
39
+ context ||= RDF::Graph.load uri.to_s
40
+
41
+ mappings.each do |m|
42
+ h[m.name] = case m.selector
43
+ when Selector
44
+ m.selector.evaluate(self, uri, context).map do |x|
45
+ next x unless m.field_type
46
+ RDF::Literal.new(x.to_s, datatype: m.field_type).canonicalize.object
47
+ end
48
+ else
49
+ Array(m.selector)
50
+ end
51
+ end
52
+
53
+ h
54
+ end
122
55
 
123
- wsp?
124
- }
125
-
126
- rule(:atomic_or_testing_or_path_selector) {
127
- (path_selector | atomic_or_testing_selector).as(:result)
128
- }
129
-
130
- rule(:atomic_or_testing_selector) {
131
- (testing_selector | atomic_selector).as(:result)
132
- }
133
-
134
- rule(:atomic_selector) {
135
- (
136
- self_selector |
137
- property_selector |
138
- wildcard_selector |
139
- reverse_property_selector |
140
- function_selector |
141
- # string_constant_selector |
142
- # recursive_path_selector |
143
- grouped_selector
144
- ).as(:result)
145
- }
146
-
147
- rule(:self_selector) {
148
- wsp? >> self_op >> wsp?
149
- }
150
-
151
- rule(:property_selector) {
152
- wsp? >> uri >> wsp?
153
- }
154
-
155
- rule(:reverse_property_selector) {
156
- wsp? >> inverse >> uri.as(:uri) >> wsp?
157
- }
158
-
159
- rule(:wildcard_selector) {
160
- wsp? >> star >> wsp?
161
- }
162
-
163
- rule(:testing_selector) {
164
- wsp? >>
165
- atomic_selector.as(:delegate) >>
166
- str("[") >>
167
- wsp? >>
168
- node_test.as(:test) >>
169
- wsp? >>
170
- str("]") >> wsp?
171
- }
172
-
173
- rule(:node_test) {
174
- grouped_test |
175
- not_test |
176
- and_test |
177
- or_test |
178
- atomic_node_test
179
- }
180
-
181
- rule(:grouped_test) {
182
- wsp? >> str("(") >> wsp? >> node_test >> wsp? >> str(")") >> wsp?
183
- }
184
-
185
- rule(:atomic_node_test) {
186
- # literal_language_test |
187
- # literal_type_test |
188
- is_a_test |
189
- path_equality_test |
190
- function_test |
191
- path_test
192
- }
193
-
194
- rule(:not_test) {
195
- wsp? >> not_op >> node_test.as(:delegate) >> wsp?
196
- }
197
-
198
- rule(:and_test) {
199
- wsp? >> atomic_node_test >> wsp? >> and_op >> wsp? >> node_test >> wsp?
200
- }
201
-
202
- rule(:or_test) {
203
- wsp? >> atomic_node_test >> wsp? >> or_op >> wsp? >> node_test >> wsp?
204
- }
205
-
206
- rule(:strlit) {
207
- wsp? >> str('"') >> (str('"').absent? >> any).repeat >> str('"') >> wsp?
208
- }
209
-
210
- rule(:node) {
211
- uri.as(:uri) | strlit.as(:literal)
212
- }
213
-
214
- rule(:is_a_test) {
215
- wsp? >> is_a >> wsp? >> node.as(:node) >> wsp?
216
- }
217
-
218
- rule(:path_equality_test) {
219
- selector.as(:path) >> is >> node.as(:node)
220
- }
221
-
222
- rule(:function_selector) {
223
- (
224
- func >> identifier.as(:fname) >> str("()") |
225
- func >> identifier.as(:fname) >> str("(") >>
226
- wsp? >>
227
- (selector.as(:argument)) >>
228
- (
229
- (wsp? >> str(",") >> wsp? >> selector.as(:argument)).repeat
230
- ).maybe >>
231
- wsp? >>
232
- str(")")
233
- )
234
- }
235
-
236
- rule(:function_test) {
237
- (
238
- func >> identifier.as(:fname) >> str("()") |
239
- func >> identifier.as(:fname) >> str("(") >>
240
- wsp? >>
241
- (selector.as(:argument)) >>
242
- (
243
- (wsp? >> str(",") >> wsp? >> selector.as(:argument)).repeat
244
- ).maybe >>
245
- wsp? >>
246
- str(")")
247
- )
248
- }
249
-
250
- rule(:path_test) {
251
- (
252
- path_selector |
253
- testing_selector |
254
- atomic_selector
255
- ).as(:path)
256
- }
257
-
56
+ def func_call fname, uri, context, *arguments
57
+ if function_method? fname
58
+ public_send(fname, uri, context, *arguments)
59
+ else
60
+ raise "No such function: #{fname}"
61
+ end
62
+ end
63
+
64
+ private
65
+ def function_method? function
66
+ Functions.public_instance_methods(false).include? function.to_sym
67
+ end
258
68
 
69
+ end
259
70
  end
@@ -0,0 +1,124 @@
1
+ module Ldpath
2
+ class Selector
3
+ def evaluate program, uris, context
4
+ Array(uris).map do |uri|
5
+ loading program, uri, context
6
+ evaluate_one uri, context
7
+ end.flatten.compact
8
+ end
9
+
10
+ def loading program, uri, context
11
+ program.loading uri, context
12
+ end
13
+ end
14
+
15
+ class SelfSelector < Selector
16
+ def evaluate_one uri, context
17
+ uri
18
+ end
19
+ end
20
+
21
+ class FunctionSelector < Selector
22
+ attr_reader :fname, :arguments
23
+
24
+ def initialize fname, arguments
25
+ @fname = fname
26
+ @arguments = arguments
27
+ end
28
+
29
+ def evaluate program, uris, context
30
+ Array(uris).map do |uri|
31
+ loading program, uri, context
32
+ args = arguments.map do |i|
33
+ case i
34
+ when Selector
35
+ i.evaluate(program, uri, context)
36
+ else
37
+ i
38
+ end
39
+ end
40
+ program.func_call fname, uri, context, *arguments
41
+ end.flatten.compact
42
+ end
43
+ end
44
+
45
+ class PropertySelector < Selector
46
+ attr_reader :property
47
+ def initialize property
48
+ @property = property
49
+ end
50
+
51
+ def evaluate_one uri, context
52
+ context.query([uri, property, nil]).map(&:object)
53
+ end
54
+ end
55
+
56
+ class WildcardSelector < Selector
57
+ def evaluate_one uri, context
58
+ context.query([uri, nil, nil]).map(&:object)
59
+ end
60
+ end
61
+
62
+ class ReversePropertySelector < Selector
63
+ attr_reader :property
64
+ def initialize property
65
+ @property = property
66
+ end
67
+
68
+ def evaluate_one uri, context
69
+ context.query([nil, property, uri]).map(&:subject)
70
+ end
71
+ end
72
+
73
+ class RecursivePathSelector < Selector
74
+ attr_reader :property, :repeat
75
+ def initialize property, repeat
76
+ @property = property
77
+ @repeat = repeat
78
+ end
79
+
80
+ def evaluate program, uris, context
81
+ result = []
82
+ input = Array(uris)
83
+
84
+ Range.new(0,repeat.min,true).each do
85
+ input = property.evaluate program, input, context
86
+ end
87
+
88
+ repeat.each_with_index do |i, idx|
89
+ break if input.empty? or idx > 25 # we're probably lost..
90
+ input = property.evaluate program, input, context
91
+ result |= input
92
+ end
93
+ result.flatten.compact
94
+ end
95
+ end
96
+
97
+ class CompoundSelector < Selector
98
+ attr_reader :left, :right
99
+ def initialize left, right
100
+ @left = left
101
+ @right = right
102
+ end
103
+ end
104
+
105
+ class PathSelector < CompoundSelector
106
+ def evaluate program, uris, context
107
+ output = left.evaluate(program, uris, context)
108
+ right.evaluate(program, output, context)
109
+ end
110
+ end
111
+
112
+ class UnionSelector < CompoundSelector
113
+ def evaluate program, uris, context
114
+ left.evaluate(program, uris, context) | right.evaluate(program, uris, context)
115
+ end
116
+ end
117
+
118
+ class IntersectionSelector < CompoundSelector
119
+ def evaluate program, uris, context
120
+ left.evaluate(program, uris, context) & right.evaluate(program, uris, context)
121
+ end
122
+ end
123
+
124
+ end
@@ -0,0 +1,102 @@
1
+ module Ldpath
2
+
3
+ class TestSelector < Selector
4
+ attr_reader :delegate, :test
5
+
6
+ def initialize delegate, test
7
+ @delegate = delegate
8
+ @test = test
9
+ end
10
+
11
+ def evaluate program, uris, context
12
+ entries = delegate.evaluate program, uris, context
13
+ entries.select do |uri|
14
+ Array(test.evaluate(program, uri, context)).any? do |x|
15
+ x
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ class LanguageTest < TestSelector
22
+ attr_reader :lang
23
+ def initialize lang
24
+ @lang = lang
25
+ end
26
+
27
+ def evaluate program, uri, context
28
+ return unless uri.literal?
29
+ if (lang == "none" && !uri.has_language?) or uri.language == lang
30
+ uri
31
+ end
32
+ end
33
+ end
34
+
35
+ class TypeTest < TestSelector
36
+ attr_reader :type
37
+ def initialize type
38
+ @type = type
39
+ end
40
+
41
+ def evaluate program, uri, context
42
+ return unless uri.literal?
43
+ if uri.has_datatype? and uri.datatype == type
44
+ uri
45
+ end
46
+ end
47
+ end
48
+
49
+ class NotTest < TestSelector
50
+ attr_reader :delegate
51
+
52
+ def initialize delegate
53
+ @delegate = delegate
54
+ end
55
+
56
+ def evaluate program, uri, context
57
+ !Array(delegate.evaluate(program, uri, context)).any? { |x| x }
58
+ end
59
+ end
60
+
61
+ class OrTest < TestSelector
62
+ attr_reader :left, :right
63
+
64
+ def initialize left, right
65
+ @left = left
66
+ @right = right
67
+ end
68
+
69
+ def evaluate program, uri, context
70
+ left.evaluate(program, uri, context).any? || right.evaluate(program, uri, context).any?
71
+ end
72
+ end
73
+
74
+ class AndTest < TestSelector
75
+
76
+ attr_reader :left, :right
77
+
78
+ def initialize left, right
79
+ @left = left
80
+ @right = right
81
+ end
82
+
83
+ def evaluate program, uri, context
84
+ left.evaluate(program, uri, context).compact.any? &&
85
+ right.evaluate(program, uri, context).compact.any?
86
+ end
87
+ end
88
+
89
+ class IsTest < TestSelector
90
+
91
+ attr_reader :left, :right
92
+
93
+ def initialize left, right
94
+ @left = left
95
+ @right = right
96
+ end
97
+
98
+ def evaluate program, uri, context
99
+ left.evaluate(program, uri, context).compact.include?(right)
100
+ end
101
+ end
102
+ end