WIP parsing
This commit is contained in:
parent
8e4754b932
commit
f09f9d8edf
3 changed files with 104 additions and 27 deletions
|
@ -16,14 +16,16 @@ defmodule Lytlang do
|
|||
:world
|
||||
end
|
||||
|
||||
@spec transpile_file(String.t()) :: String.t()
|
||||
def transpile_file(file_path) do
|
||||
file_path
|
||||
|> File.read!()
|
||||
|> transpile_block()
|
||||
end
|
||||
@doc """
|
||||
Echo.
|
||||
|
||||
def transpile_block(s) do
|
||||
"#{s}"
|
||||
## Examples
|
||||
|
||||
iex> Lytlang.echo("world")
|
||||
"world"
|
||||
|
||||
"""
|
||||
def echo(s) do
|
||||
s
|
||||
end
|
||||
end
|
||||
|
|
93
lib/transpiler.ex
Normal file
93
lib/transpiler.ex
Normal file
|
@ -0,0 +1,93 @@
|
|||
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
|
|
@ -1,27 +1,9 @@
|
|||
defmodule LytlangTest do
|
||||
use ExUnit.Case
|
||||
doctest Lytlang
|
||||
doctest Lytlang.Transpiler
|
||||
|
||||
test "greets the world" do
|
||||
assert Lytlang.hello() == :world
|
||||
end
|
||||
|
||||
test "transpiles module" do
|
||||
input = """
|
||||
!mod EmptyModule
|
||||
"""
|
||||
|
||||
output = """
|
||||
defmodule EmptyModule
|
||||
end
|
||||
"""
|
||||
|
||||
assert Lytlang.transpile_block(input) == output
|
||||
end
|
||||
|
||||
@tag :skip
|
||||
test "transpiles example tiny module file correctly" do
|
||||
output = File.read!("./test/fixtures/tiny.exs")
|
||||
assert Lytlang.transpile_file("./test/fixtures/tiny.lyt") == output
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue