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

.new(input = nil, **options, &block) ⇒ Object

Redirect for Freebase Reader



58
59
60
61
62
63
64
65
66
67
# File 'lib/rdf/turtle/reader.rb', line 58

def self.new(input = nil, **options, &block)
  klass = if options[:freebase]
    FreebaseReader
  else
    self
  end
  reader = klass.allocate
  reader.send(:initialize, input, **options, &block)
  reader
end

.options ⇒ Object

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

#each_statement {|statement| ... } ⇒ void

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| ... } ⇒ void

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

#inspect ⇒ Object



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