Class: RDF::Turtle::Reader

Inherits:
Reader
  • Object
show all
Includes:
EBNF::LL1::Parser, Terminals, Util::Logger
Defined in:
lib/rdf/turtle/reader.rb

Overview

A parser for the Turtle 2

Defined Under Namespace

Classes: Recovery, SyntaxError

Constant Summary

Constants included from Terminals

Terminals::ANON, Terminals::BASE, Terminals::BLANK_NODE_LABEL, Terminals::DECIMAL, Terminals::DOUBLE, Terminals::ECHAR, Terminals::EXPONENT, Terminals::INTEGER, Terminals::IRIREF, Terminals::IRI_RANGE, Terminals::LANG_DIR, Terminals::PERCENT, Terminals::PLX, Terminals::PNAME_LN, Terminals::PNAME_NS, Terminals::PN_CHARS, Terminals::PN_CHARS_BASE, Terminals::PN_CHARS_BODY, Terminals::PN_CHARS_U, Terminals::PN_LOCAL, Terminals::PN_LOCAL_BODY, Terminals::PN_LOCAL_ESC, Terminals::PN_PREFIX, Terminals::PREFIX, Terminals::STRING_LITERAL_LONG_QUOTE, Terminals::STRING_LITERAL_LONG_SINGLE_QUOTE, Terminals::STRING_LITERAL_QUOTE, Terminals::STRING_LITERAL_SINGLE_QUOTE, Terminals::UCHAR, Terminals::U_CHARS1, Terminals::U_CHARS2, Terminals::WS

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input = nil, **options, &block) ⇒ RDF::Turtle::Reader

Initializes a new reader instance.

Note, the spec does not define a default mapping for the empty prefix, but it is so commonly used in examples that we define it to be the empty string anyway, except when validating.

Parameters:

  • input (String, #to_s) (defaults to: nil)
  • options (Hash{Symbol => Object})

Options Hash (**options):

  • :prefixes (Hash) — default: Hash.new

    the prefix mappings to use (for acessing intermediate parser productions)

  • :base_uri (#to_s) — default: nil

    the base URI to use when resolving relative URIs (for acessing intermediate parser productions)

  • :anon_base (#to_s) — default: "b0"

    Basis for generating anonymous Nodes

  • :validate (Boolean) — default: false

    whether to validate the parsed statements and values. If not validating, the parser will attempt to recover from errors.

  • :logger (Logger, #write, #<<)

    Record error/info/debug output

  • :freebase (Boolean) — default: false

    Use optimized Freebase reader



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/rdf/turtle/reader.rb', line 92

def initialize(input = nil, **options, &block)
  super do
    @options = {
      anon_base:  "b0",
      whitespace:  WS,
      depth: 0,
    }.merge(@options)
    @prod_stack = []

    @options[:base_uri] = RDF::URI(base_uri || "")
    debug("base IRI") {base_uri.inspect}
    
    debug("validate") {validate?.inspect}
    debug("canonicalize") {canonicalize?.inspect}
    debug("intern") {intern?.inspect}

    @lexer = EBNF::LL1::Lexer.new(input, self.class.patterns, **@options)

    if block_given?
      case block.arity
        when 0 then instance_eval(&block)
        else block.call(self)
      end
    end
  end
end

Class Method Details

.optionsObject

Reader options



44
45
46
47
48
49
50
51
52
# File 'lib/rdf/turtle/reader.rb', line 44

def self.options
  super + [
    RDF::CLI::Option.new(
      symbol: :freebase,
      datatype: TrueClass,
      on: ["--freebase"],
      description: "Use optimized Freebase reader.") {true},
  ]
end

Instance Method Details

#add_statement(production, statement) ⇒ RDF::Statement

add a statement, object can be literal or URI or bnode

Parameters:

  • production (Symbol)
  • statement (RDF::Statement)

    the subject of the statement

Returns:

  • (RDF::Statement)

    Added statement

Raises:

  • (RDF::ReaderError)

    Checks parameter types and raises if they are incorrect if parsing mode is validate.



172
173
174
175
176
177
178
# File 'lib/rdf/turtle/reader.rb', line 172

def add_statement(production, statement)
  error("Statement is invalid: #{statement.inspect}", production: produciton) if validate? && statement.invalid?
  @callback.call(statement) if statement.subject &&
                               statement.predicate &&
                               statement.object &&
                               (validate? ? statement.valid? : true)
end

#bnode(value = nil) ⇒ Object

Keep track of allocated BNodes



243
244
245
246
247
# File 'lib/rdf/turtle/reader.rb', line 243

def bnode(value = nil)
  return RDF::Node.new unless value
  @bnode_cache ||= {}
  @bnode_cache[value.to_s] ||= RDF::Node.new(value)
end

#debug(*args, &block) ⇒ Object (protected)



636
637
638
639
640
641
642
# File 'lib/rdf/turtle/reader.rb', line 636

def debug(*args, &block)
  lineno = (options[:token].lineno if options[:token].respond_to?(:lineno)) || (@lexer && @lexer.lineno)
  opts = args.last.is_a?(Hash) ? args.pop : {}
  opts[:level] ||= 0
  opts[:lineno] ||= lineno
  log_debug(*args, **opts, &block)
end

#each_statement {|statement| ... }

This method returns an undefined value.

Iterates the given block for each RDF statement in the input.

Yields:

  • (statement)

Yield Parameters:

  • statement (RDF::Statement)


129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/rdf/turtle/reader.rb', line 129

def each_statement(&block)
  if block_given?
    log_recover
    @callback = block

    begin
      while (@lexer.first rescue true)
        read_statement
      end
    rescue EBNF::LL1::Lexer::Error, SyntaxError, EOFError, Recovery
      # Terminate loop if EOF found while recovering
    end

    if validate? && log_statistics[:error]
      raise RDF::ReaderError, "Errors found during processing"
    end
  end
  enum_for(:each_statement)
end

#each_triple {|subject, predicate, object| ... }

This method returns an undefined value.

Iterates the given block for each RDF triple in the input.

Yields:

  • (subject, predicate, object)

Yield Parameters:

  • subject (RDF::Resource)
  • predicate (RDF::URI)
  • object (RDF::Value)


157
158
159
160
161
162
163
164
# File 'lib/rdf/turtle/reader.rb', line 157

def each_triple(&block)
  if block_given?
    each_statement do |statement|
      block.call(*statement.to_triple)
    end
  end
  enum_for(:each_triple)
end

#error(node, message, options) ⇒ Object (protected)

Error information, used as level 0 debug messages.

Parameters:

  • node (String)

    Relevant location associated with message

  • message (String)

    Error string

  • options (Hash)

Options Hash (options):

  • :production (URI, #to_s)
  • :token (Token)

See Also:

  • RDF::Turtle::Reader.{{#debug}


654
655
656
657
658
659
660
661
662
663
664
665
# File 'lib/rdf/turtle/reader.rb', line 654

def error(*args)
  ctx = ""
  ctx += "(found #{options[:token].inspect})" if options[:token]
  ctx += ", production = #{options[:production].inspect}" if options[:production]
  lineno = @lineno || (options[:token].lineno if options[:token].respond_to?(:lineno)) || @lexer.lineno
  log_error(*args, ctx,
    lineno:     lineno,
    token:      options[:token],
    production: options[:production],
    depth:      options[:depth],
    exception:  SyntaxError,)
end

#inspectObject



119
120
121
# File 'lib/rdf/turtle/reader.rb', line 119

def inspect
  sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, base_uri.to_s)
end

#literal(value, **options) ⇒ Object

Create a literal



194
195
196
197
198
199
200
201
202
203
204
# File 'lib/rdf/turtle/reader.rb', line 194

def literal(value, **options)
  debug("literal", depth: @options[:depth]) do
    "value: #{value.inspect}, " +
    "options: #{options.inspect}, " +
    "validate: #{validate?.inspect}, " +
    "c14n?: #{canonicalize?.inspect}"
  end
  RDF::Literal.new(value, validate:  validate?, canonicalize:  canonicalize?, **options)
rescue ArgumentError => e
  error("Argument Error #{e.message}", production: :literal, token: @lexer.first)
end

#pname(prefix, suffix) ⇒ Object

Expand a PNAME using string concatenation



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/rdf/turtle/reader.rb', line 220

def pname(prefix, suffix)
  # Prefixes must be defined, except special case for empty prefix being alias for current @base
  base = if prefix(prefix)
    prefix(prefix).to_s
  elsif prefix.to_s.empty? && !validate?
    base_uri.to_s
  else
    error("undefined prefix", production: :pname, token: prefix)
    ''
  end

  # Unescape PN_LOCAL_ESC
  suffix = suffix.gsub(PN_LOCAL_ESC) {|esc| esc[1]} if
    suffix.match?(PN_LOCAL_ESC)

  # Remove any redundant leading hash from suffix
  suffix = suffix.sub(/^\#/, "") if base.index("#")

  debug("pname", depth: options[:depth]) {"base: '#{base}', suffix: '#{suffix}'"}
  process_iri(base + suffix.to_s)
end

#prefix(prefix, iri = nil) ⇒ Object

Override #prefix to take a relative IRI

prefix directives map a local name to an IRI, also resolved against the current In-Scope Base URI. Spec confusion, presume that an undefined empty prefix has an empty relative IRI, which uses string contatnation rules against the in-scope IRI at the time of use



212
213
214
215
216
# File 'lib/rdf/turtle/reader.rb', line 212

def prefix(prefix, iri = nil)
  # Relative IRIs are resolved against @base
  iri = process_iri(iri) if iri
  super(prefix, iri)
end

#process_iri(iri) ⇒ Object

Process a URI against base



181
182
183
184
185
186
187
188
189
190
191
# File 'lib/rdf/turtle/reader.rb', line 181

def process_iri(iri)
  iri = iri.value[1..-2] if iri === :IRIREF
  value = RDF::URI(iri)
  value = base_uri.join(value) if value.relative?
  value.validate! if validate?
  value.canonicalize! if canonicalize? && !value.frozen?
  value = RDF::URI.intern(value) if intern?
  value
rescue ArgumentError => e
  error("process_iri", e)
end

#prod(production, recover_to = []) ⇒ Object (protected)



566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
# File 'lib/rdf/turtle/reader.rb', line 566

def prod(production, recover_to = [])
  @prod_stack << {prod: production, recover_to: recover_to}
  @options[:depth] += 1
  recover("#{production}(start)", depth: options[:depth], token: @lexer.first)
  yield
rescue EBNF::LL1::Lexer::Error, SyntaxError, Recovery =>  e
  # Lexer encountered an illegal token or the parser encountered
  # a terminal which is inappropriate for the current production.
  # Perform error recovery to find a reasonable terminal based
  # on the follow sets of the relevant productions. This includes
  # remaining terms from the current production and the stacked
  # productions
  case e
  when EBNF::LL1::Lexer::Error
    @lexer.recover
    begin
      error("Lexer error", "With input '#{e.input}': #{e.message}",
        production: production,
        token: e.token)
    rescue SyntaxError
    end
  end
  raise EOFError, "End of input found when recovering" if @lexer.first.nil?
  debug("recovery", "current token: #{@lexer.first.inspect}", depth: options[:depth])

  unless e.is_a?(Recovery)
    # Get the list of follows for this sequence, this production and the stacked productions.
    debug("recovery", "stack follows:", depth: options[:depth])
    @prod_stack.reverse.each do |prod|
      debug("recovery", level: 4, depth: options[:depth]) {"  #{prod[:prod]}: #{prod[:recover_to].inspect}"}
    end
  end

  # Find all follows to the top of the stack
  follows = @prod_stack.map {|prod| Array(prod[:recover_to])}.flatten.compact.uniq

  # Skip tokens until one is found in follows
  while (token = (@lexer.first rescue @lexer.recover)) && follows.none? {|t| token === t}
    skipped = @lexer.shift
    debug("recovery", depth: options[:depth]) {"skip #{skipped.inspect}"}
  end
  debug("recovery", depth: options[:depth]) {"found #{token.inspect} in follows"}

  # Re-raise the error unless token is a follows of this production
  raise Recovery unless Array(recover_to).any? {|t| token === t}

  # Skip that token to get something reasonable to start the next production with
  @lexer.shift
ensure
  progress("#{production}(finish)", depth: options[:depth])
  @options[:depth] -= 1
  @prod_stack.pop
end

#progress(*args, &block) ⇒ Object (protected)



620
621
622
623
624
625
626
# File 'lib/rdf/turtle/reader.rb', line 620

def progress(*args, &block)
  lineno = (options[:token].lineno if options[:token].respond_to?(:lineno)) || (@lexer && @lexer.lineno)
  opts = args.last.is_a?(Hash) ? args.pop : {}
  opts[:level] ||= 1
  opts[:lineno] ||= lineno
  log_info(*args, **opts, &block)
end

#read_annotation(subject, predicate, object) ⇒ Object (protected)

Read an annotation on a triple



445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'lib/rdf/turtle/reader.rb', line 445

def read_annotation(subject, predicate, object)
  error("Unexpected end of file", production: :annotation) unless token = @lexer.first
  if token === '{|'
    prod(:annotation, %(|})) do
      @lexer.shift

      # Statement becomes subject for predicateObjectList
      statement = RDF::Statement(subject, predicate, object, quoted: true)
      read_predicateObjectList(statement) ||
        error("Expected predicateObjectList", production: :annotation, token: @lexer.first)
      error("annotation", "Expected closing '|}'") unless @lexer.first === '|}'
      @lexer.shift
    end
  end

end

#read_BlankNodeRDF::Node (protected)

Returns:

  • (RDF::Node)


558
559
560
561
562
563
564
# File 'lib/rdf/turtle/reader.rb', line 558

def read_BlankNode
  token = @lexer.first
  case token && token.type
  when :BLANK_NODE_LABEL then prod(:BlankNode) {bnode(@lexer.shift.value[2..-1])}
  when :ANON then @lexer.shift && prod(:BlankNode) {bnode}
  end
end

#read_blankNodePropertyListRDF::Node (protected)

Returns:

  • (RDF::Node)


511
512
513
514
515
516
517
518
519
520
521
522
523
524
# File 'lib/rdf/turtle/reader.rb', line 511

def read_blankNodePropertyList
  token = @lexer.first
  if token === '['
    prod(:blankNodePropertyList, %{]}) do
      @lexer.shift
      progress("blankNodePropertyList", depth: options[:depth]) {"token: #{token.inspect}"}
      node = bnode
      read_predicateObjectList(node)
      error("blankNodePropertyList", "Expected closing ']'") unless @lexer.first === ']'
      @lexer.shift
      node
    end
  end
end

#read_collectionRDF::Node (protected)

Returns:

  • (RDF::Node)


527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
# File 'lib/rdf/turtle/reader.rb', line 527

def read_collection
  if @lexer.first === '('
    prod(:collection, %{)}) do
      @lexer.shift
      token = @lexer.first
      progress("collection", depth: options[:depth]) {"token: #{token.inspect}"}
      objects = []
      while object = read_object
        objects << object
      end
      list = RDF::List.new(values: objects)
      list.each_statement do |statement|
        add_statement("collection", statement)
      end
      error("collection", "Expected closing ')'") unless @lexer.first === ')'
      @lexer.shift
      list.subject
    end
  end
end

#read_directive (protected)

This method returns an undefined value.



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/rdf/turtle/reader.rb', line 271

def read_directive
  prod(:directive, %w{.}) do
    token = @lexer.first
    case token.type
    when :BASE
      prod(:base) do
        @lexer.shift
        terminated = token.value == '@base'
        iri = @lexer.shift
        error("Expected IRIREF", production: :base, token: iri) unless iri === :IRIREF
        @options[:base_uri] = process_iri(iri)
        error("base", "#{token} should be downcased") if token.value.start_with?('@') && token.value != '@base'

        if terminated
          error("base", "Expected #{token} to be terminated") unless @lexer.first === '.'
          @lexer.shift
        elsif @lexer.first === '.'
          error("base", "Expected #{token} not to be terminated") 
        else
          true
        end
      end
    when :PREFIX
      prod(:prefixID, %w{.}) do
        @lexer.shift
        pfx, iri = @lexer.shift, @lexer.shift
        terminated = token.value == '@prefix'
        error("Expected PNAME_NS", production: :prefix, token: pfx) unless pfx === :PNAME_NS
        error("Expected IRIREF", production: :prefix, token: iri) unless iri === :IRIREF
        debug("prefixID", depth: options[:depth]) {"Defined prefix #{pfx.inspect} mapping to #{iri.inspect}"}
        prefix(pfx.value[0..-2], process_iri(iri))
        error("prefixId", "#{token} should be downcased") if token.value.start_with?('@') && token.value != '@prefix'

        if terminated
          error("prefixID", "Expected #{token} to be terminated") unless @lexer.first === '.'
          @lexer.shift
        elsif @lexer.first === '.'
          error("prefixID", "Expected #{token} not to be terminated") 
        else
          true
        end
      end
    end
  end
end

#read_iriRDF::URI (protected)

Returns:

  • (RDF::URI)


549
550
551
552
553
554
555
# File 'lib/rdf/turtle/reader.rb', line 549

def read_iri
  token = @lexer.first
  case token && token.type
  when :IRIREF then prod(:iri)  {process_iri(@lexer.shift)}
  when :PNAME_LN, :PNAME_NS then prod(:iri) {pname(*@lexer.shift.value.split(':', 2))}
  end
end

#read_literalRDF::Literal (protected)

Returns:

  • (RDF::Literal)


463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
# File 'lib/rdf/turtle/reader.rb', line 463

def read_literal
  error("Unexpected end of file", production: :literal) unless token = @lexer.first
  case token.type || token.value
  when :INTEGER then prod(:literal) {literal(@lexer.shift.value, datatype:  RDF::XSD.integer)}
  when :DECIMAL
    prod(:literal) do
      value = @lexer.shift.value
      value = "0#{value}" if value.start_with?(".")
      literal(value, datatype:  RDF::XSD.decimal)
    end
  when :DOUBLE then prod(:literal) {literal(@lexer.shift.value.sub(/\.([eE])/, '.0\1'), datatype:  RDF::XSD.double)}
  when "true", "false" then prod(:literal) {literal(@lexer.shift.value, datatype: RDF::XSD.boolean)}
  when :STRING_LITERAL_QUOTE, :STRING_LITERAL_SINGLE_QUOTE
    prod(:literal) do
      value = @lexer.shift.value[1..-2]
      error("read_literal", "Unexpected end of file") unless token = @lexer.first
      case token.type || token.value
      when :LANG_DIR
        lang_dir = @lexer.shift.value[1..-1]
        language, direction = lang_dir.split('--')
        literal(value, language: language, direction: direction)
      when '^^'
        @lexer.shift
        literal(value, datatype: read_iri)
      else
        literal(value)
      end
    end
  when :STRING_LITERAL_LONG_QUOTE, :STRING_LITERAL_LONG_SINGLE_QUOTE
    prod(:literal) do
      value = @lexer.shift.value[3..-4]
      error("read_literal", "Unexpected end of file") unless token = @lexer.first
      case token.type || token.value
      when :LANG_DIR
        lang_dir = @lexer.shift.value[1..-1]
        language, direction = lang_dir.split('--')
        literal(value, language: language, direction: direction)
      when '^^'
        @lexer.shift
        literal(value, datatype: read_iri)
      else
        literal(value)
      end
    end
  end
end

#read_object(subject = nil, predicate = nil) (protected)

This method returns an undefined value.



389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/rdf/turtle/reader.rb', line 389

def read_object(subject = nil, predicate = nil)
  prod(:object) do
    if object = read_iri ||
      read_BlankNode ||
      read_collection ||
      read_blankNodePropertyList ||
      read_literal ||
      read_quotedTriple

      add_statement(:object, RDF::Statement(subject, predicate, object)) if subject && predicate
      object
    end
  end
end

#read_objectList(subject, predicate) ⇒ RDF::Term (protected)

Returns the last matched subject.

Returns:

  • (RDF::Term)

    the last matched subject



352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/rdf/turtle/reader.rb', line 352

def read_objectList(subject, predicate)
  prod(:objectList, %{,}) do
    last_object = nil
    while object = prod(:_objectList_2) {read_object(subject, predicate)}
      last_object = object

      # If object is followed by an annotation, read that and also emit an embedded triple.
      read_annotation(subject, predicate, object)

      break unless @lexer.first === ','
      @lexer.shift while @lexer.first === ','
    end
    last_object
  end
end

#read_predicateObjectList(subject) ⇒ RDF::URI (protected)

Returns the last matched verb.

Parameters:

  • subject (RDF::Resource)

Returns:

  • (RDF::URI)

    the last matched verb



336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/rdf/turtle/reader.rb', line 336

def read_predicateObjectList(subject)
  prod(:predicateObjectList, %{;}) do
    last_verb = nil
    while verb = read_verb
      last_verb = verb
      prod(:_predicateObjectList_5) do
        read_objectList(subject, verb) || error("Expected objectList", production: :predicateObjectList, token: @lexer.first)
      end
      break unless @lexer.first === ';'
      @lexer.shift while @lexer.first === ';'
    end
    last_verb
  end
end

#read_qtObject(subject = nil, predicate = nil) ⇒ RDF::Term (protected)

Returns:

  • (RDF::Term)


435
436
437
438
439
440
441
442
# File 'lib/rdf/turtle/reader.rb', line 435

def read_qtObject(subject = nil, predicate = nil)
  prod(:qtObject) do
    read_iri ||
    read_BlankNode ||
    read_literal ||
    read_quotedTriple
  end
end

#read_qtSubjectRDF::Resource (protected)

Returns:

  • (RDF::Resource)


425
426
427
428
429
430
431
432
# File 'lib/rdf/turtle/reader.rb', line 425

def read_qtSubject
  prod(:qtSubject) do
    read_iri ||
    read_BlankNode ||
    read_quotedTriple ||
    error( "Expected embedded subject", production: :qtSubject, token: @lexer.first)
  end
end

#read_quotedTripleRDF::Statement (protected)

Read a quoted triple

Returns:

  • (RDF::Statement)


406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
# File 'lib/rdf/turtle/reader.rb', line 406

def read_quotedTriple
  return unless @options[:rdfstar]
  if @lexer.first.value == '<<'
    prod(:quotedTriple) do
      @lexer.shift # eat <<
      subject = read_qtSubject || error("Failed to parse subject", production: :quotedTriple, token: @lexer.first)
      predicate = read_verb || error("Failed to parse predicate", production: :quotedTriple, token: @lexer.first)
      object = read_qtObject || error("Failed to parse object", production: :quotedTriple, token: @lexer.first)
      unless @lexer.first.value == '>>'
        error("Failed to end of embedded triple", production: :quotedTriple, token: @lexer.first)
      end
      @lexer.shift
      statement = RDF::Statement(subject, predicate, object, quoted: true)
      statement
    end
  end
end

#read_statement (protected)

This method returns an undefined value.



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/rdf/turtle/reader.rb', line 251

def read_statement
  prod(:statement, %w{.}) do
    error("read_statement", "Unexpected end of file") unless token = @lexer.first
    case token.type
    when :BASE, :PREFIX
      read_directive || error("Failed to parse directive", production: :directive, token: token)
    else
      read_triples || error("Expected token", production: :statement, token: token)
      if !log_recovering? || @lexer.first === '.'
        # If recovering, we will have eaten the closing '.'
        token = @lexer.shift
        unless token && token.value == '.'
          error("Expected '.' following triple", production: :statement, token: token)
        end
      end
    end
  end
end

#read_subjectRDF::Resource (protected)

Returns:

  • (RDF::Resource)


378
379
380
381
382
383
384
385
386
# File 'lib/rdf/turtle/reader.rb', line 378

def read_subject
  prod(:subject) do
    read_iri ||
    read_BlankNode ||
    read_collection ||
    read_quotedTriple ||
    error( "Expected subject", production: :subject, token: @lexer.first)
  end
end

#read_triplesObject (protected)

Returns the last verb matched, or subject BNode on predicateObjectList?

Returns:

  • (Object)

    returns the last verb matched, or subject BNode on predicateObjectList?



318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/rdf/turtle/reader.rb', line 318

def read_triples
  prod(:triples, %w{.}) do
    error("read_triples", "Unexpected end of file") unless token = @lexer.first
    case token.type || token.value
    when '['
      # blankNodePropertyList predicateObjectList? 
      subject = read_blankNodePropertyList || error("Failed to parse blankNodePropertyList", production: :triples, token: @lexer.first)
      read_predicateObjectList(subject) || subject
    else
      # subject predicateObjectList
      subject = read_subject || error("Failed to parse subject", production: :triples, token: @lexer.first)
      read_predicateObjectList(subject) || error("Expected predicateObjectList", production: :triples, token: @lexer.first)
    end
  end
end

#read_verbRDF::URI (protected)

Returns:

  • (RDF::URI)


369
370
371
372
373
374
375
# File 'lib/rdf/turtle/reader.rb', line 369

def read_verb
  error("read_verb", "Unexpected end of file") unless token = @lexer.first
  case token.type || token.value
  when 'a' then prod(:verb) {@lexer.shift && RDF.type}
  else prod(:verb) {read_iri}
  end
end

#recover(*args, &block) ⇒ Object (protected)



628
629
630
631
632
633
634
# File 'lib/rdf/turtle/reader.rb', line 628

def recover(*args, &block)
  lineno = (options[:token].lineno if options[:token].respond_to?(:lineno)) || (@lexer && @lexer.lineno)
  opts = args.last.is_a?(Hash) ? args.pop : {}
  opts[:level] ||= 1
  opts[:lineno] ||= lineno
  log_recover(*args, **opts, &block)
end