Skip to content

Instantly share code, notes, and snippets.

@sionide21
Last active May 4, 2017 12:50
Show Gist options
  • Save sionide21/733102c7cfcbb46500e414fb233aba36 to your computer and use it in GitHub Desktop.
Save sionide21/733102c7cfcbb46500e414fb233aba36 to your computer and use it in GitHub Desktop.
defmodule Polish do
defmacro reverse_polish_notation(code) do
code
|> do_reverse_polish()
|> List.flatten()
|> Enum.join(" ")
end
defmacro sigil_p({:<<>>, _, [code]}, '') do
build_code(code)
end
defmacro sigil_p({:<<>>, _, [code]}, 's') do
code
|> build_code()
|> Macro.to_string()
end
def lex(n) do
tokens = [{:num, ~r/^\d+$/}, {:var, ~r/^\$\w+$/}, {:op, ~r/^(\w+|[\+\*\-\/])$/}, {:fun, ~r/^\w+\/\d+$/}]
{type, _} = Enum.find(tokens, {:unknown, nil}, fn {_, reg} ->
String.match?(n, reg)
end)
{type, n}
end
def build_code(code) when is_binary(code) do
code
|> String.split()
|> Enum.map(&lex/1)
|> build_code([])
end
def build_code([{:num, n} | input], stack) do
build_code(input, [String.to_integer(n) | stack])
end
def build_code([{:var, var} | input], stack) do
name = var |> String.trim("$") |> String.to_atom()
build_code(input, [Macro.var(name, nil) | stack])
end
def build_code([{:op, fun} | input], [rhs, lhs | rest]) do
build_code(input, [{String.to_atom(fun), [], [lhs, rhs]} | rest])
end
def build_code([{:fun, fun} | input], stack) do
[name, arity] = String.split(fun, "/")
arity = String.to_integer(arity)
{args, rest} = Enum.split(stack, arity)
^arity = length(args)
build_code(input, [{String.to_atom(name), [], Enum.reverse(args)} | rest])
end
def build_code([{:unknown, token} | _], _) do
raise "Unknown token: #{token}"
end
def build_code([], [code]) do
code
end
def do_reverse_polish({fun, _, [lhs, rhs]}) do
[do_reverse_polish(lhs), do_reverse_polish(rhs), to_string(fun)]
end
def do_reverse_polish({fun, _, args}) when is_list(args) do
[Enum.map(args, &do_reverse_polish/1), "#{fun}/#{length(args)}"]
end
def do_reverse_polish(_var={name, _, nil}) when is_atom(name) do
"$#{name}"
end
def do_reverse_polish(n) when is_number(n) do
n
end
end
import Polish
a = 1
"2 $a 1 - * abs/1 $a 20 min +" = Polish.reverse_polish_notation abs(2 * (a - 1)) + min(a, 20)
1 = ~p[2 $a 1 - * abs/1 $a 20 min +]
"abs(2 * (a - 1)) + min(a, 20)" = ~p[2 $a 1 - * abs/1 $a 20 min +]s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment