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