2019-05-11 13:01:39 -05:00
|
|
|
defmodule Lytlang do
|
2019-11-15 11:19:38 -06:00
|
|
|
@root_module :Lytlang
|
|
|
|
|
2019-11-15 00:51:27 -06:00
|
|
|
def eval(string) do
|
|
|
|
{value, _} =
|
|
|
|
string
|
|
|
|
|> from_lytlang()
|
|
|
|
|> Code.eval_quoted()
|
2019-05-11 13:01:39 -05:00
|
|
|
|
2019-11-15 00:51:27 -06:00
|
|
|
value
|
|
|
|
end
|
2019-05-11 13:01:39 -05:00
|
|
|
|
2019-11-15 00:51:27 -06:00
|
|
|
def from_lytlang(string, binding \\ [], opts \\ []) do
|
2019-11-15 11:19:38 -06:00
|
|
|
string
|
|
|
|
|> String.to_charlist()
|
|
|
|
|> tokenize()
|
|
|
|
|> parse()
|
|
|
|
|> to_ast_node(binding, opts)
|
|
|
|
|> to_elixir_ast(opts)
|
2019-11-15 00:51:27 -06:00
|
|
|
end
|
2019-05-11 13:01:39 -05:00
|
|
|
|
2019-11-15 00:51:27 -06:00
|
|
|
def tokenize(string) do
|
|
|
|
{:ok, tokens, _} =
|
|
|
|
:lytlang_lexer.string(string)
|
2019-11-15 11:19:38 -06:00
|
|
|
|> IO.inspect(label: "Tokens")
|
2019-05-11 13:01:39 -05:00
|
|
|
|
2019-11-15 00:51:27 -06:00
|
|
|
tokens
|
2019-05-11 13:01:39 -05:00
|
|
|
end
|
|
|
|
|
2019-11-15 00:51:27 -06:00
|
|
|
def parse(tokens) do
|
|
|
|
{:ok, tree} =
|
|
|
|
:lytlang_parser.parse(tokens)
|
2019-11-15 11:19:38 -06:00
|
|
|
|> IO.inspect(label: "Parsed")
|
2019-05-11 14:00:46 -05:00
|
|
|
|
2019-11-15 00:51:27 -06:00
|
|
|
tree
|
|
|
|
end
|
2019-05-11 14:00:46 -05:00
|
|
|
|
2019-11-15 11:19:38 -06:00
|
|
|
@type astnode :: {any, {keyword, keyword, keyword}, list}
|
|
|
|
@type ex_astnode :: {any, keyword, any}
|
|
|
|
|
|
|
|
# @spec to_elixir_ast(astnode, keyword) :: ex_astnode
|
|
|
|
def to_elixir_ast(astnode, opts \\ [])
|
|
|
|
|
|
|
|
def to_elixir_ast(s, opts) when is_list(s) do
|
|
|
|
Enum.map(s, fn node ->
|
|
|
|
to_elixir_ast(node, opts)
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_elixir_ast({{:literal, expr}, {_meta, _binding, _ast_opts}, _leaves}, _opts) do
|
|
|
|
expr
|
|
|
|
|> IO.inspect(label: "EX AST")
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_elixir_ast({s, {_meta, binding, _ast_opts}, leaves}, _opts) do
|
|
|
|
{s, binding, to_elixir_ast(leaves)}
|
|
|
|
|> IO.inspect(label: "EX AST")
|
|
|
|
end
|
|
|
|
|
|
|
|
@spec to_ast_node(any, keyword, keyword) :: astnode
|
|
|
|
def to_ast_node(ast, binding \\ [], opts \\ [])
|
|
|
|
|
|
|
|
def to_ast_node([], binding, opts), do: {[], {binding, opts}}
|
2019-05-11 13:01:39 -05:00
|
|
|
|
2019-11-15 11:19:38 -06:00
|
|
|
def to_ast_node(expr_list, binding, opts) when is_list(expr_list) do
|
|
|
|
Enum.reduce(expr_list, {nil, {[], binding, opts}, []}, fn expr,
|
|
|
|
{_s, {_meta, binding, opts},
|
|
|
|
_operands} ->
|
|
|
|
{s, {nmeta, nbinding, nopts}, operands} = to_ast_node(expr, binding, opts)
|
2019-11-15 00:51:27 -06:00
|
|
|
|
2019-11-15 11:19:38 -06:00
|
|
|
{s, {nmeta, Keyword.merge(binding, nbinding), Keyword.merge(opts, nopts)}, operands}
|
|
|
|
end)
|
2019-05-11 13:01:39 -05:00
|
|
|
end
|
2019-11-15 00:51:27 -06:00
|
|
|
|
2019-11-15 11:19:38 -06:00
|
|
|
def to_ast_node({:list_op, line, op, operands}, binding, opts) when is_list(operands) do
|
|
|
|
{op, {[line: line], binding, opts},
|
|
|
|
[Enum.map(operands, fn o -> to_ast_node(o, binding, opts) end)]}
|
2019-11-15 00:51:27 -06:00
|
|
|
end
|
|
|
|
|
2019-11-15 11:19:38 -06:00
|
|
|
def to_ast_node({:binary_op, line, op, left, right}, binding, opts) do
|
|
|
|
{op, {[line: line], binding, opts}, [to_ast_node(left), to_ast_node(right)]}
|
2019-11-15 00:51:27 -06:00
|
|
|
end
|
|
|
|
|
2019-11-15 11:19:38 -06:00
|
|
|
def to_ast_node({:unary_op, line, op, left}, binding, opts) do
|
|
|
|
{op, {[line: line], binding, opts}, [to_ast_node(left)]}
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_ast_node({:var, line, left} = _expr, binding, opts) do
|
|
|
|
# TODO: probably need to look in bindings? or shadowing so maybe not?
|
|
|
|
to_ast_node({:unary_op, line, left, {:literal, line, @root_module}}, binding, opts)
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_ast_node({:match, line, left, right} = _expr, binding, opts) do
|
|
|
|
to_ast_node({:binary_op, line, :=, left, right}, binding, opts)
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_ast_node({:integer, line, val} = _expr, binding, opts) do
|
|
|
|
to_ast_node({:literal, line, val}, binding, opts)
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_ast_node({:literal, line, val} = _expr, binding, opts) do
|
|
|
|
{{:literal, val}, {[line: line], binding, opts}, []}
|
|
|
|
end
|
2019-05-11 13:01:39 -05:00
|
|
|
end
|
2019-11-15 00:51:27 -06:00
|
|
|
|
|
|
|
"""
|
|
|
|
-module(elixir).
|
|
|
|
-export([eval/1, from_elixir/1, from_erlang/1]).
|
|
|
|
|
|
|
|
eval(String) ->
|
|
|
|
{value, Value, _} = erl_eval:expr(from_elixir(String), []),
|
|
|
|
Value.
|
|
|
|
|
|
|
|
% Temporary to aid debugging
|
|
|
|
from_elixir(String) ->
|
|
|
|
transform(parse(String)).
|
|
|
|
|
|
|
|
% Temporary to aid debugging
|
|
|
|
from_erlang(String) ->
|
|
|
|
{ok, Tokens, _} = erl_scan:string(String),
|
|
|
|
{ok, [Form]} = erl_parse:parse_exprs(Tokens),
|
|
|
|
Form.
|
|
|
|
|
|
|
|
parse(String) ->
|
|
|
|
{ok, Tokens, _} = elixir_lexer:string(String),
|
|
|
|
{ok, ParseTree} = elixir_parser:parse(Tokens),
|
|
|
|
ParseTree.
|
|
|
|
|
|
|
|
transform({ binary_op, Line, Op, Left, Right }) ->
|
|
|
|
{op, Line, Op, transform(Left), transform(Right)};
|
|
|
|
|
|
|
|
transform({ unary_op, Line, Op, Right }) ->
|
|
|
|
{op, Line, Op, transform(Right)};
|
|
|
|
|
|
|
|
|
|
|
|
transform({ integer, _, _ } = Expr) -> Expr.
|
|
|
|
"""
|