Page MenuHomePhorge

No OneTemporary

Size
9 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/bbcode/parser.ex b/lib/bbcode/parser.ex
index d6adff0..20fb96a 100644
--- a/lib/bbcode/parser.ex
+++ b/lib/bbcode/parser.ex
@@ -1,318 +1,298 @@
# SPDX-FileCopyrightText: 2019-2022 Pleroma Authors
# SPDX-License-Identifier: LGPL-3.0-only
defmodule BBCode.Parser do
import NimbleParsec
@moduledoc """
Parse BBCode into an abstract tree.
"""
text = utf8_string([not: ?[, not: ?], not: ?\r, not: ?\n], min: 1)
# 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")
ruby_tag = string("ruby")
img_tag = string("img")
# WARNING: Order of span tags matters. In particular img has to be before i and url before u
@block_tags [quote_tag, ul_tag, ol_tag, li_tag, code_tag, table_tag, tr_tag, th_tag, td_tag]
@span_tags [b_tag, img_tag, i_tag, url_tag, u_tag, s_tag, ruby_tag]
@tags @block_tags ++ @span_tags
end_tag =
ignore(string("[/"))
|> choice(@tags)
|> ignore(string("]"))
li_end_tag = ignore(string("[/li]"))
ul_end_tag = ignore(string("[/ul]"))
ol_end_tag = ignore(string("[/ol]"))
# special tags
star_tag = ignore(string("[*]"))
# newline
newline = utf8_char([?\r, ?\n])
- defcombinatorp(
- :ol_block_tag,
- ignore(string("["))
- |> concat(ol_tag)
- |> ignore(string("]"))
- |> ignore(optional(utf8_string([?\n, ?\r], min: 1, max: 2)))
- )
-
- defcombinatorp(
- :ul_block_tag,
- ignore(string("["))
- |> concat(ul_tag)
- |> ignore(string("]"))
- |> ignore(optional(utf8_string([?\n, ?\r], min: 1, max: 2)))
- )
-
- defcombinatorp(
- :list_element_tag,
- ignore(string("["))
- |> concat(li_tag)
- |> ignore(string("]"))
- |> ignore(optional(utf8_string([?\n, ?\r], min: 1, max: 2)))
- )
-
defcombinatorp(
:li_block_stanza,
- parsec(:list_element_tag)
+ parsec(:li_element_tag)
|> repeat(lookahead_not(li_end_tag) |> choice([parsec(:child_stanza), text]))
|> concat(li_end_tag)
|> ignore(optional(utf8_string([?\n, ?\r], min: 1, max: 2)))
|> post_traverse(:emit_tree_node_list)
)
- defcombinatorp(
- :ul_block_tag_stanza,
- parsec(:ul_block_tag)
- |> repeat(
- lookahead_not(ul_end_tag)
- |> choice([parsec(:li_block_stanza), parsec(:star_stanza)])
+ for {name, tag} <- [
+ {:ul_block_tag, ul_tag},
+ {:ol_block_tag, ol_tag},
+ {:li_element_tag, li_tag}
+ ] do
+ defcombinatorp(
+ name,
+ ignore(string("["))
+ |> concat(tag)
+ |> ignore(string("]"))
+ |> ignore(optional(utf8_string([?\n, ?\r], min: 1, max: 2)))
)
- |> concat(ul_end_tag)
- |> ignore(optional(utf8_string([?\n, ?\r], min: 1, max: 2)))
- |> post_traverse(:emit_tree_node_block)
- )
+ end
- defcombinatorp(
- :ol_block_tag_stanza,
- parsec(:ol_block_tag)
- |> repeat(
- lookahead_not(ol_end_tag)
- |> choice([parsec(:li_block_stanza), parsec(:star_stanza)])
+ for {name, tag, end_tag} <- [
+ {:ul_block_tag_stanza, :ul_block_tag, ul_end_tag},
+ {:ol_block_tag_stanza, :ol_block_tag, ol_end_tag},
+ ] do
+ defcombinatorp(
+ name,
+ parsec(tag)
+ |> repeat(
+ lookahead_not(end_tag)
+ |> choice([parsec(:li_block_stanza), parsec(:star_stanza)])
+ )
+ |> concat(end_tag)
+ |> ignore(optional(utf8_string([?\n, ?\r], min: 1, max: 2)))
+ |> post_traverse(:emit_tree_node_block)
)
- |> concat(ol_end_tag)
- |> ignore(optional(utf8_string([?\n, ?\r], min: 1, max: 2)))
- |> post_traverse(:emit_tree_node_block)
- )
+ end
defcombinatorp(
:block_tag,
ignore(string("["))
|> choice([quote_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(end_tag) |> 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, ruby_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(end_tag) |> 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(choice([ul_end_tag, ol_end_tag, star_tag, 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(end_tag) |> 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(end_tag) |> 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(
:bracket_text_stanza,
string("[")
|> optional(string("/"))
|> concat(text)
|> string("]")
|> reduce({List, :to_string, []})
|> wrap()
|> post_traverse(:emit_tree_node)
)
defcombinatorp(
:stray_bracket_stanza,
utf8_string([?[, ?]], min: 1)
|> optional(choice([utf8_char([?\s]), text]))
|> wrap()
|> reduce({List, :to_string, []})
)
defcombinatorp(
:child_stanza,
choice([
parsec(:newline_stanza),
parsec(:ul_block_tag_stanza),
parsec(:ol_block_tag_stanza),
parsec(:block_stanza),
parsec(:img_stanza_with_size_property),
parsec(:span_stanza_with_property),
parsec(:span_stanza),
parsec(:bracket_text_stanza),
parsec(:stray_bracket_stanza)
])
)
defcombinatorp(
:root_stanza,
choice([parsec(:child_stanza), parsec(:text_stanza)])
)
defparsecp(
:parse_tree,
repeat(parsec(:root_stanza)) |> eos()
)
defp emit_tree_node_newline(rest, _args, context, _line, _offset),
do: {rest, [{:br}], context}
defp emit_tree_node_star(rest, [nodes], context, _line, _offset) when length(nodes) == 1,
do: {rest, [{:li, List.first(nodes)}], context}
defp emit_tree_node_star(rest, [nodes], context, _line, _offset),
do: {rest, [{:li, nodes}], context}
defp emit_tree_node_list(rest, [inside | _], context, _line, _offset) do
{rest, [{:li, inside}], context}
end
defp emit_tree_node_size_property(
rest,
[tag, [tag, width, height, inside]],
context,
_line,
_offset
),
do: {rest, [{String.to_atom(tag), width, height, inside}], context}
defp emit_tree_node_property(rest, [tag, [tag, property, inside]], context, _line, _offset),
do: {rest, [{String.to_atom(tag), property, inside}], context}
defp emit_tree_node_property(rest, [tag, [tag, property | nodes]], context, _line, _offset),
do: {rest, [{String.to_atom(tag), property, nodes}], context}
defp emit_tree_node_block(rest, nodes, context, _line, _offset) do
# nodes is a Keyword list of [li] blocks in reverse order with the list type at the end.
# To fix this, the list is reversed and the now first element is popped into the tag.
nodes = Enum.reverse(nodes)
{tag, nodes} = List.pop_at(nodes, 0)
# Expected output of lists with a single item is a tuple instead of a list.
# The incoming list is a Keyword, so the first element is actually a tuple, which we need.
nodes = if length(nodes) == 1, do: List.first(nodes), else: nodes
{rest, [{String.to_atom(tag), nodes}], context}
end
defp emit_tree_node(rest, [tag, [tag, inside]], context, _line, _offset),
do: {rest, [{String.to_atom(tag), inside}], context}
defp emit_tree_node(rest, [tag, [tag | nodes]], context, _line, _offset),
do: {rest, [{String.to_atom(tag), nodes}], context}
defp emit_tree_node(rest, [[text]], context, _line, _offset),
do: {rest, [text], context}
- defp emit_tree_node(rest, [[tag | nodes]], context, _line, _offset),
- do: {rest, [{String.to_atom(tag), nodes}], context}
-
defp emit_tree_node(rest, [["[", text, "]"]], context, _line, _offset),
do: {rest, ["[" <> 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
Sat, Aug 8, 8:55 PM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1723964
Default Alt Text
(9 KB)

Event Timeline