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