lib/lexer.ex

defmodule MfmParser.Lexer do
  alias MfmParser.Reader

  alias MfmParser.Token
  alias MfmParser.Token.MFM
  alias MfmParser.Token.Text

  def peek(input) do
    case next(input) do
      {token, _} -> token
      :eof -> :eof
    end
  end

  def next(input) do
    recursive_extract_next_token(Reader.next(input), get_empty_token(input))
  end

  defp recursive_extract_next_token(:eof, :eof) do
    :eof
  end

  defp recursive_extract_next_token(:eof, %MFM.Open{content: content}) do
    {%Text{content: content}, ""}
  end

  defp recursive_extract_next_token(:eof, token) do
    {token, ""}
  end

  defp recursive_extract_next_token({"]", rest}, %MFM.Open{content: content}) do
    {%Text{content: content <> "]"}, rest}
  end

  defp recursive_extract_next_token({char, rest}, token) do
    if is_last_char_of_token?(char, rest, token) do
      {token |> Token.append(char), rest}
    else
      recursive_extract_next_token(Reader.next(rest), token |> Token.append(char))
    end
  end

  defp get_empty_token(input) do
    case Reader.peek(input) do
      :eof ->
        :eof

      "$" ->
        if Reader.peek(input, 2) == "[" do
          %MFM.Open{}
        else
          %Text{}
        end

      "]" ->
        %MFM.Close{}

      _ ->
        %Text{}
    end
  end

  defp is_last_char_of_token?(char, _, %MFM.Open{}) do
    char == " "
  end

  defp is_last_char_of_token?(_, _, %MFM.Close{}) do
    true
  end

  defp is_last_char_of_token?(_, rest, %Text{}) do
    case Reader.next(rest) do
      :eof -> true
      {"]", _} -> true
      {"$", new_rest} -> Reader.peek(new_rest) == "["
      _ -> false
    end
  end
end