Interesting progress
This commit is contained in:
parent
db508c0d0f
commit
58f9f43896
4 changed files with 83 additions and 126 deletions
|
@ -1,109 +1,49 @@
|
||||||
defmodule Lytlang do
|
defmodule Lytlang do
|
||||||
@root_module :Lytlang
|
|
||||||
|
|
||||||
def eval(string) do
|
def eval(string) do
|
||||||
{value, _} =
|
{value, _bindings_maybe} =
|
||||||
string
|
string
|
||||||
|> from_lytlang()
|
|> from_lytlang()
|
||||||
|> Code.eval_quoted()
|
|> eval_elixir()
|
||||||
|
|
||||||
value
|
value
|
||||||
end
|
end
|
||||||
|
|
||||||
def from_lytlang(
|
def from_lytlang(
|
||||||
code,
|
code,
|
||||||
binding \\ [context: @root_module, import: Kernel, import: Elixir],
|
meta \\ [],
|
||||||
opts \\ []
|
opts \\ []
|
||||||
)
|
)
|
||||||
|
|
||||||
def from_lytlang(string, binding, opts) do
|
def from_lytlang(string, _binding, opts) do
|
||||||
string
|
string
|
||||||
|
|> IO.inspect(label: "Lytlang Code")
|
||||||
|> String.to_charlist()
|
|> String.to_charlist()
|
||||||
|> tokenize()
|
|> tokenize(opts)
|
||||||
|> parse()
|
|> parse(opts)
|
||||||
|> to_ast_node(binding, opts)
|
|
||||||
|> to_elixir_ast(opts)
|
|
||||||
|> IO.inspect(label: "EX AST")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def tokenize(string) do
|
def tokenize(string, _opts \\ []) do
|
||||||
{:ok, tokens, _} =
|
{:ok, tokens, _num_lines_maybe} =
|
||||||
:lytlang_lexer.string(string)
|
:lytlang_lexer.string(string)
|
||||||
|> IO.inspect(label: "Tokens")
|
|> IO.inspect(label: "Tokens")
|
||||||
|
|
||||||
tokens
|
tokens
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse(tokens) do
|
def parse(tokens, _opts \\ []) do
|
||||||
{:ok, tree} =
|
{:ok, [tree]} =
|
||||||
:lytlang_parser.parse(tokens)
|
:lytlang_parser.parse(tokens)
|
||||||
|> IO.inspect(label: "Parsed")
|
|> IO.inspect(label: "Parsed")
|
||||||
|
|
||||||
tree
|
tree
|
||||||
end
|
end
|
||||||
|
|
||||||
@type astnode :: {any, {keyword, keyword, keyword}, list}
|
def eval_elixir(ast, opts \\ [])
|
||||||
@type ex_astnode :: {any, keyword, any}
|
|
||||||
|
|
||||||
# @spec to_elixir_ast(astnode, keyword) :: ex_astnode
|
def eval_elixir(ast, _opts) do
|
||||||
def to_elixir_ast(astnode, opts \\ [])
|
{:__block__, [], [ast]}
|
||||||
|
|> Code.eval_quoted()
|
||||||
def to_elixir_ast(s, opts) when is_list(s) do
|
|> IO.inspect()
|
||||||
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
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_elixir_ast({s, {_meta, binding, _ast_opts}, leaves}, _opts) do
|
|
||||||
{s, binding, to_elixir_ast(leaves)}
|
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ Rules.
|
||||||
- : { token, { '-', TokenLine } }.
|
- : { token, { '-', TokenLine } }.
|
||||||
\* : { token, { '*', TokenLine } }.
|
\* : { token, { '*', TokenLine } }.
|
||||||
/ : { token, { '/', TokenLine } }.
|
/ : { token, { '/', TokenLine } }.
|
||||||
|
// : { token, { '//', TokenLine } }.
|
||||||
\( : { token, { '(', TokenLine } }.
|
\( : { token, { '(', TokenLine } }.
|
||||||
\) : { token, { ')', TokenLine } }.
|
\) : { token, { ')', TokenLine } }.
|
||||||
= : { token, { '=', TokenLine } }.
|
= : { token, { '=', TokenLine } }.
|
||||||
|
|
|
@ -4,24 +4,35 @@ Nonterminals
|
||||||
expr
|
expr
|
||||||
assign_expr
|
assign_expr
|
||||||
add_expr
|
add_expr
|
||||||
|
sub_expr
|
||||||
mult_expr
|
mult_expr
|
||||||
|
div_expr
|
||||||
|
idiv_expr
|
||||||
unary_expr
|
unary_expr
|
||||||
fun_expr
|
|
||||||
body
|
|
||||||
stabber
|
|
||||||
max_expr
|
max_expr
|
||||||
number
|
number
|
||||||
unary_op
|
unary_op
|
||||||
add_op
|
add_op
|
||||||
|
sub_op
|
||||||
mult_op
|
mult_op
|
||||||
|
div_op
|
||||||
|
idiv_op
|
||||||
.
|
.
|
||||||
|
|
||||||
Terminals
|
Terminals
|
||||||
var float integer eol
|
var float integer eol
|
||||||
'+' '-' '*' '/' '(' ')' '=' '->'
|
'+' '-' '*' '/' '//' '(' ')' '='
|
||||||
.
|
.
|
||||||
|
|
||||||
Rootsymbol grammar.
|
Rootsymbol grammar.
|
||||||
|
Endsymbol '$end'.
|
||||||
|
|
||||||
|
Right 100 '='.
|
||||||
|
Left 300 '+'.
|
||||||
|
Left 300 '-'.
|
||||||
|
Left 400 '*'.
|
||||||
|
Left 400 '/'.
|
||||||
|
Left 400 '//'.
|
||||||
|
|
||||||
grammar -> expr_list : '$1'.
|
grammar -> expr_list : '$1'.
|
||||||
grammar -> '$empty' : [].
|
grammar -> '$empty' : [].
|
||||||
|
@ -32,36 +43,6 @@ expr_list -> expr eol : ['$1'].
|
||||||
expr_list -> eol expr_list : '$2'.
|
expr_list -> eol expr_list : '$2'.
|
||||||
expr_list -> expr eol expr_list : ['$1'|'$3'].
|
expr_list -> expr eol expr_list : ['$1'|'$3'].
|
||||||
|
|
||||||
expr -> assign_expr : '$1'.
|
|
||||||
|
|
||||||
assign_expr -> add_expr '=' assign_expr :
|
|
||||||
{ match, ?line('$2'), '$1', '$3' }.
|
|
||||||
|
|
||||||
assign_expr -> add_expr : '$1'.
|
|
||||||
|
|
||||||
%% Arithmetic operations
|
|
||||||
add_expr -> add_expr add_op mult_expr :
|
|
||||||
{ binary_op, ?line('$1'), ?op('$2'), '$1', '$3' }.
|
|
||||||
|
|
||||||
add_expr -> mult_expr : '$1'.
|
|
||||||
|
|
||||||
mult_expr -> mult_expr mult_op unary_expr :
|
|
||||||
{ binary_op, ?line('$1'), ?op('$2'), '$1', '$3' }.
|
|
||||||
|
|
||||||
mult_expr -> unary_expr : '$1'.
|
|
||||||
|
|
||||||
unary_expr -> unary_op max_expr :
|
|
||||||
{ unary_op, ?line('$1'), ?op('$1'), '$2' }.
|
|
||||||
|
|
||||||
unary_expr -> max_expr : '$1'.
|
|
||||||
|
|
||||||
fun_expr -> stabber eol body :
|
|
||||||
{ 'fn', ?line('$1')
|
|
||||||
, { clauses, [ { clause, ?line('$1'), [], [], '$3' } ] }
|
|
||||||
}.
|
|
||||||
|
|
||||||
fun_expr -> max_expr : '$1'.
|
|
||||||
|
|
||||||
%% Minimum expressions
|
%% Minimum expressions
|
||||||
max_expr -> var : '$1'.
|
max_expr -> var : '$1'.
|
||||||
max_expr -> number : '$1'.
|
max_expr -> number : '$1'.
|
||||||
|
@ -69,23 +50,55 @@ max_expr -> '(' expr ')' : '$2'.
|
||||||
|
|
||||||
%% Numbers
|
%% Numbers
|
||||||
number -> float : '$1'.
|
number -> float : '$1'.
|
||||||
number -> integer : '$1'.
|
number -> integer : ?e(3, '$1').
|
||||||
|
|
||||||
%% Unary operator
|
%% Unary operator
|
||||||
unary_op -> '+' : '$1'.
|
unary_op -> '+' : '$1'.
|
||||||
unary_op -> '-' : '$1'.
|
unary_op -> '-' : '$1'.
|
||||||
|
|
||||||
%% Addition operators
|
|
||||||
add_op -> '+' : '$1'.
|
|
||||||
add_op -> '-' : '$1'.
|
|
||||||
|
|
||||||
%% Multiplication operators
|
|
||||||
mult_op -> '*' : '$1'.
|
|
||||||
mult_op -> '/' : '$1'.
|
|
||||||
|
|
||||||
Erlang code.
|
|
||||||
|
|
||||||
-define(op(Node), element(1, Node)).
|
|
||||||
-define(line(Node), element(2, Node)).
|
|
||||||
-define(char(Node), element(3, Node)).
|
|
||||||
|
|
||||||
|
%% Infix Operators
|
||||||
|
add_op -> '+' : '$1'.
|
||||||
|
sub_op -> '-' : '$1'.
|
||||||
|
mult_op -> '*' : '$1'.
|
||||||
|
div_op -> '/' : '$1'.
|
||||||
|
idiv_op -> '//' : '$1'.
|
||||||
|
|
||||||
|
expr -> mult_expr : '$1'.
|
||||||
|
expr -> div_expr : '$1'.
|
||||||
|
expr -> idiv_expr : '$1'.
|
||||||
|
expr -> assign_expr : '$1'.
|
||||||
|
expr -> add_expr : '$1'.
|
||||||
|
expr -> sub_expr : '$1'.
|
||||||
|
expr -> unary_expr : '$1'.
|
||||||
|
expr -> max_expr : '$1'.
|
||||||
|
|
||||||
|
assign_expr -> var '=' expr :
|
||||||
|
{ '=', ?meta('$2'), [?var('$1'), '$3'] }.
|
||||||
|
|
||||||
|
%% Arithmetic operations
|
||||||
|
add_expr -> expr add_op expr :
|
||||||
|
{ '+', ?meta('$2'), ['$1', '$3'] }.
|
||||||
|
|
||||||
|
sub_expr -> expr sub_op expr :
|
||||||
|
{ '-', ?meta('$2'), ['$1', '$3'] }.
|
||||||
|
|
||||||
|
mult_expr -> expr mult_op expr :
|
||||||
|
{ '*', ?meta('$2'), ['$1', '$3'] }.
|
||||||
|
|
||||||
|
div_expr -> expr div_op expr :
|
||||||
|
{ '/', ?meta('$2'), ['$1', '$3'] }.
|
||||||
|
|
||||||
|
idiv_expr -> expr idiv_op expr :
|
||||||
|
{ 'div', ?meta('$2'), ['$1', '$3'] }.
|
||||||
|
|
||||||
|
unary_expr -> unary_op expr :
|
||||||
|
{ unary_op, ?meta('$1'), ?op('$1'), '$2' }.
|
||||||
|
|
||||||
|
Erlang code.
|
||||||
|
|
||||||
|
-define(e(N, Node), element(N, Node)).
|
||||||
|
-define(op(Node), ?e(1, Node)).
|
||||||
|
-define(char(Node), ?e(3, Node)).
|
||||||
|
-define(meta(Node), [?line(Node)]).
|
||||||
|
-define(line(Node), {line, ?e(2, Node)}).
|
||||||
|
-define(var(Node), {?e(3, Node), [], 'Lytlang'}).
|
||||||
|
|
|
@ -4,14 +4,17 @@ defmodule LytlangTest do
|
||||||
|
|
||||||
test "arithmetic" do
|
test "arithmetic" do
|
||||||
[
|
[
|
||||||
|
{"2", 2},
|
||||||
|
{"1 + 2", 3},
|
||||||
{"2 + 3 + 8", 13},
|
{"2 + 3 + 8", 13},
|
||||||
|
{"(2 + 3) * 8", 40},
|
||||||
{"2 * 3 + 8", 14},
|
{"2 * 3 + 8", 14},
|
||||||
{"2 + 3 * 8", 26},
|
{"2 + 3 * 8", 26},
|
||||||
{"(2 + 3) * 8", 40},
|
|
||||||
{"2 * (3 + 8)", 22},
|
{"2 * (3 + 8)", 22},
|
||||||
{"2 + (3 * 8)", 26},
|
{"2 + (3 * 8)", 26},
|
||||||
{"8 - 3", 5},
|
{"8 - 3", 5},
|
||||||
{"8 / 4", 2.0},
|
{"8 / 4", 2.0},
|
||||||
|
{"8 // 4", 2},
|
||||||
{"a = 8\na + 9", 17},
|
{"a = 8\na + 9", 17},
|
||||||
{"2 + 3", 5}
|
{"2 + 3", 5}
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in a new issue