Created
May 4, 2015 01:44
-
-
Save pmarreck/90aeac54022044877ef7 to your computer and use it in GitHub Desktop.
no matches in this elixir attempt to rpn calc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# rpn.exs | |
# A commandline RPN calculator in Elixir... just for practice. | |
Code.require_file "math_integer_power.exs", __DIR__ | |
defmodule RPN do | |
def initialize do | |
initialize(System.argv) | |
end | |
def initialize(input) when is_binary(input) do | |
initialize(String.split(input, " ", trim: true)) | |
end | |
def initialize(input) when is_list(input) do | |
input |> normalize |> compute([], [dict: %{}]) | |
end | |
def normalize(input) when is_list(input) do | |
input | |
|> Enum.map(fn(elem) -> | |
case elem do | |
"+" -> :+ | |
"-" -> :- | |
"*" -> :* | |
"/" -> :/ | |
"**" -> :"**" | |
"pi" -> :pi | |
"sin" -> :sin | |
"cos" -> :cos | |
"tan" -> :tan | |
"sqrt" -> :sqrt | |
"drop" -> :drop | |
"dup" -> :dup | |
"emit" -> :emit | |
"." -> :. | |
":" -> :":" | |
";" -> :";" | |
num -> fn -> | |
num_or_symbol = validate_num(num) | |
if num_or_symbol == :NaN do | |
String.to_atom(num) | |
else | |
num_or_symbol | |
end | |
end.() | |
end #case | |
end #fn | |
) #Enum.map | |
end | |
defp validate_num(num) when is_float(num), do: num | |
defp validate_num(num) when is_integer(num), do: num | |
defp validate_num(num) when is_binary(num) do | |
cond do | |
num =~ ~r/^-?[0-9]+$/ -> String.to_integer(num) | |
num =~ ~r/^-?[0-9]+(?:\.[0-9]+)?$/ -> String.to_float(num) | |
true -> :NaN | |
end | |
end | |
def compute([], [last_val], _) do | |
last_val | |
end | |
def compute([ :+ | remaining_input], [y, x | stack], dict) do | |
compute(remaining_input, [x + y | stack], dict) | |
end | |
def compute([ :- | remaining_input], [y, x | stack], dict) do | |
compute(remaining_input, [x - y | stack], dict) | |
end | |
def compute([ :* | remaining_input], [y, x | stack], dict) do | |
compute(remaining_input, [x * y | stack], dict) | |
end | |
def compute([ :/ | remaining_input], [y, x | stack], dict) do | |
compute(remaining_input, [x / y | stack], dict) | |
end | |
def compute([ :"**" | remaining_input], [y, x | stack], dict) when is_integer(y) and is_integer(x) do | |
compute(remaining_input, [Math.Integer.ipow(x, y) | stack], dict) | |
end | |
def compute([ :"**" | remaining_input], [y, x | stack], dict) do | |
compute(remaining_input, [:math.pow(x, y) | stack], dict) | |
end | |
def compute([ :pi | remaining_input], stack, dict) do | |
compute(remaining_input, [:math.pi | stack], dict) | |
end | |
def compute([ :sin | remaining_input], [x | stack], dict) do | |
compute(remaining_input, [:math.sin(x) | stack], dict) | |
end | |
def compute([ :cos | remaining_input], [x | stack], dict) do | |
compute(remaining_input, [:math.cos(x) | stack], dict) | |
end | |
def compute([ :tan | remaining_input], [x | stack], dict) do | |
compute(remaining_input, [:math.tan(x) | stack], dict) | |
end | |
def compute([ :sqrt | remaining_input], [x | stack], dict) do | |
compute(remaining_input, [:math.sqrt(x) | stack], dict) | |
end | |
def compute([ :drop | remaining_input], [_ | stack], dict) do | |
compute(remaining_input, stack, dict) | |
end | |
def compute([ :dup | remaining_input], [x | stack], dict) do | |
compute(remaining_input, [x, x | stack], dict) | |
end | |
# new definitions! | |
def compute([ :":", name | remaining_input ], stack, dict) do | |
{definition, [:";" | remainder]} = Enum.split_while(remaining_input, fn(ins) -> ins != :";" end) | |
dict = Dict.put(dict, name, definition) | |
compute(remainder, stack, dict) | |
end | |
# side effects! | |
def compute([ :emit | remaining_input], [x | stack], dict) do | |
IO.write <<x>> | |
compute(remaining_input, stack, dict) | |
end | |
def compute([ :. | remaining_input], [x | stack], dict) do | |
IO.puts x | |
compute(remaining_input, stack, dict) | |
end | |
# definition lookup | |
def compute([ symbol | remaining_input], stack, dict) when is_atom(symbol) do | |
if Dict.has_key?(dict, symbol) do | |
compute(remaining_input, dict[symbol] ++ stack, dict) | |
else | |
raise "Undefined symbol: #{symbol}" | |
end | |
end | |
# fallthrough | |
# def compute([n | remaining_input], stack, dict) do | |
# compute(remaining_input, [n | stack], dict) | |
# end | |
# def compute(remaining_input, stack) do | |
# end | |
end | |
# run this inline suite with "elixir #{__ENV__.file} test" | |
if System.argv |> List.first == "test" do | |
ExUnit.start | |
defmodule RPNTest do | |
use ExUnit.Case, async: true | |
test "normalizing input list" do | |
assert RPN.normalize(["1", "5", "+"]) === [1, 5, :+] | |
end | |
test "adding 2 numbers" do | |
assert RPN.initialize(~w[1.0 2.0 +]) === 3.0 | |
end | |
test "subtracting 2 numbers" do | |
assert RPN.initialize(~w[3 2 -]) === 1 | |
end | |
test "multiplying 2 numbers" do | |
assert RPN.initialize(~w[3 2 *]) === 6 | |
end | |
test "dividing 2 numbers" do | |
assert RPN.initialize(~w[3 2 /]) === 1.5 | |
end | |
test "sequence of simple math operations" do | |
assert RPN.initialize(~w[ 1 2 3 4 5 * + + + 2 /]) === 13.0 | |
end | |
test "integer power" do | |
assert RPN.initialize(~w[ 10 3 ** ]) === 1000 | |
end | |
test "float power" do | |
assert RPN.initialize(~w[ 10.0 3.0 ** ]) === 1000.0 | |
end | |
test "pi" do | |
assert RPN.initialize(~w[ pi ]) === 3.141592653589793 | |
end | |
test "sin" do | |
assert RPN.initialize(~w[ 3 sin ]) === 0.1411200080598672 | |
end | |
test "cos" do | |
assert RPN.initialize(~w[ 3 cos ]) === -0.9899924966004454 | |
end | |
test "tan" do | |
assert RPN.initialize(~w[ 3 tan ]) === -0.1425465430742778 | |
end | |
test "sqrt" do | |
assert RPN.initialize(~w[ 3 sqrt ]) === 1.7320508075688772 | |
end | |
test "drop" do | |
assert RPN.initialize(~w[ 3 3 drop ]) === 3 | |
end | |
test "dup" do | |
assert RPN.initialize(~w[ 3 dup * ]) === 9 | |
end | |
test "defining new words" do | |
assert RPN.initialize(~w[ : square dup * ; 5 square ]) === 25 | |
end | |
end | |
else | |
IO.puts RPN.initialize | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment