class LD::Patch::Parser

A parser for the LD Patch grammar.

@see www.w3.org/TR/ldpatch/#concrete-syntax @see en.wikipedia.org/wiki/LR_parser

Attributes

errors[R]

Accumulated errors found during processing @return [Array<String>]

input[RW]

The current input string being processed.

@return [String]

options[R]

Any additional options for the parser.

@return [Hash]

result[RW]

The internal representation of the result @return [Array]

tokens[R]

The current input tokens being processed.

@return [Array<Token>]

Public Class Methods

new(input = nil, **options, &block) click to toggle source

Initializes a new parser instance.

@param [String, IO, StringIO, to_s] input @param [Hash{Symbol => Object}] options @option options [#to_s] :base_uri (nil)

the base URI to use when resolving relative URIs

@option options [#to_s] :anon_base (“b0”)

Basis for generating anonymous Nodes

@option options [Boolean] :resolve_iris (false)

Resolve prefix and relative IRIs, otherwise, when serializing the parsed SSE as S-Expressions, use the original prefixed and relative URIs along with `base` and `prefix` definitions.

@option options [Boolean] :validate (false)

whether to validate the parsed statements and values

@option options [Array] :errors

array for placing errors found when parsing

@option options [Array] :warnings

array for placing warnings found when parsing

@option options [Boolean] :progress

Show progress of parser productions

@option options [Boolean] :debug

Detailed debug output

@yield [parser] ‘self` @yieldparam [LD::Patch::Parser] parser @return [LD::Patch::Parser] The parser instance, or result returned from block

# File lib/ld/patch/parser.rb, line 326
def initialize(input = nil, **options, &block)
  @input = case input
  when IO, StringIO then input.read
  else input.to_s.dup
  end
  @input.encode!(Encoding::UTF_8) if @input.respond_to?(:encode!)
  @options = {anon_base: "b0", validate: false}.merge(options)
  @errors = @options[:errors]
  @options[:debug] ||= case
  when options[:progress] then 2
  when options[:validate] then (@errors ? nil : 1)
  end

  debug("base IRI") {base_uri.inspect}
  debug("validate") {validate?.inspect}

  @vars = {}

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

Public Instance Methods

base_uri() click to toggle source

Returns the Base URI defined for the parser, as specified or when parsing a BASE prologue element.

@example

base  #=> RDF::URI('http://example.com/')

@return [HRDF::URI]

# File lib/ld/patch/parser.rb, line 426
def base_uri
  RDF::URI(@options[:base_uri])
end
base_uri=(iri) click to toggle source

Set the Base URI to use for this parser.

@param [RDF::URI, to_s] iri

@example

base_uri = RDF::URI('http://purl.org/dc/terms/')

@return [RDF::URI]

# File lib/ld/patch/parser.rb, line 439
def base_uri=(iri)
  @options[:base_uri] = RDF::URI(iri)
end
ll1_parse(prod = START)
Alias for: parse
parse(prod = START) click to toggle source

Parse patch

The result is an S-List. Productions return an array such as the following:

(prefix ((: <http://example/>))

@param [Symbol, to_s] prod The starting production for the parser.

It may be a URI from the grammar, or a symbol representing the local_name portion of the grammar URI.

@return [SPARQL::Algebra::Operator, Object] @raise [ParseError] when illegal grammar detected.

# File lib/ld/patch/parser.rb, line 368
def parse(prod = START)
  ll1_parse(@input,
    prod.to_sym,
    branch: BRANCH,
    first: FIRST,
    follow: FOLLOW,
    whitespace: WS,
    **@options
  ) do |context, *data|
    case context
    when :trace
      level, lineno, depth, *args = data
      message = args.to_sse
      d_str = depth > 100 ? ' ' * 100 + '+' : ' ' * depth
      str = "[#{lineno}](#{level})#{d_str}#{message}".chop
      if @errors && level == 0
        @errors << str
      else
        case @options[:debug]
        when Array
          @options[:debug] << str
        when TrueClass
          $stderr.puts str
        when Integer
          $stderr.puts(str) if level <= @options[:debug]
        end
      end
    end
  end

  # The last thing on the @prod_data stack is the result
  @result = case
  when !prod_data.is_a?(Hash)
    prod_data
  when prod_data.empty?
    nil
  when prod_data[:ldpatch]
    prod_data[:ldpatch]
  else
    key = prod_data.keys.first
    [key] + Array(prod_data[key])  # Creates [:key, [:triple], ...]
  end

  # Validate resulting expression
  @result.validate! if @result && validate?
  @result
rescue EBNF::LL1::Parser::Error, EBNF::LL1::Lexer::Error =>  e
  raise LD::Patch::ParseError.new(e.message, lineno: e.lineno, token: e.token)
end
Also aliased as: ll1_parse
prefix(name = nil, iri = nil) click to toggle source

Defines the given named URI prefix for this parser.

@example Defining a URI prefix

prefix :dc, RDF::URI('http://purl.org/dc/terms/')

@example Returning a URI prefix

prefix(:dc)    #=> RDF::URI('http://purl.org/dc/terms/')

@overload prefix(name, uri)

@param  [Symbol, #to_s]   name
@param  [RDF::URI, #to_s] uri

@overload prefix(name)

@param  [Symbol, #to_s]   name

@return [RDF::URI]

# File lib/ld/patch/parser.rb, line 472
def prefix(name = nil, iri = nil)
  name = name.to_s.empty? ? nil : (name.respond_to?(:to_sym) ? name.to_sym : name.to_s.to_sym)
  iri.nil? ? prefixes[name] : prefixes[name] = iri
end
prefixes() click to toggle source

Returns the URI prefixes currently defined for this parser.

@example

prefixes[:dc]  #=> RDF::URI('http://purl.org/dc/terms/')

@return [Hash{Symbol => RDF::URI}] @since 0.3.0

# File lib/ld/patch/parser.rb, line 451
def prefixes
  @options[:prefixes] ||= {}
end

Private Instance Methods

bnode(id = nil) click to toggle source

Generate a BNode identifier

# File lib/ld/patch/parser.rb, line 510
def bnode(id = nil)
  unless id
    id = @options[:anon_base]
    @options[:anon_base] = @options[:anon_base].succ
  end
  # Don't use provided ID to avoid aliasing issues when re-serializing the graph, when the bnode identifiers are re-used
  (@bnode_cache ||= {})[id.to_s] ||= begin
    new_bnode = RDF::Node.new
    new_bnode.lexical = "_:#{id}"
    new_bnode
  end
end
iri(value) click to toggle source

Create URIs

# File lib/ld/patch/parser.rb, line 524
def iri(value)
  # If we have a base URI, use that when constructing a new URI
  iri = if base_uri
    u = base_uri.join(value.to_s)
    u.lexical = "<#{value}>" unless u.to_s == value.to_s || resolve_iris?
    u
  else
    RDF::URI(value)
  end

  iri.validate! if validate? && iri.respond_to?(:validate)
  #iri = RDF::URI.intern(iri) if intern?
  iri
end
literal(value, **options) click to toggle source

Create a literal

# File lib/ld/patch/parser.rb, line 551
def literal(value, **options)
  options = options.dup
  # Internal representation is to not use xsd:string, although it could arguably go the other way.
  options.delete(:datatype) if options[:datatype] == RDF::XSD.string
  debug("literal") do
    "value: #{value.inspect}, " +
    "options: #{options.inspect}, " +
    "validate: #{validate?.inspect}, "
  end
  RDF::Literal.new(value, validate: validate?, **options)
end
ns(prefix, suffix) click to toggle source
# File lib/ld/patch/parser.rb, line 539
def ns(prefix, suffix)
  error("pname", "undefined prefix #{prefix.inspect}") unless prefix(prefix)
  base = prefix(prefix).to_s
  suffix = suffix.to_s.sub(/^\#/, "") if base.index("#")
  debug {"ns(#{prefix.inspect}): base: '#{base}', suffix: '#{suffix}'"}
  iri = iri(base + suffix.to_s)
  # Cause URI to be serialized as a lexical
  iri.lexical = "#{prefix}:#{suffix}" unless resolve_iris?
  iri
end
resolve_iris?() click to toggle source

Returns ‘true` if parsed statements and values should be validated.

@return [Boolean] ‘true` or `false` @since 0.3.0

# File lib/ld/patch/parser.rb, line 483
def resolve_iris?
  @options[:resolve_iris]
end
validate?() click to toggle source

Returns ‘true` when resolving IRIs, otherwise BASE and PREFIX are retained in the output algebra.

@return [Boolean] ‘true` or `false` @since 1.0.3

# File lib/ld/patch/parser.rb, line 492
def validate?
  @options[:validate]
end
variable(id, distinguished = true) click to toggle source

Return variable allocated to an ID. If no ID is provided, a new variable is allocated. Otherwise, any previous assignment will be used. @return [RDF::Query::Variable]

# File lib/ld/patch/parser.rb, line 501
def variable(id, distinguished = true)
  @vars[id] ||= begin
    v = RDF::Query::Variable.new(id)
    v.distinguished = distinguished
    v
  end
end