Current section
Files
Jump to
Current section
Files
lib/format.ex
defmodule Abacus.Format do
@moduledoc """
Function definitions on how to pretty-print expressions.
See `Abacus.format/1` for more information.
"""
@binary_operators [:add, :subtract, :divide, :multiply, :power,
:and, :or, :xor, :shift_left, :shift_right,
:eq, :neq, :gt, :gte, :lt, :lte,
:logical_and, :logical_or]
@unary_operators [:logical_not, :not, :factorial]
@operators Enum.concat([@binary_operators, @unary_operators])
@spec format(expr::tuple | number | boolean | nil) :: String.t
def format(number) when is_integer(number), do: Integer.to_string(number)
def format(number) when is_float(number), do: Float.to_string(number)
def format({operator, a, b} = expr) when operator in @binary_operators do
op_string = format(operator)
lhs = format a
rhs = format b
without_parantheses = "#{lhs} #{op_string} #{rhs}"
with_left_parantheses = "(#{lhs}) #{op_string} #{rhs}"
with_right_parantheses = "#{lhs} #{op_string} (#{rhs})"
with_both_parantheses = "(#{lhs}) #{op_string} (#{rhs})"
result = {
Abacus.parse(without_parantheses),
Abacus.parse(with_left_parantheses),
Abacus.parse(with_right_parantheses),
Abacus.parse(with_both_parantheses)
}
case result do
{{:ok, ^expr}, _, _, _} -> without_parantheses
{_, {:ok, ^expr}, _, _} -> with_left_parantheses
{_, _, {:ok, ^expr}, _} -> with_right_parantheses
{_, _, _, {:ok, ^expr}} -> with_both_parantheses
end
end
def format({:function, name, arguments}) do
arguments = arguments
|> Enum.map(&format/1)
|> Enum.join(", ")
"#{name}(#{arguments})"
end
def format({:factorial, a} = expr) do
a = format a
with_parantheses = "(#{a})!"
without_parantheses = "#{a}!"
result = {
Abacus.parse(without_parantheses),
Abacus.parse(with_parantheses)
}
case result do
{{:ok, ^expr}, _} -> without_parantheses
{_, {:ok, ^expr}} -> with_parantheses
end
end
def format({operator, a} = expr) when operator in @unary_operators do
a = format a
op = format operator
with_parantheses = "#{op}(#{a})"
without_parantheses = "#{op}#{a}"
result = {
Abacus.parse(without_parantheses),
Abacus.parse(with_parantheses)
}
case result do
{{:ok, ^expr}, _} -> without_parantheses
{_, {:ok, ^expr}} -> with_parantheses
end
end
def format({:ternary_if, condition, if_true, if_false} = expr) do
fcondition = format condition
ftrue = format if_true
ffalse = format if_false
v = [false, true]
permutations = for c <- v, t <- v, f <- v do
format = "#{parantheses fcondition, c} ? #{parantheses ftrue, t} : #{parantheses ffalse, f}"
{:ok, expr} = Abacus.parse(format)
{format, expr}
end
{format, _} = permutations
|> Enum.filter(fn {_format, e} -> e == expr end)
|> List.first
format
end
def format(operator) when operator in @operators do
case operator do
:add -> "+"
:subtract -> "-"
:divide -> "/"
:multiply -> "*"
:power -> "^"
:and -> "&"
:or -> "|"
:xor -> "|^"
:not -> "~"
:shift_left -> "<<"
:shift_right -> ">>"
:eq -> "=="
:neq -> "!="
:gt -> ">"
:gte -> ">="
:lt -> "<"
:lte -> "<="
:logical_and -> "&&"
:logical_or -> "||"
:logical_not -> "not"
end
end
def format({:access, [{:variable, name} | rest]}) do
case rest do
[] -> name
[{:index, _} | _] -> "#{name}#{format {:access, rest}}"
[{:variable, _} | _] -> "#{name}.#{format {:access, rest}}"
end
end
def format({:access, [{:index, expr} | rest]}) do
case rest do
[] -> "[#{format expr}]"
[{:index, _} | _] -> "[#{format expr}]#{format {:access, rest}}"
[{:variable, _} | _] -> "[#{format expr}].#{format {:access, rest}}"
end
end
def format(false), do: "false"
def format(true), do: "true"
def format(nil), do: "null"
def format(expr), do: {:error, "Can't format #{inspect expr}"}
defp parantheses(expr, true), do: "(#{expr})"
defp parantheses(expr, false), do: expr
end