From 58f9f43896d6fbd83fba51aceb9265ff009565b8 Mon Sep 17 00:00:00 2001 From: Daniel Flanagan Date: Tue, 19 Nov 2019 22:24:17 -0600 Subject: [PATCH] Interesting progress --- lib/lytlang.ex | 92 ++++++---------------------------- src/lytlang_lexer.xrl | 1 + src/lytlang_parser.yrl | 111 +++++++++++++++++++++++------------------ test/lytlang_test.exs | 5 +- 4 files changed, 83 insertions(+), 126 deletions(-) diff --git a/lib/lytlang.ex b/lib/lytlang.ex index d6b3442..0c6de13 100644 --- a/lib/lytlang.ex +++ b/lib/lytlang.ex @@ -1,109 +1,49 @@ defmodule Lytlang do - @root_module :Lytlang - def eval(string) do - {value, _} = + {value, _bindings_maybe} = string |> from_lytlang() - |> Code.eval_quoted() + |> eval_elixir() value end def from_lytlang( code, - binding \\ [context: @root_module, import: Kernel, import: Elixir], + meta \\ [], opts \\ [] ) - def from_lytlang(string, binding, opts) do + def from_lytlang(string, _binding, opts) do string + |> IO.inspect(label: "Lytlang Code") |> String.to_charlist() - |> tokenize() - |> parse() - |> to_ast_node(binding, opts) - |> to_elixir_ast(opts) - |> IO.inspect(label: "EX AST") + |> tokenize(opts) + |> parse(opts) end - def tokenize(string) do - {:ok, tokens, _} = + def tokenize(string, _opts \\ []) do + {:ok, tokens, _num_lines_maybe} = :lytlang_lexer.string(string) |> IO.inspect(label: "Tokens") tokens end - def parse(tokens) do - {:ok, tree} = + def parse(tokens, _opts \\ []) 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} + def eval_elixir(ast, opts \\ []) - # @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 - 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}, []} + def eval_elixir(ast, _opts) do + {:__block__, [], [ast]} + |> Code.eval_quoted() + |> IO.inspect() end end diff --git a/src/lytlang_lexer.xrl b/src/lytlang_lexer.xrl index d0c3fed..77153c3 100644 --- a/src/lytlang_lexer.xrl +++ b/src/lytlang_lexer.xrl @@ -19,6 +19,7 @@ Rules. - : { token, { '-', TokenLine } }. \* : { token, { '*', TokenLine } }. / : { token, { '/', TokenLine } }. +// : { token, { '//', TokenLine } }. \( : { token, { '(', TokenLine } }. \) : { token, { ')', TokenLine } }. = : { token, { '=', TokenLine } }. diff --git a/src/lytlang_parser.yrl b/src/lytlang_parser.yrl index 24864e8..a83adf7 100644 --- a/src/lytlang_parser.yrl +++ b/src/lytlang_parser.yrl @@ -4,24 +4,35 @@ Nonterminals expr assign_expr add_expr + sub_expr mult_expr + div_expr + idiv_expr unary_expr - fun_expr - body - stabber max_expr number unary_op add_op + sub_op mult_op + div_op + idiv_op . Terminals var float integer eol - '+' '-' '*' '/' '(' ')' '=' '->' + '+' '-' '*' '/' '//' '(' ')' '=' . Rootsymbol grammar. +Endsymbol '$end'. + +Right 100 '='. +Left 300 '+'. +Left 300 '-'. +Left 400 '*'. +Left 400 '/'. +Left 400 '//'. grammar -> expr_list : '$1'. grammar -> '$empty' : []. @@ -32,36 +43,6 @@ expr_list -> expr eol : ['$1']. expr_list -> eol expr_list : '$2'. 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 max_expr -> var : '$1'. max_expr -> number : '$1'. @@ -69,23 +50,55 @@ max_expr -> '(' expr ')' : '$2'. %% Numbers number -> float : '$1'. -number -> integer : '$1'. +number -> integer : ?e(3, '$1'). %% Unary operator 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'}). diff --git a/test/lytlang_test.exs b/test/lytlang_test.exs index ed15710..4d25af8 100644 --- a/test/lytlang_test.exs +++ b/test/lytlang_test.exs @@ -4,14 +4,17 @@ defmodule LytlangTest do test "arithmetic" do [ + {"2", 2}, + {"1 + 2", 3}, {"2 + 3 + 8", 13}, + {"(2 + 3) * 8", 40}, {"2 * 3 + 8", 14}, {"2 + 3 * 8", 26}, - {"(2 + 3) * 8", 40}, {"2 * (3 + 8)", 22}, {"2 + (3 * 8)", 26}, {"8 - 3", 5}, {"8 / 4", 2.0}, + {"8 // 4", 2}, {"a = 8\na + 9", 17}, {"2 + 3", 5} ]