Add RDF.Literal.Guards
This commit is contained in:
parent
da48d02977
commit
93b932620c
7 changed files with 114 additions and 29 deletions
|
@ -22,6 +22,7 @@ This project adheres to [Semantic Versioning](http://semver.org/) and
|
|||
- `RDF.LangString.match_language?/2`
|
||||
- possibility to configure an application-specific default base IRI; for now it
|
||||
is used only on reading of RDF serializations (when no `base` specified)
|
||||
- `RDF.Literal.Guards` which allow pattern matching of common literal datatypes
|
||||
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -8,6 +8,9 @@ defmodule RDF.Numeric do
|
|||
|
||||
alias Elixir.Decimal, as: D
|
||||
|
||||
import RDF.Literal.Guards
|
||||
|
||||
|
||||
@types MapSet.new [
|
||||
XSD.integer,
|
||||
XSD.decimal,
|
||||
|
@ -27,9 +30,6 @@ defmodule RDF.Numeric do
|
|||
XSD.positiveInteger,
|
||||
]
|
||||
|
||||
@xsd_decimal XSD.decimal
|
||||
@xsd_double XSD.double
|
||||
|
||||
|
||||
@doc """
|
||||
The list of all numeric datatypes.
|
||||
|
@ -68,7 +68,7 @@ defmodule RDF.Numeric do
|
|||
|
||||
def equal_value?(%Literal{datatype: left_datatype, value: left},
|
||||
%Literal{datatype: right_datatype, value: right})
|
||||
when left_datatype == @xsd_decimal or right_datatype == @xsd_decimal,
|
||||
when is_xsd_decimal(left_datatype) or is_xsd_decimal(right_datatype),
|
||||
do: equal_decimal_value?(left, right)
|
||||
|
||||
def equal_value?(%Literal{datatype: left_datatype, value: left},
|
||||
|
@ -94,8 +94,8 @@ defmodule RDF.Numeric do
|
|||
defp zero_value?(_), do: false
|
||||
|
||||
|
||||
def negative_zero?(%Literal{value: zero, uncanonical_lexical: "-" <> _, datatype: @xsd_double})
|
||||
when zero == 0, do: true
|
||||
def negative_zero?(%Literal{value: zero, uncanonical_lexical: "-" <> _, datatype: datatype})
|
||||
when zero == 0 and is_xsd_double(datatype), do: true
|
||||
|
||||
def negative_zero?(%Literal{value: %D{sign: -1, coef: 0}}), do: true
|
||||
|
||||
|
@ -241,7 +241,7 @@ defmodule RDF.Numeric do
|
|||
"""
|
||||
def abs(literal)
|
||||
|
||||
def abs(%Literal{datatype: @xsd_decimal} = literal) do
|
||||
def abs(%Literal{datatype: datatype} = literal) when is_xsd_decimal(datatype) do
|
||||
if RDF.Decimal.valid?(literal) do
|
||||
literal.value
|
||||
|> D.abs()
|
||||
|
@ -279,7 +279,7 @@ defmodule RDF.Numeric do
|
|||
"""
|
||||
def round(literal, precision \\ 0)
|
||||
|
||||
def round(%Literal{datatype: @xsd_decimal} = literal, precision) do
|
||||
def round(%Literal{datatype: datatype} = literal, precision) when is_xsd_decimal(datatype) do
|
||||
if RDF.Decimal.valid?(literal) do
|
||||
literal.value
|
||||
|> xpath_round(precision)
|
||||
|
@ -288,10 +288,11 @@ defmodule RDF.Numeric do
|
|||
end
|
||||
end
|
||||
|
||||
def round(%Literal{datatype: @xsd_double, value: value} = literal, _)
|
||||
when value in ~w[nan positive_infinity negative_infinity]a, do: literal
|
||||
def round(%Literal{datatype: datatype, value: value} = literal, _)
|
||||
when is_xsd_double(datatype) and value in ~w[nan positive_infinity negative_infinity]a,
|
||||
do: literal
|
||||
|
||||
def round(%Literal{datatype: @xsd_double} = literal, precision) do
|
||||
def round(%Literal{datatype: datatype} = literal, precision) when is_xsd_double(datatype) do
|
||||
if RDF.Double.valid?(literal) do
|
||||
literal.value
|
||||
|> D.new()
|
||||
|
@ -330,7 +331,7 @@ defmodule RDF.Numeric do
|
|||
"""
|
||||
def ceil(literal)
|
||||
|
||||
def ceil(%Literal{datatype: @xsd_decimal} = literal) do
|
||||
def ceil(%Literal{datatype: datatype} = literal) when is_xsd_decimal(datatype)do
|
||||
if RDF.Decimal.valid?(literal) do
|
||||
literal.value
|
||||
|> D.round(0, (if literal.value.sign == -1, do: :down, else: :up))
|
||||
|
@ -339,10 +340,11 @@ defmodule RDF.Numeric do
|
|||
end
|
||||
end
|
||||
|
||||
def ceil(%Literal{datatype: @xsd_double, value: value} = literal)
|
||||
when value in ~w[nan positive_infinity negative_infinity]a, do: literal
|
||||
def ceil(%Literal{datatype: datatype, value: value} = literal)
|
||||
when is_xsd_double(datatype) and value in ~w[nan positive_infinity negative_infinity]a,
|
||||
do: literal
|
||||
|
||||
def ceil(%Literal{datatype: @xsd_double} = literal) do
|
||||
def ceil(%Literal{datatype: datatype} = literal) when is_xsd_double(datatype) do
|
||||
if RDF.Double.valid?(literal) do
|
||||
literal.value
|
||||
|> Float.ceil()
|
||||
|
@ -368,7 +370,7 @@ defmodule RDF.Numeric do
|
|||
"""
|
||||
def floor(literal)
|
||||
|
||||
def floor(%Literal{datatype: @xsd_decimal} = literal) do
|
||||
def floor(%Literal{datatype: datatype} = literal) when is_xsd_decimal(datatype)do
|
||||
if RDF.Decimal.valid?(literal) do
|
||||
literal.value
|
||||
|> D.round(0, (if literal.value.sign == -1, do: :up, else: :down))
|
||||
|
@ -377,10 +379,11 @@ defmodule RDF.Numeric do
|
|||
end
|
||||
end
|
||||
|
||||
def floor(%Literal{datatype: @xsd_double, value: value} = literal)
|
||||
when value in ~w[nan positive_infinity negative_infinity]a, do: literal
|
||||
def floor(%Literal{datatype: datatype, value: value} = literal)
|
||||
when is_xsd_double(datatype) and value in ~w[nan positive_infinity negative_infinity]a,
|
||||
do: literal
|
||||
|
||||
def floor(%Literal{datatype: @xsd_double} = literal) do
|
||||
def floor(%Literal{datatype: datatype} = literal) when is_xsd_double(datatype)do
|
||||
if RDF.Double.valid?(literal) do
|
||||
literal.value
|
||||
|> Float.floor()
|
||||
|
@ -410,18 +413,21 @@ defmodule RDF.Numeric do
|
|||
end
|
||||
|
||||
|
||||
defp type_conversion(%Literal{datatype: @xsd_decimal} = arg1,
|
||||
%Literal{value: arg2}, @xsd_decimal),
|
||||
defp type_conversion(%Literal{datatype: datatype} = arg1,
|
||||
%Literal{value: arg2}, datatype) when is_xsd_decimal(datatype),
|
||||
do: {arg1, RDF.decimal(arg2)}
|
||||
|
||||
defp type_conversion(%Literal{value: arg1},
|
||||
%Literal{datatype: @xsd_decimal} = arg2, @xsd_decimal),
|
||||
%Literal{datatype: datatype} = arg2, datatype)
|
||||
when is_xsd_decimal(datatype),
|
||||
do: {RDF.decimal(arg1), arg2}
|
||||
|
||||
defp type_conversion(%Literal{datatype: @xsd_decimal, value: arg1}, arg2, @xsd_double),
|
||||
defp type_conversion(%Literal{datatype: input_datatype, value: arg1}, arg2, output_datatype)
|
||||
when is_xsd_decimal(input_datatype) and is_xsd_double(output_datatype),
|
||||
do: {arg1 |> D.to_float() |> RDF.double(), arg2}
|
||||
|
||||
defp type_conversion(arg1, %Literal{datatype: @xsd_decimal, value: arg2}, @xsd_double),
|
||||
defp type_conversion(arg1, %Literal{datatype: input_datatype, value: arg2}, output_datatype)
|
||||
when is_xsd_decimal(input_datatype) and is_xsd_double(output_datatype),
|
||||
do: {arg1, arg2 |> D.to_float() |> RDF.double()}
|
||||
|
||||
defp type_conversion(arg1, arg2, _), do: {arg1, arg2}
|
||||
|
@ -445,4 +451,5 @@ defmodule RDF.Numeric do
|
|||
true -> XSD.integer
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ defmodule RDF.Literal do
|
|||
|
||||
alias RDF.Datatype.NS.XSD
|
||||
|
||||
# to be able to pattern-match on plain types
|
||||
# to be able to pattern-match on plain types; we can't use RDF.Literal.Guards here since these aren't compiled here yet
|
||||
@xsd_string XSD.string
|
||||
@lang_string RDF.iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#langString")
|
||||
@plain_types [@xsd_string, @lang_string]
|
||||
|
|
77
lib/rdf/literal_guards.ex
Normal file
77
lib/rdf/literal_guards.ex
Normal file
|
@ -0,0 +1,77 @@
|
|||
defmodule RDF.Literal.Guards do
|
||||
@moduledoc """
|
||||
Guards for working with `RDF.Literal`s.
|
||||
|
||||
These are useful for pattern matching on datatypes of literals, since
|
||||
Elixir doesn't allow function calls in pattern matching clauses, which means
|
||||
the qualified terms of a `RDF.Vocabulary.Namespace` can't be used.
|
||||
|
||||
## Examples
|
||||
|
||||
defmodule M do
|
||||
import RDF.Literal.Guards
|
||||
|
||||
def f(%RDF.Literal{datatype: datatype} = literal) when is_xsd_integer(datatype) do
|
||||
# ...
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
|
||||
alias RDF.Datatype.NS.XSD
|
||||
|
||||
@xsd_integer XSD.integer()
|
||||
@xsd_decimal XSD.decimal()
|
||||
@xsd_float XSD.float()
|
||||
@xsd_double XSD.double()
|
||||
@xsd_string XSD.string()
|
||||
@xsd_boolean XSD.boolean()
|
||||
@xsd_dateTime XSD.dateTime()
|
||||
@xsd_any_uri XSD.anyURI()
|
||||
@rdf_lang_string RDF.langString
|
||||
|
||||
@doc """
|
||||
Returns `true` if the given datatype is `xsd:integer`; otherwise returns `false`.
|
||||
"""
|
||||
defguard is_xsd_integer(datatype) when datatype == @xsd_integer
|
||||
|
||||
@doc """
|
||||
Returns `true` if the given datatype is `xsd:decimal`; otherwise returns `false`.
|
||||
"""
|
||||
defguard is_xsd_decimal(datatype) when datatype == @xsd_decimal
|
||||
|
||||
@doc """
|
||||
Returns `true` if the given datatype is `xsd:float`; otherwise returns `false`.
|
||||
"""
|
||||
defguard is_xsd_float(datatype) when datatype == @xsd_float
|
||||
|
||||
@doc """
|
||||
Returns `true` if the given datatype is `xsd:double`; otherwise returns `false`.
|
||||
"""
|
||||
defguard is_xsd_double(datatype) when datatype == @xsd_double
|
||||
|
||||
@doc """
|
||||
Returns `true` if the given datatype is `xsd:string`; otherwise returns `false`.
|
||||
"""
|
||||
defguard is_xsd_string(datatype) when datatype == @xsd_string
|
||||
@doc """
|
||||
Returns `true` if the given datatype is `xsd:boolean`; otherwise returns `false`.
|
||||
"""
|
||||
defguard is_xsd_boolean(datatype) when datatype == @xsd_boolean
|
||||
|
||||
@doc """
|
||||
Returns `true` if the given datatype is `xsd:dateTime`; otherwise returns `false`.
|
||||
"""
|
||||
defguard is_xsd_datetime(datatype) when datatype == @xsd_dateTime
|
||||
|
||||
@doc """
|
||||
Returns `true` if the given datatype is `xsd:anyURI`; otherwise returns `false`.
|
||||
"""
|
||||
defguard is_xsd_any_uri(datatype) when datatype == @xsd_any_uri
|
||||
|
||||
@doc """
|
||||
Returns `true` if the given datatype is `rdf:langString`; otherwise returns `false`.
|
||||
"""
|
||||
defguard is_rdf_lang_string(datatype) when datatype == @rdf_lang_string
|
||||
|
||||
end
|
|
@ -28,6 +28,8 @@ defmodule RDF.Serialization.Encoder do
|
|||
quote bind_quoted: [], unquote: true do
|
||||
@behaviour unquote(__MODULE__)
|
||||
|
||||
import RDF.Literal.Guards
|
||||
|
||||
def encode!(data, opts \\ []) do
|
||||
case encode(data, opts) do
|
||||
{:ok, data} -> data
|
||||
|
|
|
@ -5,7 +5,6 @@ defmodule RDF.NTriples.Encoder do
|
|||
|
||||
alias RDF.{IRI, Literal, BlankNode}
|
||||
|
||||
@xsd_string RDF.Datatype.NS.XSD.string
|
||||
|
||||
def encode(data, _opts \\ []) do
|
||||
result =
|
||||
|
@ -34,7 +33,7 @@ defmodule RDF.NTriples.Encoder do
|
|||
~s["#{value}"@#{language}]
|
||||
end
|
||||
|
||||
def term(%Literal{datatype: @xsd_string} = literal) do
|
||||
def term(%Literal{datatype: datatype} = literal) when is_xsd_string(datatype) do
|
||||
~s["#{Literal.lexical(literal)}"]
|
||||
end
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ defmodule RDF.Turtle.Encoder do
|
|||
@indentation_char " "
|
||||
@indentation 4
|
||||
|
||||
@xsd_string RDF.Datatype.NS.XSD.string
|
||||
@native_supported_datatypes [
|
||||
RDF.Datatype.NS.XSD.boolean,
|
||||
RDF.Datatype.NS.XSD.integer,
|
||||
|
@ -279,7 +278,7 @@ defmodule RDF.Turtle.Encoder do
|
|||
defp term(%Literal{value: value, language: language}, _,_ , _) when not is_nil(language),
|
||||
do: ~s["#{value}"@#{language}]
|
||||
|
||||
defp term(%Literal{datatype: @xsd_string} = literal, _, _,_),
|
||||
defp term(%Literal{datatype: datatype} = literal, _, _,_) when is_xsd_string(datatype),
|
||||
do: literal |> Literal.lexical |> quoted()
|
||||
|
||||
defp term(%Literal{datatype: datatype} = literal, state, _, nesting)
|
||||
|
|
Loading…
Reference in a new issue