Packages

C compiler built with Elixir. This library will only allow you to compile basic C programs.

Current section

Files

Jump to
exc lib modules code_generator.ex
Raw

lib/modules/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
{assembly_code, _result_my_context, _result_free_context, _result_uid} =
generate_raw_string_code(abstract_syntax_tree)
assembly_code
|> cleanup()
|> IO.Printer.check_for_verbose(verbose)
end
defp generate_raw_string_code(abstract_syntax_tree, incoming_free_context \\
get_available_registers(), sibling_number \\ 0, start_string \\ "", uid \\ 0) do
children_list = abstract_syntax_tree.children
{children_string, children_context, free_context, current_uid} =
print_next_children(
Enum.reverse(children_list), [], incoming_free_context, Enum.count(children_list)-1, start_string, uid
)
{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
<> String.replace(
return_string, ":u", Integer.to_string(current_uid)
)
<> "\n", return_my_context, return_free_context, current_uid+1
}
else
{ children_string
<> (String.replace(
return_string, ":u", Integer.to_string(current_uid+1)
) |>
String.replace(":t", abstract_syntax_tree.token.expression
))
<> "\n", return_my_context, return_free_context, current_uid+1
}
end
end
defp print_next_children([], incoming_siblings_context, incoming_free_context,
_sibling_number, incoming_string, incoming_uid) do
{incoming_string, incoming_siblings_context, incoming_free_context, incoming_uid}
end
defp print_next_children(children_list, incoming_siblings_context,
incoming_free_context, sibling_number, incoming_string, incoming_uid) do
[head | tail] = children_list
{return_string, return_siblings_context, return_free_context, next_uid} =
generate_raw_string_code(
head, incoming_free_context, sibling_number,
incoming_string, incoming_uid
)
print_next_children(tail,
return_siblings_context ++ incoming_siblings_context,
return_free_context, sibling_number - 1, return_string, next_uid
)
end
defp get_available_registers() do
["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([], _) do
[]
end
defp fetch_registers(candidates, free_registers) do
candidates_count = candidates |> Enum.count
free_registers_count = free_registers |> Enum.count
Enum.slice(free_registers, 0..candidates_count-1)
++ generate_ram_variables(candidates_count-free_registers_count)
end
defp generate_ram_variables(_number_needed) do
[]#TODO Implement this or it will fail someday
end
defp cleanup(raw_asm_code) do
s1 = Regex.replace(~r/\n+/, raw_asm_code, "\n")
s2 = Regex.replace(~r/[\t\r ]{2,}/, s1, "")
String.split(s2, "\n")
|> Enum.map(
fn line ->
if String.contains?(line, ":") do line
else " "<>line end
end)
|> Enum.join("\n")
end
end