Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Level 2 Group Qualifiers #13

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions lib/edtf/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,24 @@ defmodule EDTF.Parser do

# Signed year with optional qualifier
qualified_year =
optional(component_qualifier |> tag(:qualifier))
optional(component_qualifier |> tag(:pre_qualifier))
|> concat(optional(sign) |> tag(:sign))
|> concat(year |> tag(:value))
|> concat(optional(component_qualifier |> tag(:post_qualifier)))
|> post_traverse({Helpers, :bitmask, [0]})

# Month with optional qualifier
qualified_month =
optional(component_qualifier |> tag(:qualifier))
optional(component_qualifier |> tag(:pre_qualifier))
|> concat(month |> tag(:value))
|> concat(optional(component_qualifier |> tag(:post_qualifier)))
|> post_traverse({Helpers, :bitmask, [4]})

# Day with optional qualifier
qualified_day =
optional(component_qualifier |> tag(:qualifier))
optional(component_qualifier |> tag(:pre_qualifier))
|> concat(day |> tag(:value))
|> concat(optional(component_qualifier |> tag(:post_qualifier)))
|> post_traverse({Helpers, :bitmask, [6]})

# Basic [-]YYYY[-MM[-DD]] with optional qualifiers
Expand Down
27 changes: 22 additions & 5 deletions lib/edtf/parser/helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,42 @@ defmodule EDTF.Parser.Helpers do
being fully masked (15 for year, 48 for month, or 192 for day). Unspecified digits
(`X`) flip individual bits.

A pre-component qualifier results in only that component being masked. A post-
component qualifier results in that component plus all components to the left
being masked.

Example:
```elixir
iex> bitmask("-%02", [value: ~c"200X", sign: ~c"-"], %{}, nil, nil, 0)
{"-%02", [[value: -2000, attributes: [unspecified: 8]]], %{}}

iex> bitmask("", [value: ~c"02", qualifier: ~c"%"], %{}, nil, nil, 4)
iex> bitmask("", [value: ~c"02", pre_qualifier: ~c"%"], %{}, nil, nil, 4)
{"", [[value: 2, attributes: [approximate: 48, uncertain: 48]]], %{}}

iex> bitmask("", [value: ~c"02", post_qualifier: ~c"%"], %{}, nil, nil, 4)
{"", [[value: 2, attributes: [approximate: 63, uncertain: 63]]], %{}}
```
"""
def bitmask(rest, value, context, _line, _offset, shift) do
qualifier =
cond do
Keyword.get(value, :pre_qualifier) -> {:pre, Keyword.get(value, :pre_qualifier)}
Keyword.get(value, :post_qualifier) -> {:post, Keyword.get(value, :post_qualifier)}
true -> nil
end

{rest,
[
bitmask(
Keyword.get(value, :value),
Keyword.get(value, :sign, ~c""),
Keyword.get(value, :qualifier, ~c""),
qualifier,
shift
)
], context}
end

def bitmask(bitstring, sign, [], shift) do
def bitmask(bitstring, sign, nil, shift) do
{output, mask} =
bitstring
|> Enum.with_index()
Expand All @@ -71,8 +85,8 @@ defmodule EDTF.Parser.Helpers do
end
end

def bitmask(bitstring, sign, qualifier, shift) do
mask = ((1 <<< length(bitstring)) - 1) <<< shift
def bitmask(bitstring, sign, {qualifier_position, qualifier}, shift) do
mask = calculate_mask(bitstring, shift, qualifier_position)

attributes =
Map.get(@qualifier_attributes, qualifier)
Expand All @@ -84,6 +98,9 @@ defmodule EDTF.Parser.Helpers do
]
end

defp calculate_mask(bitstring, shift, :pre), do: ((1 <<< length(bitstring)) - 1) <<< shift
defp calculate_mask(bitstring, shift, :post), do: (1 <<< (length(bitstring) + shift)) - 1

@doc """
Apply a parsed sign to a parsed integer value.

Expand Down
10 changes: 10 additions & 0 deletions test/edtf/date_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ defmodule EDTF.DateTest do
assert subject.attributes[:uncertain] == 192
refute subject.attributes[:unspecified]
end

@tag edtf: "1978-02?-23"
test "approximate month and year", %{subject: subject} do
assert subject.type == :date
assert subject.values == [1978, 1, 23]
assert subject.level == 2
refute subject.attributes[:approximate]
assert subject.attributes[:uncertain] == 63
refute subject.attributes[:unspecified]
end
end

describe "unspecified" do
Expand Down
Loading