• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ruby-rdf / rdf-trig / 14785116514

01 May 2025 11:04PM UTC coverage: 92.887% (+0.03%) from 92.857%
14785116514

push

github

gkellogg
Updates for TriG grammar and 1.2 semantics.

1 of 1 new or added line in 1 file covered. (100.0%)

222 of 239 relevant lines covered (92.89%)

187.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.84
/lib/rdf/trig/reader.rb
1
require 'rdf/turtle'
2✔
2

3
module RDF::TriG
2✔
4
  ##
5
  # A parser for the TriG
6
  #
7
  # Leverages the Turtle reader
8
  class Reader < RDF::Turtle::Reader
2✔
9
    format Format
2✔
10

11
    # Terminals passed to lexer. Order matters!
12
    terminal(:ANON,                             ANON)
2✔
13
    terminal(:BLANK_NODE_LABEL,                 BLANK_NODE_LABEL)
2✔
14
    terminal(:IRIREF,                           IRIREF, unescape:  true)
2✔
15
    terminal(:DOUBLE,                           DOUBLE)
2✔
16
    terminal(:DECIMAL,                          DECIMAL)
2✔
17
    terminal(:INTEGER,                          INTEGER)
2✔
18
    terminal(:PNAME_LN,                         PNAME_LN, unescape:  true)
2✔
19
    terminal(:PNAME_NS,                         PNAME_NS)
2✔
20
    terminal(:STRING_LITERAL_LONG_SINGLE_QUOTE, STRING_LITERAL_LONG_SINGLE_QUOTE, unescape:  true, partial_regexp: /^'''/)
2✔
21
    terminal(:STRING_LITERAL_LONG_QUOTE,        STRING_LITERAL_LONG_QUOTE,        unescape:  true, partial_regexp: /^"""/)
2✔
22
    terminal(:STRING_LITERAL_QUOTE,             STRING_LITERAL_QUOTE,             unescape:  true)
2✔
23
    terminal(:STRING_LITERAL_SINGLE_QUOTE,      STRING_LITERAL_SINGLE_QUOTE,      unescape:  true)
2✔
24
 
25
    # String terminals
26
    terminal(nil,                               %r(
2✔
27
                                                    <<\(|\)>>
28
                                                  | [\(\),.;~\[\]Aa]
29
                                                  | \^\^
30
                                                  | \{\|
31
                                                  | \|\}
32
                                                  | [\{\}]
33
                                                  | true|false
34
                                                  | <<|>>
35
                                                )x)
36

37
    terminal(:GRAPH,                            /graph/i)
2✔
38
    terminal(:PREFIX,                           PREFIX)
2✔
39
    terminal(:BASE,                             BASE)
2✔
40
    terminal(:VERSION,                          RDF_VERSION)
2✔
41
    terminal(:LANG_DIR,                         LANG_DIR)
2✔
42

43
    ##
44
    # Iterates the given block for each RDF statement in the input.
45
    #
46
    # @yield  [statement]
47
    # @yieldparam [RDF::Statement] statement
48
    # @return [void]
49
    def each_statement(&block)
2✔
50
      if block_given?
658✔
51
        @recovering = false
560✔
52
        @callback = block
560✔
53

54
        begin
55
          while (@lexer.first rescue true)
2,286✔
56
            read_trigDoc
1,244✔
57
          end
58
        rescue EBNF::LL1::Lexer::Error, SyntaxError, EOFError, Recovery
59
          # Terminate loop if EOF found while recovering
60
        end
61

62
        if validate? && log_statistics[:error]
558✔
63
          raise RDF::ReaderError, "Errors found during processing"
118✔
64
        end
65
      end
66
      enum_for(:each_statement)
538✔
67
    end
68

69
    ##
70
    # Iterates the given block for each RDF quad in the input.
71
    #
72
    # @yield  [subject, predicate, object, graph_name]
73
    # @yieldparam [RDF::Resource] subject
74
    # @yieldparam [RDF::URI]      predicate
75
    # @yieldparam [RDF::Value]    object
76
    # @yieldparam [RDF::URI]      graph_name
77
    # @return [void]
78
    def each_quad(&block)
2✔
79
      if block_given?
6✔
80
        each_statement do |statement|
4✔
81
          block.call(*statement.to_quad)
180✔
82
        end
83
      end
84
      enum_for(:each_quad)
6✔
85
    end
86

87
    # add a statement, object can be literal or URI or bnode
88
    #
89
    # @param [Symbol] production
90
    # @param [RDF::Statement] statement the subject of the statement
91
    # @return [RDF::Statement] Added statement
92
    # @raise [RDF::ReaderError] Checks parameter types and raises if they are incorrect if parsing mode is _validate_.
93
    def add_statement(production, statement)
2✔
94
      error("Statement is invalid: #{statement.inspect.inspect}", production: produciton) if validate? && statement.invalid?
2,098✔
95
      statement.graph_name = @graph_name if @graph_name
2,098✔
96
      @callback.call(statement) if statement.subject &&
4,196✔
97
                                   statement.predicate &&
98
                                   statement.object &&
99
                                   (validate? ? statement.valid? : true)
2,098✔
100
    end
101

102
  protected
2✔
103
    # @return [Object]
104
    def read_trigDoc
2✔
105
      prod(:trigDoc, %(} .)) do
1,244✔
106
        read_directive || read_block
1,238✔
107
      end
108
    end
109

110
    # @return [Object]
111
    def read_block
2✔
112
      prod(:block, %(})) do
706✔
113
        @graph_name = nil
706✔
114
        token = @lexer.first
706✔
115
        case token && (token.type || token.value)
1,412✔
116
        when :GRAPH
117
          @lexer.shift
42✔
118
          @graph_name = read_labelOrSubject || error("Expected label or subject", production: :block, token: @lexer.first)
42✔
119
          read_wrappedGraph || error("Expected wrappedGraph", production: :block, token: @lexer.first)
36✔
120
          @graph_name = nil
28✔
121
        when :IRIREF, :BLANK_NODE_LABEL, :ANON, :PNAME_LN, :PNAME_NS
122
          read_triplesOrGraph || error("Expected triplesOrGraph", production: :block, token: @lexer.first)
336✔
123
        when '{'
124
          read_wrappedGraph || error("Expected wrappedGraph", production: :block, token: @lexer.first)
250✔
125
        when '(', '[', '<<'
126
          read_triples2 || error("Expected collection or blankNodePropertyList", production: :block, token: @lexer.first)
52✔
127
        when nil
128
          # End of input
129
        else
130
          error("Unexpected token: #{@lexer.first.inspect}", production: :block, token: @lexer.first)
26✔
131
        end
132
      end
133
    end
134

135
    # @return [Object]
136
    def read_triplesOrGraph
2✔
137
      if name = read_labelOrSubject
336✔
138
        prod(:triplesOrGraph, %(} .)) do
336✔
139
          # labelOrSubject ( wrappedGraph | predicateObjectList '.' )
140
          token = @lexer.first
336✔
141
          case token && token.value
336✔
142
          when '{'
143
            # wrappedGraph
144
            @graph_name = name
86✔
145
            read_wrappedGraph || error("Expected wrappedGraph", production: :triplesOrGraph, token: @lexer.first)
86✔
146
            @graph_name = nil
82✔
147
            true
82✔
148
          else
149
            # predicateObjectList '.'
150
            read_predicateObjectList(name) || error("Expected predicateObjectList", production: :triplesOrGraph, token: @lexer.first)
250✔
151
            unless @recovering
240✔
152
              # If recovering, we will have eaten the closing '.'
153
              token = @lexer.shift
240✔
154
              unless token && token.value == '.'
240✔
155
                error("Expected '.' following triple", production: :triplesOrGraph, token: token)
×
156
              end
157
            end
158
          end
159
        end
160
      elsif name = read_reifiedTriple
×
161
        prod(:triplesOrGraph, %(} .)) do
×
162
          # reifiedTriple predicateObjectList? '.'
163
          read_predicateObjectList(name)
×
164
          unless @recovering
×
165
            # If recovering, we will have eaten the closing '.'
166
            token = @lexer.shift
×
167
            unless token && token.value == '.'
×
168
              error("Expected '.' following triple", production: :triplesOrGraph, token: token)
×
169
            end
170
          end
171
        end
172
      end
173
      !!name
322✔
174
    end
175

176
    # @return [Object]
177
    def read_triples2
2✔
178
      token = @lexer.first
52✔
179
      case token && token.value
52✔
180
      when '['
181
        # blankNodePropertyList predicateObjectList? '.'
182
        prod(:triples2) do
10✔
183
          # blankNodePropertyList predicateObjectList? 
184
          subject = read_blankNodePropertyList || error("Failed to parse blankNodePropertyList", production: :triples2, token: @lexer.first)
10✔
185
          read_predicateObjectList(subject)
10✔
186
          if !@recovering || @lexer.first === '.'
10✔
187
            # If recovering, we will have eaten the closing '.'
188
            token = @lexer.shift
10✔
189
            unless token && token.value == '.'
10✔
190
              error("Expected '.' following triple", production: :triples2, token: token)
2✔
191
            end
192
          end
193
          true
8✔
194
        end
195
      when '<<'
196
        prod(:triples2) do
32✔
197
          subject = read_reifiedTriple || error("Failed to parse reifiedTriple", production: :triples2, token: @lexer.first)
32✔
198
          read_predicateObjectList(subject) || subject
30✔
199
          if !@recovering || @lexer.first === '.'
30✔
200
            # If recovering, we will have eaten the closing '.'
201
            token = @lexer.shift
30✔
202
            unless token && token.value == '.'
30✔
203
              error("Expected '.' following triple", production: :triples2, token: token)
×
204
            end
205
          end
206
          true
30✔
207
        end
208
      when '('
209
        # collection predicateObjectList '.'
210
        prod(:triples2) do
10✔
211
          subject = read_collection || error("Failed to parse read_collection", production: :triples2, token: @lexer.first)
10✔
212
          token = @lexer.first
10✔
213
          case token && (token.type || token.value)
20✔
214
          when 'a', :IRIREF, :PNAME_LN, :PNAME_NS then read_predicateObjectList(subject)
6✔
215
          else error("Expected predicateObjectList after collection subject", production: :triples2, token: token)
4✔
216
          end
217
          if !@recovering || @lexer.first === '.'
4✔
218
            # If recovering, we will have eaten the closing '.'
219
            token = @lexer.shift
4✔
220
            unless token && token.value == '.'
4✔
221
              error("Expected '.' following triple", production: :triples2, token: token)
×
222
            end
223
          end
224
          true
4✔
225
        end
226
      else
227
        false
×
228
      end
229
    end
230

231
    # @return [Object]
232
    def read_wrappedGraph
2✔
233
      token = @lexer.first
372✔
234
      if token && token.value == '{'
372✔
235
        prod(:wrappedGraph, %w(})) do
366✔
236
          @lexer.shift
366✔
237
          while read_triplesBlock
366✔
238
            # Read until nothing found
239
          end
240
          if !@recovering || @lexer.first === '}'
324✔
241
            # If recovering, we will have eaten the closing '}'
242
            token = @lexer.shift
324✔
243
            unless token && token.value == '}'
324✔
244
              error("Expected '}' following triple", production: :wrappedGraph, token: token)
×
245
            end
246
          end
247
          true
324✔
248
        end
249
      end
250
    end
251

252
    # @return [Object]
253
    def read_triplesBlock
2✔
254
      prod(:triplesBlock, %w(.)) do
378✔
255
        while (token = @lexer.first) && token.value != '}' && read_triples
1,054✔
256
          unless log_recovering?
396✔
257
            break unless @lexer.first === '.'
340✔
258
            @lexer.shift
310✔
259
          end
260
        end
261
      end
262
    end
263

264
    # @return [RDF::Resource]
265
    def read_labelOrSubject
2✔
266
      prod(:labelOrSubject) do
378✔
267
        read_iri || read_BlankNode
378✔
268
      end
269
    end
270

271
  end # class Reader
272
end # module RDF::Turtle
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc