Add implementation of RDF.Datatype.cast/1 on numeric datatypes
This commit is contained in:
parent
1e94842bc0
commit
56c153198f
6 changed files with 255 additions and 0 deletions
|
@ -4,6 +4,8 @@ defmodule RDF.Decimal do
|
|||
"""
|
||||
use RDF.Datatype, id: RDF.Datatype.NS.XSD.decimal
|
||||
|
||||
import RDF.Literal.Guards
|
||||
|
||||
alias Elixir.Decimal, as: D
|
||||
|
||||
|
||||
|
@ -64,6 +66,36 @@ defmodule RDF.Decimal do
|
|||
do: canonical_decimal(%{decimal | coef: Kernel.div(coef, 10), exp: exp + 1})
|
||||
|
||||
|
||||
|
||||
def cast(%RDF.Literal{datatype: datatype} = literal) do
|
||||
cond do
|
||||
not RDF.Literal.valid?(literal) ->
|
||||
nil
|
||||
|
||||
is_xsd_decimal(datatype) ->
|
||||
literal
|
||||
|
||||
literal == RDF.false ->
|
||||
new(0.0)
|
||||
|
||||
literal == RDF.true ->
|
||||
new(1.0)
|
||||
|
||||
is_xsd_string(datatype) ->
|
||||
literal.value
|
||||
|> new()
|
||||
|> canonical()
|
||||
|
||||
is_number(literal.value) and (is_xsd_integer(datatype) or
|
||||
is_xsd_double(datatype) or is_xsd_float(datatype)) ->
|
||||
new(literal.value)
|
||||
|
||||
true ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def equal_value?(left, right), do: RDF.Numeric.equal_value?(left, right)
|
||||
|
||||
end
|
||||
|
|
|
@ -5,6 +5,8 @@ defmodule RDF.Double do
|
|||
|
||||
use RDF.Datatype, id: RDF.Datatype.NS.XSD.double
|
||||
|
||||
import RDF.Literal.Guards
|
||||
|
||||
|
||||
def build_literal_by_value(value, opts) do
|
||||
case convert(value, opts) do
|
||||
|
@ -91,6 +93,41 @@ defmodule RDF.Double do
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def cast(%RDF.Literal{datatype: datatype} = literal) do
|
||||
cond do
|
||||
not RDF.Literal.valid?(literal) ->
|
||||
nil
|
||||
|
||||
is_xsd_double(datatype) ->
|
||||
literal
|
||||
|
||||
literal == RDF.false ->
|
||||
new(0.0)
|
||||
|
||||
literal == RDF.true ->
|
||||
new(1.0)
|
||||
|
||||
is_xsd_string(datatype) ->
|
||||
literal.value
|
||||
|> new()
|
||||
|> canonical()
|
||||
|
||||
is_xsd_decimal(datatype) ->
|
||||
literal.value
|
||||
|> Decimal.to_float()
|
||||
|> new()
|
||||
|
||||
is_xsd_integer(datatype) or is_xsd_float(datatype) ->
|
||||
new(literal.value)
|
||||
|
||||
true ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def equal_value?(left, right), do: RDF.Numeric.equal_value?(left, right)
|
||||
|
||||
end
|
||||
|
|
|
@ -5,6 +5,8 @@ defmodule RDF.Integer do
|
|||
|
||||
use RDF.Datatype, id: RDF.Datatype.NS.XSD.integer
|
||||
|
||||
import RDF.Literal.Guards
|
||||
|
||||
|
||||
def convert(value, _) when is_integer(value), do: value
|
||||
|
||||
|
@ -18,6 +20,44 @@ defmodule RDF.Integer do
|
|||
|
||||
def convert(value, opts), do: super(value, opts)
|
||||
|
||||
|
||||
def cast(%RDF.Literal{datatype: datatype} = literal) do
|
||||
cond do
|
||||
not RDF.Literal.valid?(literal) ->
|
||||
nil
|
||||
|
||||
is_xsd_integer(datatype) ->
|
||||
literal
|
||||
|
||||
literal == RDF.false ->
|
||||
new(0)
|
||||
|
||||
literal == RDF.true ->
|
||||
new(1)
|
||||
|
||||
is_xsd_string(datatype) ->
|
||||
literal.value
|
||||
|> new()
|
||||
|> canonical()
|
||||
|
||||
is_xsd_decimal(datatype) ->
|
||||
literal.value
|
||||
|> Decimal.round(0, :down)
|
||||
|> Decimal.to_integer()
|
||||
|> new()
|
||||
|
||||
is_float(literal.value) and
|
||||
(is_xsd_double(datatype) or is_xsd_float(datatype)) ->
|
||||
literal.value
|
||||
|> trunc()
|
||||
|> new()
|
||||
|
||||
true ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def equal_value?(left, right), do: RDF.Numeric.equal_value?(left, right)
|
||||
|
||||
end
|
||||
|
|
|
@ -65,6 +65,57 @@ defmodule RDF.DecimalTest do
|
|||
end
|
||||
|
||||
|
||||
describe "cast/1" do
|
||||
test "casting a decimal returns the input as it is" do
|
||||
assert RDF.decimal(0) |> RDF.Decimal.cast() == RDF.decimal(0)
|
||||
assert RDF.decimal("-0.0") |> RDF.Decimal.cast() == RDF.decimal("-0.0")
|
||||
assert RDF.decimal(1) |> RDF.Decimal.cast() == RDF.decimal(1)
|
||||
assert RDF.decimal(0.1) |> RDF.Decimal.cast() == RDF.decimal(0.1)
|
||||
end
|
||||
|
||||
test "casting a boolean" do
|
||||
assert RDF.true |> RDF.Decimal.cast() == RDF.decimal(1.0)
|
||||
assert RDF.false |> RDF.Decimal.cast() == RDF.decimal(0.0)
|
||||
end
|
||||
|
||||
test "casting a string" do
|
||||
assert RDF.string("0") |> RDF.Decimal.cast() == RDF.decimal(0)
|
||||
assert RDF.string("3.14") |> RDF.Decimal.cast() == RDF.decimal(3.14)
|
||||
end
|
||||
|
||||
test "casting an integer" do
|
||||
assert RDF.integer(0) |> RDF.Decimal.cast() == RDF.decimal(0.0)
|
||||
assert RDF.integer(42) |> RDF.Decimal.cast() == RDF.decimal(42.0)
|
||||
end
|
||||
|
||||
test "casting a double" do
|
||||
assert RDF.double(0.0) |> RDF.Decimal.cast() == RDF.decimal(0.0)
|
||||
assert RDF.double("-0.0") |> RDF.Decimal.cast() == RDF.decimal(0.0)
|
||||
assert RDF.double(0.1) |> RDF.Decimal.cast() == RDF.decimal(0.1)
|
||||
assert RDF.double(1) |> RDF.Decimal.cast() == RDF.decimal(1.0)
|
||||
assert RDF.double(3.14) |> RDF.Decimal.cast() == RDF.decimal(3.14)
|
||||
assert RDF.double(10.1e1) |> RDF.Decimal.cast() == RDF.decimal(101.0)
|
||||
|
||||
assert RDF.double("NAN") |> RDF.Decimal.cast() == nil
|
||||
assert RDF.double("+INF") |> RDF.Decimal.cast() == nil
|
||||
end
|
||||
|
||||
@tag skip: "TODO: RDF.Float datatype"
|
||||
test "casting a float"
|
||||
|
||||
test "with invalid literals" do
|
||||
assert RDF.boolean("42") |> RDF.Decimal.cast() == nil
|
||||
assert RDF.integer(3.14) |> RDF.Decimal.cast() == nil
|
||||
assert RDF.decimal("NAN") |> RDF.Decimal.cast() == nil
|
||||
assert RDF.double(true) |> RDF.Decimal.cast() == nil
|
||||
end
|
||||
|
||||
test "with literals of unsupported datatypes" do
|
||||
assert RDF.DateTime.now() |> RDF.Decimal.cast() == nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
defmacrop sigil_d(str, _opts) do
|
||||
quote do
|
||||
Elixir.Decimal.new(unquote(str))
|
||||
|
|
|
@ -58,4 +58,48 @@ defmodule RDF.DoubleTest do
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
describe "cast/1" do
|
||||
test "casting a double returns the input as it is" do
|
||||
assert RDF.double(3.14) |> RDF.Double.cast() == RDF.double(3.14)
|
||||
assert RDF.double("NAN") |> RDF.Double.cast() == RDF.double("NAN")
|
||||
assert RDF.double("+INF") |> RDF.Double.cast() == RDF.double("+INF")
|
||||
end
|
||||
|
||||
test "casting a boolean" do
|
||||
assert RDF.true |> RDF.Double.cast() == RDF.double(1.0)
|
||||
assert RDF.false |> RDF.Double.cast() == RDF.double(0.0)
|
||||
end
|
||||
|
||||
test "casting a string" do
|
||||
assert RDF.string("1.0") |> RDF.Double.cast() == RDF.double("1.0E0")
|
||||
assert RDF.string("3.14") |> RDF.Double.cast() == RDF.double("3.14E0")
|
||||
end
|
||||
|
||||
test "casting an integer" do
|
||||
assert RDF.integer(0) |> RDF.Double.cast() == RDF.double(0.0)
|
||||
assert RDF.integer(42) |> RDF.Double.cast() == RDF.double(42.0)
|
||||
end
|
||||
|
||||
test "casting a decimal" do
|
||||
assert RDF.decimal(0) |> RDF.Double.cast() == RDF.double(0)
|
||||
assert RDF.decimal(1) |> RDF.Double.cast() == RDF.double(1)
|
||||
assert RDF.decimal(3.14) |> RDF.Double.cast() == RDF.double(3.14)
|
||||
end
|
||||
|
||||
@tag skip: "TODO: RDF.Float datatype"
|
||||
test "casting a float"
|
||||
|
||||
test "with invalid literals" do
|
||||
assert RDF.boolean("42") |> RDF.Double.cast() == nil
|
||||
assert RDF.integer(3.14) |> RDF.Double.cast() == nil
|
||||
assert RDF.decimal("NAN") |> RDF.Double.cast() == nil
|
||||
assert RDF.double(true) |> RDF.Double.cast() == nil
|
||||
end
|
||||
|
||||
test "with literals of unsupported datatypes" do
|
||||
assert RDF.DateTime.now() |> RDF.Double.cast() == nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -16,6 +16,57 @@ defmodule RDF.IntegerTest do
|
|||
invalid: ~w(foo 10.1 12xyz) ++ [true, false, 3.14, "1 2", "foo 1", "1 foo"]
|
||||
|
||||
|
||||
describe "cast/1" do
|
||||
test "casting an integer returns the input as it is" do
|
||||
assert RDF.integer(0) |> RDF.Integer.cast() == RDF.integer(0)
|
||||
assert RDF.integer(1) |> RDF.Integer.cast() == RDF.integer(1)
|
||||
end
|
||||
|
||||
test "casting a boolean" do
|
||||
assert RDF.false |> RDF.Integer.cast() == RDF.integer(0)
|
||||
assert RDF.true |> RDF.Integer.cast() == RDF.integer(1)
|
||||
end
|
||||
|
||||
test "casting a string" do
|
||||
assert RDF.string("0") |> RDF.Integer.cast() == RDF.integer(0)
|
||||
end
|
||||
|
||||
test "casting an decimal" do
|
||||
assert RDF.decimal(0) |> RDF.Integer.cast() == RDF.integer(0)
|
||||
assert RDF.decimal(1.0) |> RDF.Integer.cast() == RDF.integer(1)
|
||||
assert RDF.decimal(3.14) |> RDF.Integer.cast() == RDF.integer(3)
|
||||
end
|
||||
|
||||
test "casting a double" do
|
||||
assert RDF.double(0) |> RDF.Integer.cast() == RDF.integer(0)
|
||||
assert RDF.double(0.0) |> RDF.Integer.cast() == RDF.integer(0)
|
||||
assert RDF.double(0.1) |> RDF.Integer.cast() == RDF.integer(0)
|
||||
assert RDF.double("+0") |> RDF.Integer.cast() == RDF.integer(0)
|
||||
assert RDF.double("+0.0") |> RDF.Integer.cast() == RDF.integer(0)
|
||||
assert RDF.double("-0.0") |> RDF.Integer.cast() == RDF.integer(0)
|
||||
assert RDF.double("0.0E0") |> RDF.Integer.cast() == RDF.integer(0)
|
||||
assert RDF.double(1) |> RDF.Integer.cast() == RDF.integer(1)
|
||||
assert RDF.double(3.14) |> RDF.Integer.cast() == RDF.integer(3)
|
||||
|
||||
assert RDF.double("NAN") |> RDF.Integer.cast() == nil
|
||||
assert RDF.double("+INF") |> RDF.Integer.cast() == nil
|
||||
end
|
||||
|
||||
@tag skip: "TODO: RDF.Float datatype"
|
||||
test "casting a float"
|
||||
|
||||
test "with invalid literals" do
|
||||
assert RDF.integer(3.14) |> RDF.Integer.cast() == nil
|
||||
assert RDF.decimal("NAN") |> RDF.Integer.cast() == nil
|
||||
assert RDF.double(true) |> RDF.Integer.cast() == nil
|
||||
end
|
||||
|
||||
test "with literals of unsupported datatypes" do
|
||||
assert RDF.DateTime.now() |> RDF.Integer.cast() == nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "equality" do
|
||||
test "two literals are equal when they have the same datatype and lexical form" do
|
||||
[
|
||||
|
|
Loading…
Reference in a new issue