lib/sparql/algebra/expression.rb in sparql-3.1.3 vs lib/sparql/algebra/expression.rb in sparql-3.1.4

- old
+ new

@@ -111,11 +111,11 @@ else raise TypeError, "invalid SPARQL::Algebra::Expression operand: #{operand.inspect}" end end debug(options) {"#{operator.inspect}(#{operands.map(&:inspect).join(',')})"} - options.delete_if {|k, v| [:debug, :depth, :prefixes, :base_uri, :update, :validate].include?(k) } + options.delete_if {|k, v| [:debug, :logger, :depth, :prefixes, :base_uri, :update, :validate].include?(k) } operands << options unless options.empty? operator.new(*operands) end ## @@ -220,25 +220,49 @@ when RDF::Literal::Numeric RDF::Literal::Boolean.new(value.object != 0) when RDF::Literal::DateTime, RDF::Literal::Date, RDF::Literal::Time, RDF::URI, RDF::Node raise TypeError, "Value #{value.inspect} cannot be cast as #{datatype}" else - RDF::Literal.new(!value.to_s.empty?, datatype: datatype, validate: true) + RDF::Literal::Boolean.new(value.value, datatype: datatype, validate: true) end when RDF::XSD.decimal, RDF::XSD.integer case value when RDF::Literal::Boolean RDF::Literal.new(value.object ? 1 : 0, datatype: datatype) - when RDF::Literal::Integer, RDF::Literal::Decimal - RDF::Literal.new(value, datatype: datatype) + when RDF::Literal::Numeric + RDF::Literal.new(value.object, datatype: datatype) when RDF::Literal::DateTime, RDF::Literal::Date, RDF::Literal::Time, RDF::URI, RDF::Node raise TypeError, "Value #{value.inspect} cannot be cast as #{datatype}" else RDF::Literal.new(value.value, datatype: datatype, validate: true) end when RDF::XSD.string - RDF::Literal.new(value, datatype: datatype) + # Cast to string rules based on https://www.w3.org/TR/xpath-functions/#casting-to-string + case value + when RDF::Literal::Integer + RDF::Literal.new(value.canonicalize.to_s, datatype: datatype) + when RDF::Literal::Decimal + if value == value.ceil + RDF::Literal.new(value.ceil, datatype: datatype) + else + RDF::Literal.new(value.canonicalize.to_s, datatype: datatype) + end + when RDF::Literal::Float, RDF::Literal::Double + if value.abs >= 0.000001 && value.abs < 1000000 + # If SV has an absolute value that is greater than or equal to 0.000001 (one millionth) and less than 1000000 (one million), then the value is converted to an xs:decimal and the resulting xs:decimal is converted to an xs:string according to the rules above, as though using an implementation of xs:decimal that imposes no limits on the totalDigits or fractionDigits facets. + cast(datatype, RDF::Literal::Decimal.new(value.object)) + elsif value.object.zero? + # If SV has the value positive or negative zero, TV is "0" or "-0" respectively. + RDF::Literal.new(value.to_s.start_with?('-') ? '-0' : '0', datatype: datatype) + else + # If SV is positive or negative infinity, TV is the string "INF" or "-INF" respectively. + # In other cases, the result consists of a mantissa, which has the lexical form of an xs:decimal, followed by the letter "E", followed by an exponent which has the lexical form of an xs:integer. Leading zeroes and "+" signs are prohibited in the exponent. For the mantissa, there must be a decimal point, and there must be exactly one digit before the decimal point, which must be non-zero. The "+" sign is prohibited. There must be at least one digit after the decimal point. Apart from this mandatory digit, trailing zero digits are prohibited. + RDF::Literal.new(value.canonicalize.to_s, datatype: datatype) + end + else + RDF::Literal.new(value.canonicalize.to_s, datatype: datatype) + end else raise TypeError, "Expected datatype (#{datatype}) to be a recognized XPath function" end rescue raise TypeError, $!.message @@ -354,41 +378,33 @@ private # @overload: May be called with node, message and an option hash # @param [String] node processing node # @param [String] message # @param [Hash{Symbol => Object}] options - # @option options [Boolean] :debug output debug messages to $stderr + # @option options [Logger] :logger for logging progress # @option options [Integer] :depth (@productions.length) # Processing depth for indenting message output. # @yieldreturn [String] appended to message, to allow for lazy-evaulation of message # # @overload: May be called with node and an option hash # @param [String] node processing node # @param [Hash{Symbol => Object}] options - # @option options [Boolean] :debug output debug messages to $stderr + # @option options [Logger] :logger for logging progress # @option options [Integer] :depth (@productions.length) # Processing depth for indenting message output. # @yieldreturn [String] appended to message, to allow for lazy-evaulation of message # # @overload: May be called with only options, in which case the block is used to return the output message # @param [String] node processing node # @param [Hash{Symbol => Object}] options - # @option options [Boolean] :debug output debug messages to $stderr + # @option options [Logger] :logger for logging progress # @option options [Integer] :depth (@productions.length) # Processing depth for indenting message output. # @yieldreturn [String] appended to message, to allow for lazy-evaulation of message - def self.debug(*args) + def self.debug(*args, &block) options = args.last.is_a?(Hash) ? args.pop : {} - return unless options[:debug] - message = args.join(": ") - message = message + yield if block_given? - depth = options[:depth] || 0 - case options[:debug] - when Array - options[:debug] << "#{' ' * depth}#{message}" - else - $stderr.puts("#{' ' * depth}#{message}") - end + return unless options[:logger] + options[:logger].debug(*args, **options, &block) end def debug(*args, &block) Expression.debug(*args, &block) end