Page MenuHomePhorge

No OneTemporary

Size
6 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/bbcode/parser.ex b/lib/bbcode/parser.ex
index e9a238f..aa4afca 100644
--- a/lib/bbcode/parser.ex
+++ b/lib/bbcode/parser.ex
@@ -1,102 +1,102 @@
defmodule BBCode.Parser do
import NimbleParsec
@moduledoc """
Parse BBCode into an abstract tree.
"""
tag = utf8_string([?a..?z, ?A..?Z, ?0..?9], min: 1)
text = utf8_string([not: ?[, not: ?]], min: 1)
text_without_newline = utf8_string([not: ?[, not: ?\n], min: 1)
start_tag = ignore(string("[")) |> concat(tag) |> ignore(string("]"))
end_tag = ignore(string("[/")) |> concat(tag) |> ignore(string("]"))
start_tag_with_property =
ignore(string("["))
|> concat(tag)
|> ignore(string("="))
|> concat(text)
|> ignore(string("]"))
star_tag = ignore(string("[*]"))
defcombinatorp(
:basic_stanza,
start_tag
|> repeat(lookahead_not(string("[/")) |> choice([parsec(:child_stanza), text]))
|> wrap()
|> concat(end_tag)
|> post_traverse(:emit_tree_node)
)
defcombinatorp(
:text_stanza,
text
|> wrap()
|> post_traverse(:emit_tree_node)
)
defcombinatorp(
:star_stanza,
star_tag
|> repeat(
lookahead_not(string("\n"))
|> choice([parsec(:child_stanza), text_without_newline])
)
|> wrap()
|> concat(ignore(string("\n")))
|> post_traverse(:emit_tree_node_star)
)
defparsec(
:prop_stanza,
start_tag_with_property
|> repeat(lookahead_not(string("[/")) |> choice([parsec(:child_stanza), text]))
|> wrap()
|> concat(end_tag)
|> post_traverse(:emit_tree_node_property)
)
defcombinatorp(
:child_stanza,
choice([parsec(:star_stanza), parsec(:prop_stanza), parsec(:basic_stanza)])
)
defcombinatorp(
:root_stanza,
choice([parsec(:child_stanza), parsec(:text_stanza)])
)
defparsecp(
:parse_tree,
repeat(lookahead_not(string("[/")) |> parsec(:root_stanza)) |> eos()
)
- defp emit_tree_node_star(_rest, [[text]], context, _line, _offset),
- do: {[{:li, text}], context}
+ defp emit_tree_node_star(_rest, [nodes], context, _line, _offset),
+ do: {[{:li, nodes}], context}
defp emit_tree_node_property(_rest, [tag, [tag, property, inside]], context, _line, _offset),
do: {[{String.to_atom(tag), property, inside}], context}
defp emit_tree_node_property(_rest, [tag, [tag, property | nodes]], context, _line, _offset),
do: {[{String.to_atom(tag), property, nodes}], context}
defp emit_tree_node(_rest, [tag, [tag, inside]], context, _line, _offset),
do: {[{String.to_atom(tag), inside}], context}
defp emit_tree_node(_rest, [tag, [tag | nodes]], context, _line, _offset),
do: {[{String.to_atom(tag), nodes}], context}
defp emit_tree_node(_rest, [[text]], context, _line, _offset),
do: {[text], context}
def parse(text) do
with {:ok, nodes, _, _, _, _} <- parse_tree(text) do
{:ok, nodes}
else
{:error, e, _, _, _, _} ->
{:error, e}
end
end
end
diff --git a/test/bbcode/parser_test.exs b/test/bbcode/parser_test.exs
index bc3724b..9bdd541 100644
--- a/test/bbcode/parser_test.exs
+++ b/test/bbcode/parser_test.exs
@@ -1,76 +1,90 @@
defmodule BBCode.Parser.Test do
use ExUnit.Case
alias BBCode.Parser
describe "simple tags" do
test "it parses [b] tags correctly" do
assert {:ok, [b: "testing"]} = Parser.parse("[b]testing[/b]")
end
test "it parses [i] tags correctly" do
assert {:ok, [i: "testing"]} = Parser.parse("[i]testing[/i]")
end
test "it parses [u] tags correctly" do
assert {:ok, [u: "testing"]} = Parser.parse("[u]testing[/u]")
end
test "it parses [s] tags correctly" do
assert {:ok, [s: "testing"]} = Parser.parse("[s]testing[/s]")
end
test "it parses [code] tags correctly" do
assert {:ok, [code: "testing"]} = Parser.parse("[code]testing[/code]")
end
test "it parses [quote] tags correctly" do
assert {:ok, [quote: "testing"]} = Parser.parse("[quote]testing[/quote]")
end
end
describe "nested tags" do
test "it parses [ul] lists correctly" do
- assert {:ok, [{:ul, [{:li, "a"}, {:li, "b"}]}]} = Parser.parse("[ul][li]a[/li][li]b[/li][/ul]")
+ assert {:ok, [{:ul, [{:li, "a"}, {:li, "b"}]}]} =
+ Parser.parse("[ul][li]a[/li][li]b[/li][/ul]")
end
test "it parses [ol] lists correctly" do
- assert {:ok, [{:ol, [{:li, "a"}, {:li, "b"}]}]} = Parser.parse("[ol][li]a[/li][li]b[/li][/ol]")
+ assert {:ok, [{:ol, [{:li, "a"}, {:li, "b"}]}]} =
+ Parser.parse("[ol][li]a[/li][li]b[/li][/ol]")
end
end
describe "multiline" do
test "it parses a multiline [li] list" do
data = """
[ul]
[li]a[/li]
[li]b[/li]
[/ul]
"""
assert {:ok, [{:ul, ["\n", {:li, "a"}, "\n", {:li, "b"}, "\n"]}, "\n"]} = Parser.parse(data)
end
test "it parses a multiline [*] list" do
data = """
[ul]
[*]a
[*]b
[/ul]
"""
- assert {:ok, [{:ul, ["\n", {:li, "a"}, {:li, "b"}]}, "\n"]} = Parser.parse(data)
+ assert {:ok, [{:ul, ["\n", {:li, ["a"]}, {:li, ["b"]}]}, "\n"]} = Parser.parse(data)
+ end
+
+ test "it parses a multiline [*] list with children" do
+ data = """
+ [ul]
+ [*][url=http://example.com]Example[/url]
+ [/ul]
+ """
+
+ assert {:ok, [{:ul, ["\n", {:li, [{:url, "http://example.com", "Example"}]}]}, "\n"]} =
+ Parser.parse(data)
end
end
describe "property tags" do
test "it parses [url=] tags correctly" do
- assert {:ok, [{:url, "http://example.com", "Example"}]} = Parser.parse("[url=http://example.com]Example[/url]")
+ assert {:ok, [{:url, "http://example.com", "Example"}]} =
+ Parser.parse("[url=http://example.com]Example[/url]")
end
end
describe "invalid input" do
test "it fails to parse unterminated input" do
{:error, "expected end of string"} = Parser.parse("hello [b]world")
end
end
end

File Metadata

Mime Type
text/x-diff
Expires
Mon, Nov 25, 11:28 PM (1 d, 8 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40113
Default Alt Text
(6 KB)

Event Timeline