defmodule Lytlang do @root_module :Lytlang def eval(string) do {value, _} = string |> from_lytlang() |> Code.eval_quoted() value end def from_lytlang(string, binding \\ [], opts \\ []) do string |> String.to_charlist() |> tokenize() |> parse() |> to_ast_node(binding, opts) |> to_elixir_ast(opts) end def tokenize(string) do {:ok, tokens, _} = :lytlang_lexer.string(string) |> IO.inspect(label: "Tokens") tokens end def parse(tokens) do {:ok, tree} = :lytlang_parser.parse(tokens) |> IO.inspect(label: "Parsed") tree end @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}} 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) {s, {nmeta, Keyword.merge(binding, nbinding), Keyword.merge(opts, nopts)}, operands} end) end 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)]} end 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)]} end 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 end """ -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. """