diff --git a/lib/lexer.ex b/lib/lexer.ex index 90b9337..d648f9f 100644 --- a/lib/lexer.ex +++ b/lib/lexer.ex @@ -2,14 +2,13 @@ defmodule MfmParser.Lexer do alias MfmParser.Reader alias MfmParser.Token - alias MfmParser.Token.MFMOpen - alias MfmParser.Token.MFMClose + alias MfmParser.Token.MFM alias MfmParser.Token.Newline alias MfmParser.Token.Text def peek(input) do case next(input) do - {:ok, token, _} -> {:ok, token} + {token, _} -> token :eof -> :eof end end @@ -22,9 +21,9 @@ defmodule MfmParser.Lexer do :eof end - defp recursive_extract_next_token({:ok, char, rest}, token) do + defp recursive_extract_next_token({char, rest}, token) do if is_last_char_of_token?(char, rest, token) do - {:ok, token |> Token.append(char), rest} + {token |> Token.append(char), rest} else recursive_extract_next_token(Reader.next(rest), token |> Token.append(char)) end @@ -33,18 +32,18 @@ defmodule MfmParser.Lexer do defp get_empty_token(input) do case Reader.peek(input) do :eof -> :eof - {:ok, "$"} -> %MFMOpen{} - {:ok, "]"} -> %MFMClose{} - {:ok, "\n"} -> %Newline{} + "$" -> %MFM.Open{} + "]" -> %MFM.Close{} + "\n" -> %Newline{} _ -> %Text{} end end - defp is_last_char_of_token?(char, _, %MFMOpen{}) do + defp is_last_char_of_token?(char, _, %MFM.Open{}) do char == " " end - defp is_last_char_of_token?(_, _, %MFMClose{}) do + defp is_last_char_of_token?(_, _, %MFM.Close{}) do true end @@ -55,8 +54,8 @@ defmodule MfmParser.Lexer do defp is_last_char_of_token?(_, rest, %Text{}) do case Reader.next(rest) do :eof -> true - {:ok, "]", _} -> true - {:ok, "$", new_rest} -> Reader.peek(new_rest) == {:ok, "["} + {"]", _} -> true + {"$", new_rest} -> Reader.peek(new_rest) == "[" _ -> false end end diff --git a/lib/node/mfm/blur.ex b/lib/node/mfm/blur.ex new file mode 100644 index 0000000..ed41ec7 --- /dev/null +++ b/lib/node/mfm/blur.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.MFM.Blur do + defstruct props: %{}, children: [] +end diff --git a/lib/node/mfm/bounce.ex b/lib/node/mfm/bounce.ex new file mode 100644 index 0000000..44ddc0f --- /dev/null +++ b/lib/node/mfm/bounce.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.MFM.Bounce do + defstruct props: %{ speed: "0.75s" }, children: [] +end diff --git a/lib/node/mfm/flip.ex b/lib/node/mfm/flip.ex new file mode 100644 index 0000000..59fbc85 --- /dev/null +++ b/lib/node/mfm/flip.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.MFM.Flip do + defstruct props: %{ v: false, h: false }, children: [] +end diff --git a/lib/node/mfm/font.ex b/lib/node/mfm/font.ex new file mode 100644 index 0000000..e18f80a --- /dev/null +++ b/lib/node/mfm/font.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.MFM.Font do + defstruct props: %{ font: nil }, children: [] +end diff --git a/lib/node/mfm/jelly.ex b/lib/node/mfm/jelly.ex new file mode 100644 index 0000000..e3e5561 --- /dev/null +++ b/lib/node/mfm/jelly.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.MFM.Jelly do + defstruct props: %{ speed: "1s" }, children: [] +end diff --git a/lib/node/mfm/jump.ex b/lib/node/mfm/jump.ex new file mode 100644 index 0000000..e65bb5e --- /dev/null +++ b/lib/node/mfm/jump.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.MFM.Jump do + defstruct props: %{ speed: "0.75s" }, children: [] +end diff --git a/lib/node/mfm/rainbow.ex b/lib/node/mfm/rainbow.ex new file mode 100644 index 0000000..51042a6 --- /dev/null +++ b/lib/node/mfm/rainbow.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.MFM.Rainbow do + defstruct props: %{ speed: "1s" }, children: [] +end diff --git a/lib/node/mfm/rotate.ex b/lib/node/mfm/rotate.ex new file mode 100644 index 0000000..8b745ae --- /dev/null +++ b/lib/node/mfm/rotate.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.MFM.Rotate do + defstruct props: %{}, children: [] +end diff --git a/lib/node/mfm/shake.ex b/lib/node/mfm/shake.ex new file mode 100644 index 0000000..44aa5c7 --- /dev/null +++ b/lib/node/mfm/shake.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.MFM.Shake do + defstruct props: %{ speed: "0.5s" }, children: [] +end diff --git a/lib/node/mfm/sparkle.ex b/lib/node/mfm/sparkle.ex new file mode 100644 index 0000000..9eff541 --- /dev/null +++ b/lib/node/mfm/sparkle.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.MFM.Sparkle do + defstruct props: %{}, children: [] +end diff --git a/lib/node/mfm/spin.ex b/lib/node/mfm/spin.ex new file mode 100644 index 0000000..efb40fd --- /dev/null +++ b/lib/node/mfm/spin.ex @@ -0,0 +1,13 @@ +defmodule MfmParser.Node.MFM.Spin do + # keyframes_name: + # x -> mfm-spinX + # y -> mfm-spinY + # _ -> mfm-spin + # + # direction: + # left -> reverse + # alternate -> alternate + # _ -> normal + # + defstruct props: %{keyframes_name: "mfm-spin", direction: "normal", speed: "1.5s"}, children: [] +end diff --git a/lib/node/mfm/tada.ex b/lib/node/mfm/tada.ex new file mode 100644 index 0000000..857e5a7 --- /dev/null +++ b/lib/node/mfm/tada.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.MFM.Tada do + defstruct props: %{ speed: "1s" }, children: [] +end diff --git a/lib/node/mfm/twitch.ex b/lib/node/mfm/twitch.ex new file mode 100644 index 0000000..87b9a4c --- /dev/null +++ b/lib/node/mfm/twitch.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.MFM.Twitch do + defstruct props: %{ speed: "0.5s" }, children: [] +end diff --git a/lib/node/mfm/undefined.ex b/lib/node/mfm/undefined.ex new file mode 100644 index 0000000..36ba3bf --- /dev/null +++ b/lib/node/mfm/undefined.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.MFM.Undefined do + defstruct props: %{}, children: [] +end diff --git a/lib/node/mfm/x.ex b/lib/node/mfm/x.ex new file mode 100644 index 0000000..80040fd --- /dev/null +++ b/lib/node/mfm/x.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.MFM.X do + defstruct props: %{ size: nil }, children: [] +end diff --git a/lib/node/newline.ex b/lib/node/newline.ex new file mode 100644 index 0000000..8fe4b54 --- /dev/null +++ b/lib/node/newline.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.Newline do + defstruct props: %{text: "\n"} +end diff --git a/lib/node/text.ex b/lib/node/text.ex new file mode 100644 index 0000000..d644d28 --- /dev/null +++ b/lib/node/text.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Node.Text do + defstruct props: %{text: ""} +end diff --git a/lib/parser.ex b/lib/parser.ex new file mode 100644 index 0000000..fb324b0 --- /dev/null +++ b/lib/parser.ex @@ -0,0 +1,124 @@ +defmodule MfmParser.Parser do + alias MfmParser.Token + alias MfmParser.Node + alias MfmParser.Lexer + + def parse(input, tree \\ [], is_end_token \\ fn _ -> false end) do + case Lexer.next(input) do + :eof -> + tree + + {token, rest} -> + if is_end_token.(token) do + parse(rest, tree) + 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) + + %Token.Text{} -> + parse( + rest, + tree ++ [%Node.Text{props: %{text: token.content}}] + ) + + %Token.Newline{} -> + parse( + rest, + tree ++ [%Node.Newline{props: %{text: token.content}}] + ) + end + end + end + end + + def get_node(token = %{content: content}) do + cond do + content =~ "$[flip" -> %Node.MFM.Flip{} |> fill_props(token) + content =~ "$[font" -> %Node.MFM.Font{} |> fill_props(token) + content =~ "$[x" -> %Node.MFM.X{} |> fill_props(token) + content =~ "$[blur" -> %Node.MFM.Blur{} |> fill_props(token) + content =~ "$[jelly" -> %Node.MFM.Jelly{} |> fill_props(token) + content =~ "$[tada" -> %Node.MFM.Tada{} |> fill_props(token) + content =~ "$[jump" -> %Node.MFM.Jump{} |> fill_props(token) + content =~ "$[bounce" -> %Node.MFM.Bounce{} |> fill_props(token) + content =~ "$[spin" -> %Node.MFM.Spin{} |> fill_props(token) + content =~ "$[shake" -> %Node.MFM.Shake{} |> fill_props(token) + content =~ "$[twitch" -> %Node.MFM.Twitch{} |> fill_props(token) + content =~ "$[rainbow" -> %Node.MFM.Rainbow{} |> fill_props(token) + content =~ "$[sparkle" -> %Node.MFM.Sparkle{} |> fill_props(token) + content =~ "$[rotate" -> %Node.MFM.Rotate{} |> fill_props(token) + true -> %Node.MFM.Undefined{} |> fill_props(token) + end + end + + defp fill_props(node = %{props: props}, %{content: content}) do + new_props = props |> Map.merge(Token.MFM.to_props(content)) + + 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/lib/reader.ex b/lib/reader.ex index 2dd2e32..89569b2 100644 --- a/lib/reader.ex +++ b/lib/reader.ex @@ -4,7 +4,7 @@ defmodule MfmParser.Reader do case next_char do nil -> :eof - _ -> {:ok, next_char} + _ -> next_char end end @@ -13,7 +13,7 @@ defmodule MfmParser.Reader do case next_char do "" -> :eof - _ -> {:ok, next_char, rest} + _ -> {next_char, rest} end end end diff --git a/lib/token/mfm.ex b/lib/token/mfm.ex new file mode 100644 index 0000000..02cedfe --- /dev/null +++ b/lib/token/mfm.ex @@ -0,0 +1,57 @@ +defmodule MfmParser.Token.MFM do + def to_props(opts_string) when is_binary(opts_string) do + if opts_string =~ "." do + Regex.replace(~r/^.*\./u, opts_string, "") + |> String.trim() + |> String.split(",") + |> Enum.reduce(%{}, fn opt, acc -> + acc + |> Map.merge( + cond do + opt =~ "speed" -> + %{speed: String.replace(opt, "speed=", "")} + + opt =~ "v" -> + %{v: true} + + opt =~ "h" -> + %{h: true} + + opt =~ "x" -> + %{keyframes_name: "mfm-spinX"} + + opt =~ "y" -> + %{keyframes_name: "mfm-spinY"} + + opt =~ "left" -> + %{direction: "reverse"} + + opt =~ "alternate" -> + %{direction: "alternate"} + + true -> + if Regex.match?(~r/^\$\[font/, opts_string) do + %{font: opt} + else + %{} + end + end + ) + end) + else + if opts_string =~ "$[x" do + %{ + size: + case opts_string |> String.replace("$[x", "") |> String.trim() do + "2" -> "200%" + "3" -> "400%" + "4" -> "600%" + _ -> "100%" + end + } + else + %{} + end + end + end +end diff --git a/lib/token/mfm/close.ex b/lib/token/mfm/close.ex new file mode 100644 index 0000000..91e5eca --- /dev/null +++ b/lib/token/mfm/close.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Token.MFM.Close do + defstruct content: "" +end diff --git a/lib/token/mfm/open.ex b/lib/token/mfm/open.ex new file mode 100644 index 0000000..8c3720b --- /dev/null +++ b/lib/token/mfm/open.ex @@ -0,0 +1,3 @@ +defmodule MfmParser.Token.MFM.Open do + defstruct content: "" +end diff --git a/lib/token/mfm_close.ex b/lib/token/mfm_close.ex deleted file mode 100644 index 2245bb3..0000000 --- a/lib/token/mfm_close.ex +++ /dev/null @@ -1,3 +0,0 @@ -defmodule MfmParser.Token.MFMClose do - defstruct content: "" -end diff --git a/lib/token/mfm_open.ex b/lib/token/mfm_open.ex deleted file mode 100644 index 647eede..0000000 --- a/lib/token/mfm_open.ex +++ /dev/null @@ -1,3 +0,0 @@ -defmodule MfmParser.Token.MFMOpen do - defstruct content: "" -end diff --git a/test/lexer_test.exs b/test/lexer_test.exs index 2e99cf9..f8eb0db 100644 --- a/test/lexer_test.exs +++ b/test/lexer_test.exs @@ -3,8 +3,7 @@ defmodule MfmParser.LexerTest do alias MfmParser.Lexer - alias MfmParser.Token.MFMOpen - alias MfmParser.Token.MFMClose + alias MfmParser.Token.MFM alias MfmParser.Token.Newline alias MfmParser.Token.Text @@ -20,11 +19,11 @@ defmodule MfmParser.LexerTest do describe "mfm $[ token" do test "it ends with a space" do - assert Lexer.peek("$[ola puerca]") == {:ok, %MFMOpen{content: "$[ola "}} - assert Lexer.next("$[ola puerca]") == {:ok, %MFMOpen{content: "$[ola "}, "puerca]"} + assert Lexer.peek("$[ola puerca]") == %MFM.Open{content: "$[ola "} + assert Lexer.next("$[ola puerca]") == {%MFM.Open{content: "$[ola "}, "puerca]"} assert Lexer.next("$[ola.x,speed=5s puerca]") == - {:ok, %MFMOpen{content: "$[ola.x,speed=5s "}, "puerca]"} + {%MFM.Open{content: "$[ola.x,speed=5s "}, "puerca]"} end test "it doesn't crash if the token can't be completed" do @@ -35,54 +34,55 @@ defmodule MfmParser.LexerTest do describe "] token" do test "it handles ] as a token" do - assert Lexer.peek("]ve anime") == {:ok, %MFMClose{content: "]"}} - assert Lexer.next("]ve anime") == {:ok, %MFMClose{content: "]"}, "ve anime"} + assert Lexer.peek("]ve anime") == %MFM.Close{content: "]"} + assert Lexer.next("]ve anime") == {%MFM.Close{content: "]"}, "ve anime"} end test "it works at the eof" do - assert Lexer.peek("]") == {:ok, %MFMClose{content: "]"}} - assert Lexer.next("]") == {:ok, %MFMClose{content: "]"}, ""} + assert Lexer.peek("]") == %MFM.Close{content: "]"} + assert Lexer.next("]") == {%MFM.Close{content: "]"}, ""} end end describe "text token" do test "it ends when a mfm token opens while a $ alone doesn't end the text token" do assert Lexer.peek("Tu abuela ve anime y no se lava el $[spin culo]") == - {:ok, %Text{content: "Tu abuela ve anime y no se lava el "}} + %Text{content: "Tu abuela ve anime y no se lava el "} assert Lexer.next("Tu abuela ve anime y no se lava el $[spin culo]") == - {:ok, %Text{content: "Tu abuela ve anime y no se lava el "}, "$[spin culo]"} + {%Text{content: "Tu abuela ve anime y no se lava el "}, "$[spin culo]"} - assert Lexer.peek("A $2 chocolatine") == {:ok, %Text{content: "A $2 chocolatine"}} - assert Lexer.next("A $2 chocolatine") == {:ok, %Text{content: "A $2 chocolatine"}, ""} + assert Lexer.peek("A $2 chocolatine") == %Text{content: "A $2 chocolatine"} + assert Lexer.next("A $2 chocolatine") == {%Text{content: "A $2 chocolatine"}, ""} - assert Lexer.peek("Eyes like $$") == {:ok, %Text{content: "Eyes like $$"}} - assert Lexer.next("Eyes like $$") == {:ok, %Text{content: "Eyes like $$"}, ""} + assert Lexer.peek("Eyes like $$") == %Text{content: "Eyes like $$"} + assert Lexer.next("Eyes like $$") == {%Text{content: "Eyes like $$"}, ""} end test "it ends when a mfm token closes" do - assert Lexer.peek("el culo]") == {:ok, %Text{content: "el culo"}} - assert Lexer.next("el culo]") == {:ok, %Text{content: "el culo"}, "]"} + assert Lexer.peek("el culo]") == %Text{content: "el culo"} + assert Lexer.next("el culo]") == {%Text{content: "el culo"}, "]"} end test "it ends when the eof is reached" do - assert Lexer.peek("Tu abuela ve anime y no se lava el culo") == - {:ok, %Text{content: "Tu abuela ve anime y no se lava el culo"}} + assert Lexer.peek("Tu abuela ve anime y no se lava el culo") == %Text{ + content: "Tu abuela ve anime y no se lava el culo" + } assert Lexer.next("Tu abuela ve anime y no se lava el culo") == - {:ok, %Text{content: "Tu abuela ve anime y no se lava el culo"}, ""} + {%Text{content: "Tu abuela ve anime y no se lava el culo"}, ""} end end describe "newline token" do test "it handles \n as a token" do - assert Lexer.peek("\nchocolat") == {:ok, %Newline{content: "\n"}} - assert Lexer.next("\nchocolat") == {:ok, %Newline{content: "\n"}, "chocolat"} + assert Lexer.peek("\nchocolat") == %Newline{content: "\n"} + assert Lexer.next("\nchocolat") == {%Newline{content: "\n"}, "chocolat"} end test "it works at the eof" do - assert Lexer.peek("\n") == {:ok, %Newline{content: "\n"}} - assert Lexer.next("\n") == {:ok, %Newline{content: "\n"}, ""} + assert Lexer.peek("\n") == %Newline{content: "\n"} + assert Lexer.next("\n") == {%Newline{content: "\n"}, ""} end end end diff --git a/test/parser_test.exs b/test/parser_test.exs new file mode 100644 index 0000000..e21f615 --- /dev/null +++ b/test/parser_test.exs @@ -0,0 +1,686 @@ +defmodule MfmParser.ParserTest do + use ExUnit.Case + alias MfmParser.Parser + + describe "single element input" do + test "it can handle an empty string as input" do + input = "" + + assert Parser.parse(input) == [] + end + + test "it can handle text as input" do + input = "pain au chocolat" + + output = [%MfmParser.Node.Text{props: %{text: "pain au chocolat"}}] + + assert Parser.parse(input) == output + end + + test "it can handle a newline as input" do + input = "\n" + + output = [%MfmParser.Node.Newline{props: %{text: "\n"}}] + + assert Parser.parse(input) == output + end + + test "it can handle a flip element" do + input_default = "$[flip ]" + input_v = "$[flip.v ]" + input_hv = "$[flip.h,v ]" + + output_default = [ + %MfmParser.Node.MFM.Flip{ + props: %{ + v: false, + h: false + }, + children: [] + } + ] + + output_v = [ + %MfmParser.Node.MFM.Flip{ + props: %{ + v: true, + h: false + }, + children: [] + } + ] + + output_hv = [ + %MfmParser.Node.MFM.Flip{ + props: %{ + v: true, + h: true + }, + children: [] + } + ] + + assert Parser.parse(input_default) == output_default + assert Parser.parse(input_v) == output_v + assert Parser.parse(input_hv) == output_hv + end + + test "it can handle a font element" do + input = "$[font.serif ]" + + output = [ + %MfmParser.Node.MFM.Font{ + props: %{ + font: "serif" + }, + children: [] + } + ] + + assert Parser.parse(input) == output + end + + test "it can handle an x element" do + input2 = "$[x2 ]" + input3 = "$[x3 ]" + input4 = "$[x4 ]" + + output2 = [ + %MfmParser.Node.MFM.X{ + props: %{ + size: "200%" + }, + children: [] + } + ] + + output3 = [ + %MfmParser.Node.MFM.X{ + props: %{ + size: "400%" + }, + children: [] + } + ] + + output4 = [ + %MfmParser.Node.MFM.X{ + props: %{ + size: "600%" + }, + children: [] + } + ] + + assert Parser.parse(input2) == output2 + assert Parser.parse(input3) == output3 + assert Parser.parse(input4) == output4 + end + + test "it can handle a blur element" do + input = "$[blur ]" + + output = [ + %MfmParser.Node.MFM.Blur{ + props: %{}, + children: [] + } + ] + + assert Parser.parse(input) == output + end + + test "it can handle a jelly element" do + input_default = "$[jelly ]" + + output_default = [ + %MfmParser.Node.MFM.Jelly{ + props: %{ + speed: "1s" + }, + children: [] + } + ] + + input_speed = "$[jelly.speed=20s ]" + + output_speed = [ + %MfmParser.Node.MFM.Jelly{ + props: %{ + speed: "20s" + }, + children: [] + } + ] + + assert Parser.parse(input_default) == output_default + assert Parser.parse(input_speed) == output_speed + end + + test "it can handle a tada element" do + input_default = "$[tada ]" + + output_default = [ + %MfmParser.Node.MFM.Tada{ + props: %{ + speed: "1s" + }, + children: [] + } + ] + + input_speed = "$[tada.speed=20s ]" + + output_speed = [ + %MfmParser.Node.MFM.Tada{ + props: %{ + speed: "20s" + }, + children: [] + } + ] + + assert Parser.parse(input_default) == output_default + assert Parser.parse(input_speed) == output_speed + end + + test "it can handle a jump element" do + input_default = "$[jump ]" + + output_default = [ + %MfmParser.Node.MFM.Jump{ + props: %{ + speed: "0.75s" + }, + children: [] + } + ] + + input_speed = "$[jump.speed=20s ]" + + output_speed = [ + %MfmParser.Node.MFM.Jump{ + props: %{ + speed: "20s" + }, + children: [] + } + ] + + assert Parser.parse(input_default) == output_default + assert Parser.parse(input_speed) == output_speed + end + + test "it can handle a bounce element" do + input_default = "$[bounce ]" + + output_default = [ + %MfmParser.Node.MFM.Bounce{ + props: %{ + speed: "0.75s" + }, + children: [] + } + ] + + input_speed = "$[bounce.speed=20s ]" + + output_speed = [ + %MfmParser.Node.MFM.Bounce{ + props: %{ + speed: "20s" + }, + children: [] + } + ] + + assert Parser.parse(input_default) == output_default + assert Parser.parse(input_speed) == output_speed + end + + test "it can handle a spin element" do + input_default = "$[spin ]" + + output_default = [ + %MfmParser.Node.MFM.Spin{ + props: %{ + keyframes_name: "mfm-spin", + direction: "normal", + speed: "1.5s" + }, + children: [] + } + ] + + input_left = "$[spin.left ]" + + output_left = [ + %MfmParser.Node.MFM.Spin{ + props: %{ + keyframes_name: "mfm-spin", + direction: "reverse", + speed: "1.5s" + }, + children: [] + } + ] + + input_alternate = "$[spin.alternate ]" + + output_alternate = [ + %MfmParser.Node.MFM.Spin{ + props: %{ + keyframes_name: "mfm-spin", + direction: "alternate", + speed: "1.5s" + }, + children: [] + } + ] + + input_x = "$[spin.x ]" + + output_x = [ + %MfmParser.Node.MFM.Spin{ + props: %{ + keyframes_name: "mfm-spinX", + direction: "normal", + speed: "1.5s" + }, + children: [] + } + ] + + input_x_left = "$[spin.x,left ]" + + output_x_left = [ + %MfmParser.Node.MFM.Spin{ + props: %{ + keyframes_name: "mfm-spinX", + direction: "reverse", + speed: "1.5s" + }, + children: [] + } + ] + + input_x_alternate = "$[spin.x,alternate ]" + + output_x_alternate = [ + %MfmParser.Node.MFM.Spin{ + props: %{ + keyframes_name: "mfm-spinX", + direction: "alternate", + speed: "1.5s" + }, + children: [] + } + ] + + input_y = "$[spin.y ]" + + output_y = [ + %MfmParser.Node.MFM.Spin{ + props: %{ + keyframes_name: "mfm-spinY", + direction: "normal", + speed: "1.5s" + }, + children: [] + } + ] + + input_y_left = "$[spin.y,left ]" + + output_y_left = [ + %MfmParser.Node.MFM.Spin{ + props: %{ + keyframes_name: "mfm-spinY", + direction: "reverse", + speed: "1.5s" + }, + children: [] + } + ] + + input_y_alternate = "$[spin.y,alternate ]" + + output_y_alternate = [ + %MfmParser.Node.MFM.Spin{ + props: %{ + keyframes_name: "mfm-spinY", + direction: "alternate", + speed: "1.5s" + }, + children: [] + } + ] + + input_speed = "$[spin.speed=20s ]" + + output_speed = [ + %MfmParser.Node.MFM.Spin{ + props: %{ + keyframes_name: "mfm-spin", + direction: "normal", + speed: "20s" + }, + children: [] + } + ] + + assert Parser.parse(input_default) == output_default + assert Parser.parse(input_left) == output_left + assert Parser.parse(input_alternate) == output_alternate + assert Parser.parse(input_x) == output_x + assert Parser.parse(input_x_left) == output_x_left + assert Parser.parse(input_x_alternate) == output_x_alternate + assert Parser.parse(input_y) == output_y + assert Parser.parse(input_y_left) == output_y_left + assert Parser.parse(input_y_alternate) == output_y_alternate + assert Parser.parse(input_speed) == output_speed + end + + test "it can handle a shake element" do + input_default = "$[shake ]" + + output_default = [ + %MfmParser.Node.MFM.Shake{ + props: %{ + speed: "0.5s" + }, + children: [] + } + ] + + input_speed = "$[shake.speed=20s ]" + + output_speed = [ + %MfmParser.Node.MFM.Shake{ + props: %{ + speed: "20s" + }, + children: [] + } + ] + + assert Parser.parse(input_default) == output_default + assert Parser.parse(input_speed) == output_speed + end + + test "it can handle a twitch element" do + input_default = "$[twitch ]" + + output_default = [ + %MfmParser.Node.MFM.Twitch{ + props: %{ + speed: "0.5s" + }, + children: [] + } + ] + + input_speed = "$[twitch.speed=20s ]" + + output_speed = [ + %MfmParser.Node.MFM.Twitch{ + props: %{ + speed: "20s" + }, + children: [] + } + ] + + assert Parser.parse(input_default) == output_default + assert Parser.parse(input_speed) == output_speed + end + + test "it can handle a rainbow element" do + input_default = "$[rainbow ]" + + output_default = [ + %MfmParser.Node.MFM.Rainbow{ + props: %{ + speed: "1s" + }, + children: [] + } + ] + + input_speed = "$[rainbow.speed=20s ]" + + output_speed = [ + %MfmParser.Node.MFM.Rainbow{ + props: %{ + speed: "20s" + }, + children: [] + } + ] + + assert Parser.parse(input_default) == output_default + assert Parser.parse(input_speed) == output_speed + end + + test "it can handle a sparkle element" do + input = "$[sparkle ]" + + output = [ + %MfmParser.Node.MFM.Sparkle{ + props: %{}, + children: [] + } + ] + + assert Parser.parse(input) == output + end + + test "it can handle a rotate element" do + input = "$[rotate ]" + + output = [ + %MfmParser.Node.MFM.Rotate{ + props: %{}, + children: [] + } + ] + + assert Parser.parse(input) == output + end + + test "it can handle an undefined element" do + input = "$[blabla ]" + + output = [ + %MfmParser.Node.MFM.Undefined{ + props: %{}, + children: [] + } + ] + + assert Parser.parse(input) == output + 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 + + ###################### + # + # OLD STUFF BELOW + # + ###################### + # + + # 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 +end diff --git a/test/reader_test.exs b/test/reader_test.exs index a5dbcf5..9de4a32 100644 --- a/test/reader_test.exs +++ b/test/reader_test.exs @@ -3,11 +3,11 @@ defmodule MfmParser.ReaderTest do alias MfmParser.Reader test "it can peek at the next character" do - assert Reader.peek("chocolatine") == {:ok, "c"} + assert Reader.peek("chocolatine") == "c" end test "it step to the next character" do - assert Reader.next("chocolatine") == {:ok, "c", "hocolatine"} + assert Reader.next("chocolatine") == {"c", "hocolatine"} end test "it returns eof" do diff --git a/test/token/mfm_test.exs b/test/token/mfm_test.exs new file mode 100644 index 0000000..41caeb7 --- /dev/null +++ b/test/token/mfm_test.exs @@ -0,0 +1,33 @@ +defmodule MfmParser.MFMTest do + use ExUnit.Case + + alias MfmParser.Token.MFM + + test "it returns speed in the list of parameters" do + assert %{speed: "5s"} = MFM.to_props("$[blabla.speed=5s") + end + + test "it returns v and h in the list of parameters" do + assert %{v: true} = MFM.to_props("$[blabla.v") + assert %{v: true, h: true} = MFM.to_props("$[blabla.h,v") + end + + test "it returns fonts" do + assert %{font: "some_font"} = MFM.to_props("$[font.some_font") + end + + test "it returns a size for an x element" do + assert %{size: "200%"} = MFM.to_props("$[x2") + assert %{size: "400%"} = MFM.to_props("$[x3") + assert %{size: "600%"} = MFM.to_props("$[x4") + assert %{size: "100%"} = MFM.to_props("$[xqsdfqsf") + end + + test "it returns an empty list when there are no parameters" do + assert %{} = MFM.to_props("$[blabla") + end + + test "it ignores unknown parameters" do + assert %{} = MFM.to_props("$[blabla.idk") + end +end