Page MenuHomePhorge

No OneTemporary

Size
5 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/bbcode/parser.ex b/lib/bbcode/parser.ex
index 17f34c3..e9a238f 100644
--- a/lib/bbcode/parser.ex
+++ b/lib/bbcode/parser.ex
@@ -1,80 +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: ?[], 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(:basic_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_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 9a25ff2..bc3724b 100644
--- a/test/bbcode/parser_test.exs
+++ b/test/bbcode/parser_test.exs
@@ -1,70 +1,76 @@
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]")
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]")
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)
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]")
+ 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
Tue, Nov 26, 5:46 AM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40212
Default Alt Text
(5 KB)

Event Timeline