Make parser work for multiple token input

We can now handle multiple tokens and nesting.
This commit is contained in:
Ilja 2022-07-24 09:02:17 +02:00
parent 45519a3c2a
commit 5bb5620778
3 changed files with 98 additions and 258 deletions

View file

@ -11,16 +11,12 @@ defmodule MfmParser do
## Examples ## Examples
iex> MfmParser.Parser.parse("$[twitch.speed=5s 🍮]") iex> MfmParser.Parser.parse("$[twitch.speed=5s 🍮]")
{:ok, [
[ %MfmParser.Node.MFM.Twitch{
%{ children: [%MfmParser.Node.Text{props: %{text: "🍮"}}],
content: [%{content: "🍮", type: "text"}], props: %{speed: "5s"}
name: "twitch.speed=5s", }
type: "mfm" ]
}
]
}
iex> MfmParser.Parser.parse("$[twitch.speed=5s 🍮]") |> MfmParser.Converter.to_html() 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>" "<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>"
""" """

View file

@ -10,34 +10,44 @@ defmodule MfmParser.Parser do
{token, rest} -> {token, rest} ->
if is_end_token.(token) do if is_end_token.(token) do
parse(rest, tree) {tree, rest}
else else
case token do case token do
%Token.MFM.Open{} -> %Token.MFM.Open{} ->
parse(rest, tree ++ [get_node(token)], fn token -> {children, rest} = parse(rest, [], &is_mfm_close_token?/1)
case token do
%Token.MFM.Close{} -> true parse(
_ -> false rest,
end tree ++ [token |> get_node() |> Map.put(:children, children)],
end) is_end_token
)
%Token.Text{} -> %Token.Text{} ->
parse( parse(
rest, rest,
tree ++ [%Node.Text{props: %{text: token.content}}] tree ++ [%Node.Text{props: %{text: token.content}}],
is_end_token
) )
%Token.Newline{} -> %Token.Newline{} ->
parse( parse(
rest, rest,
tree ++ [%Node.Newline{props: %{text: token.content}}] tree ++ [%Node.Newline{props: %{text: token.content}}],
is_end_token
) )
end end
end end
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 cond do
content =~ "$[flip" -> %Node.MFM.Flip{} |> fill_props(token) content =~ "$[flip" -> %Node.MFM.Flip{} |> fill_props(token)
content =~ "$[font" -> %Node.MFM.Font{} |> 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}) node |> Map.merge(%{props: new_props})
end 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 end

View file

@ -502,185 +502,78 @@ defmodule MfmParser.ParserTest do
end end
end end
# test "it returns a parse tree with content" do describe "multiple element input" do
# input = "$[twitch twitching text]" test "it can handle multiple elements as input" do
# input = "$[twitch ]chocolatine$[blabla ]\n$[jump ]"
# 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
###################### assert Parser.parse(input) == [
# %MfmParser.Node.MFM.Twitch{children: [], props: %{speed: "0.5s"}},
# OLD STUFF BELOW %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 test "it can handle nesting" do
# input = "blablabla" input = "$[twitch chocolatine]"
#
# output = [ assert Parser.parse(input) == [
# %{ %MfmParser.Node.MFM.Twitch{
# type: "text", children: [%MfmParser.Node.Text{props: %{text: "chocolatine"}}],
# content: "blablabla" props: %{speed: "0.5s"}
# } }
# ] ]
# end
# assert Parser.parse(input) == {:ok, output}
# end test "it can handle multiple nesting" do
# input = "$[twitch $[spin chocolatine]]"
# 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>" assert Parser.parse(input) == [
# %MfmParser.Node.MFM.Twitch{
# output = [ children: [
# %{ %MfmParser.Node.MFM.Spin{
# type: "html", children: [%MfmParser.Node.Text{props: %{text: "chocolatine"}}],
# name: "h1", props: %{direction: "normal", keyframes_name: "mfm-spin", speed: "1.5s"}
# attributes: "", }
# content: [%{type: "text", content: "My thought on Chocolatines"}] ],
# }, props: %{speed: "0.5s"}
# %{ }
# type: "html", ]
# name: "div", end
# attributes: "",
# content: [ test "it can handle a complex structure of multiple elements and nesting" do
# %{type: "text", content: "Also known as "}, input =
# %{ "It's not $[twitch chocolatine]\nit's $[x4 $[spin pain] $[rainbow au] $[jump chocolat]]"
# type: "html",
# name: "i", assert Parser.parse(input) == [
# attributes: "", %MfmParser.Node.Text{props: %{text: "It's not "}},
# content: [%{type: "text", content: "Pain au chocolat"}] %MfmParser.Node.MFM.Twitch{
# }, children: [%MfmParser.Node.Text{props: %{text: "chocolatine"}}],
# %{type: "text", content: "."} props: %{speed: "0.5s"}
# ] },
# } %MfmParser.Node.Newline{props: %{text: "\n"}},
# ] %MfmParser.Node.Text{props: %{text: "it's "}},
# %MfmParser.Node.MFM.X{
# assert Parser.parse(input) == {:ok, output} children: [
# end %MfmParser.Node.MFM.Spin{
# children: [%MfmParser.Node.Text{props: %{text: "pain"}}],
# test "it returns a parse tree with mutiple entries and contents and text and newlines" do props: %{direction: "normal", keyframes_name: "mfm-spin", speed: "1.5s"}
# input = },
# "<h1>My thought on Chocolatines</h1><div>Also \nknown as <br><i>Pain au chocolat</i>.</div>" %MfmParser.Node.Text{props: %{text: " "}},
# %MfmParser.Node.MFM.Rainbow{
# output = [ children: [%MfmParser.Node.Text{props: %{text: "au"}}],
# %{ props: %{speed: "1s"}
# type: "html", },
# name: "h1", %MfmParser.Node.Text{props: %{text: " "}},
# attributes: "", %MfmParser.Node.MFM.Jump{
# content: [%{type: "text", content: "My thought on Chocolatines"}] children: [%MfmParser.Node.Text{props: %{text: "chocolat"}}],
# }, props: %{speed: "0.75s"}
# %{ }
# type: "html", ],
# name: "div", props: %{size: "600%"}
# attributes: "", }
# content: [ ]
# %{type: "text", content: "Also "}, end
# %{type: "newline"}, end
# %{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
end end