Page MenuHomePhorge

No OneTemporary

Size
8 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/bbcode/generator.ex b/lib/bbcode/generator.ex
index 33ff34b..2f0375a 100644
--- a/lib/bbcode/generator.ex
+++ b/lib/bbcode/generator.ex
@@ -1,79 +1,73 @@
defmodule BBCode.Generator do
@moduledoc """
Generate HTML from BBCode fragments in AST form.
The BBCode syntax supported is described at [bbcode.org][bbcode].
[bbcode]: https://www.bbcode.org/reference.php
"""
defp start_tag(tagname), do: Enum.join(["<", tagname, ">"])
defp end_tag(tagname), do: Enum.join(["</", tagname, ">"])
defp simple_tag(tagname, subtree) do
{:ok, text} = reduce_subtree(subtree)
{:ok, [start_tag(tagname), text, end_tag(tagname)] |> Enum.join()}
end
defp link_tag(url), do: Enum.join(["<a href=\"", url, "\">"])
defp reduce_subtree({:b, subtree}), do: simple_tag("strong", subtree)
defp reduce_subtree({:i, subtree}), do: simple_tag("em", subtree)
defp reduce_subtree({:u, subtree}), do: simple_tag("u", subtree)
defp reduce_subtree({:s, subtree}), do: simple_tag("del", subtree)
defp reduce_subtree({:ul, subtree}), do: simple_tag("ul", subtree)
defp reduce_subtree({:ol, subtree}), do: simple_tag("ol", subtree)
defp reduce_subtree({:li, subtree}), do: simple_tag("li", subtree)
defp reduce_subtree({:code, subtree}), do: simple_tag("pre", subtree)
defp reduce_subtree({:quote, subtree}), do: simple_tag("blockquote", subtree)
defp reduce_subtree({:table, subtree}), do: simple_tag("table", subtree)
defp reduce_subtree({:tr, subtree}), do: simple_tag("tr", subtree)
defp reduce_subtree({:th, subtree}), do: simple_tag("th", subtree)
defp reduce_subtree({:td, subtree}), do: simple_tag("td", subtree)
defp reduce_subtree({:url, text}),
do: {:ok, [link_tag(text), text, end_tag("a")] |> Enum.join()}
defp reduce_subtree({:url, address, text}),
do: {:ok, [link_tag(address), text, end_tag("a")] |> Enum.join()}
defp reduce_subtree({:img, address}),
do: {:ok, "<img src=\"#{address}\">"}
- defp reduce_subtree({:img, size, address}) do
- [width, height] = String.split(size, "x")
-
- {width, ""} = Integer.parse(width)
- {height, ""} = Integer.parse(height)
-
- {:ok, "<img src=\"#{address}\" width=\"#{width}\" height=\"#{height}\">"}
- end
+ defp reduce_subtree({:img, width, height, address}),
+ do: {:ok, "<img src=\"#{address}\" width=\"#{width}\" height=\"#{height}\">"}
defp reduce_subtree({:br}), do: {:ok, "<br>\n"}
defp reduce_subtree(text_node) when is_binary(text_node),
do: {:ok, text_node}
defp reduce_subtree(children) when is_list(children) do
with {:ok, new_tree} <-
Enum.reduce_while(children, {:ok, []}, fn x, {:ok, acc} ->
with {:ok, new_tree} <- reduce_subtree(x) do
{:cont, {:ok, acc ++ [new_tree]}}
else
{:error, e} ->
{:halt, {:error, e}}
end
end) do
{:ok, Enum.join(new_tree)}
else
{:error, e} ->
{:error, e}
end
end
defp reduce_subtree(tree), do: {:error, "unknown input #{inspect(tree)}"}
def to_html(tree) when is_list(tree), do: reduce_subtree(tree)
def to_html(_), do: {:error, "not a valid tree"}
end
diff --git a/lib/bbcode/parser.ex b/lib/bbcode/parser.ex
index b67b480..6845a82 100644
--- a/lib/bbcode/parser.ex
+++ b/lib/bbcode/parser.ex
@@ -1,170 +1,201 @@
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: ?], not: ?\r, not: ?\n], min: 1)
end_tag =
ignore(string("[/"))
|> concat(tag)
|> ignore(string("]"))
# block tags
quote_tag = string("quote")
ul_tag = string("ul")
ol_tag = string("ol")
li_tag = string("li")
code_tag = string("code")
table_tag = string("table")
tr_tag = string("tr")
th_tag = string("th")
td_tag = string("td")
# span tags
b_tag = string("b")
i_tag = string("i")
u_tag = string("u")
s_tag = string("s")
url_tag = string("url")
img_tag = string("img")
# special tags
star_tag = ignore(string("[*]"))
# newline
newline = utf8_char([?\r, ?\n])
defcombinatorp(
:block_tag,
ignore(string("["))
|> choice([quote_tag, ul_tag, ol_tag, li_tag, code_tag, table_tag, tr_tag, th_tag, td_tag])
|> ignore(string("]"))
|> ignore(optional(utf8_string([?\n, ?\r], min: 1, max: 2)))
)
defcombinatorp(
:block_stanza,
parsec(:block_tag)
|> repeat(lookahead_not(string("[/")) |> choice([parsec(:child_stanza), text]))
|> wrap()
|> concat(end_tag)
|> ignore(optional(utf8_string([?\n, ?\r], min: 1, max: 2)))
|> post_traverse(:emit_tree_node)
)
defcombinatorp(
:span_tag,
ignore(string("["))
|> choice([url_tag, img_tag, b_tag, i_tag, u_tag, s_tag])
|> ignore(string("]"))
|> ignore(optional(utf8_string([?\n, ?\r], min: 1, max: 2)))
)
defcombinatorp(
:span_tag_with_property,
ignore(string("["))
- |> choice([url_tag, img_tag])
+ |> concat(url_tag)
|> ignore(string("="))
|> concat(text)
|> ignore(string("]"))
|> ignore(optional(utf8_string([?\n, ?\r], min: 1, max: 2)))
)
+ defcombinatorp(
+ :img_tag_with_size_property,
+ ignore(string("["))
+ |> concat(img_tag)
+ |> ignore(string("="))
+ |> integer(min: 1)
+ |> ignore(string("x"))
+ |> integer(min: 1)
+ |> ignore(string("]"))
+ |> ignore(optional(utf8_string([?\n, ?\r], min: 1, max: 2)))
+ )
+
defcombinatorp(
:span_stanza,
parsec(:span_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])
)
|> wrap()
|> concat(ignore(optional(utf8_string([?\n, ?\r], min: 1, max: 2))))
|> post_traverse(:emit_tree_node_star)
)
defcombinatorp(
:span_stanza_with_property,
parsec(:span_tag_with_property)
|> repeat(lookahead_not(string("[/")) |> choice([parsec(:child_stanza), text]))
|> wrap()
|> concat(end_tag)
|> post_traverse(:emit_tree_node_property)
)
+ defcombinatorp(
+ :img_stanza_with_size_property,
+ parsec(:img_tag_with_size_property)
+ |> repeat(lookahead_not(string("[/")) |> choice([parsec(:child_stanza), text]))
+ |> wrap()
+ |> concat(end_tag)
+ |> post_traverse(:emit_tree_node_size_property)
+ )
+
defcombinatorp(
:newline_stanza,
newline
|> post_traverse(:emit_tree_node_newline)
)
defcombinatorp(
:child_stanza,
choice([
parsec(:newline_stanza),
parsec(:star_stanza),
parsec(:block_stanza),
+ parsec(:img_stanza_with_size_property),
parsec(:span_stanza_with_property),
parsec(:span_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_newline(_rest, _args, context, _line, _offset),
do: {[{:br}], context}
defp emit_tree_node_star(_rest, [nodes], context, _line, _offset),
do: {[{:li, nodes}], context}
+ defp emit_tree_node_size_property(
+ _rest,
+ [tag, [tag, width, height, inside]],
+ context,
+ _line,
+ _offset
+ ),
+ do: {[{String.to_atom(tag), width, height, inside}], 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

File Metadata

Mime Type
text/x-diff
Expires
Mon, Nov 25, 4:29 AM (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39576
Default Alt Text
(8 KB)

Event Timeline