From 5bb562077803b0c1c75b09046981dd7690d2b331 Mon Sep 17 00:00:00 2001 From: Ilja Date: Sun, 24 Jul 2022 09:02:17 +0200 Subject: [PATCH] Make parser work for multiple token input We can now handle multiple tokens and nesting. --- lib/mfm_parser.ex | 16 ++- lib/parser.ex | 89 ++++----------- test/parser_test.exs | 251 +++++++++++++------------------------------ 3 files changed, 98 insertions(+), 258 deletions(-) diff --git a/lib/mfm_parser.ex b/lib/mfm_parser.ex index 3c0e488..59ade44 100644 --- a/lib/mfm_parser.ex +++ b/lib/mfm_parser.ex @@ -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() "🍮" """ diff --git a/lib/parser.ex b/lib/parser.ex index fb324b0..932a00f 100644 --- a/lib/parser.ex +++ b/lib/parser.ex @@ -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/
|\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 diff --git a/test/parser_test.exs b/test/parser_test.exs index e21f615..9ef692a 100644 --- a/test/parser_test.exs +++ b/test/parser_test.exs @@ -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 = "

My thought on Chocolatines

Also known as Pain au chocolat.
" - # - # 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 = - # "

My thought on Chocolatines

Also \nknown as
Pain au chocolat.
" - # - # 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 $[ ]" do - # input = "
blabla$[flip $[x2 :blobcatwitch:]]bla$[twitch.speed=20s yadayada]
" - # - # 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 = "" - # - # 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