Make parser work for single token input
We can handle all needed tokens. We still need to test for multiple tokens and for nesting.
This commit is contained in:
parent
733388fa6e
commit
45519a3c2a
29 changed files with 1006 additions and 46 deletions
23
lib/lexer.ex
23
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
|
||||
|
|
3
lib/node/mfm/blur.ex
Normal file
3
lib/node/mfm/blur.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.MFM.Blur do
|
||||
defstruct props: %{}, children: []
|
||||
end
|
3
lib/node/mfm/bounce.ex
Normal file
3
lib/node/mfm/bounce.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.MFM.Bounce do
|
||||
defstruct props: %{ speed: "0.75s" }, children: []
|
||||
end
|
3
lib/node/mfm/flip.ex
Normal file
3
lib/node/mfm/flip.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.MFM.Flip do
|
||||
defstruct props: %{ v: false, h: false }, children: []
|
||||
end
|
3
lib/node/mfm/font.ex
Normal file
3
lib/node/mfm/font.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.MFM.Font do
|
||||
defstruct props: %{ font: nil }, children: []
|
||||
end
|
3
lib/node/mfm/jelly.ex
Normal file
3
lib/node/mfm/jelly.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.MFM.Jelly do
|
||||
defstruct props: %{ speed: "1s" }, children: []
|
||||
end
|
3
lib/node/mfm/jump.ex
Normal file
3
lib/node/mfm/jump.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.MFM.Jump do
|
||||
defstruct props: %{ speed: "0.75s" }, children: []
|
||||
end
|
3
lib/node/mfm/rainbow.ex
Normal file
3
lib/node/mfm/rainbow.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.MFM.Rainbow do
|
||||
defstruct props: %{ speed: "1s" }, children: []
|
||||
end
|
3
lib/node/mfm/rotate.ex
Normal file
3
lib/node/mfm/rotate.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.MFM.Rotate do
|
||||
defstruct props: %{}, children: []
|
||||
end
|
3
lib/node/mfm/shake.ex
Normal file
3
lib/node/mfm/shake.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.MFM.Shake do
|
||||
defstruct props: %{ speed: "0.5s" }, children: []
|
||||
end
|
3
lib/node/mfm/sparkle.ex
Normal file
3
lib/node/mfm/sparkle.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.MFM.Sparkle do
|
||||
defstruct props: %{}, children: []
|
||||
end
|
13
lib/node/mfm/spin.ex
Normal file
13
lib/node/mfm/spin.ex
Normal file
|
@ -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
|
3
lib/node/mfm/tada.ex
Normal file
3
lib/node/mfm/tada.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.MFM.Tada do
|
||||
defstruct props: %{ speed: "1s" }, children: []
|
||||
end
|
3
lib/node/mfm/twitch.ex
Normal file
3
lib/node/mfm/twitch.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.MFM.Twitch do
|
||||
defstruct props: %{ speed: "0.5s" }, children: []
|
||||
end
|
3
lib/node/mfm/undefined.ex
Normal file
3
lib/node/mfm/undefined.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.MFM.Undefined do
|
||||
defstruct props: %{}, children: []
|
||||
end
|
3
lib/node/mfm/x.ex
Normal file
3
lib/node/mfm/x.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.MFM.X do
|
||||
defstruct props: %{ size: nil }, children: []
|
||||
end
|
3
lib/node/newline.ex
Normal file
3
lib/node/newline.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.Newline do
|
||||
defstruct props: %{text: "\n"}
|
||||
end
|
3
lib/node/text.ex
Normal file
3
lib/node/text.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Node.Text do
|
||||
defstruct props: %{text: ""}
|
||||
end
|
124
lib/parser.ex
Normal file
124
lib/parser.ex
Normal file
|
@ -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/<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
|
|
@ -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
|
||||
|
|
57
lib/token/mfm.ex
Normal file
57
lib/token/mfm.ex
Normal file
|
@ -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
|
3
lib/token/mfm/close.ex
Normal file
3
lib/token/mfm/close.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Token.MFM.Close do
|
||||
defstruct content: ""
|
||||
end
|
3
lib/token/mfm/open.ex
Normal file
3
lib/token/mfm/open.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule MfmParser.Token.MFM.Open do
|
||||
defstruct content: ""
|
||||
end
|
|
@ -1,3 +0,0 @@
|
|||
defmodule MfmParser.Token.MFMClose do
|
||||
defstruct content: ""
|
||||
end
|
|
@ -1,3 +0,0 @@
|
|||
defmodule MfmParser.Token.MFMOpen do
|
||||
defstruct content: ""
|
||||
end
|
|
@ -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
|
||||
|
|
686
test/parser_test.exs
Normal file
686
test/parser_test.exs
Normal file
|
@ -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 = "<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
|
||||
end
|
|
@ -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
|
||||
|
|
33
test/token/mfm_test.exs
Normal file
33
test/token/mfm_test.exs
Normal file
|
@ -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
|
Loading…
Reference in a new issue