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:
Ilja 2022-07-24 07:55:55 +02:00
parent 733388fa6e
commit 45519a3c2a
29 changed files with 1006 additions and 46 deletions

View file

@ -2,14 +2,13 @@ defmodule MfmParser.Lexer do
alias MfmParser.Reader alias MfmParser.Reader
alias MfmParser.Token alias MfmParser.Token
alias MfmParser.Token.MFMOpen alias MfmParser.Token.MFM
alias MfmParser.Token.MFMClose
alias MfmParser.Token.Newline alias MfmParser.Token.Newline
alias MfmParser.Token.Text alias MfmParser.Token.Text
def peek(input) do def peek(input) do
case next(input) do case next(input) do
{:ok, token, _} -> {:ok, token} {token, _} -> token
:eof -> :eof :eof -> :eof
end end
end end
@ -22,9 +21,9 @@ defmodule MfmParser.Lexer do
:eof :eof
end 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 if is_last_char_of_token?(char, rest, token) do
{:ok, token |> Token.append(char), rest} {token |> Token.append(char), rest}
else else
recursive_extract_next_token(Reader.next(rest), token |> Token.append(char)) recursive_extract_next_token(Reader.next(rest), token |> Token.append(char))
end end
@ -33,18 +32,18 @@ defmodule MfmParser.Lexer do
defp get_empty_token(input) do defp get_empty_token(input) do
case Reader.peek(input) do case Reader.peek(input) do
:eof -> :eof :eof -> :eof
{:ok, "$"} -> %MFMOpen{} "$" -> %MFM.Open{}
{:ok, "]"} -> %MFMClose{} "]" -> %MFM.Close{}
{:ok, "\n"} -> %Newline{} "\n" -> %Newline{}
_ -> %Text{} _ -> %Text{}
end end
end end
defp is_last_char_of_token?(char, _, %MFMOpen{}) do defp is_last_char_of_token?(char, _, %MFM.Open{}) do
char == " " char == " "
end end
defp is_last_char_of_token?(_, _, %MFMClose{}) do defp is_last_char_of_token?(_, _, %MFM.Close{}) do
true true
end end
@ -55,8 +54,8 @@ defmodule MfmParser.Lexer do
defp is_last_char_of_token?(_, rest, %Text{}) do defp is_last_char_of_token?(_, rest, %Text{}) do
case Reader.next(rest) do case Reader.next(rest) do
:eof -> true :eof -> true
{:ok, "]", _} -> true {"]", _} -> true
{:ok, "$", new_rest} -> Reader.peek(new_rest) == {:ok, "["} {"$", new_rest} -> Reader.peek(new_rest) == "["
_ -> false _ -> false
end end
end end

3
lib/node/mfm/blur.ex Normal file
View file

@ -0,0 +1,3 @@
defmodule MfmParser.Node.MFM.Blur do
defstruct props: %{}, children: []
end

3
lib/node/mfm/bounce.ex Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,3 @@
defmodule MfmParser.Node.MFM.Rotate do
defstruct props: %{}, children: []
end

3
lib/node/mfm/shake.ex Normal file
View 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
View file

@ -0,0 +1,3 @@
defmodule MfmParser.Node.MFM.Sparkle do
defstruct props: %{}, children: []
end

13
lib/node/mfm/spin.ex Normal file
View 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
View 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
View file

@ -0,0 +1,3 @@
defmodule MfmParser.Node.MFM.Twitch do
defstruct props: %{ speed: "0.5s" }, children: []
end

View file

@ -0,0 +1,3 @@
defmodule MfmParser.Node.MFM.Undefined do
defstruct props: %{}, children: []
end

3
lib/node/mfm/x.ex Normal file
View file

@ -0,0 +1,3 @@
defmodule MfmParser.Node.MFM.X do
defstruct props: %{ size: nil }, children: []
end

3
lib/node/newline.ex Normal file
View file

@ -0,0 +1,3 @@
defmodule MfmParser.Node.Newline do
defstruct props: %{text: "\n"}
end

3
lib/node/text.ex Normal file
View file

@ -0,0 +1,3 @@
defmodule MfmParser.Node.Text do
defstruct props: %{text: ""}
end

124
lib/parser.ex Normal file
View 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

View file

@ -4,7 +4,7 @@ defmodule MfmParser.Reader do
case next_char do case next_char do
nil -> :eof nil -> :eof
_ -> {:ok, next_char} _ -> next_char
end end
end end
@ -13,7 +13,7 @@ defmodule MfmParser.Reader do
case next_char do case next_char do
"" -> :eof "" -> :eof
_ -> {:ok, next_char, rest} _ -> {next_char, rest}
end end
end end
end end

57
lib/token/mfm.ex Normal file
View 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
View file

@ -0,0 +1,3 @@
defmodule MfmParser.Token.MFM.Close do
defstruct content: ""
end

3
lib/token/mfm/open.ex Normal file
View file

@ -0,0 +1,3 @@
defmodule MfmParser.Token.MFM.Open do
defstruct content: ""
end

View file

@ -1,3 +0,0 @@
defmodule MfmParser.Token.MFMClose do
defstruct content: ""
end

View file

@ -1,3 +0,0 @@
defmodule MfmParser.Token.MFMOpen do
defstruct content: ""
end

View file

@ -3,8 +3,7 @@ defmodule MfmParser.LexerTest do
alias MfmParser.Lexer alias MfmParser.Lexer
alias MfmParser.Token.MFMOpen alias MfmParser.Token.MFM
alias MfmParser.Token.MFMClose
alias MfmParser.Token.Newline alias MfmParser.Token.Newline
alias MfmParser.Token.Text alias MfmParser.Token.Text
@ -20,11 +19,11 @@ defmodule MfmParser.LexerTest do
describe "mfm $[ token" do describe "mfm $[ token" do
test "it ends with a space" do test "it ends with a space" do
assert Lexer.peek("$[ola puerca]") == {:ok, %MFMOpen{content: "$[ola "}} assert Lexer.peek("$[ola puerca]") == %MFM.Open{content: "$[ola "}
assert Lexer.next("$[ola puerca]") == {:ok, %MFMOpen{content: "$[ola "}, "puerca]"} assert Lexer.next("$[ola puerca]") == {%MFM.Open{content: "$[ola "}, "puerca]"}
assert Lexer.next("$[ola.x,speed=5s 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 end
test "it doesn't crash if the token can't be completed" do test "it doesn't crash if the token can't be completed" do
@ -35,54 +34,55 @@ defmodule MfmParser.LexerTest do
describe "] token" do describe "] token" do
test "it handles ] as a token" do test "it handles ] as a token" do
assert Lexer.peek("]ve anime") == {:ok, %MFMClose{content: "]"}} assert Lexer.peek("]ve anime") == %MFM.Close{content: "]"}
assert Lexer.next("]ve anime") == {:ok, %MFMClose{content: "]"}, "ve anime"} assert Lexer.next("]ve anime") == {%MFM.Close{content: "]"}, "ve anime"}
end end
test "it works at the eof" do test "it works at the eof" do
assert Lexer.peek("]") == {:ok, %MFMClose{content: "]"}} assert Lexer.peek("]") == %MFM.Close{content: "]"}
assert Lexer.next("]") == {:ok, %MFMClose{content: "]"}, ""} assert Lexer.next("]") == {%MFM.Close{content: "]"}, ""}
end end
end end
describe "text token" do describe "text token" do
test "it ends when a mfm token opens while a $ alone doesn't end the 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]") == 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]") == 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.peek("A $2 chocolatine") == %Text{content: "A $2 chocolatine"}
assert Lexer.next("A $2 chocolatine") == {:ok, %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.peek("Eyes like $$") == %Text{content: "Eyes like $$"}
assert Lexer.next("Eyes like $$") == {:ok, %Text{content: "Eyes like $$"}, ""} assert Lexer.next("Eyes like $$") == {%Text{content: "Eyes like $$"}, ""}
end end
test "it ends when a mfm token closes" do test "it ends when a mfm token closes" do
assert Lexer.peek("el culo]") == {:ok, %Text{content: "el culo"}} assert Lexer.peek("el culo]") == %Text{content: "el culo"}
assert Lexer.next("el culo]") == {:ok, %Text{content: "el culo"}, "]"} assert Lexer.next("el culo]") == {%Text{content: "el culo"}, "]"}
end end
test "it ends when the eof is reached" do test "it ends when the eof is reached" do
assert Lexer.peek("Tu abuela ve anime y no se lava el culo") == assert Lexer.peek("Tu abuela ve anime y no se lava el culo") == %Text{
{:ok, %Text{content: "Tu abuela ve anime y no se lava el culo"}} content: "Tu abuela ve anime y no se lava el culo"
}
assert Lexer.next("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
end end
describe "newline token" do describe "newline token" do
test "it handles \n as a token" do test "it handles \n as a token" do
assert Lexer.peek("\nchocolat") == {:ok, %Newline{content: "\n"}} assert Lexer.peek("\nchocolat") == %Newline{content: "\n"}
assert Lexer.next("\nchocolat") == {:ok, %Newline{content: "\n"}, "chocolat"} assert Lexer.next("\nchocolat") == {%Newline{content: "\n"}, "chocolat"}
end end
test "it works at the eof" do test "it works at the eof" do
assert Lexer.peek("\n") == {:ok, %Newline{content: "\n"}} assert Lexer.peek("\n") == %Newline{content: "\n"}
assert Lexer.next("\n") == {:ok, %Newline{content: "\n"}, ""} assert Lexer.next("\n") == {%Newline{content: "\n"}, ""}
end end
end end
end end

686
test/parser_test.exs Normal file
View 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

View file

@ -3,11 +3,11 @@ defmodule MfmParser.ReaderTest do
alias MfmParser.Reader alias MfmParser.Reader
test "it can peek at the next character" do test "it can peek at the next character" do
assert Reader.peek("chocolatine") == {:ok, "c"} assert Reader.peek("chocolatine") == "c"
end end
test "it step to the next character" do test "it step to the next character" do
assert Reader.next("chocolatine") == {:ok, "c", "hocolatine"} assert Reader.next("chocolatine") == {"c", "hocolatine"}
end end
test "it returns eof" do test "it returns eof" do

33
test/token/mfm_test.exs Normal file
View 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