Current section

Files

Jump to
hologram lib demo holograf transpiler generator.ex
Raw

lib/demo/holograf/transpiler/generator.ex

defmodule Hologram.Transpiler.Generator do
alias Hologram.Transpiler.AST.{AtomType, BooleanType, IntegerType, StringType}
alias Hologram.Transpiler.AST.{ListType, MapType, StructType}
alias Hologram.Transpiler.AST.{MapAccess}
alias Hologram.Transpiler.AST.{Call, Function, Module, Variable}
# PRIMITIVE TYPES
def generate(%AtomType{value: value}) do
generate_primitive_type(:atom, "'#{value}'")
end
def generate(%BooleanType{value: value}) do
generate_primitive_type(:boolean, "#{value}")
end
def generate(%IntegerType{value: value}) do
generate_primitive_type(:integer, "#{value}")
end
def generate(%StringType{value: value}) do
generate_primitive_type(:string, "'#{value}'")
end
defp generate_primitive_type(type, value) do
"{ type: '#{type}', value: #{value} }"
end
# DATA STRUCTURES
def generate(%MapType{data: data}) do
data = generate_map_data(data)
"{ type: 'map', data: #{data} }"
end
def generate(%StructType{module: module, data: data}) do
module = generate_module_fully_qualified_name(module)
data = generate_map_data(data)
"{ type: 'struct', module: '#{module}', data: #{data} }"
end
def generate_map_data(ast) do
fields =
Enum.map(ast, fn {k, v} ->
"'#{generate_object_key(k)}': #{generate(v)}"
end)
|> Enum.join(", ")
if fields != "" do
"{ #{fields} }"
else
"{}"
end
end
defp generate_object_key(%AtomType{value: value}) do
"~Hologram.Transpiler.AST.AtomType[#{value}]"
end
defp generate_object_key(%BooleanType{value: value}) do
"~Hologram.Transpiler.AST.BooleanType[#{value}]"
end
defp generate_object_key(%IntegerType{value: value}) do
"~Hologram.Transpiler.AST.IntegerType[#{value}]"
end
defp generate_object_key(%StringType{value: value}) do
"~Hologram.Transpiler.AST.StringType[#{value}]"
end
# OTHER
def generate(%Module{name: name} = module) do
functions =
aggregate_functions(module)
|> Enum.map(fn {k, v} ->
body =
case generate_function_body(v) do
"" ->
"{}\n"
exprs ->
"{\n#{exprs}}"
end
"static #{k}() #{body}\n"
end)
|> Enum.join("\n")
"""
class #{generate_class_name(name)} {
#{functions}
}
"""
end
defp aggregate_functions(module) do
Enum.reduce(module.functions, %{}, fn expr, acc ->
case expr do
%Function{name: name} = fun ->
if Map.has_key?(acc, name) do
Map.put(acc, name, acc[name] ++ [fun])
else
Map.put(acc, name, [fun])
end
_ ->
acc
end
end)
end
defp generate_function_body(variants) do
Enum.reduce(variants, "", fn variant, acc ->
statement = if acc == "", do: "if", else: "else if"
params = generate_function_params(variant)
vars = generate_function_vars(variant)
body = generate_function_expressions(variant)
code = """
#{statement} (Hologram.patternMatchFunctionArgs(#{params}, arguments)) {
#{vars}
#{body}
}
"""
acc <> code
end)
end
defp generate_function_expressions(variant) do
expr_count = Enum.count(variant.body)
Stream.with_index(variant.body)
|> Enum.map(fn {expr, idx} ->
return = if idx == expr_count - 1, do: "return ", else: ""
"#{return}#{generate(expr)};"
end)
|> Enum.join("\n")
end
defp generate_function_params(variant) do
params =
Enum.map(variant.params, fn param -> generate(param) end)
|> Enum.join(", ")
"[ #{params} ]"
end
defp generate_function_vars(variant) do
Stream.with_index(variant.bindings)
|> Enum.map(fn {binding, idx} ->
Enum.reduce(binding, "", fn access, accumulator ->
part =
case access do
%Variable{name: name} ->
"let #{name} = arguments[#{idx}]"
%MapAccess{key: key} ->
"['#{key}']"
end
accumulator <> part
end)
<> ";"
end)
|> Enum.join("\n")
end
def generate(%Variable{}) do
"{ type: 'variable' }"
end
def generate(%Call{module: module, function: function, params: params}) do
class = generate_class_name(module)
params =
Enum.map(params, fn param ->
case param do
%Variable{name: name} ->
name
_ ->
generate(param)
end
end)
|> Enum.join(", ")
"#{class}.#{function}(#{params})"
end
# HELPERS
defp generate_class_name(module) do
generate_module_fully_qualified_name(module)
|> String.replace(".", "")
end
defp generate_module_fully_qualified_name(module) do
Enum.join(module, ".")
end
end