Packages

A simple Push programming language interpreter implemented in Elixir.

Current section

Files

Jump to
elixush lib util.ex
Raw

lib/util.ex

defmodule Elixush.Util do
import Elixush.Globals.Agent
@doc "If thing is a literal, return its type -- otherwise return false."
def recognize_literal(thing) do
# HACK: anonymous functions used to emulate loop/recur from clojure
loop = fn(f, m) ->
if m |> Map.to_list |> List.first |> is_nil do
nil
else
{type, pred} = hd(Map.to_list(m))
if pred.(thing) do
type
else
f.(f, Map.new(tl(Map.to_list(m))))
end
end
end
loop.(loop, get_globals(:literals))
end
@doc "really make-list-if-not-seq, but close enough for here"
def ensure_list(thing) do
if is_list(thing) do
thing
else
# REVIEW: Is just Enum.to_list sufficient here?
Enum.to_list(thing)
end
end
@doc "Prints the provided thing and returns it."
def print_return(thing) do
IO.puts(Macro.to_string(thing))
thing
end
@doc "Returns a version of n that obeys limit parameters."
@spec keep_number_reasonable(number) :: number
def keep_number_reasonable(n) do
max_number_magnitude = get_globals(:max_number_magnitude)
cond do
is_integer(n) ->
cond do
n > max_number_magnitude -> max_number_magnitude
n < -max_number_magnitude -> -max_number_magnitude
true -> n
end
true ->
cond do
n > max_number_magnitude -> max_number_magnitude * 1.0
n < -max_number_magnitude -> -max_number_magnitude * 1.0
n < -max_number_magnitude and n > max_number_magnitude -> 0.0
true -> n
end
end
end
@doc "If a number, rounds float f to n decimal places."
def round_to_n_decimal_places(f, n) do
if !is_number(f) do
f
else
factor = :math.pow(10, n)
round(f * factor) / factor
end
end
@doc "Returns the number of paren pairs in tree"
@spec count_parens(Enum.t) :: integer
def count_parens(tree) do
# HACK: anonymous functions used to emulate loop/recur from clojure
loop = fn(f, remaining, total) ->
cond do
not is_list(remaining) -> total
Enum.empty?(remaining) -> total + 1
not is_list(hd(remaining)) -> f.(f, tl(remaining), total)
true -> f.(f, remaining, total + 1)
end
end
loop.(loop, tree, 0)
end
@doc """
Returns the number of points in tree, where each atom and each pair of parentheses
counts as a point.
"""
@spec count_points(Enum.t) :: integer
def count_points(tree) do
# HACK: anonymous functions used to emulate loop/recur from clojure
loop = fn(f, remaining, total) ->
cond do
not is_list(remaining) -> total + 1
Enum.empty?(remaining) -> total + 1
not is_list(hd(remaining)) -> f.(f, tl(remaining), total + 1)
true -> f.(f, remaining, total + 1)
end
end
loop.(loop, tree, 0)
end
@doc "Like walk, but only for lists."
def walklist(inner, outer, form) do
if is_list(form) do
outer.(Enum.map(form, inner))
else
outer.(form)
end
end
@doc "Like postwalk, but only for lists"
# REVIEW: check that this works correctly
def postwalklist(f, form) do
walklist(&(postwalklist(f, &1)), f, form)
end
# TODO: insert-code-at-point, remove-code-at-point
@doc "computes the next row using the prev-row current-element and the other seq"
def compute_next_row(prev_row, current_element, other_seq, pred) do
Enum.reduce(List.zip([prev_row, Enum.fetch(prev_row, 1), other_seq]), (hd(prev_row) + 1),
fn(row, [diagonal, above, other_element]) ->
update_val = if pred.(other_element, current_element) do
# if the elements are deemed equivalent according to the predicate
# pred, then no change has taken place to the string, so we are
# going to set it the same value as diagonal (which is the previous edit-distance)
diagonal
else
# in the case where the elements are not considered equivalent, then we are going
# to figure out if its a substitution (then there is a change of 1 from the previous
# edit distance) thus the value is diagonal + 1 or if its a deletion, then the value
# is present in the columns, but not in the rows, the edit distance is the edit-distance
# of last of row + 1 (since we will be using vectors, peek is more efficient)
# or it could be a case of insertion, then the value is above+1, and we chose
# the minimum of the three
Enum.min([diagonal, above, hd(row)]) + 1
end
List.insert_at(row, 0, update_val)
end)
end
@doc """
Levenshtein Distance - http://en.wikipedia.org/wiki/Levenshtein_distance
In information theory and computer science, the Levenshtein distance is a
metric for measuring the amount of difference between two sequences. This
is a functional implementation of the levenshtein edit
distance with as little mutability as possible.
Still maintains the O(n*m) guarantee.
"""
def levenshtein_distance(a, b, p) do
cond do
List.empty? a -> length b
List.empty? b -> length a
true -> Enum.reduce()
fn(prev_row, current_element) ->
compute_next_row(prev_row, current_element, b, p)
end
Range.new(0, length(b) + 1)
end
end
end