rdf_context 0.4.8 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (248) hide show
  1. data/History.txt +27 -0
  2. data/README.rdoc +22 -9
  3. data/Rakefile +11 -6
  4. data/VERSION +1 -1
  5. data/bin/rdf_context +12 -4
  6. data/lib/rdf_context.rb +3 -1
  7. data/lib/rdf_context/aggregate_graph.rb +86 -0
  8. data/lib/rdf_context/array_hacks.rb +53 -0
  9. data/lib/rdf_context/conjunctive_graph.rb +1 -1
  10. data/lib/rdf_context/exceptions.rb +9 -7
  11. data/lib/rdf_context/graph.rb +174 -15
  12. data/lib/rdf_context/literal.rb +34 -2
  13. data/lib/rdf_context/n3_grammar.treetop +213 -129
  14. data/lib/rdf_context/n3parser.rb +281 -57
  15. data/lib/rdf_context/namespace.rb +23 -12
  16. data/lib/rdf_context/parser.rb +4 -3
  17. data/lib/rdf_context/quoted_graph.rb +38 -0
  18. data/lib/rdf_context/rdfaparser.rb +2 -1
  19. data/lib/rdf_context/rdfxmlparser.rb +4 -3
  20. data/lib/rdf_context/store/abstract_sql_store.rb +4 -4
  21. data/lib/rdf_context/store/abstract_store.rb +5 -1
  22. data/lib/rdf_context/store/sqlite3_store.rb +10 -8
  23. data/lib/rdf_context/string_hacks.rb +44 -21
  24. data/lib/rdf_context/term_utils.rb +73 -4
  25. data/lib/rdf_context/triple.rb +32 -47
  26. data/lib/rdf_context/uriref.rb +33 -26
  27. data/spec/aggregate_graph_spec.rb +59 -0
  28. data/spec/conjunctive_graph_spec.rb +1 -1
  29. data/spec/cwm_spec.rb +32 -0
  30. data/spec/graph_spec.rb +114 -3
  31. data/spec/literal_spec.rb +107 -5
  32. data/spec/matchers.rb +104 -51
  33. data/spec/n3parser_spec.rb +798 -99
  34. data/spec/namespaces_spec.rb +26 -0
  35. data/spec/quoted_graph_spec.rb +0 -0
  36. data/spec/rdf_helper.rb +197 -0
  37. data/spec/rdfa_helper.rb +15 -11
  38. data/spec/rdfa_parser_spec.rb +6 -8
  39. data/spec/rdfxml_spec.rb +21 -28
  40. data/spec/spec_helper.rb +20 -0
  41. data/spec/sqlite3_store_spec.rb +6 -5
  42. data/spec/store_helper.rb +9 -1
  43. data/spec/string_hacks_spec.rb +14 -0
  44. data/spec/{rdfxml_helper.rb → swap_helper.rb} +8 -9
  45. data/spec/swap_spec.rb +77 -0
  46. data/spec/swap_test/animal.rdf +17 -0
  47. data/spec/swap_test/anon-prop.n3 +14 -0
  48. data/spec/swap_test/anonymous_loop.n3 +2 -0
  49. data/spec/swap_test/contexts.n3 +16 -0
  50. data/spec/swap_test/daml-pref.n3 +10 -0
  51. data/spec/swap_test/i18n/hiragana.n3 +22 -0
  52. data/spec/swap_test/i18n/n3string.n3 +4 -0
  53. data/spec/swap_test/list/itemType.rdf +12 -0
  54. data/spec/swap_test/lists-simple.n3 +40 -0
  55. data/spec/swap_test/lists.n3 +35 -0
  56. data/spec/swap_test/n3/n3parser.tests_n3_10001.nt +3 -0
  57. data/spec/swap_test/n3/n3parser.tests_n3_10002.nt +7 -0
  58. data/spec/swap_test/n3/n3parser.tests_n3_10003.nt +3 -0
  59. data/spec/swap_test/n3/n3parser.tests_n3_10004.nt +119 -0
  60. data/spec/swap_test/n3/n3parser.tests_n3_10005.nt +3 -0
  61. data/spec/swap_test/n3/n3parser.tests_n3_10006.nt +225 -0
  62. data/spec/swap_test/n3/n3parser.tests_n3_10007.nt +79 -0
  63. data/spec/swap_test/n3/n3parser.tests_n3_10008.nt +5 -0
  64. data/spec/swap_test/n3/n3parser.tests_n3_10009.nt +13 -0
  65. data/spec/swap_test/n3/n3parser.tests_n3_10010.nt +21 -0
  66. data/spec/swap_test/n3/n3parser.tests_n3_10011.nt +9 -0
  67. data/spec/swap_test/n3/n3parser.tests_n3_10012.nt +53 -0
  68. data/spec/swap_test/n3/n3parser.tests_n3_10013.nt +19 -0
  69. data/spec/swap_test/n3/n3parser.tests_n3_10014.nt +103 -0
  70. data/spec/swap_test/n3/n3parser.tests_n3_10015.nt +103 -0
  71. data/spec/swap_test/n3/n3parser.tests_n3_10016.nt +3 -0
  72. data/spec/swap_test/n3/n3parser.tests_n3_10017.nt +151 -0
  73. data/spec/swap_test/n3/n3parser.tests_n3_10018.nt +9 -0
  74. data/spec/swap_test/n3/n3parser.tests_n3_10019.nt +3 -0
  75. data/spec/swap_test/n3/n3parser.tests_n3_10020.nt +13 -0
  76. data/spec/swap_test/n3parser.tests +160 -0
  77. data/spec/swap_test/nodeID/classes.n3 +9 -0
  78. data/spec/swap_test/nodeID/classes.ref.rdf +15 -0
  79. data/spec/swap_test/nodeID/ex1.rdf +15 -0
  80. data/spec/swap_test/norm/fix.rdf +33 -0
  81. data/spec/swap_test/owl-ex.rdf +150 -0
  82. data/spec/swap_test/ref/animal.n3 +11 -0
  83. data/spec/swap_test/ref/anon-prop-1.n3 +8 -0
  84. data/spec/swap_test/ref/anonymous_loop.ref +7 -0
  85. data/spec/swap_test/ref/bnode.n3 +6 -0
  86. data/spec/swap_test/ref/bnode.rdf +16 -0
  87. data/spec/swap_test/ref/colon-in-uri.n3 +15 -0
  88. data/spec/swap_test/ref/daml-ex.n3 +103 -0
  89. data/spec/swap_test/ref/daml-ont.n3 +289 -0
  90. data/spec/swap_test/ref/djb1a-out.n3 +4 -0
  91. data/spec/swap_test/ref/dot-dash.n3 +8 -0
  92. data/spec/swap_test/ref/in-xml-t.n3 +4 -0
  93. data/spec/swap_test/ref/itemType.n3 +9 -0
  94. data/spec/swap_test/ref/keywords1.n3 +11 -0
  95. data/spec/swap_test/ref/keywords2.n3 +11 -0
  96. data/spec/swap_test/ref/lists-simple-1.rdf +108 -0
  97. data/spec/swap_test/ref/lists.n3 +43 -0
  98. data/spec/swap_test/ref/lstring-out.n3 +20 -0
  99. data/spec/swap_test/ref/n3string.n3 +13 -0
  100. data/spec/swap_test/ref/no-last-nl.n3 +3 -0
  101. data/spec/swap_test/ref/numbers.n3 +18 -0
  102. data/spec/swap_test/ref/path1.n3 +8 -0
  103. data/spec/swap_test/ref/path2.n3 +39 -0
  104. data/spec/swap_test/ref/prefix1.rdf +31 -0
  105. data/spec/swap_test/ref/prefix3.rdf +31 -0
  106. data/spec/swap_test/ref/rdf-redefine.rdf +11 -0
  107. data/spec/swap_test/ref/reluri-1.rdf +18 -0
  108. data/spec/swap_test/ref/strquot.n3 +23 -0
  109. data/spec/swap_test/ref/strquot_a.n3 +23 -0
  110. data/spec/swap_test/ref/xml-base3.n3 +5 -0
  111. data/spec/swap_test/ref/xml-redefine.rdf +20 -0
  112. data/spec/swap_test/ref/xml-redefine2.rdf +23 -0
  113. data/spec/swap_test/ref/xml-syntax-basic-serialization.rdf +10 -0
  114. data/spec/swap_test/ref/xmllit.nt +3 -0
  115. data/spec/swap_test/regression.n3 +231 -0
  116. data/spec/swap_test/reluri-1.n3 +10 -0
  117. data/spec/swap_test/strquot.n3 +23 -0
  118. data/spec/swap_test/syntax/colon-in-uri.rdf +27 -0
  119. data/spec/swap_test/syntax/djb1a.n3 +3 -0
  120. data/spec/swap_test/syntax/dot-dash.n3 +15 -0
  121. data/spec/swap_test/syntax/equals1.n3 +1 -0
  122. data/spec/swap_test/syntax/equals2.n3 +1 -0
  123. data/spec/swap_test/syntax/keywords1.n3 +17 -0
  124. data/spec/swap_test/syntax/keywords2.n3 +18 -0
  125. data/spec/swap_test/syntax/lstring.n3 +26 -0
  126. data/spec/swap_test/syntax/neg-formula-predicate.n3 +1 -0
  127. data/spec/swap_test/syntax/neg-keywords3.n3 +1 -0
  128. data/spec/swap_test/syntax/neg-literal-predicate.n3 +1 -0
  129. data/spec/swap_test/syntax/neg-single-quote.n3 +1 -0
  130. data/spec/swap_test/syntax/neg-thisadoc.n3 +1 -0
  131. data/spec/swap_test/syntax/no-last-nl.n3 +2 -0
  132. data/spec/swap_test/syntax/numbers.n3 +26 -0
  133. data/spec/swap_test/syntax/path1.n3 +23 -0
  134. data/spec/swap_test/syntax/path2.n3 +31 -0
  135. data/spec/swap_test/syntax/qvars1.n3 +19 -0
  136. data/spec/swap_test/syntax/qvars2.n3 +19 -0
  137. data/spec/swap_test/syntax/this-quantifiers.n3 +164 -0
  138. data/spec/swap_test/syntax/this-rules.n3 +43 -0
  139. data/spec/swap_test/syntax/too-nested.n3 +25 -0
  140. data/spec/swap_test/syntax/trailing-semicolon.n3 +12 -0
  141. data/spec/swap_test/syntax/zero-objects.n3 +1 -0
  142. data/spec/swap_test/syntax/zero-predicates.n3 +3 -0
  143. data/spec/swap_test/tests-work.txt +25 -0
  144. data/spec/swap_test/xml-syntax/basic-serialization.n3 +8 -0
  145. data/spec/swap_test/xml-syntax/in-xml.xml +13 -0
  146. data/spec/swap_test/xml-syntax/non-ascii-pred.rdf +14 -0
  147. data/spec/swap_test/xml-syntax/rdf_prefix.n3 +2 -0
  148. data/spec/swap_test/xml-syntax/xml_prefix.n3 +7 -0
  149. data/spec/swap_test/xml-syntax/xml_prefix2.n3 +9 -0
  150. data/spec/swap_test/xml-syntax/xmlbase3.rdf +10 -0
  151. data/spec/swap_test/xml-syntax/xmllit.rdf +33 -0
  152. data/spec/triple_spec.rb +90 -46
  153. data/spec/turtle/README.txt +20 -0
  154. data/spec/turtle/bad-00.ttl +2 -0
  155. data/spec/turtle/bad-01.ttl +3 -0
  156. data/spec/turtle/bad-02.ttl +3 -0
  157. data/spec/turtle/bad-03.ttl +3 -0
  158. data/spec/turtle/bad-04.ttl +3 -0
  159. data/spec/turtle/bad-05.ttl +4 -0
  160. data/spec/turtle/bad-06.ttl +3 -0
  161. data/spec/turtle/bad-07.ttl +4 -0
  162. data/spec/turtle/bad-08.ttl +2 -0
  163. data/spec/turtle/bad-09.ttl +3 -0
  164. data/spec/turtle/bad-10.ttl +3 -0
  165. data/spec/turtle/bad-11.ttl +3 -0
  166. data/spec/turtle/bad-12.ttl +3 -0
  167. data/spec/turtle/bad-13.ttl +3 -0
  168. data/spec/turtle/bad-14.ttl +6 -0
  169. data/spec/turtle/manifest-bad.ttl +88 -0
  170. data/spec/turtle/manifest.ttl +215 -0
  171. data/spec/turtle/rdf-schema.out +126 -0
  172. data/spec/turtle/rdf-schema.ttl +156 -0
  173. data/spec/turtle/rdfq-results.out +36 -0
  174. data/spec/turtle/rdfq-results.ttl +39 -0
  175. data/spec/turtle/rdfs-namespace.out +131 -0
  176. data/spec/turtle/rdfs-namespace.ttl +160 -0
  177. data/spec/turtle/test-00.out +1 -0
  178. data/spec/turtle/test-00.ttl +2 -0
  179. data/spec/turtle/test-01.out +3 -0
  180. data/spec/turtle/test-01.ttl +7 -0
  181. data/spec/turtle/test-02.out +3 -0
  182. data/spec/turtle/test-02.ttl +5 -0
  183. data/spec/turtle/test-03.out +3 -0
  184. data/spec/turtle/test-03.ttl +5 -0
  185. data/spec/turtle/test-04.out +2 -0
  186. data/spec/turtle/test-04.ttl +4 -0
  187. data/spec/turtle/test-05.out +4 -0
  188. data/spec/turtle/test-05.ttl +4 -0
  189. data/spec/turtle/test-06.out +1 -0
  190. data/spec/turtle/test-06.ttl +3 -0
  191. data/spec/turtle/test-07.out +5 -0
  192. data/spec/turtle/test-07.ttl +3 -0
  193. data/spec/turtle/test-08.out +1 -0
  194. data/spec/turtle/test-08.ttl +3 -0
  195. data/spec/turtle/test-09.out +4 -0
  196. data/spec/turtle/test-09.ttl +10 -0
  197. data/spec/turtle/test-10.out +5 -0
  198. data/spec/turtle/test-10.ttl +5 -0
  199. data/spec/turtle/test-11.out +4 -0
  200. data/spec/turtle/test-11.ttl +10 -0
  201. data/spec/turtle/test-12.out +4 -0
  202. data/spec/turtle/test-12.ttl +9 -0
  203. data/spec/turtle/test-13.out +2 -0
  204. data/spec/turtle/test-13.ttl +7 -0
  205. data/spec/turtle/test-14.out +10000 -0
  206. data/spec/turtle/test-14.ttl +10002 -0
  207. data/spec/turtle/test-15.out +10000 -0
  208. data/spec/turtle/test-15.ttl +3 -0
  209. data/spec/turtle/test-16.out +10000 -0
  210. data/spec/turtle/test-16.ttl +10002 -0
  211. data/spec/turtle/test-17.out +1 -0
  212. data/spec/turtle/test-17.ttl +6 -0
  213. data/spec/turtle/test-18.out +2 -0
  214. data/spec/turtle/test-18.ttl +9 -0
  215. data/spec/turtle/test-19.out +1 -0
  216. data/spec/turtle/test-19.ttl +4 -0
  217. data/spec/turtle/test-20.out +2 -0
  218. data/spec/turtle/test-20.ttl +6 -0
  219. data/spec/turtle/test-21.out +3 -0
  220. data/spec/turtle/test-21.ttl +4 -0
  221. data/spec/turtle/test-22.out +3 -0
  222. data/spec/turtle/test-22.ttl +4 -0
  223. data/spec/turtle/test-23.out +1 -0
  224. data/spec/turtle/test-23.ttl +3 -0
  225. data/spec/turtle/test-24.out +2 -0
  226. data/spec/turtle/test-24.ttl +3 -0
  227. data/spec/turtle/test-25.out +7 -0
  228. data/spec/turtle/test-25.ttl +14 -0
  229. data/spec/turtle/test-26.out +1 -0
  230. data/spec/turtle/test-26.ttl +4 -0
  231. data/spec/turtle/test-27.out +1 -0
  232. data/spec/turtle/test-27.ttl +5 -0
  233. data/spec/turtle/test-28-out.ttl +6 -0
  234. data/spec/turtle/test-28.out +22 -0
  235. data/spec/turtle/test-28.ttl +22 -0
  236. data/spec/turtle/test-29.out +1 -0
  237. data/spec/turtle/test-29.ttl +1 -0
  238. data/spec/turtle/test-30.out +5 -0
  239. data/spec/turtle/test-30.ttl +12 -0
  240. data/spec/turtle_spec.rb +64 -0
  241. data/spec/uriref_spec.rb +74 -34
  242. data/test/n3_tests/rdflib/n3-writer-teset-26.n3 +31 -0
  243. data/test/n3_tests/rdflib/n3-writer-teset-26.nt +14 -0
  244. data/test/rdf_tests/xml-literal-mixed.nt +1 -2
  245. metadata +278 -39
  246. data/lib/rdf_context/n3_grammar.rb +0 -2171
  247. data/test/perf_test/test.rb +0 -11
  248. data/test/perf_test/tommorris.rdf +0 -2267
@@ -4,7 +4,6 @@ begin
4
4
  rescue LoadError
5
5
  end
6
6
 
7
- require 'parsedate'
8
7
  require File.join(File.dirname(__FILE__), 'duration')
9
8
 
10
9
  module RdfContext
@@ -93,6 +92,7 @@ module RdfContext
93
92
  false
94
93
  end
95
94
  end
95
+ alias_method :eql?, :==
96
96
 
97
97
  # Generate hash of type to determine uniqueness
98
98
  def hash
@@ -131,8 +131,25 @@ module RdfContext
131
131
 
132
132
  # Encode literal contents
133
133
  def encode_contents(contents, options)
134
+ return contents.to_s unless valid?(contents)
135
+
134
136
  case @value
135
- when XSD_NS.boolean then %w(1 true).include?(contents.to_s) ? "true" : "false"
137
+ when XSD_NS.boolean then %(1 true).include?(contents.to_s.downcase) ? "true" : "false"
138
+ when XSD_NS.integer then contents.to_i.to_s
139
+ when XSD_NS.decimal
140
+ # Can't use simple %f transformation do to special requirements from N3 tests in representation
141
+ i, f = contents.to_s.split(".")
142
+ f = f.to_s[0,16] # Truncate after 15 decimal places
143
+ i.sub!(/^\+?0+(\d)$/, '\1')
144
+ f.sub!(/0*$/, '')
145
+ f = "0" if f.empty?
146
+ "#{i}.#{f}"
147
+ when XSD_NS.double
148
+ i, f, e = ("%.16E" % contents.to_f).split(/[\.E]/)
149
+ f.sub!(/0*$/, '')
150
+ f = "0" if f.empty?
151
+ e.sub!(/^\+?0+(\d)$/, '\1')
152
+ "#{i}.#{f}E#{e}"
136
153
  when XSD_NS.time then contents.is_a?(Time) ? contents.strftime("%H:%M:%S%Z").sub(/\+00:00|UTC/, "Z") : contents.to_s
137
154
  when XSD_NS.dateTime then contents.is_a?(DateTime) ? contents.strftime("%Y-%m-%dT%H:%M:%S%Z").sub(/\+00:00|UTC/, "Z") : contents.to_s
138
155
  when XSD_NS.date then contents.is_a?(Date) ? contents.strftime("%Y-%m-%d%Z").sub(/\+00:00|UTC/, "Z") : contents.to_s
@@ -140,6 +157,17 @@ module RdfContext
140
157
  else contents.to_s
141
158
  end
142
159
  end
160
+
161
+ # Validate format of content
162
+ def valid?(contents)
163
+ case @value
164
+ when XSD_NS.boolean then %w(1 true 0 false).include?(contents.to_s.downcase)
165
+ when XSD_NS.decimal then !!contents.to_s.match(/^[\+\-]?\d+(\.\d*)?$/)
166
+ when XSD_NS.double then !!contents.to_s.match(/^[\+\-]?\d+(\.\d*([eE][\+\-]?\d+)?)?$/)
167
+ when XSD_NS.integer then !!contents.to_s.match(/^[\+\-]?\d+$/)
168
+ else true
169
+ end
170
+ end
143
171
  end
144
172
 
145
173
  # The null encoding
@@ -365,6 +393,10 @@ module RdfContext
365
393
  "#{self.class}[#{self.to_n3}]"
366
394
  end
367
395
 
396
+ def valid?
397
+ encoding.valid?(@contents)
398
+ end
399
+
368
400
  # Output literal in TriX format
369
401
  def to_trix
370
402
  encoding.format_as_trix(@contents, @lang)
@@ -1,143 +1,227 @@
1
+ #encoding: utf-8'
1
2
  grammar N3Grammer
2
- rule document
3
- statements
4
- end
5
-
6
- rule statements
7
- (space / (statement / directive) space* ('.' space*)? )*
8
- end
9
-
10
- rule statement
11
- subject space+ property_list
12
- end
13
-
14
- rule subject
15
- node
16
- end
17
-
18
- rule verb
19
- ">-" prop "->" # has xxx of
20
- / "<-" prop "<-" # is xxx of
21
- # / # / operator # has operator:xxx of??? NOT IMPLMENTED
22
- / prop # has xxx of -- shorthand
23
- # / "has" prop # has xxx of
24
- # / "is" prop "of" # is xxx of
25
- / "a" # has rdf:type of
26
- # / "=" # has daml:equivaent of
27
- end
28
-
29
- rule prop
30
- node
31
- end
32
-
33
- rule node
34
- uri_ref / anonnode / 'this'
35
- end
36
-
37
- rule anonnode
38
- "[" space* property_list space* "]" # something which ...
39
- / "{" statementlist "}" # the statementlist itself as a resource
40
- / "(" nodelist ")" {
41
- def anonnode; true; end
42
- }
43
- end
44
-
45
- rule property_list
46
- verb space+ object_list space* ";" space+ property_list
47
- / verb space+ object_list
48
- / ":-" anonnode #to allow two anonymous forms to be given eg [ a :Truth; :- { :sky :color :blue } ] )
49
- / ":-" anonnode ";" property_list
50
- / '.'
51
- end
52
-
53
- rule object_list
54
- object "," space* object_list / object
55
- end
56
-
57
- rule directive
58
- '@prefix' space+ nprefix:nprefix? ':' space+ uri_ref:uri_ref {
59
- def directive; true; end
60
- }
61
- end
62
-
63
- rule uri_ref
64
- qname / "<" uri:URI_Reference ">"
65
- end
66
-
67
- rule qname
68
- nprefix ":" localname / ':' localname
69
- end
70
-
71
- rule object
72
- subject / literal
73
- end
74
-
75
- rule literal
76
- (string_single / string_multi) ("^^<" uri:URI_Reference ">" / "@" language )?
77
- end
78
-
79
- rule language
80
- [a-z]+ ( "-" [a-z0-9]+ )*
81
- end
82
-
83
- rule localname
84
- fragid
85
- end
86
-
87
- rule URI_Reference
88
- [^{}<>]*
89
- end
90
-
91
- rule nprefix
92
- ((alpha / "_") alphanumeric*)
93
- end
94
-
95
- rule fragid
96
- alpha alphanumeric*
97
- end
98
-
99
- rule alpha
100
- [a-zA-Z]
101
- end
102
-
103
- rule alphanumeric
104
- alpha / [0-9] / "_"
105
- end
106
-
107
- rule space
108
- [ \t\n\r]+ / comment
109
- end
110
-
111
- rule comment
112
- '#' (![\n\r] .)*
113
- end
3
+ # Entry point to grammer
4
+ rule document
5
+ statements
6
+ end
7
+
8
+ rule alpha
9
+ [A-Z_a-z\p{Alpha}]
10
+ end
11
+
12
+ rule alphanumeric
13
+ alpha / [0-9]
14
+ end
15
+
16
+ rule barename_csl
17
+ space+ barename barename_csl_tail
18
+ / ""
19
+ end
20
+
21
+ rule barename_csl_tail
22
+ space* "," space* barename space* barename_csl_tail
23
+ / ""
24
+ end
25
+
26
+ rule boolean
27
+ "@"* ("true" / "false")
28
+ end
29
+
30
+ rule comment
31
+ '#' (![\n\r] .)*
32
+ end
33
+
34
+ rule declaration
35
+ "@"? 'keywords' barename_csl {
36
+ def declaration; true; end
37
+ def keywords; true;end
38
+ }
39
+ / "@"? 'base' space+ explicituri:explicituri {
40
+ def declaration; true; end
41
+ def base; true; end
42
+ }
43
+ / "@"? 'prefix' space+ nprefix:nprefix? ':' space* explicituri:explicituri {
44
+ def declaration; true; end
45
+ }
46
+ end
47
+
48
+ rule decimal
49
+ integer '.' [0-9]+
50
+ end
51
+
52
+ rule double
53
+ decimal [eE] integer
54
+ end
55
+
56
+ rule existential
57
+ "@"? "forSome" space+ symbol_csl
58
+ end
59
+
60
+ rule explicituri
61
+ "<" uri:URI_Reference ">"
62
+ end
63
+
64
+ rule expression
65
+ pathitem space* '.' expression
66
+ / pathitem space* "!" space* expression
67
+ / pathitem space* "^" space* expression { def reverse; true; end }
68
+ / pathitem
69
+ end
70
+
71
+ rule barename
72
+ alpha (alphanumeric / '-')*
73
+ end
74
+
75
+ rule hexdigit
76
+ [0-9a-fA-F]
77
+ end
78
+
79
+ rule integer
80
+ [+-]? [0-9]+
81
+ end
82
+
83
+ rule language
84
+ [a-z]+ ( "-" [a-z0-9]+ )*
85
+ end
86
+
87
+ rule literal
88
+ (string_single / string_multi) ("^^" symbol / "@" language )?
89
+ end
90
+
91
+ rule localname
92
+ alpha (alphanumeric / '-')*
93
+ end
94
+
95
+ rule nprefix
96
+ alpha (alphanumeric / '-')*
97
+ end
98
+
99
+ rule numericliteral
100
+ double { def numericliteral; "double"; end}
101
+ / decimal { def numericliteral; "decimal"; end}
102
+ / integer { def numericliteral; "integer"; end}
103
+ end
104
+
105
+ rule object
106
+ expression
107
+ end
108
+
109
+ rule object_list
110
+ object space* "," space* object_list
111
+ / object
112
+ end
114
113
 
114
+ rule pathitem
115
+ boolean { def boolean; true; end }
116
+ / literal { def literal; true; end }
117
+ / numericliteral
118
+ #/ quickvariable
119
+ / symbol
120
+ / "[" space* "]" { def anonnode; true; end }
121
+ / "[" space* property_list space* "]" { def anonnode; true; end }
122
+ / "{" space* statements space* "}" { def anonnode; true; end }
123
+ / "(" space* path_list space* ")" { def anonnode; true; end }
124
+ end
125
+
126
+ rule path_list
127
+ expression space* path_list
128
+ / ""
129
+ end
130
+
131
+ rule prop
132
+ expression
133
+ end
134
+
135
+ rule property_list
136
+ verb space* object_list space* ";"+ space* property_list
137
+ / verb space* object_list space* ";"*
138
+ / verb space* ";"* {def object_missing; true; end}
139
+ / ""
140
+ end
141
+
142
+ rule qname
143
+ nprefix ":" localname
144
+ / nprefix ':' { def text_value; ""; end }
145
+ / ':' localname*
146
+ / localname { def barename; true; end }
147
+ end
148
+
149
+ rule simpleStatement
150
+ subject space+ property_list
151
+ / subject # For [] and a.b
152
+ end
153
+
154
+ rule space
155
+ [ \t\n\r]+ / comment
156
+ end
157
+
158
+ rule statement
159
+ declaration
160
+ / existential
161
+ / simpleStatement
162
+ / universal
163
+ end
164
+
165
+ rule statements
166
+ (space / statement space* ('.' space*)? )*
167
+ end
168
+
115
169
  # " constant-value-with-escaping "
116
- rule string_single
117
- '""' !["] / '"' string_single_char+ '"'
118
- end
170
+ rule string_single
171
+ '""' !["] / '"' string_single_char+ '"'
172
+ end
119
173
 
120
- rule string_single_char
174
+ rule string_single_char
121
175
  !["\n\r] (
122
176
  ("\\"
123
- [\\\"nrt]
177
+ [\\\"bfnrt]
124
178
  / ( "u" hexdigit hexdigit hexdigit hexdigit )
125
179
  / ( "U" "00" hexdigit hexdigit hexdigit hexdigit hexdigit hexdigit)
126
180
  )
127
181
  / .)
128
- end
182
+ end
129
183
 
130
- rule hexdigit
131
- [0-9a-fA-F]
184
+ # """ constant value with escaping including single or double occurrences of quotes and/or newlines """
185
+ rule string_multi
186
+ '"""' string_multi_single_char* '"""'
187
+ end
188
+
189
+ rule string_multi_single_char
190
+ "\\\""
191
+ / !('"""') .
132
192
  end
133
-
134
- # """ constant value with escaping including single or double occurrences of quotes and/or newlines """
135
- rule string_multi
136
- '"""' string_multi_char* '"""'
137
- end
138
193
 
139
- rule string_multi_char
140
- !'"""' . # something like this; need to think about it some more
141
- end
142
-
194
+ rule subject
195
+ expression
196
+ end
197
+
198
+ rule symbol
199
+ qname / explicituri
200
+ end
201
+
202
+ rule symbol_csl
203
+ symbol space* "," space* symbol_csl
204
+ / symbol
205
+ end
206
+
207
+ rule verb
208
+ "@"? "has" space+ prop # has xxx
209
+ / "@"? "is" space+ prop space+ "@"? "of" { # is xxx of
210
+ def invert; true; end
211
+ }
212
+ / "@"? "a" !":" # has rdf:type
213
+ / "=>" # has log:implies
214
+ / "<=" { def invert; true; end } # is log:implies of
215
+ / "=" # has owl:sameAs
216
+ / prop # has xxx of -- shorthand
217
+ end
218
+
219
+ rule universal
220
+ "@"? "forAll" space+ symbol_csl
221
+ end
222
+
223
+ rule URI_Reference
224
+ [^{}<>]*
225
+ end
226
+
143
227
  end
@@ -6,6 +6,7 @@ Treetop.load(File.join(File.dirname(__FILE__), "n3_grammar"))
6
6
  module RdfContext
7
7
  class Parser; end
8
8
  class N3Parser < Parser
9
+ N3_KEYWORDS = %w(a is of has keywords prefix base true false forSome forAny)
9
10
 
10
11
  # Parse N3 document from a string or input stream to closure or graph.
11
12
  #
@@ -27,102 +28,274 @@ module RdfContext
27
28
  @callback = block
28
29
  parser = N3GrammerParser.new
29
30
 
30
- @doc = stream.respond_to?(:read) ? stream.read : stream
31
-
31
+ @doc = stream.respond_to?(:read) ? (stream.rewind; stream.read) : stream
32
+ @default_ns = Namespace.new("#{uri}#", "") if uri
33
+ add_debug("@default_ns", "#{@default_ns.inspect}")
34
+
35
+ @graph.allow_n3 = true
32
36
  document = parser.parse(@doc)
33
37
  unless document
38
+ puts parser.inspect if $DEBUG
34
39
  reason = parser.failure_reason
35
40
  raise ParserException.new(reason)
36
41
  end
37
42
 
38
- process_directives(document)
39
43
  process_statements(document)
40
44
  @graph
41
45
  end
42
46
 
43
47
  protected
44
48
 
45
- def process_directives(document)
46
- directives = document.elements.find_all { |e| e.elements.first.respond_to? :directive }
47
- directives.map! { |d| d.elements.first }
48
- directives.each { |d| namespace(d.uri_ref.uri.text_value, d.nprefix.text_value) }
49
- end
50
-
51
49
  def namespace(uri, prefix)
52
- uri = @uri if uri == '#'
53
- prefix = '__local__' if prefix == ''
50
+ add_debug("namesspace", "'#{prefix}' <#{uri}>")
51
+ uri = @default_ns.uri if uri == '#'
54
52
  @graph.bind(Namespace.new(uri, prefix))
55
53
  end
56
54
 
57
55
  def process_statements(document)
58
- subjects = document.elements.find_all { |e| e.elements.first.respond_to? :subject }
59
- subjects.map! { |s| s.elements.first }
60
- subjects.each do |s|
61
- subject = process_node(s.subject)
62
- properties = process_properties(s.property_list)
63
- properties.each do |p|
64
- predicate = process_verb(p.verb)
65
- objects = process_objects(p.object_list)
66
- objects.each { |object| add_triple("statement", subject, predicate, object) }
56
+ document.elements.find_all do |e|
57
+ s = e.elements.first
58
+ add_debug(*s.info("process_statements"))
59
+
60
+ if s.respond_to?(:subject)
61
+ subject = process_expression(s.subject)
62
+ add_debug(*s.info("process_statements(#{subject})"))
63
+ properties = process_properties(s.property_list)
64
+ properties.each do |p|
65
+ predicate = process_verb(p.verb)
66
+ add_debug(*p.info("process_statements(#{subject}, #{predicate})"))
67
+ raise ParserException, %Q(Illegal statment: "#{predicate}" missing object) unless p.respond_to?(:object_list)
68
+ objects = process_objects(p.object_list)
69
+ objects.each do |object|
70
+ if p.verb.respond_to?(:invert)
71
+ add_triple("statement", object, predicate, subject)
72
+ else
73
+ add_triple("statement", subject, predicate, object)
74
+ end
75
+ end
76
+ end
77
+ elsif s.respond_to?(:anonnode)
78
+ process_anonnode(s)
79
+ elsif s.respond_to?(:pathitem)
80
+ process_path(s)
81
+ elsif s.respond_to?(:declaration)
82
+ if s.respond_to?(:nprefix)
83
+ add_debug(*s.info("process_statements(namespace)"))
84
+ keyword_check("prefix") if s.text_value.index("prefix") == 0
85
+ uri = process_uri(s.explicituri.uri, false)
86
+ namespace(uri, s.nprefix.text_value)
87
+ elsif s.respond_to?(:base)
88
+ add_debug(*s.info("process_statements(base)"))
89
+ keyword_check("base") if s.text_value.index("base") == 0
90
+ # Base, set or update document URI
91
+ uri = s.explicituri.uri.text_value
92
+ @default_ns = Namespace.new(process_uri(uri, false), "") # Don't normalize
93
+ add_debug("@default_ns", "#{@default_ns.inspect}")
94
+ @uri = process_uri(uri)
95
+ add_debug("@base", "#{@uri}")
96
+ @uri
97
+ elsif s.respond_to?(:keywords)
98
+ add_debug(*s.info("process_statements(keywords)"))
99
+ keyword_check("keywords") if s.text_value.index("keywords") == 0
100
+ @keywords = process_barename_csl(s.barename_csl) ||[]
101
+ add_debug("@keywords", @keywords.inspect)
102
+ if (@keywords & N3_KEYWORDS) != @keywords
103
+ raise ParserException, "undefined keywords used: #{(@keywords - N3_KEYWORDS).to_sentence}" if @strict
104
+ end
105
+ end
67
106
  end
68
107
  end
69
108
  end
109
+
110
+ def process_barename_csl(list)
111
+ #add_debug(*list.info("process_barename_csl(list)"))
112
+ res = [list.barename.text_value] if list.respond_to?(:barename)
113
+ rest = process_barename_csl(list.barename_csl_tail) if list.respond_to?(:barename_csl_tail)
114
+ rest ? res + rest : res
115
+ end
70
116
 
71
117
  def process_anonnode(anonnode)
118
+ add_debug(*anonnode.info("process_anonnode"))
72
119
  bnode = BNode.new
73
- properties = process_properties(anonnode.property_list)
74
- properties.each do |p|
75
- predicate = process_node(p.verb)
76
- objects = process_objects(p.object_list)
77
- objects.each { |object| add_triple("anonnode", bnode, predicate, object) }
120
+
121
+ if anonnode.respond_to?(:property_list)
122
+ properties = process_properties(anonnode.property_list)
123
+ properties.each do |p|
124
+ predicate = process_verb(p.verb)
125
+ add_debug(*p.info("anonnode[#{predicate}]"))
126
+ objects = process_objects(p.object_list)
127
+ objects.each { |object| add_triple("anonnode", bnode, predicate, object) }
128
+ end
129
+ elsif anonnode.respond_to?(:path_list)
130
+ objects = process_objects(anonnode.path_list)
131
+ last = objects.pop
132
+ first_bnode = bnode
133
+ objects.each do |object|
134
+ add_triple("anonnode", first_bnode, RDF_NS.first, object)
135
+ rest_bnode = BNode.new
136
+ add_triple("anonnode", first_bnode, RDF_NS.rest, rest_bnode)
137
+ first_bnode = rest_bnode
138
+ end
139
+ if last
140
+ add_triple("anonnode", first_bnode, RDF_NS.first, last)
141
+ add_triple("anonnode", first_bnode, RDF_NS.rest, RDF_NS.nil)
142
+ else
143
+ bnode = RDF_NS.nil
144
+ end
78
145
  end
79
146
  bnode
80
147
  end
81
148
 
82
149
  def process_verb(verb)
83
- return URIRef.new('http://www.w3.org/1999/02/22-rdf-syntax-ns#type') if (verb.text_value=='a')
84
- return process_node(verb)
150
+ add_debug(*verb.info("process_verb"))
151
+ case verb.text_value
152
+ when "a"
153
+ # If "a" is a keyword, then it's RDF_TYPE, otherwise it's expanded from the default namespace
154
+ if @keywords.nil? || @keywords.include?("a")
155
+ RDF_TYPE
156
+ else
157
+ build_uri("a")
158
+ end
159
+ when "@a" then RDF_TYPE
160
+ when "=" then OWL_NS.sameAs
161
+ when "=>" then LOG_NS.implies
162
+ when "<=" then LOG_NS.implies
163
+ when /^(@?is)\s+.*\s+(@?of)$/
164
+ keyword_check("is") if $1 == "is"
165
+ keyword_check("of") if $2 == "of"
166
+ process_expression(verb.prop)
167
+ when /^has\s+/
168
+ keyword_check("has")
169
+ process_expression(verb.prop)
170
+ else
171
+ if verb.respond_to?(:prop)
172
+ process_expression(verb.prop)
173
+ else
174
+ process_expression(verb)
175
+ end
176
+ end
85
177
  end
86
178
 
87
- def process_node(node)
88
- if (node.respond_to? :uri)
89
- URIRef.new(node.uri.text_value)
90
- else
91
- prefix = (node.respond_to? :nprefix) ? node.nprefix.text_value : nil
92
- localname = node.localname.text_value
93
- build_uri(prefix, localname)
179
+ def process_expression(expression)
180
+ add_debug(*expression.info("process_expression"))
181
+ if expression.respond_to?(:pathitem) && expression.respond_to?(:expression)
182
+ process_path(expression) # Returns last object in chain
183
+ elsif expression.respond_to?(:uri)
184
+ process_uri(expression.uri)
185
+ elsif expression.respond_to?(:localname)
186
+ build_uri(expression)
187
+ elsif expression.respond_to?(:anonnode)
188
+ process_anonnode(expression)
189
+ elsif expression.respond_to?(:literal)
190
+ process_literal(expression)
191
+ elsif expression.respond_to?(:numericliteral)
192
+ process_numeric_literal(expression)
193
+ elsif expression.respond_to?(:boolean)
194
+ barename = expression.text_value.to_s
195
+ if @keywords && !@keywords.include?(barename)
196
+ build_uri(barename)
197
+ else
198
+ Literal.typed(barename.delete("@"), XSD_NS.boolean)
199
+ end
200
+ elsif expression.respond_to?(:barename)
201
+ barename = expression.text_value.to_s
202
+
203
+ # Should only happen if @keywords is defined, and text_value is not a defined keyword
204
+ case barename
205
+ when "true" then Literal.typed("true", XSD_NS.boolean)
206
+ when "false" then Literal.typed("false", XSD_NS.boolean)
207
+ else
208
+ # create URI using barename, unless it's in defined set, in which case it's an error
209
+ raise ParserException, %Q(Keyword "#{barename}" used as expression) if @keywords && @keywords.include?(barename)
210
+ build_uri(barename)
211
+ end
212
+ else
213
+ build_uri(expression)
94
214
  end
95
215
  end
96
216
 
217
+ # Process a path, such as:
218
+ # :a.:b means [is :b of :a]
219
+ # :a!:b means [is :b of :a]
220
+ # :a^:b means [:b :a]
221
+ #
222
+ # Elements may be strug together, with the last element the verb applied to the previous expression:
223
+ # :a.:b.:c means [is :c of [ is :b of :a]]
224
+ # :a!:b^:c meands [:c [ is :b of :a]]
225
+ def process_path(path)
226
+ add_debug(*path.info("process_path"))
227
+
228
+ object = process_expression(path.pathitem)
229
+
230
+ # Create a list of direction/predicate pairs
231
+ path_list = process_path_list(path.expression, path.respond_to?(:reverse))
232
+ #puts path_list.inspect
233
+ # Now we should have the following
234
+ # [
235
+ # [:forward, b]
236
+ # [:forward, c]
237
+ # ]
238
+ path_list.each do |p|
239
+ reverse, pred = p
240
+ bnode = BNode.new
241
+ if reverse
242
+ add_triple("path(#{reverse})", bnode, pred, object)
243
+ else
244
+ add_triple("path(#{reverse})", object, pred, bnode)
245
+ end
246
+ object = bnode
247
+ end
248
+ object
249
+ end
250
+
251
+ # Returns array of [:forward/:reverse, element] pairs
252
+ def process_path_list(path, reverse)
253
+ add_debug(*path.info("process_path_list(#{reverse})"))
254
+ if path.respond_to?(:pathitem)
255
+ [[reverse, process_expression(path.pathitem)]] + process_path_list(path.expression, path.respond_to?(:reverse))
256
+ else
257
+ [[reverse, process_expression(path)]]
258
+ end
259
+ end
260
+
261
+ def process_uri(uri, normalize = true)
262
+ uri = uri.text_value if uri.respond_to?(:text_value)
263
+ # Use non-normalized URI from @default_ns when constructing URIs
264
+ if uri.match(/^\#/) && @default_ns
265
+ @default_ns + uri.rdf_escape
266
+ else
267
+ base_uri = @default_ns ? @default_ns.uri : @uri
268
+ URIRef.new(uri, base_uri, :normalize => normalize)
269
+ end
270
+ end
271
+
97
272
  def process_properties(properties)
273
+ add_debug(*properties.info("process_properties"))
98
274
  result = []
99
- result << properties if (properties.respond_to? :verb)
100
- result << process_properties(properties.property_list) if (properties.respond_to? :property_list)
275
+ result << properties if properties.respond_to?(:verb)
276
+ result << process_properties(properties.property_list) if properties.respond_to?(:property_list)
101
277
  result.flatten
102
278
  end
103
279
 
104
280
  def process_objects(objects)
281
+ add_debug(*objects.info("process_objects"))
105
282
  result = []
106
- if (objects.respond_to? :object)
107
- result << process_object(objects.object)
108
- else
109
- result << process_object(objects)
283
+ if objects.respond_to?(:object)
284
+ result << process_expression(objects.object)
285
+ elsif objects.respond_to?(:pathitem)
286
+ result << process_expression(objects)
287
+ elsif objects.respond_to?(:expression)
288
+ result << process_expression(objects.expression)
289
+ result << process_objects(objects.path_list) if objects.respond_to?(:path_list)
290
+ elsif !objects.text_value.empty? || objects.respond_to?(:nprefix)
291
+ result << process_expression(objects)
110
292
  end
111
- result << process_objects(objects.object_list) if (objects.respond_to? :object_list)
293
+ result << process_objects(objects.object_list) if objects.respond_to?(:object_list)
112
294
  result.flatten
113
295
  end
114
296
 
115
- def process_object(object)
116
- if (object.respond_to? :localname or object.respond_to? :uri)
117
- process_node(object)
118
- elsif (object.respond_to? :property_list)
119
- process_anonnode(object)
120
- else
121
- process_literal(object)
122
- end
123
- end
124
-
125
297
  def process_literal(object)
298
+ add_debug(*object.info("process_literal"))
126
299
  encoding, language = nil, nil
127
300
  string, type = object.elements
128
301
 
@@ -131,21 +304,72 @@ module RdfContext
131
304
  if (type.elements[0].text_value=='@')
132
305
  language = type.elements[1].text_value
133
306
  else
134
- encoding = type.elements[1].text_value
307
+ encoding = process_expression(type.elements[1])
135
308
  end
136
309
  end
137
310
 
138
311
  # Evaluate text_value to remove redundant escapes
139
312
  #puts string.elements[1].text_value.dump
140
- Literal.n3_encoded(string.elements[1].text_value, language, encoding)
313
+ lit = Literal.n3_encoded(string.elements[1].text_value, language, encoding)
314
+ raise ParserException, %(Typed literal has an invalid lexical value: #{encoding.to_n3} "#{lit.contents}") if @strict && !lit.valid?
315
+ lit
141
316
  end
142
317
 
143
- def build_uri(prefix, localname)
144
- prefix = '__local__' if prefix.nil?
145
- if (prefix=='_')
318
+ def process_numeric_literal(object)
319
+ add_debug(*object.info("process_numeric_literal"))
320
+
321
+ Literal.typed(object.text_value, XSD_NS.send(object.numericliteral))
322
+ end
323
+
324
+ def build_uri(expression)
325
+ prefix = expression.respond_to?(:nprefix) ? expression.nprefix.text_value.to_s : ""
326
+ localname = expression.localname.text_value if expression.respond_to?(:localname)
327
+ localname ||= (expression.respond_to?(:text_value) ? expression.text_value : expression).to_s.sub(/^:/, "")
328
+
329
+ # add_debug(*expression.info("build_uri(#{prefix.inspect}, #{localname.inspect})"))
330
+
331
+ uri = if @graph.nsbinding[prefix]
332
+ @graph.nsbinding[prefix] + localname.to_s.rdf_escape
333
+ elsif prefix == '_'
146
334
  BNode.new(localname, @named_bnodes)
335
+ elsif prefix == "rdf"
336
+ # A special case
337
+ RDF_NS + localname.to_s.rdf_escape
147
338
  else
148
- @graph.nsbinding[prefix].send(localname)
339
+ @default_ns ||= Namespace.new("#{@uri}#", "")
340
+ @default_ns + localname.to_s.rdf_escape
341
+ end
342
+ add_debug(*expression.info("build_uri: #{uri.inspect}")) if expression.respond_to?(:info)
343
+ uri
344
+ end
345
+
346
+ # Is this an allowable keyword?
347
+ def keyword_check(kw)
348
+ unless (@keywords || %w(a is of has)).include?(kw)
349
+ raise ParserException, "unqualified keyword '#{kw}' used without @keyword directive" if @strict
350
+ end
351
+ end
352
+ end
353
+ end
354
+
355
+
356
+ module Treetop
357
+ module Runtime
358
+ class SyntaxNode
359
+ # Brief information about a syntax node
360
+ def info(ctx = "")
361
+ m = self.singleton_methods(true)
362
+ if m.empty?
363
+ ["@#{self.interval.first}", "#{ctx}['#{self.text_value}']"]
364
+ else
365
+ ["@#{self.interval.first}", "#{ctx}[" +
366
+ self.singleton_methods(true).map do |m|
367
+ v = self.send(m)
368
+ v = v.text_value if v.is_a?(SyntaxNode)
369
+ "#{m}='#{v}'"
370
+ end.join(", ") +
371
+ "]"]
372
+ end
149
373
  end
150
374
  end
151
375
  end