summaryrefslogtreecommitdiff
path: root/lib/irc/parser/line.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/irc/parser/line.ex')
-rw-r--r--lib/irc/parser/line.ex143
1 files changed, 143 insertions, 0 deletions
diff --git a/lib/irc/parser/line.ex b/lib/irc/parser/line.ex
new file mode 100644
index 0000000..3ba2016
--- /dev/null
+++ b/lib/irc/parser/line.ex
@@ -0,0 +1,143 @@
+defmodule Irc.Parser.Line do
+ @moduledoc """
+ IRC line parser
+ """
+
+ defstruct tags: %{}, source: nil, command: nil, args: []
+
+ @doc "Parse a server-to-client line."
+ def parse(line) do
+ {tags, rest} = parse_tags(line)
+ {source, rest} = parse_source(rest)
+ {command, rest} = parse_cmd(rest)
+ args = parse_args(rest)
+ %__MODULE__{tags: tags, source: source, command: command, args: args}
+ end
+
+ # ARGS
+
+ def parse_args(input), do: parse_args(input, [])
+
+ def parse_args(input, acc) do
+ case parse_arg(input, []) do
+ {:continue, new, rest} -> parse_args(rest, [new | acc])
+ {:finished, new} ->
+ list = new ++ acc
+ format_args(list)
+ end
+ end
+
+ # Final argument is the only argument.
+ def parse_arg([?: | rest], acc) when length(acc) == 0 do
+ final = parse_final_arg(rest, [])
+ {:finished, [final]}
+ end
+
+ def parse_arg([0x20, ?: | rest], acc) do
+ final = parse_final_arg(rest, [])
+ {:finished, [final, acc]}
+ end
+
+ def parse_arg([0x20 | rest], acc) do
+ {:continue, acc, rest}
+ end
+
+ def parse_arg([char | rest], acc) do
+ parse_arg(rest, [char | acc])
+ end
+
+ def parse_arg([], acc) do
+ {:finished, [acc]}
+ end
+
+ def parse_final_arg([char | rest], acc) do
+ parse_final_arg(rest, [char | acc])
+ end
+
+ def parse_final_arg([], acc) do
+ acc
+ end
+
+ def format_args(list) when is_list(list) do
+ list
+ |> Enum.map(fn(arg) -> String.trim(acc_to_str(arg)) end)
+ |> Enum.reverse()
+ end
+
+ # COMMAND
+
+ def parse_cmd(input), do: parse_cmd(input, [])
+
+ def parse_cmd([0x20 | rest], acc) do
+ {acc_to_str(acc), rest}
+ end
+
+ def parse_cmd([char | rest], acc) do
+ parse_cmd(rest, [char | acc])
+ end
+
+ # SOURCE
+
+ def parse_source([?: | input]), do: parse_source(input, [])
+ def parse_source(input), do: {nil, input}
+
+ def parse_source([0x20 | rest], acc) do
+ {acc_to_str(acc), rest}
+ end
+ def parse_source([char | rest], acc), do: parse_source(rest, [char | acc])
+
+ # TAGS
+
+ def parse_tags([?@ | input]), do: parse_tags(input, [])
+ def parse_tags(input), do: {%{}, input}
+
+ def parse_tags(input, acc) do
+ case parse_tag(input) do
+ {:continue, new, rest} -> parse_tags([new | acc], rest)
+ {:finished, new, rest} -> {format_tags([new | acc]), rest}
+ end
+ end
+
+ def parse_tag(input) do
+ parse_tag({[], nil}, input)
+ end
+
+ def parse_tag({acc, nil}, [?= | rest]) do
+ parse_tag({acc, []}, rest)
+ end
+ def parse_tag({acc, nil}, [?; | rest]) do
+ {:continue, {acc, true}, rest}
+ end
+ def parse_tag({acc, nil}, [char | rest]) do
+ parse_tag({[char | acc], nil}, rest)
+ end
+
+ def parse_tag({key, acc}, [?; | rest]) do
+ {:continue, {key, acc}, rest}
+ end
+
+ def parse_tag({key, acc}, [0x20 | rest]) do
+ {:finished, {key, acc}, rest}
+ end
+ def parse_tag({key, acc}, [char | rest]) do
+ parse_tag({key, [char | acc]}, rest)
+ end
+
+ def format_tags(list) do
+ list
+ |> Enum.map(fn({k,v}) ->
+ {acc_to_str(k), acc_to_str(v)}
+ end)
+ |> Enum.into(Map.new)
+ end
+
+ def acc_to_str([]), do: nil
+ def acc_to_str(list) when is_list(list) do
+ list
+ |> Enum.reverse()
+ |> to_string()
+ end
+ def acc_to_str(other), do: other
+
+end
+