yarspg 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []