yarspg 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b2b77d4c266d555c6bd1174dee2f8b8d789126ab66cde6a5f6abcdb92e6ae676
4
+ data.tar.gz: 5b1db8e4c1b65605d5aaeb285281dd1b070d90934c7dce9da0a8d2e067f57c80
5
+ SHA512:
6
+ metadata.gz: b561d143b1d75f9d8eed956546d77bd50a2dcaa2f37503fa0522ebfc3919c25d5d5c268493f31e3117157f14726e9d9b8f5fb601c6bd8d6c30893498bd0f2807
7
+ data.tar.gz: 775e173535e0bb258ef233db97f63cf4aafa8988869bfb1264136e93314256532b2f306dd980b4859f3b9244f111a3b6e606ea920cda2ec06632fdf6a0302658
@@ -0,0 +1,152 @@
1
+ # YARS-PG Property Graph to RDF* Processor.
2
+
3
+ [YARS-PG][] reader for [RDF.rb][]. Reads [Property Graph][] descriptions and generates [RDF*][] datasets.
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/yarspg.png)](https://badge.fury.io/rb/yarspg)
6
+ [![Build Status](https://secure.travis-ci.org/gkellogg/yarspg.png?branch=master)](https://travis-ci.org/gkellogg/yarspg)
7
+ [![Coverage Status](https://coveralls.io/repos/gkellogg/yarspg/badge.svg)](https://coveralls.io/r/gkellogg/yarspg)
8
+
9
+ ## Features
10
+
11
+ YARSPG parses CSV or other Tabular Data into [RDF*][].
12
+
13
+ * Nodes, properties, and string annotations are treated as document-relative fragments.
14
+ * Edges are emitted only to the default graph, with properties and annotations emitted either to the default graph, or the specifically named graph(s).
15
+ * Node labels are treated as as document-relative `rdf:type` values.
16
+ * List complex values are emitted as `rdf:Lists`.
17
+ * Set and Struct complex values are emitted as `rdf:JSON` literals.
18
+
19
+ ## Installation
20
+ Install with `gem install yarspg`.
21
+
22
+ ## Description
23
+
24
+ YARSPG parses [YARS-PG][] formatted documents into [RDF*][] datasets, where edges are RDF triples in the default graph, which form the subject of the properties and annotations on that edge.
25
+
26
+ There is currently no extra support for [Node Schemas](https://lszeremeta.github.io/yarspg/index.html#dfn-node-schema-declaration) or [Edge Schemas](https://lszeremeta.github.io/yarspg/index.html#dfn-edge-schema-declaration).
27
+
28
+ ## Example
29
+
30
+ An example YARS-PG document follows:
31
+
32
+ # Prefix declaration
33
+ :foaf: <http://xmlns.com/foaf/0.1/>
34
+
35
+ %METADATA
36
+ -foaf:maker: "Łukasz Szeremeta and Dominik Tomaszuk"
37
+
38
+ %NODES
39
+ <"Author01">{"Author"}["fname": "John", "lname": "Smith"] #Author01
40
+ <"Author02">{"Author"}["fname": "Alice", "lname": "Brown"]
41
+ <"EI01">{"Entry", "InProceedings"}["title": "Serialization for...", "numpages": 10, "keyword": "Graph database"]
42
+ <"EA01">{"Entry", "Article"}["title": "Property Graph...", "numpages": 10, "keyword": ["Query", "Graph"]]
43
+ <"Proc01">{"Proceedings"}["title": "BDAS", "year": 2018, "month": "May"]
44
+ <"Jour01">{"Journal"}["title": "J. DB", "year": 2020, "vol": 30]
45
+
46
+ %EDGES
47
+ ("EI01")-{"has_author"}["order": 1]->("Author01")
48
+ ("EI01")-{"has_author"}["order": 2]->("Author02")
49
+ ("EA01")-{"has_author"}["order": 1]->("Author02")
50
+ ("EA01")-{"cites"}->("EI01")
51
+ ("EI01")-{"booktitle"}["pages": "111-121"]->("Proc01")
52
+ ("EA01")-{"published_in"}["pages": "222-232"]->("Jour01")
53
+
54
+ This results in the following TriG*:
55
+
56
+ @prefix foaf: <http://xmlns.com/foaf/0.1/> .
57
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
58
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
59
+
60
+ <> foaf:maker "Łukasz Szeremeta and Dominik Tomaszuk" .
61
+
62
+ <#Author01> a <#Author>; <#fname> "John"; <#lname> "Smith" .
63
+ <#Author02> a <#Author>; <#fname> "Alice"; <#lname> "Brown" .
64
+
65
+ <#EI01> a <#Entry>, <#InProceedings>;
66
+ <#title> "Serialization for...";
67
+ <#numpages> 10;
68
+ <#keyword> "Graph database";
69
+ <#booktitle> <#Proc01>;
70
+ <#has_author> <#Author02>, <#Author01> .
71
+
72
+ <#EA01> a <#Entry>, <#Article>;
73
+ <#title> "Property Graph...";
74
+ <#numpages> 10;
75
+ <#keyword> ("Query" "Graph");
76
+ <#cites> <#EI01>;
77
+ <#has_author> <#Author02>;
78
+ <#published_in> <#Jour01> .
79
+
80
+ <#Proc01> a <#Proceedings>;
81
+ <#month> "May";
82
+ <#title> "BDAS";
83
+ <#year> 2018 .
84
+
85
+ <#Jour01> a <#Journal>;
86
+ <#title> "J. DB";
87
+ <#vol> 30;
88
+ <#year> 2020 .
89
+
90
+ <<<#EI01> <#has_author> <#Author01>>> <#order> 1 .
91
+ <<<#EI01> <#has_author> <#Author02>>> <#order> 2 .
92
+ <<<#EA01> <#has_author> <#Author02>>> <#order> 1 .
93
+ <<<#EI01> <#booktitle> <#Proc01>>> <#pages> "111-121" .
94
+ <<<#EA01> <#published_in> <#Jour01>>> <#pages> "222-232" .
95
+
96
+ ## RDF Reader
97
+ YARS acts as a normal RDF reader, using the standard RDF.rb Reader interface:
98
+
99
+ graph = RDF::Graph.load("etc/doap.yarspg")
100
+
101
+ alternatively
102
+
103
+ graph = RDF::Graph.new {|g| YARSPG::Reader.open("etc/doap.yarspg") {|r| g << r}}
104
+
105
+ ### Principal Classes
106
+ * {YARSPG}
107
+ * {YARSPG::Format}
108
+ * {YARSPG::Metadata}
109
+ * {YARSPG::Reader}
110
+ * {YARSPG::Terminals}
111
+
112
+ ## Dependencies
113
+ * [Ruby](https://ruby-lang.org/) (>= 2.4)
114
+ * [RDF.rb][] (~> 3.1)
115
+ * [EBNF][] (~> 2.0)
116
+
117
+ ## Installation
118
+ The recommended installation method is via [RubyGems](https://rubygems.org/).
119
+ To install the latest official release of the `RDF::Tabular` gem, do:
120
+
121
+ % [sudo] gem install yarspg
122
+
123
+ ## Mailing List
124
+ * <https://lists.w3.org/Archives/Public/public-rdf-ruby/>
125
+
126
+ ## Author
127
+ * [Gregg Kellogg](https://github.com/gkellogg) - <https://greggkellogg.net/>
128
+
129
+ ## Contributing
130
+ * Do your best to adhere to the existing coding conventions and idioms.
131
+ * Don't use hard tabs, and don't leave trailing whitespace on any line.
132
+ * Do document every method you add using [YARD][] annotations. Read the
133
+ [tutorial][YARD-GS] or just look at the existing code for examples.
134
+ * Don't touch the `rdf-tabular.gemspec`, `VERSION` or `AUTHORS` files. If you need to change them, do so on your private branch only.
135
+ * Do feel free to add yourself to the `CREDITS` file and the corresponding list in the the `README`. Alphabetical order applies.
136
+ * Do note that in order for us to merge any non-trivial changes (as a rule of thumb, additions larger than about 15 lines of code), we need an explicit [public domain dedication][PDD] on record from you.
137
+
138
+ ##License
139
+
140
+ This is free and unencumbered public domain software. For more information,
141
+ see <https://unlicense.org/> or the accompanying {file:UNLICENSE} file.
142
+
143
+ [Ruby]: https://ruby-lang.org/
144
+ [RDF]: https://www.w3.org/RDF/
145
+ [YARD]: https://yardoc.org/
146
+ [YARD-GS]: https://rubydoc.info/docs/yard/file/docs/GettingStarted.md
147
+ [PDD]: https://lists.w3.org/Archives/Public/public-rdf-ruby/2010May/0013.html
148
+ [EBNF]: https://rubygems.org/gems/ebnf
149
+ [RDF.rb]: https://rubygems.org/gems/rdf
150
+ [RDF*]: https://lists.w3.org/Archives/Public/public-rdf-star/
151
+ [YARS-PG]: https://lszeremeta.github.io/yarspg/index.html
152
+ [Property Graph]: http://graphdatamodeling.com/Graph%20Data%20Modeling/GraphDataModeling/page/PropertyGraphs.html
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <https://unlicense.org/>
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,25 @@
1
+ require 'rdf'
2
+
3
+ ##
4
+ # **`YARSPG`** is an YARS-PG extension for RDF.rb.
5
+ #
6
+ # @example Requiring the `YARSPG` module
7
+ # require 'yarspg'
8
+ #
9
+ # @example Parsing statements from a YARS-PG file into a graph using RDF*.
10
+ # YARSPG::Reader.open("etc/foaf.yarspg") do |reader|
11
+ # reader.each_statement do |statement|
12
+ # puts statement.inspect
13
+ # end
14
+ # end
15
+ #
16
+ # @see https://rubydoc.info/github/ruby-rdf/rdf/
17
+ # @see https://lszeremeta.github.io/yarspg/index.html
18
+ #
19
+ # @author [Gregg Kellogg](https://greggkellogg.net/)
20
+ module YARSPG
21
+ require 'yarspg/format'
22
+ autoload :Reader, 'yarspg/reader'
23
+ autoload :VERSION, 'yarspg/version'
24
+ autoload :Writer, 'yarspg/writer'
25
+ end
@@ -0,0 +1,42 @@
1
+ module YARSPG
2
+ ##
3
+ # YARS-PG format specification.
4
+ #
5
+ # @example Obtaining an ## format class
6
+ # RDF::Format.for("etc/foaf.yarspg")
7
+ # RDF::Format.for(file_name: "etc/foaf.yarspg")
8
+ # RDF::Format.for(file_extension: "yarspg")
9
+ # RDF::Format.for(content_type: "text/yarspg")
10
+ #
11
+ # @example Obtaining serialization format MIME types
12
+ # RDF::Format.content_types #=> {"text/yarspg" => [YARSPG::Format]}
13
+ #
14
+ # @example Obtaining serialization format file extension mappings
15
+ # RDF::Format.file_extensions #=> {yarspg: "text/yarspg"}
16
+ #
17
+ # @see https://www.w3.org/TR/rdf-testcases/#ntriples
18
+ class Format < RDF::Format
19
+ content_type 'text/yarspg',
20
+ extension: :yarspg
21
+ content_encoding 'utf-8'
22
+
23
+ reader { YARSPG::Reader }
24
+ writer { YARSPG::Writer }
25
+
26
+ ##
27
+ # Sample detection to see if it matches YARS-PG
28
+ #
29
+ # Use a text sample to detect the format of an input file. Sub-classes implement a matcher sufficient to detect probably format matches, including disambiguating between other similar formats.
30
+ #
31
+ # @param [String] sample Beginning several bytes (~ 1K) of input.
32
+ # @return [Boolean]
33
+ def self.detect(sample)
34
+ !!sample.match(%r(
35
+ (?:%(METADATA|NODE SCHEMAS|EDGE SCHEMAS|NODES|EDGES)) | # Section Names
36
+ (?:[S\{.*\}]-) | # Node Schema
37
+ (?:[S\(.*\)]-) | # Edge Schema
38
+ (?:\(.*\)-) # Edge
39
+ )mx)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,176 @@
1
+ # This file is automatically generated by ebnf version 2.0.0
2
+ # Derived from etc/yars-pg.ebnf
3
+ module YARSPG::Meta
4
+ RULES = [
5
+ EBNF::Rule.new(:yarspg, "1", [:star, :statement]).extend(EBNF::PEG::Rule),
6
+ EBNF::Rule.new(:statement, "2", [:alt, :node, :edge, :prefix_directive, :metadata, :node_schema, :edge_schema, :section]).extend(EBNF::PEG::Rule),
7
+ EBNF::Rule.new(:prefix_directive, "3", [:seq, :_prefix_directive_1, :IRI]).extend(EBNF::PEG::Rule),
8
+ EBNF::Rule.new(:_prefix_directive_1, "3.1", [:alt, "::", :pname]).extend(EBNF::PEG::Rule),
9
+ EBNF::Rule.new(:pname, "4", [:seq, ":", :ALNUM_PLUS, ":"]).extend(EBNF::PEG::Rule),
10
+ EBNF::Rule.new(:pn_local, "5", [:seq, :ALNUM_PLUS]).extend(EBNF::PEG::Rule),
11
+ EBNF::Rule.new(:metadata, "6", [:seq, "-", :_metadata_1, :_metadata_2]).extend(EBNF::PEG::Rule),
12
+ EBNF::Rule.new(:_metadata_1, "6.1", [:alt, :_metadata_3, :_metadata_4]).extend(EBNF::PEG::Rule),
13
+ EBNF::Rule.new(:_metadata_3, "6.3", [:seq, :pn_local, :pname]).extend(EBNF::PEG::Rule),
14
+ EBNF::Rule.new(:_metadata_4, "6.4", [:seq, :IRI, ":"]).extend(EBNF::PEG::Rule),
15
+ EBNF::Rule.new(:_metadata_2, "6.2", [:alt, :STRING, :IRI]).extend(EBNF::PEG::Rule),
16
+ EBNF::Rule.new(:graph_name, "6a", [:seq, :STRING]).extend(EBNF::PEG::Rule),
17
+ EBNF::Rule.new(:annotation, "7", [:alt, :string_annotation, :rdf_annotation]).extend(EBNF::PEG::Rule),
18
+ EBNF::Rule.new(:string_annotation, "8", [:seq, :STRING, ":", :STRING]).extend(EBNF::PEG::Rule),
19
+ EBNF::Rule.new(:rdf_annotation, "9", [:seq, :_rdf_annotation_1, :_rdf_annotation_2]).extend(EBNF::PEG::Rule),
20
+ EBNF::Rule.new(:_rdf_annotation_1, "9.1", [:alt, :_rdf_annotation_3, :_rdf_annotation_4]).extend(EBNF::PEG::Rule),
21
+ EBNF::Rule.new(:_rdf_annotation_3, "9.3", [:seq, :pn_local, :pname]).extend(EBNF::PEG::Rule),
22
+ EBNF::Rule.new(:_rdf_annotation_4, "9.4", [:seq, :IRI, ":"]).extend(EBNF::PEG::Rule),
23
+ EBNF::Rule.new(:_rdf_annotation_2, "9.2", [:alt, :STRING, :IRI]).extend(EBNF::PEG::Rule),
24
+ EBNF::Rule.new(:annotations_list, "10", [:seq, "+", :annotation, :_annotations_list_1]).extend(EBNF::PEG::Rule),
25
+ EBNF::Rule.new(:_annotations_list_1, "10.1", [:star, :_annotations_list_2]).extend(EBNF::PEG::Rule),
26
+ EBNF::Rule.new(:_annotations_list_2, "10.2", [:seq, ",", :annotation]).extend(EBNF::PEG::Rule),
27
+ EBNF::Rule.new(:props_list, "11", [:seq, "[", :prop, :_props_list_1, "]"]).extend(EBNF::PEG::Rule),
28
+ EBNF::Rule.new(:_props_list_1, "11.1", [:star, :_props_list_2]).extend(EBNF::PEG::Rule),
29
+ EBNF::Rule.new(:_props_list_2, "11.2", [:seq, ",", :prop]).extend(EBNF::PEG::Rule),
30
+ EBNF::Rule.new(:graphs_list, "12", [:seq, "/", :graph_name, :_graphs_list_1, "/"]).extend(EBNF::PEG::Rule),
31
+ EBNF::Rule.new(:_graphs_list_1, "12.1", [:star, :_graphs_list_2]).extend(EBNF::PEG::Rule),
32
+ EBNF::Rule.new(:_graphs_list_2, "12.2", [:seq, ",", :graph_name]).extend(EBNF::PEG::Rule),
33
+ EBNF::Rule.new(:node, "13", [:seq, "<", :node_id, ">", :_node_1, :_node_2, :_node_3, :_node_4]).extend(EBNF::PEG::Rule),
34
+ EBNF::Rule.new(:_node_1, "13.1", [:opt, :_node_5]).extend(EBNF::PEG::Rule),
35
+ EBNF::Rule.new(:_node_5, "13.5", [:seq, "{", :node_label, :_node_6, "}"]).extend(EBNF::PEG::Rule),
36
+ EBNF::Rule.new(:_node_6, "13.6", [:star, :_node_7]).extend(EBNF::PEG::Rule),
37
+ EBNF::Rule.new(:_node_7, "13.7", [:seq, ",", :node_label]).extend(EBNF::PEG::Rule),
38
+ EBNF::Rule.new(:_node_2, "13.2", [:opt, :props_list]).extend(EBNF::PEG::Rule),
39
+ EBNF::Rule.new(:_node_3, "13.3", [:opt, :graphs_list]).extend(EBNF::PEG::Rule),
40
+ EBNF::Rule.new(:_node_4, "13.4", [:opt, :annotations_list]).extend(EBNF::PEG::Rule),
41
+ EBNF::Rule.new(:edge, "14", [:alt, :directed, :undirected]).extend(EBNF::PEG::Rule),
42
+ EBNF::Rule.new(:section, "15", [:seq, "%", :SECTION_NAME]).extend(EBNF::PEG::Rule),
43
+ EBNF::Rule.new(:directed, "16", [:seq, "(", :node_id, ")", "-", :_directed_1, "{", :edge_label, "}", :_directed_2, "->", "(", :node_id, ")", :_directed_3, :_directed_4]).extend(EBNF::PEG::Rule),
44
+ EBNF::Rule.new(:_directed_1, "16.1", [:opt, :_directed_5]).extend(EBNF::PEG::Rule),
45
+ EBNF::Rule.new(:_directed_5, "16.5", [:seq, "<", :edge_id, ">"]).extend(EBNF::PEG::Rule),
46
+ EBNF::Rule.new(:_directed_2, "16.2", [:opt, :props_list]).extend(EBNF::PEG::Rule),
47
+ EBNF::Rule.new(:_directed_3, "16.3", [:opt, :graphs_list]).extend(EBNF::PEG::Rule),
48
+ EBNF::Rule.new(:_directed_4, "16.4", [:opt, :annotations_list]).extend(EBNF::PEG::Rule),
49
+ EBNF::Rule.new(:undirected, "17", [:seq, "(", :node_id, ")", "-", :_undirected_1, "{", :edge_label, "}", :_undirected_2, "-", "(", :node_id, ")", :_undirected_3, :_undirected_4]).extend(EBNF::PEG::Rule),
50
+ EBNF::Rule.new(:_undirected_1, "17.1", [:opt, :_undirected_5]).extend(EBNF::PEG::Rule),
51
+ EBNF::Rule.new(:_undirected_5, "17.5", [:seq, "<", :edge_id, ">"]).extend(EBNF::PEG::Rule),
52
+ EBNF::Rule.new(:_undirected_2, "17.2", [:opt, :props_list]).extend(EBNF::PEG::Rule),
53
+ EBNF::Rule.new(:_undirected_3, "17.3", [:opt, :graphs_list]).extend(EBNF::PEG::Rule),
54
+ EBNF::Rule.new(:_undirected_4, "17.4", [:opt, :annotations_list]).extend(EBNF::PEG::Rule),
55
+ EBNF::Rule.new(:node_id, "18", [:seq, :STRING]).extend(EBNF::PEG::Rule),
56
+ EBNF::Rule.new(:node_label, "19", [:seq, :STRING]).extend(EBNF::PEG::Rule),
57
+ EBNF::Rule.new(:prop, "20", [:seq, :key, ":", :value]).extend(EBNF::PEG::Rule),
58
+ EBNF::Rule.new(:edge_id, "21", [:seq, :STRING]).extend(EBNF::PEG::Rule),
59
+ EBNF::Rule.new(:edge_label, "22", [:seq, :STRING]).extend(EBNF::PEG::Rule),
60
+ EBNF::Rule.new(:key, "23", [:seq, :STRING]).extend(EBNF::PEG::Rule),
61
+ EBNF::Rule.new(:value, "24", [:alt, :primitive_value, :complex_value]).extend(EBNF::PEG::Rule),
62
+ EBNF::Rule.new(:primitive_value, "25", [:alt, :STRING, :DATETYPE, :NUMBER, :BOOL, "null"]).extend(EBNF::PEG::Rule),
63
+ EBNF::Rule.new(:complex_value, "26", [:alt, :set, :list, :struct]).extend(EBNF::PEG::Rule),
64
+ EBNF::Rule.new(:set, "27", [:seq, "{", :_set_1, :_set_2, "}"]).extend(EBNF::PEG::Rule),
65
+ EBNF::Rule.new(:_set_1, "27.1", [:alt, :primitive_value, :set]).extend(EBNF::PEG::Rule),
66
+ EBNF::Rule.new(:_set_2, "27.2", [:star, :_set_3]).extend(EBNF::PEG::Rule),
67
+ EBNF::Rule.new(:_set_3, "27.3", [:seq, ",", :_set_4]).extend(EBNF::PEG::Rule),
68
+ EBNF::Rule.new(:_set_4, "27.4", [:alt, :primitive_value, :set]).extend(EBNF::PEG::Rule),
69
+ EBNF::Rule.new(:list, "28", [:seq, "[", :_list_1, :_list_2, "]"]).extend(EBNF::PEG::Rule),
70
+ EBNF::Rule.new(:_list_1, "28.1", [:alt, :primitive_value, :list]).extend(EBNF::PEG::Rule),
71
+ EBNF::Rule.new(:_list_2, "28.2", [:star, :_list_3]).extend(EBNF::PEG::Rule),
72
+ EBNF::Rule.new(:_list_3, "28.3", [:seq, ",", :_list_4]).extend(EBNF::PEG::Rule),
73
+ EBNF::Rule.new(:_list_4, "28.4", [:alt, :primitive_value, :list]).extend(EBNF::PEG::Rule),
74
+ EBNF::Rule.new(:struct, "29", [:seq, "{", :key, ":", :_struct_1, :_struct_2, "}"]).extend(EBNF::PEG::Rule),
75
+ EBNF::Rule.new(:_struct_1, "29.1", [:alt, :primitive_value, :struct]).extend(EBNF::PEG::Rule),
76
+ EBNF::Rule.new(:_struct_2, "29.2", [:star, :_struct_3]).extend(EBNF::PEG::Rule),
77
+ EBNF::Rule.new(:_struct_3, "29.3", [:seq, ",", :key, ":", :_struct_4]).extend(EBNF::PEG::Rule),
78
+ EBNF::Rule.new(:_struct_4, "29.4", [:alt, :primitive_value, :struct]).extend(EBNF::PEG::Rule),
79
+ EBNF::Rule.new(:node_schema, "30", [:seq, "S", :_node_schema_1, :_node_schema_2, :_node_schema_3, :_node_schema_4]).extend(EBNF::PEG::Rule),
80
+ EBNF::Rule.new(:_node_schema_1, "30.1", [:seq, "{", :node_label, :_node_schema_5, "}"]).extend(EBNF::PEG::Rule),
81
+ EBNF::Rule.new(:_node_schema_5, "30.5", [:star, :_node_schema_6]).extend(EBNF::PEG::Rule),
82
+ EBNF::Rule.new(:_node_schema_6, "30.6", [:seq, ",", :node_label]).extend(EBNF::PEG::Rule),
83
+ EBNF::Rule.new(:_node_schema_2, "30.2", [:opt, :props_list_schema]).extend(EBNF::PEG::Rule),
84
+ EBNF::Rule.new(:_node_schema_3, "30.3", [:opt, :graphs_list]).extend(EBNF::PEG::Rule),
85
+ EBNF::Rule.new(:_node_schema_4, "30.4", [:opt, :annotations_list]).extend(EBNF::PEG::Rule),
86
+ EBNF::Rule.new(:props_list_schema, "31", [:seq, "[", :prop_schema, :_props_list_schema_1, "]"]).extend(EBNF::PEG::Rule),
87
+ EBNF::Rule.new(:_props_list_schema_1, "31.1", [:star, :_props_list_schema_2]).extend(EBNF::PEG::Rule),
88
+ EBNF::Rule.new(:_props_list_schema_2, "31.2", [:seq, ",", :prop_schema]).extend(EBNF::PEG::Rule),
89
+ EBNF::Rule.new(:prop_schema, "32", [:seq, :key, ":", :value_schema]).extend(EBNF::PEG::Rule),
90
+ EBNF::Rule.new(:value_schema, "33", [:alt, :primitive_value_schema, :complex_value_schema]).extend(EBNF::PEG::Rule),
91
+ EBNF::Rule.new(:primitive_value_schema, "34", [:alt, "Decimal", "SmallInt", "Integer", "BigInt", "Float", "Real", "Double", "Bool", "Null", "String", "Date", "Timestamp", "Time"]).extend(EBNF::PEG::Rule),
92
+ EBNF::Rule.new(:complex_value_schema, "35", [:alt, :set_schema, :list_schema, :struct_schema]).extend(EBNF::PEG::Rule),
93
+ EBNF::Rule.new(:set_schema, "36", [:seq, "Set", "(", :_set_schema_1, ")"]).extend(EBNF::PEG::Rule),
94
+ EBNF::Rule.new(:_set_schema_1, "36.1", [:alt, :primitive_value_schema, :set_schema]).extend(EBNF::PEG::Rule),
95
+ EBNF::Rule.new(:list_schema, "37", [:seq, "List", "(", :_list_schema_1, ")"]).extend(EBNF::PEG::Rule),
96
+ EBNF::Rule.new(:_list_schema_1, "37.1", [:alt, :primitive_value_schema, :list_schema]).extend(EBNF::PEG::Rule),
97
+ EBNF::Rule.new(:struct_schema, "38", [:seq, "Struct", "(", :_struct_schema_1, ")"]).extend(EBNF::PEG::Rule),
98
+ EBNF::Rule.new(:_struct_schema_1, "38.1", [:alt, :primitive_value_schema, :struct_schema]).extend(EBNF::PEG::Rule),
99
+ EBNF::Rule.new(:edge_schema, "39", [:alt, :directed_schema, :undirected_schema]).extend(EBNF::PEG::Rule),
100
+ EBNF::Rule.new(:directed_schema, "40", [:seq, "S", :_directed_schema_1, "-", "{", :edge_label, "}", :_directed_schema_2, "->", :_directed_schema_3]).extend(EBNF::PEG::Rule),
101
+ EBNF::Rule.new(:_directed_schema_1, "40.1", [:opt, :_directed_schema_4]).extend(EBNF::PEG::Rule),
102
+ EBNF::Rule.new(:_directed_schema_4, "40.4", [:seq, "(", :node_label, ")"]).extend(EBNF::PEG::Rule),
103
+ EBNF::Rule.new(:_directed_schema_2, "40.2", [:opt, :props_list_schema]).extend(EBNF::PEG::Rule),
104
+ EBNF::Rule.new(:_directed_schema_3, "40.3", [:opt, :_directed_schema_5]).extend(EBNF::PEG::Rule),
105
+ EBNF::Rule.new(:_directed_schema_5, "40.5", [:seq, "(", :node_label, ")"]).extend(EBNF::PEG::Rule),
106
+ EBNF::Rule.new(:undirected_schema, "41", [:seq, "S", :_undirected_schema_1, "-", "{", :edge_label, "}", :_undirected_schema_2, "-", :_undirected_schema_3]).extend(EBNF::PEG::Rule),
107
+ EBNF::Rule.new(:_undirected_schema_1, "41.1", [:opt, :_undirected_schema_4]).extend(EBNF::PEG::Rule),
108
+ EBNF::Rule.new(:_undirected_schema_4, "41.4", [:seq, "(", :node_label, ")"]).extend(EBNF::PEG::Rule),
109
+ EBNF::Rule.new(:_undirected_schema_2, "41.2", [:opt, :props_list_schema]).extend(EBNF::PEG::Rule),
110
+ EBNF::Rule.new(:_undirected_schema_3, "41.3", [:opt, :_undirected_schema_5]).extend(EBNF::PEG::Rule),
111
+ EBNF::Rule.new(:_undirected_schema_5, "41.5", [:seq, "(", :node_label, ")"]).extend(EBNF::PEG::Rule),
112
+ EBNF::Rule.new(:SECTION_NAME, "42", [:alt, "METADATA", "NODE SCHEMAS", "EDGE SCHEMAS", "NODES", "EDGES"], kind: :terminal).extend(EBNF::PEG::Rule),
113
+ EBNF::Rule.new(:COMMENT, "43", [:seq, "#", :_COMMENT_1], kind: :terminal).extend(EBNF::PEG::Rule),
114
+ EBNF::Rule.new(:_COMMENT_1, "43.1", [:star, :_COMMENT_2], kind: :terminal).extend(EBNF::PEG::Rule),
115
+ EBNF::Rule.new(:_COMMENT_2, "43.2", [:range, "^#xd#xa#xc"], kind: :terminal).extend(EBNF::PEG::Rule),
116
+ EBNF::Rule.new(:STRING, "44", [:seq, :STRING_LITERAL_QUOTE], kind: :terminal).extend(EBNF::PEG::Rule),
117
+ EBNF::Rule.new(:NUMBER, "45", [:seq, :_NUMBER_1, :_NUMBER_2, :_NUMBER_3, :_NUMBER_4], kind: :terminal).extend(EBNF::PEG::Rule),
118
+ EBNF::Rule.new(:_NUMBER_1, "45.1", [:opt, :SIGN], kind: :terminal).extend(EBNF::PEG::Rule),
119
+ EBNF::Rule.new(:_NUMBER_2, "45.2", [:plus, :_NUMBER_5], kind: :terminal).extend(EBNF::PEG::Rule),
120
+ EBNF::Rule.new(:_NUMBER_5, "45.5", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
121
+ EBNF::Rule.new(:_NUMBER_3, "45.3", [:opt, "."], kind: :terminal).extend(EBNF::PEG::Rule),
122
+ EBNF::Rule.new(:_NUMBER_4, "45.4", [:star, :_NUMBER_6], kind: :terminal).extend(EBNF::PEG::Rule),
123
+ EBNF::Rule.new(:_NUMBER_6, "45.6", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
124
+ EBNF::Rule.new(:BOOL, "46", [:alt, "true", "false"], kind: :terminal).extend(EBNF::PEG::Rule),
125
+ EBNF::Rule.new(:STRING_LITERAL_QUOTE, "47", [:seq, "\"", :_STRING_LITERAL_QUOTE_1, "\""], kind: :terminal).extend(EBNF::PEG::Rule),
126
+ EBNF::Rule.new(:_STRING_LITERAL_QUOTE_1, "47.1", [:star, :_STRING_LITERAL_QUOTE_2], kind: :terminal).extend(EBNF::PEG::Rule),
127
+ EBNF::Rule.new(:_STRING_LITERAL_QUOTE_2, "47.2", [:alt, :_STRING_LITERAL_QUOTE_3, "'", "\""], kind: :terminal).extend(EBNF::PEG::Rule),
128
+ EBNF::Rule.new(:_STRING_LITERAL_QUOTE_3, "47.3", [:range, "^\"#xd#xa"], kind: :terminal).extend(EBNF::PEG::Rule),
129
+ EBNF::Rule.new(:ALNUM_PLUS, "48", [:seq, :PN_CHARS_BASE, :_ALNUM_PLUS_1], kind: :terminal).extend(EBNF::PEG::Rule),
130
+ EBNF::Rule.new(:_ALNUM_PLUS_1, "48.1", [:opt, :_ALNUM_PLUS_2], kind: :terminal).extend(EBNF::PEG::Rule),
131
+ EBNF::Rule.new(:_ALNUM_PLUS_2, "48.2", [:seq, :_ALNUM_PLUS_3, :PN_CHARS], kind: :terminal).extend(EBNF::PEG::Rule),
132
+ EBNF::Rule.new(:_ALNUM_PLUS_3, "48.3", [:star, :_ALNUM_PLUS_4], kind: :terminal).extend(EBNF::PEG::Rule),
133
+ EBNF::Rule.new(:_ALNUM_PLUS_4, "48.4", [:alt, :PN_CHARS, "."], kind: :terminal).extend(EBNF::PEG::Rule),
134
+ EBNF::Rule.new(:IRI, "49", [:seq, "<", :_IRI_1, ">"], kind: :terminal).extend(EBNF::PEG::Rule),
135
+ EBNF::Rule.new(:_IRI_1, "49.1", [:star, :_IRI_2], kind: :terminal).extend(EBNF::PEG::Rule),
136
+ EBNF::Rule.new(:_IRI_2, "49.2", [:alt, :PN_CHARS, ".", ":", "/", "\\", "#", "@", "%", "&", :UCHAR], kind: :terminal).extend(EBNF::PEG::Rule),
137
+ EBNF::Rule.new(:PN_CHARS, "50", [:alt, :PN_CHARS_U, :_PN_CHARS_1], kind: :terminal).extend(EBNF::PEG::Rule),
138
+ EBNF::Rule.new(:_PN_CHARS_1, "50.1", [:range, "-0-9#xB7#x0300-#x036F#x203F-#x2040"], kind: :terminal).extend(EBNF::PEG::Rule),
139
+ EBNF::Rule.new(:PN_CHARS_U, "51", [:alt, :PN_CHARS_BASE, "_"], kind: :terminal).extend(EBNF::PEG::Rule),
140
+ EBNF::Rule.new(:DATETYPE, "52", [:alt, :TIMESTAMP, :DATE, :TIME], kind: :terminal).extend(EBNF::PEG::Rule),
141
+ EBNF::Rule.new(:DATE, "53", [:seq, :_DATE_1, :_DATE_2, :_DATE_3, :_DATE_4, "-", :_DATE_5, :_DATE_6, "-", :_DATE_7, :_DATE_8], kind: :terminal).extend(EBNF::PEG::Rule),
142
+ EBNF::Rule.new(:_DATE_1, "53.1", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
143
+ EBNF::Rule.new(:_DATE_2, "53.2", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
144
+ EBNF::Rule.new(:_DATE_3, "53.3", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
145
+ EBNF::Rule.new(:_DATE_4, "53.4", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
146
+ EBNF::Rule.new(:_DATE_5, "53.5", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
147
+ EBNF::Rule.new(:_DATE_6, "53.6", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
148
+ EBNF::Rule.new(:_DATE_7, "53.7", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
149
+ EBNF::Rule.new(:_DATE_8, "53.8", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
150
+ EBNF::Rule.new(:TIME, "54", [:seq, :_TIME_1, :_TIME_2, ":", :_TIME_3, :_TIME_4, ":", :_TIME_5, :_TIME_6, :_TIME_7], kind: :terminal).extend(EBNF::PEG::Rule),
151
+ EBNF::Rule.new(:_TIME_1, "54.1", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
152
+ EBNF::Rule.new(:_TIME_2, "54.2", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
153
+ EBNF::Rule.new(:_TIME_3, "54.3", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
154
+ EBNF::Rule.new(:_TIME_4, "54.4", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
155
+ EBNF::Rule.new(:_TIME_5, "54.5", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
156
+ EBNF::Rule.new(:_TIME_6, "54.6", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
157
+ EBNF::Rule.new(:_TIME_7, "54.7", [:opt, :TIMEZONE], kind: :terminal).extend(EBNF::PEG::Rule),
158
+ EBNF::Rule.new(:TIMEZONE, "55", [:seq, :_TIMEZONE_1, :_TIMEZONE_2, :_TIMEZONE_3, ":", :_TIMEZONE_4, :_TIMEZONE_5], kind: :terminal).extend(EBNF::PEG::Rule),
159
+ EBNF::Rule.new(:_TIMEZONE_1, "55.1", [:opt, :SIGN], kind: :terminal).extend(EBNF::PEG::Rule),
160
+ EBNF::Rule.new(:_TIMEZONE_2, "55.2", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
161
+ EBNF::Rule.new(:_TIMEZONE_3, "55.3", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
162
+ EBNF::Rule.new(:_TIMEZONE_4, "55.4", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
163
+ EBNF::Rule.new(:_TIMEZONE_5, "55.5", [:range, "0-9"], kind: :terminal).extend(EBNF::PEG::Rule),
164
+ EBNF::Rule.new(:TIMESTAMP, "56", [:seq, :DATE, "T", :TIME], kind: :terminal).extend(EBNF::PEG::Rule),
165
+ EBNF::Rule.new(:SIGN, "57", [:alt, "+", "-"], kind: :terminal).extend(EBNF::PEG::Rule),
166
+ EBNF::Rule.new(:UCHAR, "58", [:seq, :_UCHAR_1, :HEX, :HEX, :HEX, :HEX], kind: :terminal).extend(EBNF::PEG::Rule),
167
+ EBNF::Rule.new(:_UCHAR_1, "58.1", [:alt, "u", :_UCHAR_2], kind: :terminal).extend(EBNF::PEG::Rule),
168
+ EBNF::Rule.new(:_UCHAR_2, "58.2", [:seq, "U", :HEX, :HEX, :HEX, :HEX], kind: :terminal).extend(EBNF::PEG::Rule),
169
+ EBNF::Rule.new(:PN_CHARS_BASE, "59", [:range, "A-Za-z0-9#xC0-#xD6#xD8-#xF6#xF8-#x2FF#x370-#x37D#x37F-#x1FFF#x200C-#x200D#x2070-#x218F#x2C00-#x2FEF#x3001-#xD7FF#xF900-#xFDCF#xFDF0-#xFFFD"], kind: :terminal).extend(EBNF::PEG::Rule),
170
+ EBNF::Rule.new(:HEX, "60", [:range, "0-9A-Fa-f"], kind: :terminal).extend(EBNF::PEG::Rule),
171
+ EBNF::Rule.new(:WS, "61", [:plus, :_WS_1], kind: :terminal).extend(EBNF::PEG::Rule),
172
+ EBNF::Rule.new(:_WS_1, "61.1", [:alt, :_WS_2, :COMMENT], kind: :terminal).extend(EBNF::PEG::Rule),
173
+ EBNF::Rule.new(:_WS_2, "61.2", [:range, "#x20#x9#xa"], kind: :terminal).extend(EBNF::PEG::Rule),
174
+ ]
175
+ end
176
+
@@ -0,0 +1,413 @@
1
+ # coding: utf-8
2
+ require 'ebnf'
3
+ require 'yarspg/meta'
4
+ require 'yarspg/terminals'
5
+ require 'json/canonicalization'
6
+
7
+ module YARSPG
8
+ ##
9
+ # A parser for YARS-PG.
10
+ #
11
+ # Parses into RDF*, taking liberties with the meaning of a Property Graph.
12
+ #
13
+ # Issues:
14
+ # * Is it an error to parse data outside of a declared section? (warning?)
15
+ # * Node lables are treated like types
16
+ # * Node properties are treated like statements on that node
17
+ # * Node string annotations are treated like statements with the predicate created as a document fragment.
18
+ # * Edge labels and Edge ids are ignored.
19
+ # * The `graph_name` of each edge is used only for properties and annotations, the edges themselves are in the default graph (per RDF* semantics).
20
+ # * Node and Edge schemas are ignored.
21
+
22
+ class Reader < RDF::Reader
23
+ format Format
24
+ include EBNF::PEG::Parser
25
+ include YARSPG::Meta
26
+ include YARSPG::Terminals
27
+ include RDF::Util::Logger
28
+
29
+ SECTION_ORDERS = {
30
+ METADATA: 0,
31
+ "NODE SCHEMAS": 1,
32
+ "EDGE SCHEMAS": 2,
33
+ NODES: 3,
34
+ EDGES: 4
35
+ }
36
+
37
+ PartialStatement = Struct.new(:predicate, :object)
38
+
39
+ # Terminial definitions
40
+
41
+ # Always return a literal, to distinguish between actual string terminals.
42
+ terminal(:STRING, STRING) {|value| RDF::Literal(value[1..-2])}
43
+ terminal(:NUMBER, NUMBER) do |value|
44
+ value.include?('.') ?
45
+ RDF::Literal::Decimal.new(value) :
46
+ RDF::Literal::Integer.new(value)
47
+ end
48
+
49
+ terminal(:BOOL, BOOL) {|value| RDF::Literal::Boolean.new(value)}
50
+ terminal(:ALNUM_PLUS, ALNUM_PLUS)
51
+ terminal(:IRI, IRI) { |value| base_uri.join(value[1..-2])}
52
+ terminal(:DATE, DATE) {|value| RDF::Literal::Date.new(value)}
53
+ terminal(:TIME, TIME) {|value| RDF::Literal::Time.new(value)}
54
+ terminal(:TIMESTAMP, TIMESTAMP) {|value| RDF::Literal::DateTime.new(value)}
55
+
56
+ # `[3] prefix_directive ::= ('::' | pname) IRI`
57
+ production(:prefix_directive) do |value, data, callback|
58
+ if value.first[:_prefix_directive_1] == '::'
59
+ callback.call(:base_uri, value.last[:IRI])
60
+ else
61
+ pfx = value.first[:_prefix_directive_1].to_sym
62
+ prefixes[pfx] = value.last[:IRI]
63
+ end
64
+ end
65
+
66
+ # `[4] pname ::= ":" ALNUM_PLUS ":"`
67
+ production(:pname) {|value| value[1][:ALNUM_PLUS]}
68
+
69
+ # `[5] pn_local ::= ALNUM_PLUS`
70
+ #
71
+ # This must be a prefix
72
+ production(:pn_local) do |value|
73
+ pfx = value.first[:ALNUM_PLUS].to_sym
74
+ error("pn_local", "no prefix defined for #{pfx}") unless prefixes[pfx]
75
+ prefixes[pfx]
76
+ end
77
+
78
+ # `[6] metadata ::= "-" ((pn_local pname) | (IRI ":")) (STRING | IRI)`
79
+ production(:metadata) do |value, data, callback|
80
+ pred = value[1][:_metadata_1]
81
+ obj = value[2][:_metadata_2]
82
+ callback.call(:statement, :metadata, base_uri, pred, obj)
83
+ nil
84
+ end
85
+ # `(seq pn_local pname)`
86
+ production(:_metadata_3) {|value| value.first[:pn_local].join(value.last[:pname])}
87
+ # `(seq IRI ":")`
88
+ production(:_metadata_4) {|value| value.first[:IRI]}
89
+
90
+ # `[6a] graph_name ::= STRING`
91
+ production(:graph_name) {|value| base_uri.join("##{value.first[:STRING]}")}
92
+
93
+ production(:annotation) {|value| value}
94
+
95
+ # `[8] string_annotation ::= STRING ":" STRING`
96
+ #
97
+ # Treated as an RDF annotation where the string is interpreted as a predicate based on the base_uri.
98
+ production(:string_annotation) do |value|
99
+ pred = base_uri.join("##{value.first[:STRING]}")
100
+ obj = value.last[:STRING]
101
+ PartialStatement.new(pred, obj)
102
+ end
103
+
104
+ # `[9] rdf_annotation ::= ((pn_local pname) | (IRI ":")) (STRING | IRI)`
105
+ #
106
+ # Returns a statement without subject
107
+ production(:rdf_annotation) do |value|
108
+ pred = value.first[:_rdf_annotation_1]
109
+ obj = value.last[:_rdf_annotation_2]
110
+ PartialStatement.new(pred, obj)
111
+ end
112
+ # `(seq pn_local pname)`
113
+ production(:_rdf_annotation_3) {|value| value.first[:pn_local].join(value.last[:pname])}
114
+ # `(seq IRI ":")`
115
+ production(:_rdf_annotation_4) {|value| value.first[:IRI]}
116
+
117
+ # `[10] annotations_list ::= "+" annotation ("," annotation)*`
118
+ production(:annotations_list) do |value|
119
+ value.last[:_annotations_list_1].unshift(value[1][:annotation])
120
+ end
121
+ # `(star _annotations_list_2)`
122
+ production(:_annotations_list_1) {|value| value.map {|al| al[1][:annotation]}}
123
+
124
+ # `[11] props_list ::= "[" prop ("," prop)* "]"`
125
+ production(:props_list) do |value|
126
+ value[2][:_props_list_1].unshift(value[1][:prop])
127
+ end
128
+ # `(star _props_list_2)`
129
+ production(:_props_list_1) {|value| value.map {|prop| prop[1][:prop]}}
130
+
131
+ # `[12] graphs_list ::= "/" graph_name ("," graph_name)* "/"`
132
+ production(:graphs_list) do |value|
133
+ value[2][:_graphs_list_1].unshift(value[1][:graph_name])
134
+ end
135
+ # `seq "," graph_name)`
136
+ production(:_graphs_list_2) {|value| value.last[:graph_name]}
137
+
138
+ # `[13] node ::= "<" node_id ">" ("{" node_label ("," node_label)* "}")? props_list? graphs_list? annotations_list?
139
+ production(:node) do |value, data, callback|
140
+ subject = value[1][:node_id]
141
+ types = Array(value[3][:_node_1])
142
+ props = Array(value[4][:_node_2])
143
+ graphs = Array(value[5][:_node_3])
144
+ annotations = Array(value[6][:_node_4])
145
+
146
+ # Generate statements in named graphs, if present, otherwise, the default graph
147
+ graphs = [false] if graphs.empty?
148
+ graphs.each do |graph_name|
149
+ # Yield statements
150
+ types.each do |type|
151
+ callback.call(:statement, :node, subject, RDF.type, type, graph_name)
152
+ end
153
+ emit_statements(subject, props, graph_name) {|s, p, o, g| callback.call(:statement, :node, s, p, o, g)}
154
+ emit_statements(subject, annotations, graph_name) {|s, p, o, g| callback.call(:statement, :node, s, p, o, g)}
155
+ end
156
+
157
+ nil
158
+ end
159
+ # `(seq "{" node_label _node_6 "}")`
160
+ production(:_node_5) do |value|
161
+ value[2][:_node_6].unshift(value[1][:node_label])
162
+ end
163
+ # `(seq "," node_label)`
164
+ production(:_node_7) {|value| value.last[:node_label]}
165
+
166
+ # `[14] edge ::= directed | undirected`
167
+ #
168
+ # Ignores `edge_id` and `edge_label`. Treats `node_id` as a document-relative fragment.
169
+ production(:edge) do |value, data, callback|
170
+ value[:graphs].each do |graph_name|
171
+ # Statements in graphs, with inverse statement if :undirected
172
+ edges = [RDF::Statement(value[:subject], value[:predicate], value[:object])]
173
+ edges << RDF::Statement(value[:object], value[:predicate], value[:subject]) if value[:undirected]
174
+
175
+ edges.each do |edge|
176
+ # Emit each edge (per RDF*, edges don't have graph names)
177
+ callback.call(:statement, :edge, edge.subject, edge.predicate, edge.object)
178
+ emit_statements(edge, value[:props], graph_name) {|s, p, o, g| callback.call(:statement, :edge, s, p, o, g)}
179
+ emit_statements(edge, value[:annotations], graph_name) {|s, p, o, g| callback.call(:statement, :edge, s, p, o, g)}
180
+ end
181
+ end
182
+ end
183
+
184
+ # `[15] section ::= "%" SECTION_NAME`
185
+ production(:section) do |value|
186
+ # Note the section we're parsing; this can generate a warning if parsing something outside the section (other than the next section), or if seeing past the last section
187
+ section = value.last[:SECTION_NAME].to_sym
188
+ if !@in_section.nil? && @in_section > SECTION_ORDERS[section.to_sym]
189
+ warn("section", "parsing section #{section} out of order.")
190
+ end
191
+ @in_section = SECTION_ORDERS[section.to_sym]
192
+ {section: section.to_sym}
193
+ end
194
+
195
+ # `[16] directed ::= "(" node_id ")" "-" ("<" edge_id ">")? "{" edge_label "}" props_list? "->" "(" node_id ")" graphs_list? annotations_list?`
196
+ production(:directed) do |value|
197
+ {
198
+ subject: value[1][:node_id],
199
+ predicate: value[6][:edge_label],
200
+ object: value[11][:node_id],
201
+ props: Array(value[8][:_directed_2]),
202
+ graphs: Array(value[13][:_directed_3] || false),
203
+ annotations: Array(value[14][ :_directed_4])
204
+ }
205
+ end
206
+
207
+ # `[17] undirected ::= "(" node_id ")" "-" ("<" edge_id ">")? "{" edge_label "}" props_list? "-" "(" node_id ")" graphs_list?` annotations_list?
208
+ production(:undirected) do |value|
209
+ {
210
+ subject: value[1][:node_id],
211
+ predicate: value[6][:edge_label],
212
+ object: value[11][:node_id],
213
+ props: Array(value[8][:_undirected_2]),
214
+ graphs: Array(value[13][:_undirected_3] || false),
215
+ annotations: Array(value[14][ :_undirected_4]),
216
+ undirected: true
217
+ }
218
+ end
219
+
220
+ # `[18] node_id ::= STRING`
221
+ production(:node_id) {|value| base_uri.join("##{value.first[:STRING]}")}
222
+
223
+ # `[19] node_label ::= STRING`
224
+ production(:node_label) {|value| base_uri.join("##{value.first[:STRING]}")}
225
+
226
+ # `[20] prop ::= key ":" value
227
+ #
228
+ # Treated as an String annotation.
229
+ production(:prop) do |value|
230
+ pred = base_uri.join("##{value.first[:key]}")
231
+ obj = value.last[:value]
232
+ PartialStatement.new(pred, obj)
233
+ end
234
+
235
+ # Ignored
236
+ #production(:edge_id) {|value| value}
237
+ production(:edge_label) {|value| base_uri.join("##{value.first[:STRING]}")}
238
+ production(:key) {|value| value.first[:STRING].to_s}
239
+ #production(:value) {|value| value}
240
+
241
+ # `[25] primitive_value ::= STRING | DATETYPE | NUMBER | BOOL | "null"`
242
+ production(:primitive_value) {|value| value == "null" ? RDF.nil : value}
243
+
244
+ # `[26] complex_value ::= set | list | struct`
245
+ #
246
+ # At the start, record that we're from a complex value, so proper RDF values are created only after recursive calls are complete.
247
+ start_production(:complex_value) {|data| data[:from_complex_value] = true}
248
+ production(:complex_value) {|value| value}
249
+
250
+ # 27] set ::= "{" (primitive_value | set) ("," (primitive_value | set))* "}"
251
+ #
252
+ # Because this is recursive, we'll only return a JSON literal if called from complex_value
253
+ production(:set) do |value|
254
+ set = value[2][:_set_2].unshift(value[1][:_set_1])
255
+ if prod_data[:from_complex_value]
256
+ # Wrap value in a literal
257
+ RDF::Literal(set.to_json_c14n, datatype: RDF.JSON)
258
+ else
259
+ set
260
+ end
261
+ end
262
+ # `(alt primitive_value struct)`
263
+ production(:_set_1) {|value| value.is_a?(RDF::Literal) ? value.to_s : value}
264
+ # `(seq "," _set_4)`
265
+ production(:_set_3) {|value| value.last[:_set_4]}
266
+ # `(alt primitive_value struct)`
267
+ production(:_set_4) {|value| value.is_a?(RDF::Literal) ? value.to_s : value}
268
+
269
+ # `[28] list ::= "[" (primitive_value | list) ("," (primitive_value | list))* "]"`
270
+ production(:list) do |value|
271
+ RDF::List(value[2][:_list_2].unshift(value[1][:_list_1]))
272
+ end
273
+ # (star _list_3)
274
+ production(:_list_2) do |value|
275
+ value.map {|li| li.last[:_list_4]}
276
+ end
277
+
278
+ # `[29] struct ::= "{" key ":" (primitive_value | struct) ("," key ":" (primitive_value | struct))* "}"`
279
+ #
280
+ # Because this is recursive, we'll only return a JSON literal if called from complex_value
281
+ production(:struct) do |value|
282
+ struct = {value[1][:key] => value[3][:_struct_1]}.merge(value[4][:_struct_2])
283
+ if prod_data[:from_complex_value]
284
+ # Wrap value in a literal
285
+ RDF::Literal(struct.to_json_c14n, datatype: RDF.JSON)
286
+ else
287
+ struct
288
+ end
289
+ end
290
+ # `(alt primitive_value struct)`
291
+ production(:_struct_1) {|value| value.is_a?(RDF::Literal) ? value.to_s : value}
292
+ # `(star _struct_3)`
293
+ production(:_struct_2) do |value|
294
+ value.inject({}) {|memo, struct| memo.merge(struct)}
295
+ end
296
+ # `(seq "," key ":" _struct_4)`
297
+ production(:_struct_3) {|value| {value[1][:key] => value[3][:_struct_4]}}
298
+ # `(alt primitive_value struct)`
299
+ production(:_struct_4) {|value| value.is_a?(RDF::Literal) ? value.to_s : value}
300
+
301
+ production(:node_schema) {|value| value}
302
+ production(:props_list_schema) {|value| value}
303
+ production(:prop_schema) {|value| value}
304
+ production(:value_schema) {|value| value}
305
+ production(:primitive_value_schema) {|value| value}
306
+ production(:complex_value_schema) {|value| value}
307
+ production(:set_schema) {|value| value}
308
+ production(:list_schema) {|value| value}
309
+ production(:struct_schema) {|value| value}
310
+ production(:edge_schema) {|value| value}
311
+ production(:directed_schema) {|value| value}
312
+ production(:undirected_schema) {|value| value}
313
+
314
+ ##
315
+ # Initializes a new reader instance.
316
+ #
317
+ # This assumes that strings are interpreted as document-relative fragments.
318
+ #
319
+ # @param [String, #to_s] input
320
+ # @param [Hash{Symbol => Object}] options
321
+ # @option options [Hash] :prefixes (Hash.new)
322
+ # the prefix mappings to use (for acessing intermediate parser productions)
323
+ # @option options [#to_s] :base_uri (nil)
324
+ # the base URI to use when resolving relative URIs (for acessing intermediate parser productions)
325
+ # @option options [Boolean] :validate (false)
326
+ # whether to validate the parsed statements and values. If not validating,
327
+ # the parser will attempt to recover from errors.
328
+ # @option options [Logger, #write, #<<] :logger
329
+ # Record error/info/debug output
330
+ # @return [YARSPG::Reader]
331
+ def initialize(input = nil, **options, &block)
332
+ super do
333
+ @options[:base_uri] = RDF::URI(base_uri || "")
334
+ @options[:logger] = false unless @options.has_key?(:logger)
335
+ log_debug("base IRI") {base_uri.inspect}
336
+
337
+ if block_given?
338
+ case block.arity
339
+ when 0 then instance_eval(&block)
340
+ else block.call(self)
341
+ end
342
+ end
343
+ end
344
+ end
345
+
346
+ def inspect
347
+ sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, base_uri.to_s)
348
+ end
349
+
350
+ ##
351
+ # Iterates the given block for each RDF statement in the input.
352
+ #
353
+ # @yield [statement]
354
+ # @yieldparam [RDF::Statement] statement
355
+ # @return [void]
356
+ def each_statement(&block)
357
+ if block_given?
358
+ log_recover
359
+ @callback = block
360
+
361
+ begin
362
+ parse(@input, :yarspg, YARSPG::Meta::RULES, **@options) do |context, *data|
363
+ case context
364
+ when :base_uri
365
+ @options[:base_uri] = data.first
366
+ when :statement
367
+ loc = data.shift
368
+ @callback.call(RDF::Statement.from(data))
369
+ end
370
+ end
371
+ rescue EBNF::PEG::Parser::Error
372
+ # Terminate loop if Errors found while parsing
373
+ end
374
+
375
+ if validate? && log_statistics[:error]
376
+ raise RDF::ReaderError, "Errors found during processing"
377
+ end
378
+ end
379
+ enum_for(:each_statement)
380
+ end
381
+
382
+ ##
383
+ # Iterates the given block for each RDF triple in the input.
384
+ #
385
+ # @yield [subject, predicate, object]
386
+ # @yieldparam [RDF::Resource] subject
387
+ # @yieldparam [RDF::URI] predicate
388
+ # @yieldparam [RDF::Value] object
389
+ # @return [void]
390
+ def each_triple(&block)
391
+ if block_given?
392
+ each_statement do |statement|
393
+ block.call(*statement.to_triple)
394
+ end
395
+ end
396
+ enum_for(:each_triple)
397
+ end
398
+
399
+ # Emit statements, accounting for lists
400
+ def emit_statements(subject, partials, graph_name)
401
+ partials.each do |partial|
402
+ if partial.object.list?
403
+ yield(subject, partial.predicate, partial.object.subject, graph_name)
404
+ partial.object.each_statement do |st|
405
+ yield(st.subject, st.predicate, st.object, graph_name)
406
+ end
407
+ else
408
+ yield(subject, partial.predicate, partial.object, graph_name)
409
+ end
410
+ end
411
+ end
412
+ end
413
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ # Terminal definitions for EBNF Parser
3
+ module YARSPG::Terminals
4
+ U_CHARS1 = Regexp.compile(<<-EOS.gsub(/\s+/, ''))
5
+ [\\u00C0-\\u00D6]|[\\u00D8-\\u00F6]|[\\u00F8-\\u02FF]|
6
+ [\\u0370-\\u037D]|[\\u037F-\\u1FFF]|[\\u200C-\\u200D]|
7
+ [\\u2070-\\u218F]|[\\u2C00-\\u2FEF]|[\\u3001-\\uD7FF]|
8
+ [\\uF900-\\uFDCF]|[\\uFDF0-\\uFFFD]|[\\u{10000}-\\u{EFFFF}]
9
+ EOS
10
+ U_CHARS2 = Regexp.compile("\\u00B7|[\\u0300-\\u036F]|[\\u203F-\\u2040]", Regexp::FIXEDENCODING).freeze
11
+ IRI_RANGE = Regexp.compile("[[^<>\"{}|^`\\\\]&&[^\\x00-\\x20]]", Regexp::FIXEDENCODING).freeze
12
+ UCHAR = EBNF::LL1::Lexer::UCHAR
13
+
14
+ STRING_LITERAL_QUOTE = /"([^\"\\\n\r]|#{UCHAR})*"/.freeze
15
+ SIGN = %r([\+\-])u.freeze
16
+ PN_CHARS_BASE = %r([A-Z]|[a-z]|[0-9]|#{U_CHARS1})u.freeze
17
+ PN_CHARS_U = %r(_|(?:#{PN_CHARS_BASE}))u.freeze
18
+ PN_CHARS = %r(-|[0-9]|(?:#{PN_CHARS_U})|#{U_CHARS2})u.freeze
19
+ STRING = %r(#{STRING_LITERAL_QUOTE})u.freeze
20
+ NUMBER = %r((?:#{SIGN})?\d+(\.\d*)?)u.freeze
21
+ BOOL = %r(true|false)u.freeze
22
+ ALNUM_PLUS = %r((?:#{PN_CHARS_BASE})(?:(?:(?:#{PN_CHARS})|\.)*(?:#{PN_CHARS}))?)u.freeze
23
+ IRI = /<(?:(?:#{IRI_RANGE})|(?:#{UCHAR}))*>/u.freeze # prob with grammar
24
+ DATE = %r(\d\d\d\d-\d\d-\d\d)u.freeze
25
+ TIMEZONE = %r((?:#{SIGN})?\d\d:\d\d)u.freeze
26
+ TIME = %r(\d\d:\d\d:\d\d(?:#{TIMEZONE})?)u.freeze
27
+ TIMESTAMP = %r((?:#{DATE})T(?:#{TIME}))u.freeze
28
+ HEX = %r(\#x[0-9a-fA-F]+)u.freeze
29
+ COMMENT = /(?:#[^\n\r\r]*)/u.freeze
30
+ WS = /(?:\s|(?:#{COMMENT}))+/m.freeze
31
+ end
File without changes
@@ -0,0 +1,10 @@
1
+ module YARSPG
2
+ ##
3
+ # A YARS-PG serialiser
4
+ #
5
+ # @author [Gregg Kellogg](https://greggkellogg.net/)
6
+ class Writer < RDF::Writer
7
+ include RDF::Util::Logger
8
+ format YARSPG::Format
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yarspg
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Gregg Kellogg
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-06-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rdf
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ebnf
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: json-canonicalization
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sxp
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rdf-xsd
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.1'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rdf-spec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.1'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.9'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.9'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec-its
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.3'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.3'
125
+ - !ruby/object:Gem::Dependency
126
+ name: yard
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.9.20
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.9.20
139
+ description: YARSPG is an YARS-PG reader/writer for the RDF.rb library suite.
140
+ email: gregg@greggkellogg.net
141
+ executables: []
142
+ extensions: []
143
+ extra_rdoc_files: []
144
+ files:
145
+ - README.md
146
+ - UNLICENSE
147
+ - VERSION
148
+ - lib/yarspg.rb
149
+ - lib/yarspg/format.rb
150
+ - lib/yarspg/meta.rb
151
+ - lib/yarspg/reader.rb
152
+ - lib/yarspg/terminals.rb
153
+ - lib/yarspg/version.rb
154
+ - lib/yarspg/writer.rb
155
+ homepage: https://github.com/gkellogg/yarspg
156
+ licenses:
157
+ - Unlicense
158
+ metadata: {}
159
+ post_install_message:
160
+ rdoc_options: []
161
+ require_paths:
162
+ - lib
163
+ required_ruby_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '2.4'
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ requirements: []
174
+ rubygems_version: 3.1.3
175
+ signing_key:
176
+ specification_version: 4
177
+ summary: YARS-PG reader/writer for RDF.rb.
178
+ test_files: []