Skip to content
Open
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
11 changes: 11 additions & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
locals_without_parens = [
defparsec: 2,
defparsec: 3,
defparsecp: 2,
defparsecp: 3,
defcombinator: 2,
defcombinator: 3,
defcombinatorp: 2,
defcombinatorp: 3
]

[
force_do_end_blocks: true,
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
Expand Down
9 changes: 9 additions & 0 deletions lib/earmark_parser/nimble_parsers.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule EarmarkParser.NimbleParsers do
@moduledoc ~S"""
coming soon
"""

defdelegate parse_html_atts(input), to: __MODULE__.HtmlAttsParser
end

# SPDX-License-Identifier: AGPL-3.0-or-later
40 changes: 40 additions & 0 deletions lib/earmark_parser/nimble_parsers/html_atts_parser.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
defmodule EarmarkParser.NimbleParsers.HtmlAttsParser do
@moduledoc ~S"""
Parses an HTML tag
"""
import NimbleParsec
alias EarmarkParser.NimbleParsers.StringParser

html_att_name =
ascii_string([?a..?z, ?A..?z, ?-..?-], min: 1)

html_att =
html_att_name
|> choice([
"=" |> string() |> ignore() |> parsec({StringParser, :string_value}),
empty()
])
|> reduce(:reduce_att)

html_att_end =
string(">")

html_atts =
html_att
|> repeat(" " |> string() |> times(min: 1) |> ignore() |> concat(html_att))

defparsec(:parse_html_atts, html_atts |> optional() |> ignore(html_att_end))

@doc false
def reduce_att(att_ast)

def reduce_att([name]) do
{name, true}
end

def reduce_att([name | values]) do
{name, IO.chardata_to_string(values)}
end
end

# SPDX-License-Identifier: AGPL-3.0-or-later
10 changes: 10 additions & 0 deletions lib/earmark_parser/nimble_parsers/html_tag_parser.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
defmodule EarmarkParser.NimbleParsers.HtmlTagParser do

@moduledoc ~S"""
Parses an HTML tag
"""
import NimbleParsec

Check warning on line 6 in lib/earmark_parser/nimble_parsers/html_tag_parser.ex

View workflow job for this annotation

GitHub Actions / mix test (Elixir 1.15.x | OTP 25)

unused import NimbleParsec

Check warning on line 6 in lib/earmark_parser/nimble_parsers/html_tag_parser.ex

View workflow job for this annotation

GitHub Actions / mix test (Elixir 1.14.x | OTP 25)

unused import NimbleParsec
alias EarmarkParser.NimbleParsers.HtmlAttsParser

Check warning on line 7 in lib/earmark_parser/nimble_parsers/html_tag_parser.ex

View workflow job for this annotation

GitHub Actions / mix test (Elixir 1.15.x | OTP 25)

unused alias HtmlAttsParser

Check warning on line 7 in lib/earmark_parser/nimble_parsers/html_tag_parser.ex

View workflow job for this annotation

GitHub Actions / mix test (Elixir 1.14.x | OTP 25)

unused alias HtmlAttsParser

end
# SPDX-License-Identifier: Apache-2.0
38 changes: 38 additions & 0 deletions lib/earmark_parser/nimble_parsers/string_parser.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
defmodule EarmarkParser.NimbleParsers.StringParser do
@moduledoc ~S"""
String combinator
"""

import NimbleParsec

inner_string = fn combinator, ch ->
combinator
|> repeat(
lookahead_not(ascii_char([ch]))
|> choice([
IO.chardata_to_string(["\\", ch])
|> string()
|> replace(ch),
utf8_char([])
])
)
end

quoted_string = fn ch ->
empty()
|> ascii_char([ch])
|> ignore()
|> inner_string.(ch)
|> ignore(ascii_char([ch]))
end

defcombinator(
:string_value,
choice([
quoted_string.(?"),
quoted_string.(?')
])
)
end

# SPDX-License-Identifier: AGPL-3.0-or-later
4 changes: 2 additions & 2 deletions lib/earmark_parser/options.ex
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ defmodule EarmarkParser.Options do
Use normalize before passing it into any API function

iex(1)> options = normalize(annotations: "%%")
...(1)> options.annotations
~r{\A(.*)(%%.*)}
...(1)> options.annotations.source
"\\A(.*)(%%.*)"
"""
@spec normalize(t() | keyword()) :: t()
def normalize(options)
Expand Down
11 changes: 10 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
defmodule EarmarkParser.MixProject do
use Mix.Project

@version "1.4.44"
@version "1.4.45"
@url "https://github.com/RobertDober/earmark_parser"

@deps [
# production environnement
{:nimble_parsec, "~> 1.4.2", runtime: false},

# dev and test environnements
{:benchee, "~> 1.3.1", only: [:dev]},
# {:credo, "~> 1.7.5", only: [:dev]},
{:dialyxir, "~> 1.4.5", only: [:dev], runtime: false},
Expand All @@ -14,6 +18,11 @@ defmodule EarmarkParser.MixProject do
{:floki, "~> 0.36", only: [:dev, :test]}
]

def cli do
[
]
end

def project do
[
app: :earmark_parser,
Expand Down
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
Expand Down
50 changes: 50 additions & 0 deletions test/nimble_parsers/html_att_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
defmodule Test.NimbleParsers.HtmlAttTest do
use Support.NimbleTestCase

describe "empty att list" do
test "returns an empty list if end char (>) is present" do
parse_html_atts(">")
|> parsed_ok([])
end

test "does not parse an empty string" do
parse_html_atts("")
|> parsed_error("expected string \">\"")
end
end

describe "boolean attribute" do
test "just it's presence" do
parse_html_atts("hidden>")
|> parsed_ok([{"hidden", true}])
end

test "two boolean attributes" do
parse_html_atts("hidden and-visible>")
|> parsed_ok([{"hidden", true}, {"and-visible", true}])
end
end

describe "a string attribute" do
test "elixir, what else?" do
parse_html_atts(~S{lang="elixir">})
|> parsed_ok([{"lang", "elixir"}])
end

test "escaped double quote" do
parse_html_atts(~S{lang="\"lua\"">})
|> parsed_ok([{"lang", "\"lua\""}])
end

test "single quoted string too" do
parse_html_atts(~S{lang="\"pt-br\"" lang='fr-fr' lang='de-\'at\''>})
|> parsed_ok([
{"lang", "\"pt-br\""},
{"lang", "fr-fr"},
{"lang", "de-'at'"}
])
end
end
end

# SPDX-License-Identifier: Apache-2.0
13 changes: 13 additions & 0 deletions test/nimble_parsers/html_oneline_tag_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
defmodule Test.NimbleParsers.HtmlOnelineTagTest do

use Support.NimbleTestCase

describe "no attributes" do
test "br" do
# parse_html_tag("<br/>")
# |> parsed_ok({"br", [], [], %{verbatim: true}})
end
end

end
# SPDX-License-Identifier: Apache-2.0
12 changes: 12 additions & 0 deletions test/support/nimble_test_case.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule Support.NimbleTestCase do
defmacro __using__(_options) do
quote do
use ExUnit.Case, async: true

import EarmarkParser.NimbleParsers
import Support.NimbleTests
end
end
end

# SPDX-License-Identifier: Apache-2.0
22 changes: 22 additions & 0 deletions test/support/nimble_tests.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule Support.NimbleTests do
@moduledoc ~S"""
Makes asserting on NimbleParsec results simpler
"""
defmacro parsed_error(parsed, expected) do
quote do
# |> IO.inspect()
{:error, message, _, _, _, _} = unquote(parsed)
assert message == unquote(expected)
end
end

defmacro parsed_ok(parsed, expected) do
quote do
# |> IO.inspect()
{:ok, result, _, _, _, _} = unquote(parsed)
assert result == unquote(expected)
end
end
end

# SPDX-License-Identifier: Apache-2.0
Loading