Skip to content

Commit

Permalink
WIP color parser
Browse files Browse the repository at this point in the history
	NEXT: Write regexp parser
  • Loading branch information
RobertDober committed Oct 13, 2024
1 parent 0a995d8 commit dbdac79
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 76 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ ex_aequo_colors-*.tar
/tmp/
tags
.local
xcol
Binary file removed colorize
Binary file not shown.
14 changes: 11 additions & 3 deletions lib/ex_aequo_colors/cli.ex
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ defmodule ExAequoColors.Cli do

defp run(stream, options) do
stream
|> numbered_lines()
|> Stream.map(&_colorize_line(&1, options))
|> Enum.join("\n")
|> numbered_lines()
|> Stream.map(&_colorize_line(&1, options))
|> Enum.join("\n")
end

defp _add_defaults(options) do
Expand Down Expand Up @@ -113,6 +113,14 @@ defmodule ExAequoColors.Cli do
end
end

# defp put_if(map, key, pairs) do
# if Map.get(map, key) do
# pairs
# |> Enum.inject(map, fn {k, v}, a -> Map.put(a, k, v) end)
# else
# map
# end
# end
@spec _version() :: binary()
defp _version do
with {:ok, version} <- :application.get_key(:ex_aequo_colors, :vsn),
Expand Down
162 changes: 96 additions & 66 deletions lib/ex_aequo_colors/colorizer.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
defmodule ExAequoColors.Colorizer do
use ExAequoBase.Types

alias ExAequoColors.Color
import ExAequoBase.Enum, only: [map_ok: 2]
import ExAequoBase.Fn, only: [select: 1]
import ExAequoBase.RegexParser, only: [parse: 2]
import ExAequoBase.Text, only: [behead: 2, parse_up_to: 3]
import ExAequoColors.Color, only: [color: 2]
@moduledoc ~S"""
Expand All @@ -11,15 +14,31 @@ defmodule ExAequoColors.Colorizer do
@type color_t :: maybe(result_t(binary()))
@type color_fn_t :: (-> color_t())

@default_options %{
trigger: "<",
closer: ">",
resetter: "$"
}

@doc ~S"""
colorizes a binary according to options
iex(1)> colorize("hello")
{:ok, "hello"}
Here are the default options, a color is triggered by `<...`
iex(2)> colorize("<green>")
{:ok, "\e[32m"}
We can also add bold for example
iex(3)> colorize("<green, bold>success")
{:ok, "\e[32m\e[1msuccess"}
"""
@spec colorize(binary()) :: color_t()
def colorize(line), do: colorize(line, %{trigger: "<", closer: ">"})
def colorize(line), do: colorize(line, @default_options)

@spec colorize(binary(), map()) :: color_t()
def colorize(line, options) do
Expand All @@ -28,100 +47,111 @@ defmodule ExAequoColors.Colorizer do
closer = Map.get(options, :closer)
escaper = String.at(trigger, 0)
regexen = _compile_regexen(options)
case _parse(line, [], options, regexen) do
{:ok, result} -> {:ok, result <> reset}
error -> error
end
{:ok, parse(regexen, line) |> IO.chardata_to_string}
end

@spec _compile_regexen(map()) :: list(Regex.t())
defp _compile_regexen(options) do
[
_compile_double_escape(options, :trigger),
_compile_double_escape(options, :resetter),
_compile_literal(options, :trigger, &_compile_color/1),
_compile_trigger(options.trigger, options.closer),
_compile_literal(options, :resetter, :reset),
_compile_verb(options)
]
end

@splitter_rgx ~r/,\s*/
defp _compile_color_spec(color_spec) do
specs = String.split(color_spec, @splitter_rgx)
codes = specs |> Enum.map(&String.to_atom/1)
Color.color("", codes)
end

@spec _compile_double_escape(map(), atom()) :: Regex.t()
defp _compile_double_escape(options, key) do
trigger = Map.get(options, key) |> String.at(0)
rgx_trigger = Regex.escape(trigger)
# IO.inspect(options)
trigger = Map.get(options, key) |> IO.inspect(label: key) |> String.at(0)
trigger_rgx = Regex.escape(trigger)
rgx = Regex.compile!("\\A(#{trigger_rgx})#{trigger_rgx}")
{rgx, trigger}
end

@spec _compile_literal(map(), atom()) :: Regex.t()
@spec _compile_literal(map(), atom(), any()) :: Regex.t()
defp _compile_literal(options, key, fun) do
literal = Map.get(options, key)
{Regex.compile!("\\A(#{Regex.escape(literal)})"), fun}
end

@spec _compile_verb(map()) :: Regex.t()
defp _compile_verb(options) do
stop = [
Regex.escape(options.trigger),
Regex.escape(options.resetter)
] |> Enum.join("|")
rgx = Regex.compile!("\\A(.*?)(?=#{stop})")
{rgx, nil}
end

@spec _parse(binary(), list(), map()) :: color_t()
defp _parse(line, result, options)
defp _parse("", result, _) do
{:ok, IO.chardata_to_string(result)}
end
defp _parse(input, result, options) do
select([
_parse_escaped_trigger_fn(input, result, options),
_parse_escaped_resetter_fn(input, result, options),
_parse_verbatim_fn(input, result, options),
])
end

@spec _parse_escaped(binary(), list(), map(), binary()) :: color_t()
defp _parse_escaped(input, result, options, toescape) do
first_trigger = String.at(toescape, 0)
escaped_trigger = first_trigger <> first_trigger
if String.starts_with?(input, escaped_trigger) do
_parse(behead(input, 2), [result, first_trigger], options)
end
end

@spec _parse_escaped_resetter_fn(binary(), list(), map()) :: color_fn_t()
defp _parse_escaped_resetter_fn(input, result, options) do
fn ->
case Map.get(options, :resetter) do
nil -> nil
resetter -> _parse_escaped(input, result, options, resetter)
end
end
defp _compile_trigger(open, close) do
{
Regex.compile!(
"\\A#{Regex.escape(open)}(.*?)#{Regex.escape(close)}(.*)"
),
&_compile_color_spec/1
}
end

@spec _parse_escaped_trigger_fn(binary(), list(), map()) :: color_fn_t()
defp _parse_escaped_trigger_fn(input, result, options) do
fn ->
_parse_escaped(input, result, options, options.trigger)
end
@spec _compile_verb(map()) :: Regex.t()
defp _compile_verb(options) do
excluded = String.at(options.trigger, 0) <> String.at(options.resetter, 0)
rgx = Regex.compile!("\\A([^#{excluded}]+)(.*)")
{rgx}
end

@spec _parse_verbatim(binary(), list(), map()) :: color_t()
defp _parse_verbatim(input, result, options) do
case parse_up_to(input, options.non_verb, :keep) do
# @spec _parse(binary(), list(), map()) :: color_t()
# defp _parse(line, result, options)
# defp _parse("", result, _) do
# {:ok, IO.chardata_to_string(result)}
# end
# defp _parse(input, result, options) do
# select([
# _parse_escaped_trigger_fn(input, result, options),
# _parse_escaped_resetter_fn(input, result, options),
# _parse_verbatim_fn(input, result, options),
# ])
# end

# @spec _parse_escaped(binary(), list(), map(), binary()) :: color_t()
# defp _parse_escaped(input, result, options, toescape) do
# first_trigger = String.at(toescape, 0)
# escaped_trigger = first_trigger <> first_trigger
# if String.starts_with?(input, escaped_trigger) do
# _parse(behead(input, 2), [result, first_trigger], options)
# end
# end

# @spec _parse_escaped_resetter_fn(binary(), list(), map()) :: color_fn_t()
# defp _parse_escaped_resetter_fn(input, result, options) do
# fn ->
# case Map.get(options, :resetter) do
# nil -> nil
# resetter -> _parse_escaped(input, result, options, resetter)
# end
# end
# end

# @spec _parse_escaped_trigger_fn(binary(), list(), map()) :: color_fn_t()
# defp _parse_escaped_trigger_fn(input, result, options) do
# fn ->
# _parse_escaped(input, result, options, options.trigger)
# end
# end

# @spec _parse_verbatim(binary(), list(), map()) :: color_t()
# defp _parse_verbatim(input, result, options) do
# case parse_up_to(input, options.non_verb, :keep) do

end
# end

end

@spec _parse_verbatim_fn(binary(), list(), map()) :: color_fn_t()
defp _parse_verbatim_fn(input, result, options) do
fn ->
_parse_verbatim(input, result, options)
end
end
# end

# @spec _parse_verbatim_fn(binary(), list(), map()) :: color_fn_t()
# defp _parse_verbatim_fn(input, result, options) do
# fn ->
# _parse_verbatim(input, result, options)
# end
# end
# defp _colorize_line({line, lnb}, auto) do
# case line |>_parse(auto, []) do
# {:ok, result} -> {:ok, IO.chardata_to_string(result)}
Expand Down
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ defmodule ExAequoColors.MixProject do
[
{:dialyxir, "~> 1.4.3", only: [:dev], runtime: false},
# {:ex_aequo_fn, "~> 0.1.0"},
{:ex_aequo_base, "~> 0.1.3"},
{:ex_aequo_base, "~> 0.1.5"},
{:excoveralls, "~> 0.18.2", only: [:test]},
{:extractly, "~> 0.5.4", only: [:dev]},
{:minipeg, "~> 0.6.3"}
Expand All @@ -47,7 +47,7 @@ defmodule ExAequoColors.MixProject do
defp escript_config do
[
main_module: ExAequoColors.Cli,
name: "colorize"
name: "xcol"
]
end

Expand Down
9 changes: 4 additions & 5 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
%{
"dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"},
"dialyxir": {:hex, :dialyxir, "1.4.4", "fb3ce8741edeaea59c9ae84d5cec75da00fa89fe401c72d6e047d11a61f65f70", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "cd6111e8017ccd563e65621a4d9a4a1c5cd333df30cebc7face8029cacb4eff6"},
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
"ex_aequo_base": {:hex, :ex_aequo_base, "0.1.3", "945d9bb81941c8ff986cad23d4786934a4c2a9e71667444020678e9838fd9582", [:mix], [], "hexpm", "98485973baaf866439d1023ce99b8d90fcc845d454512694eb6a4633a3b7a3c5"},
"ex_aequo_fn": {:hex, :ex_aequo_fn, "0.1.0", "f6254eb29364c79efc8f31d94c126a0957108c99ec18c6962b063e3b30bb8567", [:mix], [], "hexpm", "8d806e911b8a49b7d17a14bbf8140e51433435a13ae3ff63d099ba55ff005fa5"},
"ex_aequo_kwds": {:hex, :ex_aequo_kwds, "0.1.0", "a33c6fcba2da4afb1db2375888b854a5034ace4a9691cdcd7c43cabeb4a55fc7", [:mix], [], "hexpm", "bdf4967aa8796b9d72326fbf1a397d1070847f93599bd56f07b552c6d2b94432"},
"excoveralls": {:hex, :excoveralls, "0.18.2", "86efd87a0676a3198ff50b8c77620ea2f445e7d414afa9ec6c4ba84c9f8bdcc2", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "230262c418f0de64077626a498bd4fdf1126d5c2559bb0e6b43deac3005225a4"},
"ex_aequo_base": {:hex, :ex_aequo_base, "0.1.5", "f528cb921cb97ba008457eff9bfdf657495c68b2d9d8e89a5dfd64cbd6eb34de", [:mix], [{:ex_aequo_fn, "~> 0.1.2", [hex: :ex_aequo_fn, repo: "hexpm", optional: false]}], "hexpm", "55377f8bcf6332567b451d39aa57619bcd1ed4ba3dddc880a68136d6702ef68e"},
"ex_aequo_fn": {:hex, :ex_aequo_fn, "0.1.2", "d3c6ae73bafb1f2e1661e2b2dc4b548e577ebd7cc24d22afb0efbe1b8dca16ba", [:mix], [], "hexpm", "6e8cecd6e37dc3cd896dc631c5eb2e766e0ff6b914d110cdeedace9bc7ac1d8f"},
"excoveralls": {:hex, :excoveralls, "0.18.3", "bca47a24d69a3179951f51f1db6d3ed63bca9017f476fe520eb78602d45f7756", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "746f404fcd09d5029f1b211739afb8fb8575d775b21f6a3908e7ce3e640724c6"},
"extractly": {:hex, :extractly, "0.5.4", "22ff3a624d814227ba842a2b59a38b1298df5531dbd4772dcbe9b97e05627145", [:mix], [], "hexpm", "612e16920317b2fb963b2da013019a614ba12b928d535a053a1efb21ddaa6268"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"minipeg": {:hex, :minipeg, "0.6.3", "9b1efe4ed37b8fbbadaff15f29a119cf6ea6c0b56206a4e23bbf65aa91a384cb", [:mix], [], "hexpm", "413e13b603d1e9c43722c7c63fc5a4694897c234e6aa5aa7efac647a181b5867"},
Expand Down

0 comments on commit dbdac79

Please sign in to comment.