require 'sparql/extensions' ## # A ShEx runtime for RDF.rb. # # @see https://shexspec.github.io/spec/#shexc module ShEx autoload :Algebra, 'shex/algebra' autoload :Meta, 'shex/meta' autoload :Parser, 'shex/parser' autoload :Terminals, 'shex/terminals' autoload :VERSION, 'shex/version' ## # Parse the given ShEx `query` string. # # @example parsing a ShExC schema # schema = ShEx.parse(%( # PREFIX ex: <http://schema.example/> ex:IssueShape {ex:state IRI} # ).parse # # @param [IO, StringIO, String, #to_s] expression (ShExC or ShExJ) # @param ['shexc', 'shexj', 'sse'] format ('shexc') # @param [Hash{Symbol => Object}] options # @return [ShEx::Algebra::Schema] The executable parsed expression. # @raise [ShEx::ParseError] when a syntax error is detected # @raise [ShEx::StructureError, ArgumentError] on structural problems with schema def self.parse(expression, format: 'shexc', **options) case format when 'shexc' then Parser.new(expression, options).parse when 'shexj' when 'sse' else raise "Unknown expression format: #{format.inspect}" end end ## # Parses input from the given file name or URL. # # @example parsing a ShExC schema # schema = ShEx.parse('foo.shex').parse # # @param [String, #to_s] filename # @param ['shexc', 'shexj', 'sse'] format ('shexc') # @param [Hash{Symbol => Object}] options # any additional options (see `RDF::Reader#initialize` and `RDF::Format.for`) # @yield [ShEx::Algebra::Schema] # @yieldparam [RDF::Reader] reader # @yieldreturn [void] ignored # @return [ShEx::Algebra::Schema] The executable parsed expression. # @raise [ShEx::ParseError] when a syntax error is detected # @raise [ShEx::StructureError, ArgumentError] on structural problems with schema def self.open(filename, format: 'shexc', **options, &block) RDF::Util::File.open_file(filename, options) do |file| self.parse(file, options.merge(format: format)) end end ## # Parse and validate the given ShEx `expression` string against `queriable`. # # @example executing a ShExC schema # graph = RDF::Graph.load("etc/doap.ttl") # ShEx.execute('etc/doap.shex', graph, "http://rubygems.org/gems/shex", "") # # @param [IO, StringIO, String, #to_s] expression (ShExC or ShExJ) # @param [RDF::Resource] focus # @param [RDF::Resource] shape # @param ['shexc', 'shexj', 'sse'] format ('shexc') # @param [Hash{Symbol => Object}] options # @return [Boolean] `true` if satisfied, `false` if it does not apply # @raise [ShEx::NotSatisfied] if not satisfied # @raise [ShEx::ParseError] when a syntax error is detected # @raise [ShEx::StructureError, ArgumentError] on structural problems with schema def self.execute(expression, queryable, focus, shape, format: 'shexc', **options) shex = self.parse(expression, options.merge(format: format)) queryable = queryable || RDF::Graph.new shex.satisfies?(focus, queryable, {focus => shape}, options) end class Error < StandardError # The status code associated with this error attr_reader :code ## # Initializes a new patch error instance. # # @param [String, #to_s] message # @param [Hash{Symbol => Object}] options # @option options [Integer] :code (422) def initialize(message, options = {}) @code = options.fetch(:status_code, 422) super(message.to_s) end end # Shape expectation not satisfied class StructureError < Error; end # Shape expectation not satisfied class NotSatisfied < Error; end # Indicates bad syntax found in LD Patch document class ParseError < Error ## # The invalid token which triggered the error. # # @return [String] attr_reader :token ## # The line number where the error occurred. # # @return [Integer] attr_reader :lineno ## # Initializes a new parser error instance. # # @param [String, #to_s] message # @param [Hash{Symbol => Object}] options # @option options [String] :token (nil) # @option options [Integer] :lineno (nil) # @option options [Integer] :code (400) def initialize(message, options = {}) @token = options[:token] @lineno = options[:lineno] || (@token.lineno if @token.respond_to?(:lineno)) super(message.to_s, code: options.fetch(:code, 400)) end end end