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
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>"
"""

View File

@ -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

View File

@ -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