lytlang/lib/transpiler.ex

94 lines
2.3 KiB
Elixir
Raw Normal View History

2019-05-11 14:00:46 -05:00
defmodule Lytlang.Transpiler do
@moduledoc """
Documentation for Lytlang.
"""
@spec transpile_file(String.t()) :: String.t()
def transpile_file(file_path) do
file_path
|> File.read!()
|> transpile_block()
end
@doc """
Transpiler!
## Examples
iex> Lytlang.Transpiler.transpile_block("!mod EmptyModule")
"defmodule EmptyModule do\\nend"
iex> Lytlang.Transpiler.transpile_block("!mod EmptyModule\\n\\tfn hello\\n\\t\\t:world")
"defmodule EmptyModule\\n\\tdef hello() do\\n\\t\\t:world\\n\\tend\\nend"
"""
@spec transpile_block(String.t(), Keyword.t()) :: String.t()
def transpile_block(s, _opts \\ []) do
s
|> split_lines()
|> Enum.map(&tokenize/1)
|> IO.inspect(label: "Tokens")
|> build_ast()
|> IO.inspect(label: "AST")
|> ast_to_elixir()
end
def initial_ast_state() do
%{
ast: {:__global, []}
}
end
def build_ast(token_lines, state \\ nil)
def build_ast([], state), do: state.ast |> Enum.reverse()
def build_ast([line_tokens | rest], state) do
state =
if !state do
initial_ast_state()
else
state
end
ast = state.ast
state =
case line_tokens do
["!mod", module_name | rest] ->
%{state | ast: [{:module, String.to_atom(module_name), []} | ast]}
["fn", fn_name | rest] ->
%{state | ast: [{:function, String.to_atom(fn_name), []} | ast]}
expr ->
expr |> IO.inspect(label: "lyt expression")
%{state | ast: [expr |> Enum.join(" ") | ast]}
end
build_ast(rest, state)
end
def ast_to_elixir([], state),
do: state.elixir_code |> Enum.filter(&(String.trim(&1) != "")) |> Enum.join("\n")
def ast_to_elixir([leaf | rest], state \\ %{elixir_code: []}) do
child = fn {head, tail}, state -> [head | ast_to_elixir(rest, state)] ++ [tail] end
case leaf do
:global -> child.({"", ""}, state)
{:module, mod, []} -> child.({"defmodule #{to_string(mod)} do", "end"}, state)
{:fn, fname, []} -> child.({"def #{to_string(fname)} do", "end"}, state)
expr when is_binary(expr) -> [expr]
end
end
def split_lines(s) do
String.split(s, "\n", trim: true)
end
def tokenize(s) do
Regex.split(~r/\s/, s, true)
end
end