Current section

Files

Jump to
cssex lib helpers media.ex
Raw

lib/helpers/media.ex

defmodule CSSEx.Helpers.Media do
@moduledoc false
import CSSEx.Helpers.Shared, only: [inc_col: 1]
@line_terminators CSSEx.Helpers.LineTerminators.code_points()
@media_split_regex ~r/(?<maybe_var_1>\$::)?.+(?<split>:).+(?<maybe_var_2>\$::)|(?<split_2>:)/
defstruct column: 0, acc: [], parenthesis: 0, p_acc: %{}
def parse(rem, data) do
case do_parse(rem, data, %__MODULE__{}) do
{:ok, {_, _} = result} -> result
{:error, new_data} -> {rem, new_data}
end
end
def do_parse([], data, %{acc: acc, parenthesis: 0} = _state) do
parsed = IO.chardata_to_string(acc)
{:ok, {parsed, data}}
end
# we found an opening parenthesis (,
def do_parse([?( | rem], data, %{parenthesis: p, p_acc: p_acc} = state) do
new_p = p + 1
new_p_acc = Map.put(p_acc, new_p, [])
do_parse(rem, inc_col(data), %{state | parenthesis: new_p, p_acc: new_p_acc})
end
# we found a closing parenthesis )
def do_parse([?) | rem], data, %{acc: acc, parenthesis: p, p_acc: p_acc} = state)
when p > 0 do
{accumulator, new_p_acc} = Map.pop(p_acc, p)
processed = process_parenthesis_content(accumulator, data)
previous_p = p - 1
new_state = inc_col(%{state | parenthesis: previous_p})
new_state_2 =
case Map.pop(new_p_acc, previous_p) do
# this means we're on the first opened parenthesis
{nil, new_p_acc_2} ->
%{new_state | p_acc: new_p_acc_2, acc: [acc, [?(, processed, ?)]]}
{previous_acc, new_p_acc_2} ->
%{
new_state
| p_acc: Map.put(new_p_acc_2, previous_p, [previous_acc, [?(, processed, ?)]])
}
end
do_parse(rem, data, new_state_2)
end
Enum.each(@line_terminators, fn char ->
def do_parse([unquote(char) | _rem], _data, _state), do: {:error, :newline}
end)
def do_parse(
[char | rem],
data,
%{parenthesis: p, p_acc: p_acc} = state
)
when p > 0 do
p_acc_inner = Map.fetch!(p_acc, p)
new_p_acc = Map.put(p_acc, p, [p_acc_inner, char])
do_parse(rem, data, inc_col(%{state | p_acc: new_p_acc}))
end
def do_parse([char | rem], data, %{parenthesis: 0, acc: acc} = state),
do: do_parse(rem, data, inc_col(%{state | acc: [acc, char]}))
def do_parse([?), _rem], data, %{parenthesis: 0}),
do: {:error, %{data | valid?: false, error: "Unexpected closing parenthesis"}}
def process_parenthesis_content(accumulator, data) do
acc = IO.chardata_to_string(accumulator)
Regex.split(~r/\s(and|or)\s/, acc, include_captures: true)
|> Enum.map(fn value ->
trimmed = String.trim(value)
case trimmed do
"and" ->
"and"
"or" ->
"or"
declaration ->
String.split(declaration, @media_split_regex, trim: true, on: [:split, :split_2])
|> Enum.map(fn token ->
case CSSEx.Helpers.Interpolations.maybe_replace_val(String.trim(token), data) do
{:ok, new_token} ->
new_token
{:error, _} ->
raise "#{token} was not declared"
end
end)
|> Enum.join(":")
end
end)
|> Enum.join(" ")
end
end