forked from AkkomaGang/mfm-parser
Make parser work for multiple token input
We can now handle multiple tokens and nesting.
This commit is contained in:
parent
45519a3c2a
commit
5bb5620778
3 changed files with 98 additions and 258 deletions
|
@ -11,16 +11,12 @@ defmodule MfmParser do
|
|||
## Examples
|
||||
|
||||
iex> MfmParser.Parser.parse("$[twitch.speed=5s 🍮]")
|
||||
{:ok,
|
||||
[
|
||||
%{
|
||||
content: [%{content: "🍮", type: "text"}],
|
||||
name: "twitch.speed=5s",
|
||||
type: "mfm"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
[
|
||||
%MfmParser.Node.MFM.Twitch{
|
||||
children: [%MfmParser.Node.Text{props: %{text: "🍮"}}],
|
||||
props: %{speed: "5s"}
|
||||
}
|
||||
]
|
||||
iex> MfmParser.Parser.parse("$[twitch.speed=5s 🍮]") |> MfmParser.Converter.to_html()
|
||||
"<span style=\"display: inline-block; animation: 5s ease 0s infinite normal none running mfm-twitch;\">🍮</span><style>@keyframes mfm-twitch { 0% { transform:translate(7px,-2px) } 5% { transform:translate(-3px,1px) } 10% { transform:translate(-7px,-1px) } 15% { transform:translateY(-1px) } 20% { transform:translate(-8px,6px) } 25% { transform:translate(-4px,-3px) } 30% { transform:translate(-4px,-6px) } 35% { transform:translate(-8px,-8px) } 40% { transform:translate(4px,6px) } 45% { transform:translate(-3px,1px) } 50% { transform:translate(2px,-10px) } 55% { transform:translate(-7px) } 60% { transform:translate(-2px,4px) } 65% { transform:translate(3px,-8px) } 70% { transform:translate(6px,7px) } 75% { transform:translate(-7px,-2px) } 80% { transform:translate(-7px,-8px) } 85% { transform:translate(9px,3px) } 90% { transform:translate(-3px,-2px) } 95% { transform:translate(-10px,2px) } to { transform:translate(-2px,-6px) }}</style>"
|
||||
"""
|
||||
|
|
|
@ -10,34 +10,44 @@ defmodule MfmParser.Parser do
|
|||
|
||||
{token, rest} ->
|
||||
if is_end_token.(token) do
|
||||
parse(rest, tree)
|
||||
{tree, rest}
|
||||
else
|
||||
case token do
|
||||
%Token.MFM.Open{} ->
|
||||
parse(rest, tree ++ [get_node(token)], fn token ->
|
||||
case token do
|
||||
%Token.MFM.Close{} -> true
|
||||
_ -> false
|
||||
end
|
||||
end)
|
||||
{children, rest} = parse(rest, [], &is_mfm_close_token?/1)
|
||||
|
||||
parse(
|
||||
rest,
|
||||
tree ++ [token |> get_node() |> Map.put(:children, children)],
|
||||
is_end_token
|
||||
)
|
||||
|
||||
%Token.Text{} ->
|
||||
parse(
|
||||
rest,
|
||||
tree ++ [%Node.Text{props: %{text: token.content}}]
|
||||
tree ++ [%Node.Text{props: %{text: token.content}}],
|
||||
is_end_token
|
||||
)
|
||||
|
||||
%Token.Newline{} ->
|
||||
parse(
|
||||
rest,
|
||||
tree ++ [%Node.Newline{props: %{text: token.content}}]
|
||||
tree ++ [%Node.Newline{props: %{text: token.content}}],
|
||||
is_end_token
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_node(token = %{content: content}) do
|
||||
defp is_mfm_close_token?(token) do
|
||||
case token do
|
||||
%Token.MFM.Close{} -> true
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
|
||||
defp get_node(token = %{content: content}) do
|
||||
cond do
|
||||
content =~ "$[flip" -> %Node.MFM.Flip{} |> fill_props(token)
|
||||
content =~ "$[font" -> %Node.MFM.Font{} |> fill_props(token)
|
||||
|
@ -62,63 +72,4 @@ defmodule MfmParser.Parser do
|
|||
|
||||
node |> Map.merge(%{props: new_props})
|
||||
end
|
||||
|
||||
# def parse(input, tree \\ [], end_token \\ nil) do
|
||||
# {:ok, token, rest} = MfmParser.Lexer.next(input)
|
||||
#
|
||||
# cond do
|
||||
# # EOF
|
||||
# token == "" ->
|
||||
# {:ok, tree}
|
||||
#
|
||||
# # end_token reached
|
||||
# token == end_token ->
|
||||
# {:ok, tree, rest}
|
||||
#
|
||||
# # Newline
|
||||
# Regex.match?(~r/<br>|\n/, token) ->
|
||||
# new_tree = tree ++ [%{type: "newline"}]
|
||||
# parse(rest, new_tree, end_token)
|
||||
#
|
||||
# # MFM $[ token
|
||||
# Regex.match?(~r/\$\[/, token) ->
|
||||
# {:ok, content, new_rest} = parse(rest, [], "]")
|
||||
#
|
||||
# new_tree =
|
||||
# tree ++
|
||||
# [
|
||||
# %{
|
||||
# type: "mfm",
|
||||
# name: MfmParser.Parser.MFM.get_name_from_token(token),
|
||||
# content: content
|
||||
# }
|
||||
# ]
|
||||
#
|
||||
# parse(new_rest, new_tree, end_token)
|
||||
#
|
||||
# # HTML token
|
||||
# Regex.match?(~r/<.*>/, token) ->
|
||||
# new_end_token = MfmParser.Parser.HTML.get_end_token(token)
|
||||
#
|
||||
# {:ok, content, new_rest} = parse(rest, [], new_end_token)
|
||||
#
|
||||
# new_tree =
|
||||
# tree ++
|
||||
# [
|
||||
# %{
|
||||
# type: "html",
|
||||
# name: MfmParser.Parser.HTML.get_name_from_token(token),
|
||||
# attributes: MfmParser.Parser.HTML.get_attributes_from_token(token),
|
||||
# content: content
|
||||
# }
|
||||
# ]
|
||||
#
|
||||
# parse(new_rest, new_tree, end_token)
|
||||
#
|
||||
# # Regular text
|
||||
# true ->
|
||||
# new_tree = tree ++ [%{type: "text", content: token}]
|
||||
# parse(rest, new_tree, end_token)
|
||||
# end
|
||||
# end
|
||||
end
|
||||
|
|
|
@ -502,185 +502,78 @@ defmodule MfmParser.ParserTest do
|
|||
end
|
||||
end
|
||||
|
||||
# test "it returns a parse tree with content" do
|
||||
# input = "$[twitch twitching text]"
|
||||
#
|
||||
# output = [
|
||||
# %MfmParser.MFM.Twitch{
|
||||
# props: %{
|
||||
# speed: "20s"
|
||||
# },
|
||||
# children: [
|
||||
# %MfmParser.Text{
|
||||
# props: %{
|
||||
# text: "twitching text"
|
||||
# }
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
# ]
|
||||
#
|
||||
# assert Parser.parse(input) == {:ok, output}
|
||||
# end
|
||||
#
|
||||
# test "it returns a parse tree with mutiple entries and contents" do
|
||||
# input = "look at this $[twitch twitching text]"
|
||||
#
|
||||
# output = [
|
||||
# %MfmParser.Text{
|
||||
# props: %{
|
||||
# text: "look at this "
|
||||
# }
|
||||
# },
|
||||
# %MfmParser.MFM.Twitch{
|
||||
# props: %{
|
||||
# speed: "20s"
|
||||
# },
|
||||
# children: [
|
||||
# %MfmParser.Text{
|
||||
# props: %{
|
||||
# text: "twitching text"
|
||||
# }
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
# ]
|
||||
#
|
||||
# assert Parser.parse(input) == {:ok, output}
|
||||
# end
|
||||
describe "multiple element input" do
|
||||
test "it can handle multiple elements as input" do
|
||||
input = "$[twitch ]chocolatine$[blabla ]\n$[jump ]"
|
||||
|
||||
######################
|
||||
#
|
||||
# OLD STUFF BELOW
|
||||
#
|
||||
######################
|
||||
#
|
||||
assert Parser.parse(input) == [
|
||||
%MfmParser.Node.MFM.Twitch{children: [], props: %{speed: "0.5s"}},
|
||||
%MfmParser.Node.Text{props: %{text: "chocolatine"}},
|
||||
%MfmParser.Node.MFM.Undefined{children: [], props: %{}},
|
||||
%MfmParser.Node.Newline{props: %{text: "\n"}},
|
||||
%MfmParser.Node.MFM.Jump{children: [], props: %{speed: "0.75s"}}
|
||||
]
|
||||
end
|
||||
|
||||
# test "it returns a parse tree with text" do
|
||||
# input = "blablabla"
|
||||
#
|
||||
# output = [
|
||||
# %{
|
||||
# type: "text",
|
||||
# content: "blablabla"
|
||||
# }
|
||||
# ]
|
||||
#
|
||||
# assert Parser.parse(input) == {:ok, output}
|
||||
# end
|
||||
#
|
||||
# test "it returns a parse tree with mutiple entries and contents and text" do
|
||||
# input = "<h1>My thought on Chocolatines</h1><div>Also known as <i>Pain au chocolat</i>.</div>"
|
||||
#
|
||||
# output = [
|
||||
# %{
|
||||
# type: "html",
|
||||
# name: "h1",
|
||||
# attributes: "",
|
||||
# content: [%{type: "text", content: "My thought on Chocolatines"}]
|
||||
# },
|
||||
# %{
|
||||
# type: "html",
|
||||
# name: "div",
|
||||
# attributes: "",
|
||||
# content: [
|
||||
# %{type: "text", content: "Also known as "},
|
||||
# %{
|
||||
# type: "html",
|
||||
# name: "i",
|
||||
# attributes: "",
|
||||
# content: [%{type: "text", content: "Pain au chocolat"}]
|
||||
# },
|
||||
# %{type: "text", content: "."}
|
||||
# ]
|
||||
# }
|
||||
# ]
|
||||
#
|
||||
# assert Parser.parse(input) == {:ok, output}
|
||||
# end
|
||||
#
|
||||
# test "it returns a parse tree with mutiple entries and contents and text and newlines" do
|
||||
# input =
|
||||
# "<h1>My thought on Chocolatines</h1><div>Also \nknown as <br><i>Pain au chocolat</i>.</div>"
|
||||
#
|
||||
# output = [
|
||||
# %{
|
||||
# type: "html",
|
||||
# name: "h1",
|
||||
# attributes: "",
|
||||
# content: [%{type: "text", content: "My thought on Chocolatines"}]
|
||||
# },
|
||||
# %{
|
||||
# type: "html",
|
||||
# name: "div",
|
||||
# attributes: "",
|
||||
# content: [
|
||||
# %{type: "text", content: "Also "},
|
||||
# %{type: "newline"},
|
||||
# %{type: "text", content: "known as "},
|
||||
# %{type: "newline"},
|
||||
# %{
|
||||
# type: "html",
|
||||
# name: "i",
|
||||
# attributes: "",
|
||||
# content: [%{type: "text", content: "Pain au chocolat"}]
|
||||
# },
|
||||
# %{type: "text", content: "."}
|
||||
# ]
|
||||
# }
|
||||
# ]
|
||||
#
|
||||
# assert Parser.parse(input) == {:ok, output}
|
||||
# end
|
||||
#
|
||||
# test "it returns a parse tree with mfm format $[<name_and_values> <content>]" do
|
||||
# input = "<div>blabla$[flip $[x2 :blobcatwitch:]]bla$[twitch.speed=20s yadayada]</div>"
|
||||
#
|
||||
# output = [
|
||||
# %{
|
||||
# type: "html",
|
||||
# attributes: "",
|
||||
# name: "div",
|
||||
# content: [
|
||||
# %{type: "text", content: "blabla"},
|
||||
# %{
|
||||
# type: "mfm",
|
||||
# name: "flip",
|
||||
# content: [
|
||||
# %{
|
||||
# type: "mfm",
|
||||
# name: "x2",
|
||||
# content: [
|
||||
# %{type: "text", content: ":blobcatwitch:"}
|
||||
# ]
|
||||
# }
|
||||
# ]
|
||||
# },
|
||||
# %{type: "text", content: "bla"},
|
||||
# %{
|
||||
# type: "mfm",
|
||||
# name: "twitch.speed=20s",
|
||||
# content: [%{type: "text", content: "yadayada"}]
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
# ]
|
||||
#
|
||||
# assert Parser.parse(input) == {:ok, output}
|
||||
# end
|
||||
#
|
||||
# test "it understands html attributes" do
|
||||
# input = "<some_tag some_attribute=./something.jpeg other_atr></some_tag>"
|
||||
#
|
||||
# output = [
|
||||
# %{
|
||||
# type: "html",
|
||||
# name: "some_tag",
|
||||
# attributes: "some_attribute=./something.jpeg other_atr",
|
||||
# content: []
|
||||
# }
|
||||
# ]
|
||||
#
|
||||
# assert Parser.parse(input) == {:ok, output}
|
||||
# end
|
||||
test "it can handle nesting" do
|
||||
input = "$[twitch chocolatine]"
|
||||
|
||||
assert Parser.parse(input) == [
|
||||
%MfmParser.Node.MFM.Twitch{
|
||||
children: [%MfmParser.Node.Text{props: %{text: "chocolatine"}}],
|
||||
props: %{speed: "0.5s"}
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
test "it can handle multiple nesting" do
|
||||
input = "$[twitch $[spin chocolatine]]"
|
||||
|
||||
assert Parser.parse(input) == [
|
||||
%MfmParser.Node.MFM.Twitch{
|
||||
children: [
|
||||
%MfmParser.Node.MFM.Spin{
|
||||
children: [%MfmParser.Node.Text{props: %{text: "chocolatine"}}],
|
||||
props: %{direction: "normal", keyframes_name: "mfm-spin", speed: "1.5s"}
|
||||
}
|
||||
],
|
||||
props: %{speed: "0.5s"}
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
test "it can handle a complex structure of multiple elements and nesting" do
|
||||
input =
|
||||
"It's not $[twitch chocolatine]\nit's $[x4 $[spin pain] $[rainbow au] $[jump chocolat]]"
|
||||
|
||||
assert Parser.parse(input) == [
|
||||
%MfmParser.Node.Text{props: %{text: "It's not "}},
|
||||
%MfmParser.Node.MFM.Twitch{
|
||||
children: [%MfmParser.Node.Text{props: %{text: "chocolatine"}}],
|
||||
props: %{speed: "0.5s"}
|
||||
},
|
||||
%MfmParser.Node.Newline{props: %{text: "\n"}},
|
||||
%MfmParser.Node.Text{props: %{text: "it's "}},
|
||||
%MfmParser.Node.MFM.X{
|
||||
children: [
|
||||
%MfmParser.Node.MFM.Spin{
|
||||
children: [%MfmParser.Node.Text{props: %{text: "pain"}}],
|
||||
props: %{direction: "normal", keyframes_name: "mfm-spin", speed: "1.5s"}
|
||||
},
|
||||
%MfmParser.Node.Text{props: %{text: " "}},
|
||||
%MfmParser.Node.MFM.Rainbow{
|
||||
children: [%MfmParser.Node.Text{props: %{text: "au"}}],
|
||||
props: %{speed: "1s"}
|
||||
},
|
||||
%MfmParser.Node.Text{props: %{text: " "}},
|
||||
%MfmParser.Node.MFM.Jump{
|
||||
children: [%MfmParser.Node.Text{props: %{text: "chocolat"}}],
|
||||
props: %{speed: "0.75s"}
|
||||
}
|
||||
],
|
||||
props: %{size: "600%"}
|
||||
}
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue