Packages

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

Current section

Files

Jump to
exc lib code_generator.ex
Raw

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
{assembly_code, _result_my_context, _result_free_context} = generate_raw_string_code(abstract_syntax_tree)
assembly_code
|> cleanup()
|> assembly()
|> check_for_verbose(verbose)
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
["ebx", "ecx", "edx", "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
Regex.replace(~r/\n+/, raw_asm_code, "\n")
end
defp assembly(cleaned_asm_code) do
base_header = """
.section __TEXT,__text,regular,pure_instructions
.p2align 4, 0x90
"""
main_header = """
.globl _main
_main:
#{cleaned_asm_code}
ret
"""
base_header <> main_header
end
defp check_for_verbose(assembly_code, verbose) do
if verbose do
Helpers.Printer.print_element(Helpers.StringElements.rc, assembly_code)
end
assembly_code
end
end