Current section
Files
Jump to
Current section
Files
lib/code_generator.ex
defmodule CodeGenerator do
@moduledoc """
Returns the assembly code generated given an input source code.
"""
@doc """
Returns a string of the assembly code generated given a source code.
## Specs
```abstract_syntax_tree``` : abstract sytnax tree generated by the `Parser.parse/2`.
```verbose``` a boolean value indicating if the compiler should output all of its steps.
"""
def generate_code(abstract_syntax_tree, verbose) do
{raw_code, _result_my_context, _result_free_context} = generate_raw_string_code(abstract_syntax_tree)
raw_code |> raw_code_cleanup
if verbose do
Helpers.Printer.print_element(Helpers.StringElements.rc, raw_code)
end
end
defp generate_raw_string_code(abstract_syntax_tree, incoming_free_context \\ get_available_registers(), sibling_number \\ 0) do
children_list = abstract_syntax_tree.children
{children_string, children_context, free_context} = print_next_children(children_list, [], incoming_free_context, 0, "")
{return_string, return_my_context, return_free_context} = contextualize_asm(abstract_syntax_tree.asm, children_context, free_context, sibling_number)
if abstract_syntax_tree.token == nil do
{children_string <> return_string <> "\n", return_my_context, return_free_context}
else
{children_string <> String.replace(return_string, ":t", abstract_syntax_tree.token.expression) <> "\n", return_my_context, return_free_context}
end
end
defp print_next_children([], incoming_siblings_context, incoming_free_context, _sibling_number, incoming_string) do
{incoming_string, incoming_siblings_context, incoming_free_context}
end
defp print_next_children(children_list, incoming_siblings_context, incoming_free_context, sibling_number, incoming_string) do
[head | tail] = children_list
{return_string, return_siblings_context, return_free_context} = generate_raw_string_code(head, incoming_free_context, sibling_number)
print_next_children(tail, return_siblings_context ++ incoming_siblings_context, return_free_context, sibling_number + 1, return_string <> incoming_string)
end
defp get_available_registers() do
["rbx", "rcx", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"]
end
defp contextualize_asm(code, incoming_children_context, incoming_free_context, sibling_number) do
#Find what registers I have to assign.
register_candidates = Regex.scan(~r/:r|:v[0-9]+/, code) |> Enum.uniq |> List.flatten
register_identities = fetch_registers(register_candidates, incoming_free_context)
#Create a replacement rule list for the equivalencies for placeholder-registry
replacement_list = incoming_children_context ++ Enum.zip(register_candidates,register_identities)
#Replace placeholders in string
replaced_code = Enum.reduce_while(replacement_list, code, fn {k,v}, acc -> {:cont, String.replace(acc,k,v)} end)
#Free children registers
result_registry = Enum.into(replacement_list, %{})[":r"]
canonical_registers = get_available_registers() #Get legal registers
new_free_registers = ( MapSet.intersection(canonical_registers |> MapSet.new ,Enum.map(incoming_children_context, fn {_x,y} -> y end) |> MapSet.new ) |> MapSet.to_list ) ++ Enum.filter(incoming_free_context, fn x -> x != result_registry end)
#Generate my return register equivalency
my_register = [{":#{sibling_number}", if result_registry == nil do "" else result_registry end}]
#Return tuple
{replaced_code, my_register, new_free_registers}
end
defp fetch_registers([],_free_registers) do
[]
end
defp fetch_registers(candidates,free_registers) do
free_count = free_registers |> Enum.count
need_count = candidates |> Enum.count
Enum.slice(free_registers, 0..need_count-1) ++ generate_ram_variables(need_count-free_count)
end
defp generate_ram_variables(_number_needed) do
[]#TODO Implement this or it will fail someday
end
defp raw_code_cleanup(code) do
Regex.replace(~r/\n+/, code, "\n")
end
end