Packages

A simple Push programming language interpreter implemented in Elixir.

Current section

Files

Jump to
elixush lib instructions code.ex
Raw

lib/instructions/code.ex

defmodule Elixush.Instructions.Code do
import Elixush.PushState
import Elixush.Util
import Elixush.Globals.Agent
def code_noop(state), do: state
# TODO: Look at these to make sure they're in the right place
def noop_open_paren(state), do: state
def noop_delete_prev_paren_pair(state), do: state
def code_append(state) do
if not(Enum.empty?(Enum.drop(state[:code], 1))) do
new_item = Enum.concat(:code |> stack_ref(0, state) |> ensure_list,
:code |> stack_ref(1, state) |> ensure_list)
if count_points(new_item) <= get_globals(:global_max_points) do
push_item(new_item, :code, pop_item(:code, pop_item(:code, state)))
else
state
end
else
state
end
end
def code_atom(state) do
if not(Enum.empty?(state[:code])) do
push_item(not(is_list(stack_ref(:code, 0, state))), :boolean, pop_item(:code, state))
else
state
end
end
def code_car(state) do
if not(Enum.empty?(state[:code])) and length(ensure_list(stack_ref(:code, 0, state))) > 0 do
stack_ref(:code, 0, state) |> ensure_list
|> List.first
|> push_item(:code, pop_item(:code, state))
else
state
end
end
def code_cdr(state) do
if not(Enum.empty?(state[:code])) do
stack_ref(:code, 0, state) |> ensure_list
|> Enum.drop(1)
|> push_item(:code, pop_item(:code, state))
else
state
end
end
def code_cons(state) do
if not(Enum.empty?(Enum.drop(state[:code], 1))) do
new_item = Enum.insert_at(ensure_list(stack_ref(:code, 0, state)), 0, stack_ref(:code, 1, state))
if count_points(new_item) <= get_globals(:global_max_points) do
push_item(new_item, :code, pop_item(:code, pop_item(:code, state)))
else
state
end
else
state
end
end
def code_do(state) do
if not(Enum.empty?(state[:code])) do
push_item(stack_ref(:code, 0, state), :exec, push_item(:code_pop, :exec, state))
else
state
end
end
def code_do_star(state) do
if not(Enum.empty?(state[:code])) do
push_item(stack_ref(:code, 0, state), :exec, pop_item(:code, state))
else
state
end
end
def code_do_star_range(state) do
if not(Enum.empty?(state[:code]) or Enum.empty?(Enum.drop(state[:integer], 1))) do
to_do = List.first(state[:code])
current_index = state[:integer] |> Enum.drop(1) |> List.first
destination_index = state[:integer] |> List.first
args_popped = pop_item(:integer, pop_item(:integer, pop_item(:code, state)))
increment = cond do
current_index < destination_index -> 1
current_index > destination_index -> -1
true -> 0
end
continuation = if increment == 0 do
args_popped
else
push_item([current_index + increment, destination_index, :code_quote, to_do, :code_do_star_range], :exec, args_popped)
end
push_item(to_do, :exec, push_item(current_index, :integer, continuation))
else
state
end
end
def exec_do_star_range(state) do # Differs from code_do_star_range only in the source of the code and the recursive call.
if not(Enum.empty?(state[:exec]) or Enum.empty?(Enum.drop(state[:integer], 1))) do
to_do = List.first(state[:exec])
current_index = state[:integer] |> Enum.drop(1) |> List.first
destination_index = state[:integer] |> List.first
args_popped = pop_item(:integer, pop_item(:integer, pop_item(:exec, state)))
increment = cond do
current_index < destination_index -> 1
current_index > destination_index -> -1
true -> 0
end
continuation = if increment == 0 do
args_popped
else
push_item([current_index + increment, destination_index, :exec_do_star_range, to_do], :exec, args_popped)
end
push_item(to_do, :exec, push_item(current_index, :integer, continuation))
else
state
end
end
def code_do_star_count(state) do
if not((Enum.empty?(state[:integer]) or List.first(state[:integer] < 1)) or Enum.empty?(state[:code])) do
[0, List.first(state[:integer]) - 1, :code_quote, List.first(state[:code]), :code_do_star_range] |>
push_item(:exec, pop_item(:integer, pop_item(:code, state)))
else
state
end
end
def exec_do_star_count(state) do # differs from code_do_star_count only in the source of the code and the recursive call
if not((Enum.empty?(state[:integer]) or List.first(state[:integer] < 1)) or Enum.empty?(state[:exec])) do
[0, List.first(state[:integer]) - 1, :exec_do_star_range, List.first(state[:exec])] |>
push_item(:exec, pop_item(:integer, pop_item(:exec, state)))
else
state
end
end
def code_do_star_times(state) do
if not((Enum.empty?(state[:integer]) or List.first(state[:integer] < 1)) or Enum.empty?(state[:code])) do
[0, List.first(state[:integer]) - 1, :code_quote, List.insert_at(ensure_list(List.first(state[:code])), 0, :integer_pop), :code_do_star_range] |>
push_item(:exec, pop_item(:integer, pop_item(:code, state)))
else
state
end
end
def exec_do_star_times(state) do # differs from code_do_star_times only in the source of the code and the recursive call
if not((Enum.empty?(state[:integer]) or List.first(state[:integer] < 1)) or Enum.empty?(state[:exec])) do
[0, List.first(state[:integer]) - 1, :exec_do_star_range, List.insert_at(ensure_list(List.first(state[:exec])), 0, :integer_pop)] |>
push_item(:exec, pop_item(:integer, pop_item(:exec, state)))
else
state
end
end
def exec_while(state) do
if Enum.empty?(state[:exec]) do
state
else
if Enum.empty?(state[:boolean]) do
pop_item(:exec, state)
else
if not(stack_ref(:boolean, 0, state)) do
pop_item(:exec, pop_item(:boolean, state))
else
block = stack_ref(:exec, 0, state)
pop_item(:boolean, push_item(block, :exec, push_item(:exec_while, :exec, state)))
end
end
end
end
def exec_do_star_while(state) do
if Enum.empty?(state[:exec]) do
state
else
block = stack_ref(:exec, 0, state)
push_item(block, :exec, push_item(:exec_while, :exec, state))
end
end
def code_map(state) do
if not(Enum.empty?(state[:code]) or Enum.empty?(state[:exec])) do
first = for item <- ensure_list(List.first(state[:code])) do
[:code_quote, item, List.first(state[:exec])]
end
second = [:code_wrap]
third = for _item <- Enum.drop(ensure_list(List.first(state[:code])), 1), do: :code_cons
Enum.concat(first, Enum.concat(second, third)) |>
push_item(:exec, pop_item(:code, pop_item(:exec, state)))
else
state
end
end
def code_fromboolean(state) do
if not(Enum.empty?(state[:boolean])) do
push_item(List.first(state[:boolean]), :code, pop_item(:boolean, state))
else
state
end
end
def code_fromfloat(state) do
if not(Enum.empty?(state[:float])) do
push_item(List.first(state[:float]), :code, pop_item(:float, state))
else
state
end
end
def code_frominteger(state) do
if not(Enum.empty?(state[:integer])) do
push_item(List.first(state[:integer]), :code, pop_item(:integer, state))
else
state
end
end
def code_quote(state) do
if not(Enum.empty?(state[:exec])) do
push_item(List.first(state[:exec]), :code, pop_item(:exec, state))
else
state
end
end
def code_if(state) do
if not(Enum.empty?(state[:boolean]) or Enum.empty?(Enum.drop(state[:code], 1))) do
if List.first(:boolean, state) do
List.first(Enum.drop(state[:code], 1))
else
List.first(state[:code])
end |> push_item(:exec, pop_item(:boolean, pop_item(:code, pop_item(:code, state))))
else
state
end
end
def exec_if(state) do # differs from code_if in the source of the code and in the order of the if/then parts
if not(Enum.empty?(state[:boolean]) or Enum.empty?(Enum.drop(state[:exec], 1))) do
if List.first(:boolean, state) do
List.first(state[:exec])
else
List.first(Enum.drop(state[:exec], 1))
end |> push_item(:exec, pop_item(:boolean, pop_item(:exec, pop_item(:exec, state))))
else
state
end
end
def exec_when(state) do
if not(Enum.empty?(state[:boolean]) or Enum.empty?(state[:exec])) do
if List.first(state[:boolean]) do
pop_item(:boolean, state)
else
pop_item(:boolean, pop_item(:exec, state))
end
else
state
end
end
def code_length(state) do
if not(Enum.empty?(state[:code])) do
push_item(Enum.count(ensure_list(List.first(state[:code]))), :integer, pop_item(:code, state))
else
state
end
end
def code_list(state) do
if not(Enum.empty?(state[:code])) do
new_item = [List.first(Enum.drop(state[:code], 1)), List.first(state[:code])]
if count_points(new_item) <= get_globals(:global_max_points) do
push_item(new_item, :code, pop_item(:code, pop_item(:code, state)))
else
state
end
else
state
end
end
def code_wrap(state) do
if not(Enum.empty?(state[:code])) do
new_item = [List.first(state[:code])]
if count_points(new_item) <= get_globals(:global_max_points) do
push_item(new_item, :code, pop_item(:code, state))
else
state
end
else
state
end
end
def code_member(state) do
if not(Enum.empty?(Enum.drop(state[:code], 1))) do
Enum.drop(state[:code], 1) |> List.first
|> Enum.member?(List.first(state[:code]))
|> push_item(:boolean, pop_item(:code, pop_item(:code, state)))
else
state
end
end
def code_nth(state) do # TODO: This is disgusting
if not((Enum.empty?(state[:integer]) or Enum.empty?(state[:code])) or Enum.empty?(ensure_list(List.first(state[:code])))) do
push_item(Enum.at(ensure_list(List.first(state[:code])), rem(abs(List.first(state[:integer])), Enum.count(ensure_list(List.first(state[:code]))))), :code, pop_item(:integer, pop_item(:code, state)))
else
state
end
end
def code_nthcdr(state) do
if not((Enum.empty?(state[:integer]) or Enum.empty?(state[:code])) or Enum.empty?(ensure_list(List.first(state[:code])))) do
Enum.drop(ensure_list(List.first(state[:code])), state[:integer]
|> List.first
|> abs
|> rem(Enum.count(ensure_list(List.first(state[:code])))))
|> push_item(:code, pop_item(:integer, pop_item(:code, state)))
else
state
end
end
def code_null(state) do
if not(Enum.empty?(state[:code])) do
item = List.first(state[:code])
push_item(is_list(item) and Enum.empty?(item), :boolean, pop_item(:code, state))
else
state
end
end
def code_size(state) do
if not(Enum.empty?(state[:code])) do
push_item(state[:code] |> List.first |> count_points, :integer, pop_item(:code, state))
else
state
end
end
def code_extract(state) do
if not(Enum.empty?(state[:code]) or Enum.empty?(state[:integer])) do
push_item(code_at_point(List.first(state[:code]), List.first(state[:integer])), :code, pop_item(:code, pop_item(:integer, state)))
end
end
def code_insert(state) do
if not(Enum.empty?(Enum.drop(state[:code], 1)) or Enum.empty?(state[:integer])) do
new_item = insert_code_at_point(List.first(state[:code]), List.first(state[:integer]), Enum.at(state[:code], 1))
if count_points(new_item) <= get_globals(:global_max_points) do
push_item(new_item, :code, pop_item(:code, pop_item(:code, pop_item(:integer, state))))
else
state
end
else
state
end
end
def code_subst(state) do
if not(Enum.empty?(Enum.drop(Enum.drop(state[:code], 1), 1))) do
new_item = subst(stack_ref(:code, 2, state), stack_ref(:code, 1, state), stack_ref(:code, 0, state))
if count_points(new_item) <= get_globals(:global_max_points) do
push_item(new_item, :code, pop_item(:code, pop_item(:code, pop_item(:code, state))))
else
state
end
else
state
end
end
def code_contains(state) do
if not(Enum.empty?(Enum.drop(state[:code], 1))) do
push_item(contains_subtree(stack_ref(:code, 1, state), stack_ref(:code, 0, state)), :boolean, pop_item(:code, pop_item(:code, state)))
else
state
end
end
def code_container(state) do
if not(Enum.empty?(Enum.drop(state[:code], 1))) do
push_item(containing_subtree(stack_ref(:code, 0, state), stack_ref(:code, 1, state)), :code, pop_item(:code, pop_item(:code, state)))
else
state
end
end
@doc """
Returns a lazy sequence containing the positions at which pred
is true for items in coll.
"""
def positions(pred, coll) do
with_nils = for {idx, elt} <- Enum.zip(Stream.iterate(0, &(&1+1)), coll) do
if pred.(elt), do: idx
end
Enum.filter(with_nils, &(&1 != nil))
end
def code_position(state) do
if not(Enum.empty?(Enum.drop(state[:code], 1))) do
(List.first(positions(&(&1 == stack_ref(:code, 1, state)), ensure_list(stack_ref(:code, 0, state)))) or -1)
|> push_item(:integer, pop_item(:code, pop_item(:code, state)))
end
end
def exec_k(state) do
if not(Enum.empty?(Enum.drop(state[:exec], 1))) do
push_item(List.first(state[:exec]), :exec, pop_item(:exec, pop_item(:exec, state)))
else
state
end
end
def exec_s(state) do
if not(Enum.empty?(Enum.drop(Enum.drop(state[:exec], 1), 1))) do
stk = state[:exec]
x = List.first(stk)
y = List.first(Enum.drop(stk, 1))
z = List.first(Enum.drop(Enum.drop(stk, 1), 1))
if count_points([y, z]) <= get_globals(:global_max_points) do
push_item(x, :exec, push_item(z, :exec, push_item([y, z], :exec, pop_item(:exec, pop_item(:exec, pop_item(:exec, state))))))
else
state
end
else
state
end
end
def exec_y(state) do
if not(Enum.empty?(state[:exec])) do
new_item = [:exec_y, List.first(state[:exec])]
if count_points(new_item) <= get_globals(:global_max_points) do
push_item(List.first(state[:exec]), :exec, push_item(new_item, :exec, pop_item(:exec, state)))
else
state
end
else
state
end
end
@doc "Creates new environment using the top item on the exec stack"
def environment_new(state) do
if not(Enum.empty?(state[:exec])) do
new_exec = top_item(:exec, state)
parent_env = pop_item(:exec, state)
push_item(new_exec, :exec, push_item(parent_env, :environment, state) |> Map.put(:return, []) |> Map.put(:exec, []))
else
state
end
end
@doc "Creates new environment using the entire exec stack"
def environment_begin(state) do
Map.put(push_item(Map.put(state, :exec, []), :environment, state), :return, [])
end
@doc "Ends current environment"
def environment_end(state) do
if not(Enum.empty?(state[:environment])) do
end_environment(state)
else
state
end
end
end