diff --git a/CHANGELOG.md b/CHANGELOG.md index f12e4084..b61ae1ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ -# v0.10.0-dev +# v0.10.0 +* Enhancements + * Added `env` option for `ElixirScript.transpile` adding macros for compilation + * Fixed `case` implementation to add `this` to call + * Updated `Kernel` module to translate some functions to it's JavaScript equivalent + * Added `Logger` that translates Logger functions to console # v0.9.0 * Enhancements diff --git a/README.md b/README.md index db7280f3..ab205691 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ ElixirScript can be used in the following ways: * If using as part of a project, you can add the following to your deps ```elixir - {:elixir_script, "~> 0.8"} + {:elixir_script, "~> 0.10"} ``` From there you can either use the ElixirScript module directly or the mix command, `mix ex2js` @@ -101,6 +101,38 @@ import Kernel from 'js/__lib/kernel' ["Erlang.list(1,2,3,4)"] ``` +# Macros + +Macros can be used when using ElixirScript as a library if the Macros are loaded into the current environment or if you give it a custom environment with the `env` option + +```elixir +#module with macro defined +defmodule Math do + defmacro squared(x) do + quote do + unquote(x) * unquote(x) + end + end +end + +#create an env with the module required if not already in the current enviroment +def make_custom_env do + require Logger + require Math + __ENV__ +end + + +#Now pass it to `ElixirScript.tranpile` +ElixirScript.transpile(""" + Math.squared(1) +""", env: make_custom_env) + +# returns ["1 * 1"] +``` + +You should be able to use `use` in modules now as well, but modules that have `__using__` macros must also be require'd so that they can be expanded. + # Limitations @@ -122,11 +154,8 @@ The following are defined but incomplete: #### Most of the Standard Library isn't defined yet A lot of functions in the Kernel module are implemented. The Enum, Atom, List, Tuple, Logger, and Range modules are either fully defined are not complete. The rest still need to be implemented. Some modules like System or File may not be useful or function in the browser and may end up being only useful when using ElixirScript outside of the browser. -#### No Macros -Not sure how this would be implemented right now, but looking for ideas. ### Example projects - * [todo-elixirscript](https://github.com/bryanjos/example) The TodoMVC app using ElixirScript and Phoenix. * [color_bar_spike](https://github.com/bryanjos/color_bar_spike) A canvas drawing example using ElixirScript, React and Delorean diff --git a/lib/elixir_script.ex b/lib/elixir_script.ex index ba3516c6..c518312e 100644 --- a/lib/elixir_script.ex +++ b/lib/elixir_script.ex @@ -3,6 +3,7 @@ defmodule ElixirScript do alias ElixirScript.Translator.JSModule alias ESTree.Tools.Builder alias ESTree.Tools.Generator + require Logger @moduledoc """ Transpiles Elixir into JavaScript. @@ -15,10 +16,12 @@ defmodule ElixirScript do that controls transpiler output. Available options are: - * include_path: a boolean controlling whether to return just the JavaScript code + * `:include_path` - a boolean controlling whether to return just the JavaScript code or a tuple of the file name and the JavaScript code - * root: a binary path prepended to the path of the standard lib imports if needed + * `:root` - a binary path prepended to the path of the standard lib imports if needed + * `:env` - a Macro.env struct to use. This is most useful when using macros. Make sure that the + given env has the macros required. Defaults to __ENV__. """ @doc """ @@ -38,8 +41,9 @@ defmodule ElixirScript do def transpile_quoted(quoted, opts \\ []) do include_path = Dict.get(opts, :include_path, false) root = Dict.get(opts, :root) + env = Dict.get(opts, :env, __ENV__) - case Translator.translate(quoted) do + case Translator.translate(quoted, env) do modules when is_list(modules) -> List.flatten(modules) |> Enum.map(fn(x) -> @@ -59,13 +63,14 @@ defmodule ElixirScript do def transpile_path(path, opts \\ []) do include_path = Dict.get(opts, :include_path, false) root = Dict.get(opts, :root) + env = Dict.get(opts, :env, __ENV__) path |> Path.wildcard |> Enum.map(fn(x) -> File.read!(x) |> Code.string_to_quoted! - |> Translator.translate + |> Translator.translate(env) end) |> List.flatten |> Enum.map(fn(x) -> diff --git a/lib/elixir_script/cli.ex b/lib/elixir_script/cli.ex index 454c8b9f..c090b232 100644 --- a/lib/elixir_script/cli.ex +++ b/lib/elixir_script/cli.ex @@ -1,23 +1,23 @@ defmodule ElixirScript.CLI do @moduledoc false + @switches [ + output: :binary, elixir: :boolean, root: :binary, + help: :boolean + ] + + @aliases [ + o: :output, ex: :elixir, h: :help, r: :root + ] + def main(argv) do argv |> parse_args |> process end - def parse_args(args) do - switches = [ - output: :binary, elixir: :boolean, root: :binary, - help: :boolean - ] - - aliases = [ - o: :output, ex: :elixir, h: :help, r: :root - ] - - parse = OptionParser.parse(args, switches: switches, aliases: aliases) + def parse_args(args) do + parse = OptionParser.parse(args, switches: @switches, aliases: @aliases) case parse do { [help: true] , _ , _ } -> :help @@ -50,7 +50,7 @@ defmodule ElixirScript.CLI do def do_process(input, options) do transpile_opts = [ root: options[:root], - include_path: options[:output] != nil + include_path: options[:output] != nil ] transpile_output = case options[:elixir] do @@ -78,7 +78,7 @@ defmodule ElixirScript.CLI do defp options_contains_unknown_values(options) do Enum.any?(options, fn({key, _value}) -> - if key in [:output, :elixir, :root, :help] do + if key in Keyword.keys(@switches) do false else true diff --git a/lib/elixir_script/lib/kernel.ex b/lib/elixir_script/lib/kernel.ex new file mode 100644 index 00000000..11044a66 --- /dev/null +++ b/lib/elixir_script/lib/kernel.ex @@ -0,0 +1,208 @@ +defmodule ElixirScript.Lib.Kernel do + @moduledoc false + alias ESTree.Tools.Builder, as: JS + alias ElixirScript.Translator + alias ElixirScript.Translator.Function + alias ElixirScript.Translator.Expression + alias ElixirScript.Translator.If + alias ElixirScript.Translator.Raise + + def translate_kernel_function(name, params, env) do + do_translate({name, [], params}, env) + end + + defp do_translate({operator, _, [value]}, env) when operator in [:-, :!, :+] do + Expression.make_unary_expression(operator, value, env) + end + + defp do_translate({operator, _, [left, right]}, env) when operator in [:+, :-, :/, :*, :==, :!=, :&&, :||, :>, :<, :>=, :<=, :===, :!==] do + Expression.make_binary_expression(operator, left, right, env) + end + + defp do_translate({:<>, _, [left, right]}, env) do + Expression.make_binary_expression(:+, left, right, env) + end + + defp do_translate({:++, _, [left, right]}, env) do + JS.call_expression( + JS.member_expression( + Translator.translate(left, env), + JS.identifier(:concat) + ), + [ + Translator.translate(right, env), + ] + ) + end + + defp do_translate({:.., _, [first, last]}, _) do + Translator.translate(quote do: Range.(unquote(first), unquote(last))) + end + + defp do_translate({:abs, _, [number]}, env) do + JS.call_expression( + JS.member_expression( + JS.identifier(:Map), + JS.identifier(:abs) + ), + [Translator.translate(number, env)] + ) + end + + defp do_translate({:apply, _, [fun, args]}, env) do + JS.call_expression( + JS.member_expression( + Translator.translate(fun, env), + JS.identifier(:apply) + ), + [JS.identifier(:this)] ++ Enum.map(args, &Translator.translate(&1, env)) + ) + end + + defp do_translate({:apply, _, [module, fun, args]}, env) do + JS.call_expression( + JS.member_expression( + JS.member_expression( + Translator.translate(module, env), + Translator.translate(fun, env) + ), + JS.identifier(:apply) + ), + [JS.identifier(:this)] ++ Enum.map(args, &Translator.translate(&1, env)) + ) + end + + defp do_translate({:and, _, [left, right]}, env) do + Expression.make_binary_expression(:&&, left, right, env) + end + + defp do_translate({:div, _, [left, right]}, env) do + Expression.make_binary_expression(:/, left, right, env) + end + + defp do_translate({:or, _, [left, right]}, env) do + Expression.make_binary_expression(:||, left, right, env) + end + + defp do_translate({:not, _, [value]}, env) do + Expression.make_unary_expression(:!, value, env) + end + + defp do_translate({:rem, _, [left, right]}, env) do + Expression.make_binary_expression(:%, left, right, env) + end + + defp do_translate({:round, _, [value]}, env) do + JS.call_expression( + JS.member_expression( + JS.identifier(:Math), + JS.identifier(:round) + ), + [Translator.translate(value, env)] + ) + end + + defp do_translate({:self, _, []}, _) do + JS.identifier(:self) + end + + defp do_translate({:tuple_size, _, [tuple]}, env) do + JS.member_expression( + JS.member_expression( + Translator.translate(tuple, env), + JS.identifier(:__tuple__) + ), + JS.identifier(:length) + ) + end + + defp do_translate({:map_size, _, [map]}, env) do + JS.member_expression( + JS.call_expression( + JS.member_expression( + JS.identifier(:Object), + JS.identifier(:keys) + ), + [Translator.translate(map, env)] + ), + JS.identifier(:length) + ) + end + + defp do_translate({:max, _, params}, env) do + JS.call_expression( + JS.member_expression( + JS.identifier(:Math), + JS.identifier(:max) + ), + Enum.map(params, &Translator.translate(&1, env)) + ) + end + + defp do_translate({:min, _, params}, env) do + JS.call_expression( + JS.member_expression( + JS.identifier(:Math), + JS.identifier(:min) + ), + Enum.map(params, &Translator.translate(&1, env)) + ) + end + + defp do_translate({:if, _, [test, blocks]}, _) do + If.make_if(test, blocks) + end + + defp do_translate({:|>, _, [left, right]}, _) do + case right do + {{:., meta, [module, fun]}, meta2, params} -> + Translator.translate({{:., meta, [module, fun]}, meta2, [left] ++ params}) + {fun, meta, params} -> + Translator.translate({fun, meta, [left] ++ params}) + end + end + + defp do_translate({:hd, _, [list]}, env) do + JS.member_expression( + Translator.translate(list, env), + JS.identifier(0), + true + ) + end + + defp do_translate({:tl, _, [list]}, env) do + JS.call_expression( + JS.member_expression( + Translator.translate(list, env), + JS.identifier(:splice) + ), + [1] + ) + end + + defp do_translate({:length, _, [list]}, env) when is_list(list) do + JS.member_expression( + Translator.translate(list, env), + JS.identifier(:length) + ) + end + + defp do_translate({:raise, _, [alias_info, attributes]}, _) do + {_, _, name} = alias_info + + Raise.throw_error(name, attributes) + end + + defp do_translate({:raise, _, [message]}, _) do + Raise.throw_error(message) + end + + defp do_translate({:to_string, _, [param]}, _) when is_binary(param) do + Translator.translate(param) + end + + defp do_translate({name, _, params}, env) do + Function.make_function_call(:Kernel, name, params, env) + end + +end \ No newline at end of file diff --git a/lib/elixir_script/lib/logger.ex b/lib/elixir_script/lib/logger.ex new file mode 100644 index 00000000..c6aed394 --- /dev/null +++ b/lib/elixir_script/lib/logger.ex @@ -0,0 +1,24 @@ +defmodule ElixirScript.Lib.Logger do + @moduledoc false + alias ESTree.Tools.Builder, as: JS + alias ElixirScript.Translator + + + def make_logger(:log, params, env) do + do_make_logger(hd(params), tl(params), env) + end + + def make_logger(level, params, env) do + do_make_logger(level, params, env) + end + + defp do_make_logger(level, params, env) do + JS.call_expression( + JS.member_expression( + JS.identifier("console"), + JS.identifier(level) + ), + [Translator.translate(hd(params), env)] + ) + end +end \ No newline at end of file diff --git a/lib/elixir_script/translator/pattern_matching/match.ex b/lib/elixir_script/pattern_matching/match.ex similarity index 98% rename from lib/elixir_script/translator/pattern_matching/match.ex rename to lib/elixir_script/pattern_matching/match.ex index 3835bdcb..b787dc5b 100644 --- a/lib/elixir_script/translator/pattern_matching/match.ex +++ b/lib/elixir_script/pattern_matching/match.ex @@ -1,4 +1,4 @@ -defmodule ElixirScript.Translator.PatternMatching.Match do +defmodule ElixirScript.PatternMatching.Match do alias ESTree.Tools.Builder, as: JS alias ElixirScript.Translator alias ElixirScript.Translator.Utils diff --git a/lib/elixir_script/preprocess/aliases.ex b/lib/elixir_script/preprocess/aliases.ex index a4cc4544..c54b4409 100644 --- a/lib/elixir_script/preprocess/aliases.ex +++ b/lib/elixir_script/preprocess/aliases.ex @@ -29,7 +29,7 @@ defmodule ElixirScript.Preprocess.Aliases do #the "Hello.World" would be placed in a set and later used for building an alias """ - def process(ast) do + def process(ast, env) do #These will always be added stdlib = HashSet.new @@ -42,36 +42,49 @@ defmodule ElixirScript.Preprocess.Aliases do state = %{ add: HashSet.new, defined: HashSet.new, stdlib: stdlib } {new_ast, state } = Macro.prewalk(ast, state, fn(x, acc) -> - process_aliases(x, acc) + process_aliases(x, acc, env) end) { new_ast, state.add, state.stdlib } end - def process_aliases({:alias, _, [{:__aliases__, _, _name}, [as: {:__aliases__, _, alias_name}]]} = ast, state) do + def process_aliases({:alias, _, [{:__aliases__, _, _name}, [as: {:__aliases__, _, alias_name}]]} = ast, state, _) do { ast, %{state | defined: HashSet.put(state.defined, List.last(alias_name)) } } end - def process_aliases({:alias, _, [{:__aliases__, _, name}]} = ast, state) do + def process_aliases({:alias, _, [{:__aliases__, _, name}]} = ast, state, _) do { ast, %{state | defined: HashSet.put(state.defined, List.last(name)) } } end - def process_aliases({{:., _, [{:__aliases__, _, aliases}, _]}, _, _} = ast, state) when aliases in @standard_libs do - {ast, %{ state | stdlib: HashSet.put(state.stdlib, List.last(aliases)) }} - end - def process_aliases({{:., meta1, [{:__aliases__, meta2, aliases}, function]}, meta3, params}, state) do - if HashSet.member?(state.defined, List.last(aliases)) do - { {{:., meta1, [{:__aliases__, meta2, List.last(aliases) |> List.wrap }, function]}, meta3, params}, state } + def process_aliases({{:., _, [{:__aliases__, _, aliases}, _]}, _, _} = ast, state, env) when aliases in @standard_libs do + expanded_ast = Macro.expand(ast, env) + if expanded_ast == ast do + {ast, %{ state | stdlib: HashSet.put(state.stdlib, List.last(aliases)) }} else - { - {{:., meta1, [{:__aliases__, meta2, List.last(aliases) |> List.wrap }, function]}, meta3, params}, + process_aliases(expanded_ast, state, env) + end + end + + def process_aliases({{:., meta1, [{:__aliases__, meta2, aliases}, function]}, meta3, params} = ast, state, env) do + expanded_ast = Macro.expand(ast, env) + + if expanded_ast == ast do + new_ast = {{:., meta1, [{:__aliases__, meta2, List.last(aliases) |> List.wrap }, function]}, meta3, params} + + new_state = if HashSet.member?(state.defined, List.last(aliases)) do + state + else %{ state | add: HashSet.put(state.add, aliases) } - } + end + + { new_ast, new_state } + else + process_aliases(expanded_ast, state, env) end end - def process_aliases(ast, state) do + def process_aliases(ast, state, env) do {ast, state} end end \ No newline at end of file diff --git a/lib/elixir_script/preprocess/using.ex b/lib/elixir_script/preprocess/using.ex new file mode 100644 index 00000000..1044bc15 --- /dev/null +++ b/lib/elixir_script/preprocess/using.ex @@ -0,0 +1,24 @@ +defmodule ElixirScript.Preprocess.Using do + + + def process(ast, env) do + Macro.prewalk(ast, fn(x) -> + process_using(x, env) + end) + end + + def process_using({:use, _, _} = ast, env) do + ast + |> Macro.expand(env) + |> expand__using__(env) + end + + def process_using(ast, _) do + ast + end + + defp expand__using__({:__block__, _, [{:require, _, _}, {{:., _, [_, :__using__]}, _, _} = using_ast]}, env) do + Macro.expand_once(using_ast, env) + end + +end \ No newline at end of file diff --git a/lib/elixir_script/translator.ex b/lib/elixir_script/translator.ex index 43cfaaef..05f2791d 100644 --- a/lib/elixir_script/translator.ex +++ b/lib/elixir_script/translator.ex @@ -6,91 +6,92 @@ defmodule ElixirScript.Translator do alias ElixirScript.Translator.Assignment alias ElixirScript.Translator.Map alias ElixirScript.Translator.Struct - alias ElixirScript.Translator.Raise alias ElixirScript.Translator.Function alias ElixirScript.Translator.Capture - alias ElixirScript.Translator.Expression alias ElixirScript.Translator.Import - alias ElixirScript.Translator.If alias ElixirScript.Translator.Cond alias ElixirScript.Translator.Case alias ElixirScript.Translator.For alias ElixirScript.Translator.Try alias ElixirScript.Translator.Block + alias ElixirScript.Translator.Struct alias ElixirScript.Translator.Module alias ElixirScript.Translator.Utils alias ElixirScript.Translator.Bitstring alias ElixirScript.Translator.Receive alias ElixirScript.Translator.Quote - alias ElixirScript.Translator.Kernel, as: ExKernel + alias ElixirScript.Translator.Utils + alias ElixirScript.Lib.Logger + alias ElixirScript.Lib.Kernel, as: KernelLib + alias ESTree.Tools.Builder, as: JS @doc """ Translates Elixir AST to JavaScript AST """ - def translate(ast) do - do_translate(ast) + def translate(ast, env \\ __ENV__) do + do_translate(ast, env) end - defp do_translate(ast) when is_number(ast) or is_binary(ast) or is_boolean(ast) or is_nil(ast) do + defp do_translate(ast, _) when is_number(ast) or is_binary(ast) or is_boolean(ast) or is_nil(ast) do Primitive.make_literal(ast) end - defp do_translate(ast) when is_atom(ast) do + defp do_translate(ast, _) when is_atom(ast) do Primitive.make_atom(ast) end - defp do_translate(ast) when is_list(ast) do - Primitive.make_list(ast) + defp do_translate(ast, env) when is_list(ast) do + Primitive.make_list(ast, env) end - defp do_translate({ one, two }) do - Primitive.make_tuple({one, two}) + defp do_translate({ one, two }, env) do + Primitive.make_tuple({one, two}, env) end - defp do_translate({:&, [], [number]}) when is_number(number) do + defp do_translate({:&, [], [number]}, _) when is_number(number) do Primitive.make_identifier(String.to_atom("__#{number}")) end - defp do_translate({:&, _, [{:/, _, [{{:., _, [{:__aliases__, _, module_name}, function_name]}, _, []}, arity]}]}) do + defp do_translate({:&, _, [{:/, _, [{{:., _, [{:__aliases__, _, module_name}, function_name]}, _, []}, arity]}]}, env) do function_name = Utils.filter_name(function_name) - Capture.make_capture(List.last(module_name), function_name, arity) + Capture.make_capture(List.last(module_name), function_name, arity, env) end - defp do_translate({:&, _, [{:/, _, [{function_name, _, _}, arity]}]}) do + defp do_translate({:&, _, [{:/, _, [{function_name, _, _}, arity]}]}, env) do function_name = Utils.filter_name(function_name) - Capture.make_capture(function_name, arity) + Capture.make_capture(function_name, arity, env) end - defp do_translate({:&, _, body}) do + defp do_translate({:&, _, body}, env) do params = Capture.find_value_placeholders(body) |> List.flatten - Function.make_anonymous_function([{:->, [], [params, body]}]) + Function.make_anonymous_function([{:->, [], [params, body]}], env) end - defp do_translate({:@, _, [{name, _, [value]}]}) do + defp do_translate({:@, _, [{name, _, [value]}]}, env) do name = Utils.filter_name(name) - Module.make_attribute(name, value) + Module.make_attribute(name, value, env) end - defp do_translate({:@, _, [{name, _, _}]}) do + defp do_translate({:@, _, [{name, _, _}]}, _) do name = Utils.filter_name(name) Primitive.make_identifier(name) end - defp do_translate({:%, _, [alias_info, data]}) do + defp do_translate({:%, _, [alias_info, data]}, _) do {_, _, name} = alias_info {_, _, data} = data Struct.make_struct(name, data) end - defp do_translate({:%{}, _, [{:|, _, [map, data]}]}) do + defp do_translate({:%{}, _, [{:|, _, [map, data]}]}, _) do Map.make_map_update(map, data); end - defp do_translate({:%{}, _, properties}) do + defp do_translate({:%{}, _, properties}, _) do Map.make_object(properties) end - defp do_translate({:<<>>, _, elements}) do + defp do_translate({:<<>>, _, elements}, _) do is_interpolated_string = Enum.all?(elements, fn(x) -> case x do b when is_binary(b) -> @@ -110,201 +111,192 @@ defmodule ElixirScript.Translator do end end - defp do_translate({{:., _, [Access, :get]}, _, [target, property]}) do + defp do_translate({{:., _, [{:__aliases__, _, [:Logger]}, function_name]}, _, params }, env) do + Logger.make_logger(function_name, params, env) + end + + defp do_translate({{:., _, [Access, :get]}, _, [target, property]}, _) do Map.make_get_property(target, property) end - defp do_translate({:., _, [module_name, function_name]}) do - Function.make_function_or_property_call(module_name, function_name) + defp do_translate({:., _, [module_name, function_name]} = ast, env) do + expanded_ast = Macro.expand(ast, env) + + if expanded_ast == ast do + Function.make_function_or_property_call(module_name, function_name, env) + else + translate(expanded_ast, env) + end end - defp do_translate({{:., _, [module_name, function_name]}, _, [] }) do - Function.make_function_or_property_call(module_name, function_name) + defp do_translate({{:., _, [module_name, function_name]}, _, [] } = ast, env) do + expanded_ast = Macro.expand(ast, env) + + if expanded_ast == ast do + Function.make_function_or_property_call(module_name, function_name, env) + else + translate(expanded_ast, env) + end end - defp do_translate({{:., _, [{:__aliases__, _, module_name}]}, _, params}) do - Function.make_function_call(hd(module_name), params) + defp do_translate({{:., _, [{:__aliases__, _, module_name}]}, _, params} = ast, env) do + expanded_ast = Macro.expand(ast, env) + if expanded_ast == ast do + Function.make_function_call(hd(module_name), params, env) + else + translate(expanded_ast, env) + end end - defp do_translate({{:., _, [module_name, function_name]}, _, params }) do - Function.make_function_call(module_name, function_name, params) + defp do_translate({{:., _, [module_name, function_name]}, _, params } = ast, env) do + if module_name == Kernel do + KernelLib.translate_kernel_function(function_name, params, env) + else + expanded_ast = Macro.expand(ast, env) + if expanded_ast == ast do + Function.make_function_call(module_name, function_name, params, env) + else + translate(expanded_ast, env) + end + end end - defp do_translate({:_, _, _}) do + defp do_translate({:_, _, _}, _env) do Primitive.make_identifier(:undefined) end - defp do_translate({:__aliases__, _, aliases}) do + defp do_translate({:__aliases__, _, aliases}, _) do Primitive.make_identifier(aliases) end - defp do_translate({:__block__, _, expressions }) do - Block.make_block(expressions) + defp do_translate({:__block__, _, expressions }, env) do + Block.make_block(expressions, env) end - defp do_translate({:__DIR__, _, _expressions }) do - ExKernel.make___DIR__() + defp do_translate({:__DIR__, _, _}, _) do + JS.call_expression( + JS.member_expression( + JS.member_expression( + JS.identifier(:Kernel), + JS.identifier(:SpecialForms) + ), + JS.identifier(:__DIR__) + ), + [] + ) end - defp do_translate({:try, _, [ blocks ]}) do - Try.make_try(blocks) + defp do_translate({:try, _, [ blocks ]}, env) do + Try.make_try(blocks, env) end - defp do_translate({:receive, _, [expressions] }) do - Receive.make_receive(expressions); + defp do_translate({:receive, _, [expressions] }, env) do + Receive.make_receive(expressions, env); end - defp do_translate({:super, _, _expressions }) do + defp do_translate({:super, _, _expressions }, _) do raise ElixirScript.UnsupportedError, :super end - defp do_translate({:__CALLER__, _, _expressions }) do + defp do_translate({:__CALLER__, _, _expressions }, _) do raise ElixirScript.UnsupportedError, :__CALLER__ end - defp do_translate({:__ENV__, _, _expressions }) do + defp do_translate({:__ENV__, _, _expressions }, _) do raise ElixirScript.UnsupportedError, :__ENV__ end - defp do_translate({:quote, _, [[do: expr]]}) do + defp do_translate({:quote, _, [[do: expr]]}, _) do Quote.make_quote([], expr) end - defp do_translate({:quote, _, [opts, [do: expr]]}) do + defp do_translate({:quote, _, [opts, [do: expr]]}, _) do Quote.make_quote(opts, expr) end - defp do_translate({:import, _, [{:__aliases__, _, module_name_list}]}) do + defp do_translate({:import, _, [{:__aliases__, _, module_name_list}]}, _) do Import.make_import(module_name_list, []) end - defp do_translate({:import, _, [{:__aliases__, _, module_name_list}, options ]}) do + defp do_translate({:import, _, [{:__aliases__, _, module_name_list}, options ]}, _) do Import.make_import(module_name_list, options) end - defp do_translate({:alias, _, [alias_info, options]}) do + defp do_translate({:alias, _, [alias_info, options]}, _) when is_tuple(alias_info) do Import.make_alias_import(alias_info, options) end - defp do_translate({:alias, _, [alias_info]}) do + defp do_translate({:alias, _, [alias_info]}, _) when is_tuple(alias_info) do Import.make_alias_import(alias_info, []) end - defp do_translate({:require, _, [alias_info, options]}) do + defp do_translate({:require, _, [alias_info, options]}, _) do Import.make_alias_import(alias_info, options) end - defp do_translate({:require, _, [alias_info]}) do + defp do_translate({:require, _, [alias_info]}, _) do Import.make_alias_import(alias_info, []) end - defp do_translate({:case, _, [condition, [do: clauses]]}) do - Case.make_case(condition, clauses) + defp do_translate({:case, _, [condition, [do: clauses]]}, env) do + Case.make_case(condition, clauses, env) end - defp do_translate({:cond, _, [[do: clauses]]}) do + defp do_translate({:cond, _, [[do: clauses]]}, _) do Cond.make_cond(clauses) end - defp do_translate({:for, _, generators}) do - For.make_for(generators) - end - - defp do_translate({:fn, _, clauses}) do - Function.make_anonymous_function(clauses) - end - - defp do_translate({:.., _, [first, last]}) do - ExKernel.make_range(first, last) - end - - defp do_translate({:{}, _, elements}) do - Primitive.make_tuple(elements) - end - - defp do_translate({operator, _, [value]}) when operator in [:-, :!] do - Expression.make_unary_expression(operator, value) - end - - defp do_translate({:=, _, [left, right]}) do - Assignment.make_assignment(left, right) - end - - defp do_translate({:<>, _, [left, right]}) do - Expression.make_binary_expression(:+, left, right) - end - - defp do_translate({:++, _, [left, right]}) do - ExKernel.concat_lists(left, right) + defp do_translate({:for, _, generators}, env) do + For.make_for(generators, env) end - defp do_translate({operator, _, [left, right]}) when operator in [:+, :-, :/, :*, :==, :!=, :&&, :||, :>, :<, :>=, :<=, :===] do - Expression.make_binary_expression(operator, left, right) + defp do_translate({:fn, _, clauses}, env) do + Function.make_anonymous_function(clauses, env) end - defp do_translate({:and, _, [left, right]}) do - Expression.make_binary_expression(:&&, left, right) + defp do_translate({:{}, _, elements}, env) do + Primitive.make_tuple(elements, env) end - defp do_translate({:or, _, [left, right]}) do - Expression.make_binary_expression(:||, left, right) + defp do_translate({:=, _, [left, right]}, env) do + Assignment.make_assignment(left, right, env) end - defp do_translate({function, _, [{:when, _, [{name, _, _params} | _guards] }, [do: _body]] } = ast) when function in [:def, :defp] do - Function.process_function(Utils.filter_name(name), [ast]) + defp do_translate({function, _, [{:when, _, [{name, _, _params} | _guards] }, [do: _body]] } = ast, env) when function in [:def, :defp] do + Function.process_function(Utils.filter_name(name), [ast], env) end - defp do_translate({function, _, [{name, _, _params}, [do: _body]]} = ast) when function in [:def, :defp] do - Function.process_function(Utils.filter_name(name), [ast]) + defp do_translate({function, _, [{name, _, _params}, [do: _body]]} = ast, env) when function in [:def, :defp] do + Function.process_function(Utils.filter_name(name), [ast], env) end - defp do_translate({:defstruct, _, attributes}) do + defp do_translate({:defstruct, _, attributes}, _) do Struct.make_defstruct(attributes) end - defp do_translate({:defexception, _, attributes}) do + defp do_translate({:defexception, _, attributes}, _) do Struct.make_defexception(attributes) end - defp do_translate({:raise, _, [alias_info, attributes]}) do - {_, _, name} = alias_info - - Raise.throw_error(name, attributes) - end - - defp do_translate({:raise, _, [message]}) do - Raise.throw_error(message) - end - - defp do_translate({:if, _, [test, blocks]}) do - If.make_if(test, blocks) - end - - defp do_translate({:defmodule, _, [{:__aliases__, _, module_name_list}, [do: body]]}) do - Module.make_module(module_name_list, body) - end - - defp do_translate({:|>, _, [left, right]}) do - case right do - {{:., meta, [module, fun]}, meta2, params} -> - translate({{:., meta, [module, fun]}, meta2, [left] ++ params}) - {fun, meta, params} -> - translate({fun, meta, [left] ++ params}) - end + defp do_translate({:defmodule, _, [{:__aliases__, _, module_name_list}, [do: body]]}, env) do + Module.make_module(module_name_list, body, env) end - defp do_translate({name, metadata, params}) when is_list(params) do - name = Utils.filter_name(name) - - case metadata[:import] do - Kernel -> - Function.make_function_call(:Kernel, name, params) - _ -> - Function.make_function_call(name, params) + defp do_translate({name, metadata, params} = ast, env) when is_list(params) do + if metadata[:import] == Kernel do + KernelLib.translate_kernel_function(name, params, env) + else + expanded_ast = Macro.expand(ast, env) + if expanded_ast == ast do + name = Utils.filter_name(name) + Function.make_function_call(name, params, env) + else + translate(expanded_ast) + end end end - defp do_translate({ name, _, _ }) do + defp do_translate({ name, _, _ }, _) do name = Utils.filter_name(name) Primitive.make_identifier(name) end diff --git a/lib/elixir_script/translator/assignment.ex b/lib/elixir_script/translator/assignment.ex index a4be27a0..cdb6845b 100644 --- a/lib/elixir_script/translator/assignment.ex +++ b/lib/elixir_script/translator/assignment.ex @@ -1,11 +1,10 @@ defmodule ElixirScript.Translator.Assignment do @moduledoc false - require Logger alias ESTree.Tools.Builder, as: JS alias ElixirScript.Translator - alias ElixirScript.Translator.PatternMatching.Match + alias ElixirScript.PatternMatching.Match - def make_assignment(left, right) do + def make_assignment(left, right, env) do { patterns, params } = Match.build_match([left]) declarator = JS.variable_declarator( @@ -15,7 +14,7 @@ defmodule ElixirScript.Translator.Assignment do JS.identifier("fun"), JS.identifier("bind") ), - [hd(patterns), Translator.translate(right)] + [hd(patterns), Translator.translate(right, env)] ) ) diff --git a/lib/elixir_script/translator/block.ex b/lib/elixir_script/translator/block.ex index b435e5d0..5bd958f9 100644 --- a/lib/elixir_script/translator/block.ex +++ b/lib/elixir_script/translator/block.ex @@ -3,8 +3,8 @@ defmodule ElixirScript.Translator.Block do alias ESTree.Tools.Builder alias ElixirScript.Translator - def make_block(expressions) do - Builder.block_statement(Enum.map(expressions, &Translator.translate(&1))) + def make_block(expressions, env) do + Builder.block_statement(Enum.map(expressions, &Translator.translate(&1, env))) end end \ No newline at end of file diff --git a/lib/elixir_script/translator/capture.ex b/lib/elixir_script/translator/capture.ex index 6221755d..92273491 100644 --- a/lib/elixir_script/translator/capture.ex +++ b/lib/elixir_script/translator/capture.ex @@ -1,9 +1,9 @@ defmodule ElixirScript.Translator.Capture do alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator.PatternMatching.Match + alias ElixirScript.PatternMatching.Match alias ElixirScript.Translator.Utils - def make_capture(function_name, arity) do + def make_capture(function_name, arity, _) do params = Enum.map(1..arity, fn(x) -> {String.to_atom("__#{x}"), [], ElixirScript.Translator.Capture} end) { patterns, params } = Match.build_match(params) @@ -25,7 +25,7 @@ defmodule ElixirScript.Translator.Capture do ]) end - def make_capture(module_name, function_name, arity) do + def make_capture(module_name, function_name, arity, env) do params = Enum.map(1..arity, fn(x) -> {String.to_atom("__#{x}"), [], ElixirScript.Translator.Capture} end) { patterns, params } = Match.build_match(params) @@ -38,7 +38,7 @@ defmodule ElixirScript.Translator.Capture do JS.block_statement([ JS.return_statement( JS.call_expression( - Utils.make_member_expression(module_name, function_name), + Utils.make_member_expression(module_name, function_name, env), params ) ) diff --git a/lib/elixir_script/translator/case.ex b/lib/elixir_script/translator/case.ex index 65c27b4a..ab6085cd 100644 --- a/lib/elixir_script/translator/case.ex +++ b/lib/elixir_script/translator/case.ex @@ -1,16 +1,16 @@ defmodule ElixirScript.Translator.Case do @moduledoc false - alias ESTree.Tools.Builder + alias ESTree.Tools.Builder, as: JS alias ElixirScript.Translator alias ElixirScript.Translator.Function - def make_case(condition, clauses) do - Builder.call_expression( - Builder.member_expression( - Function.make_anonymous_function(clauses), - Builder.identifier("call") + def make_case(condition, clauses, env) do + JS.call_expression( + JS.member_expression( + Function.make_anonymous_function(clauses, env), + JS.identifier("call") ), - [Translator.translate(condition)] + [JS.identifier(:this), Translator.translate(condition)] ) end end \ No newline at end of file diff --git a/lib/elixir_script/translator/expression.ex b/lib/elixir_script/translator/expression.ex index 8446e647..e3341266 100644 --- a/lib/elixir_script/translator/expression.ex +++ b/lib/elixir_script/translator/expression.ex @@ -1,15 +1,14 @@ defmodule ElixirScript.Translator.Expression do @moduledoc false - require Logger alias ESTree.Tools.Builder alias ElixirScript.Translator - def make_unary_expression(operator, expr) do - Builder.unary_expression(operator, true, Translator.translate(expr)) + def make_unary_expression(operator, expr, env) do + Builder.unary_expression(operator, true, Translator.translate(expr, env)) end - def make_binary_expression(operator, left, right) do - Builder.binary_expression(operator, Translator.translate(left), Translator.translate(right)) + def make_binary_expression(operator, left, right, env) do + Builder.binary_expression(operator, Translator.translate(left, env), Translator.translate(right, env)) end end \ No newline at end of file diff --git a/lib/elixir_script/translator/for.ex b/lib/elixir_script/translator/for.ex index e6d287fa..53678976 100644 --- a/lib/elixir_script/translator/for.ex +++ b/lib/elixir_script/translator/for.ex @@ -5,60 +5,63 @@ defmodule ElixirScript.Translator.For do alias ElixirScript.Translator.Utils - def make_for(generators) do - _results = Translator.translate(quote do: _results) - variable_declaration = Translator.translate(quote do: _results = []) + def make_for(generators, env) do + quoted = quote do: _results + _results = Translator.translate(quoted, env) - block_statement = [variable_declaration] ++ [handle_generators(generators)] ++ [Builder.return_statement(_results)] + quoted = quote do: _results = [] + variable_declaration = Translator.translate(quoted, env) + + block_statement = [variable_declaration] ++ [handle_generators(generators, env)] ++ [Builder.return_statement(_results)] Utils.wrap_in_function_closure(block_statement) end - defp handle_generators(generators) do + defp handle_generators(generators, env) do case hd(generators) do {:<-, [], [identifier, enum]} -> case identifier do {value_one, value_two} -> elements = [value_one, value_two] - make_tuple_for(elements, enum, generators) + make_tuple_for(elements, enum, generators, env) {:{}, _, elements} -> - make_tuple_for(elements, enum, generators) + make_tuple_for(elements, enum, generators, env) _ -> - i = Translator.translate(identifier) + i = Translator.translate(identifier, env) variable_declarator = Builder.variable_declarator(i) variable_declaration = Builder.variable_declaration([variable_declarator], :let) Builder.for_of_statement( variable_declaration, - Translator.translate(enum), - Builder.block_statement(List.wrap(handle_generators(tl(generators)))) + Translator.translate(enum, env), + Builder.block_statement(List.wrap(handle_generators(tl(generators), env))) ) end [into: _expression] -> raise ElixirScript.UnsupportedError, :into [do: expression] -> - push_last_expression(Translator.translate(expression)) + push_last_expression(Translator.translate(expression, env)) filter -> Builder.if_statement( - Translator.translate(filter), - handle_generators(tl(generators)), + Translator.translate(filter, env), + handle_generators(tl(generators), env), nil ) end end - defp make_tuple_for(elements, enum, generators) do + defp make_tuple_for(elements, enum, generators, env) do i = Builder.identifier("_ref") variable_declarator = Builder.variable_declarator(i) variable_declaration = Builder.variable_declaration([variable_declarator], :let) { variables, _ } = Enum.map_reduce(elements, 0, fn(x, index) -> - case Translator.translate(x) do + case Translator.translate(x, env) do %ESTree.Identifier{} -> - variable_declarator = Builder.variable_declarator(Translator.translate(x), + variable_declarator = Builder.variable_declarator(Translator.translate(x, env), Builder.call_expression( Builder.member_expression( Builder.identifier(:Kernel), @@ -78,11 +81,11 @@ defmodule ElixirScript.Translator.For do variables = Enum.filter(variables, fn(x) -> x != nil end) new_identifier = Enum.map(elements, fn(x) -> - case Translator.translate(x) do + case Translator.translate(x, env) do %ESTree.Identifier{} -> Builder.identifier(:undefined) _ -> - Translator.translate(x) + Translator.translate(x, env) end end) @@ -96,12 +99,12 @@ defmodule ElixirScript.Translator.For do Builder.for_of_statement( variable_declaration, - Translator.translate(enum), + Translator.translate(enum, env), Builder.block_statement( [ Builder.if_statement( - Utils.make_match(i, new_identifier), - Builder.block_statement(variables ++ List.wrap(handle_generators(tl(generators)))) + Utils.make_match(i, new_identifier, env), + Builder.block_statement(variables ++ List.wrap(handle_generators(tl(generators), env))) ) ] ) diff --git a/lib/elixir_script/translator/function.ex b/lib/elixir_script/translator/function.ex index 0dd8a5ce..0d91e265 100644 --- a/lib/elixir_script/translator/function.ex +++ b/lib/elixir_script/translator/function.ex @@ -1,15 +1,14 @@ defmodule ElixirScript.Translator.Function do @moduledoc false - require Logger alias ESTree.Tools.Builder, as: JS alias ElixirScript.Translator alias ElixirScript.Translator.Utils - alias ElixirScript.Translator.PatternMatching.Match + alias ElixirScript.PatternMatching.Match alias ElixirScript.Preprocess.Variables - def process_function(name, functions) do - result = make_anonymous_function(functions) + def process_function(name, functions, env) do + result = make_anonymous_function(functions, env) declarator = JS.variable_declarator( JS.identifier(name), @@ -19,34 +18,34 @@ defmodule ElixirScript.Translator.Function do JS.variable_declaration([declarator], :let) end - def make_anonymous_function(functions) do + def make_anonymous_function(functions, env \\ __ENV__) do clauses = functions |> Stream.map(fn(x) -> Variables.process(x) end) |> Stream.map(fn {:->, _, [ [{:when, _, [params | guards]}], body ]} -> { patterns, params } = Match.build_match(List.wrap(params)) params = make_params(params) - body = make_body(body) - guard_body = make_guards(guards) + body = make_body(body, env) + guard_body = make_guards(guards, env) do_make_function_clause(patterns, params, body, guard_body) ({:->, _, [params, body]}) -> { patterns, params } = Match.build_match(params) params = make_params(params) - body = make_body(body) + body = make_body(body, env) do_make_function_clause(patterns, params, body) ({_, _, [{:when, _, [{_, _, params} | guards] }, [do: body]]}) -> { patterns, params } = Match.build_match(params) params = make_params(params) - body = make_body(body) - guard_body = make_guards(guards) + body = make_body(body, env) + guard_body = make_guards(guards, env) do_make_function_clause(patterns, params, body, guard_body) ({_, _, [{_, _, params}, [do: body]]}) -> { patterns, params } = Match.build_match(params) params = make_params(params) - body = make_body(body) + body = make_body(body, env) do_make_function_clause(patterns, params, body) end) @@ -58,15 +57,15 @@ defmodule ElixirScript.Translator.Function do ) end - defp make_body(body) do + defp make_body(body, env) do body - |> prepare_function_body + |> prepare_function_body(env) |> JS.block_statement end - defp make_guards(guards) do + defp make_guards(guards, env) do hd(List.wrap(guards)) - |> prepare_function_body + |> prepare_function_body(env) |> JS.block_statement end @@ -104,7 +103,7 @@ defmodule ElixirScript.Translator.Function do ]) end - def make_function_or_property_call(module_name, function_name) do + def make_function_or_property_call(module_name, function_name, env) do the_name = case module_name do {:__aliases__, _, name} -> name @@ -131,16 +130,16 @@ defmodule ElixirScript.Translator.Function do ), [ Utils.make_module_expression_tree(the_name, false), - Translator.translate(to_string(function_name)) + Translator.translate(to_string(function_name), env) ] ) end - def make_function_call(function_name, params) do - Utils.make_call_expression(Utils.filter_name(function_name), params) + def make_function_call(function_name, params, env) do + Utils.make_call_expression(Utils.filter_name(function_name), params, env) end - def make_function_call(module_name, function_name, params) do + def make_function_call(module_name, function_name, params, env) do the_name = case module_name do {:__aliases__, _, name} -> name @@ -157,19 +156,19 @@ defmodule ElixirScript.Translator.Function do end end - Utils.make_call_expression(the_name, Utils.filter_name(function_name), params) + Utils.make_call_expression(the_name, Utils.filter_name(function_name), params, env) end - def prepare_function_body(body) do + def prepare_function_body(body, env) do case body do nil -> [] list when is_list(list) -> - Enum.map(list, &Translator.translate(&1)) + Enum.map(list, &Translator.translate(&1, env)) {:__block__, _, list} -> - Enum.map(list, &Translator.translate(&1)) + Enum.map(list, &Translator.translate(&1, env)) _ -> - [Translator.translate(body)] + [Translator.translate(body, env)] end |> Utils.inflate_groups |> return_last_expression diff --git a/lib/elixir_script/translator/import.ex b/lib/elixir_script/translator/import.ex index 23aaf9a9..abaec2c5 100644 --- a/lib/elixir_script/translator/import.ex +++ b/lib/elixir_script/translator/import.ex @@ -1,7 +1,6 @@ defmodule ElixirScript.Translator.Import do @moduledoc false - require Logger - alias ESTree.Tools.Builder + alias ESTree.Tools.Builder, as: JS def make_alias_import(alias_info, options) do {_, _, name} = alias_info @@ -9,14 +8,14 @@ defmodule ElixirScript.Translator.Import do import_specifier = if options[:as] do {_, _, alt} = options[:as] - Builder.import_specifier( - Builder.identifier("default"), - Builder.identifier(List.last(alt)) + JS.import_specifier( + JS.identifier("default"), + JS.identifier(List.last(alt)) ) else - Builder.import_default_specifier( - Builder.identifier(List.last(name)), - Builder.identifier(List.last(name)) + JS.import_default_specifier( + JS.identifier(List.last(name)), + JS.identifier(List.last(name)) ) end @@ -26,24 +25,24 @@ defmodule ElixirScript.Translator.Import do make_source(name) end - Builder.import_declaration( + JS.import_declaration( [import_specifier], - Builder.identifier(import_path) + JS.identifier(import_path) ) end def make_import(module_name_list, options) do - mod = List.last(module_name_list) |> Builder.identifier + mod = List.last(module_name_list) |> JS.identifier specifiers = if options[:only] do Enum.map(options[:only], fn({name, _arity}) -> - Builder.import_specifier( - Builder.identifier(name), - Builder.identifier(name) + JS.import_specifier( + JS.identifier(name), + JS.identifier(name) ) end) else - List.wrap(Builder.import_namespace_specifier(mod)) + List.wrap(JS.import_namespace_specifier(mod)) end import_path = if options[:from] do @@ -52,7 +51,7 @@ defmodule ElixirScript.Translator.Import do make_source(module_name_list) end - Builder.import_declaration(specifiers, Builder.identifier(import_path)) + JS.import_declaration(specifiers, JS.identifier(import_path)) end defp make_source(name) do diff --git a/lib/elixir_script/translator/kernel.ex b/lib/elixir_script/translator/kernel.ex deleted file mode 100644 index e61c296f..00000000 --- a/lib/elixir_script/translator/kernel.ex +++ /dev/null @@ -1,53 +0,0 @@ -defmodule ElixirScript.Translator.Kernel do - @moduledoc false - require Logger - alias ESTree.Tools.Builder - alias ElixirScript.Translator - alias ElixirScript.Utils - - def make_range(first, last) do - Translator.translate(quote do: Range.(unquote(first), unquote(last))) - end - - def make_unquote(expr) do - Builder.call_expression( - Builder.function_expression([],[], - Builder.block_statement([ - Builder.return_statement( - Translator.translate(expr) - ) - ]) - ), - [] - ) - end - - - def make___DIR__() do - Utils.wrap_in_function_closure( - Builder.if_statement( - Builder.identifier(:__dirname), - Builder.block_statement([ - Builder.return_statement(Builder.identifier(:__dirname)) - ]), - Builder.block_statement([ - Builder.return_statement(Builder.literal(nil)) - ]) - ) - ) - end - - def concat_lists(list1, list2) do - Builder.call_expression( - Builder.member_expression( - Builder.identifier(:List), - Builder.identifier(:concat) - ), - [ - Translator.translate(list1), - Translator.translate(list2) - ] - ) - end - -end \ No newline at end of file diff --git a/lib/elixir_script/translator/map.ex b/lib/elixir_script/translator/map.ex index dd40796b..c7df6864 100644 --- a/lib/elixir_script/translator/map.ex +++ b/lib/elixir_script/translator/map.ex @@ -1,6 +1,5 @@ defmodule ElixirScript.Translator.Map do @moduledoc false - require Logger alias ESTree.Tools.Builder, as: JS alias ElixirScript.Translator alias ElixirScript.Translator.Utils diff --git a/lib/elixir_script/translator/module.ex b/lib/elixir_script/translator/module.ex index 3f72be12..c7919763 100644 --- a/lib/elixir_script/translator/module.ex +++ b/lib/elixir_script/translator/module.ex @@ -1,11 +1,11 @@ defmodule ElixirScript.Translator.Module do @moduledoc false - require Logger alias ESTree.Tools.Builder, as: JS alias ElixirScript.Translator alias ElixirScript.Translator.Utils alias ElixirScript.Translator.JSModule alias ElixirScript.Preprocess.Aliases + alias ElixirScript.Preprocess.Using alias ElixirScript.Translator.Function @standard_libs [ @@ -23,21 +23,21 @@ defmodule ElixirScript.Translator.Module do {:fun, from: "__lib/funcy/fun" }, ] - def make_module(module_name_list, nil) do - [%JSModule{ name: module_name_list, body: List.wrap(create__module__(module_name_list)) }] + def make_module(module_name_list, nil, env) do + [%JSModule{ name: module_name_list, body: List.wrap(create__module__(module_name_list, env)) }] end - def make_module(module_name_list, body) do + def make_module(module_name_list, body, env) do body = make_inner_module_aliases(module_name_list, body) - - { body, aliases, used_stdlibs } = Aliases.process(body) + body = Using.process(body, env) + { body, aliases, used_stdlibs } = Aliases.process(body, env) { body, functions } = extract_functions_from_module(body) - { exported_functions, private_functions } = process_functions(functions) + { exported_functions, private_functions } = process_functions(functions, env) #Translate body - body = Translator.translate(body) + body = Translator.translate(body, env) body = case body do [%ESTree.BlockStatement{ body: body }] -> @@ -92,7 +92,7 @@ defmodule ElixirScript.Translator.Module do result = [ %JSModule{ name: module_name_list, - body: imports ++ List.wrap(create__module__(module_name_list)) ++ structs ++ private_functions ++ exported_functions ++ body ++ [default], + body: imports ++ List.wrap(create__module__(module_name_list, env)) ++ structs ++ private_functions ++ exported_functions ++ body ++ [default], stdlibs: used_stdlibs |> HashSet.to_list } ] ++ List.flatten(modules) @@ -226,33 +226,33 @@ defmodule ElixirScript.Translator.Module do end) end - defp process_functions(%{ exported: exported, private: private }) do + defp process_functions(%{ exported: exported, private: private }, env) do exported_functions = Enum.map(Dict.keys(exported), fn(key) -> functions = Dict.get(exported, key) - { key, Function.process_function(key, functions) } + { key, Function.process_function(key, functions, env) } end) private_functions = Enum.map(Dict.keys(private), fn(key) -> functions = Dict.get(private, key) - { key, Function.process_function(key, functions) } + { key, Function.process_function(key, functions, env) } end) { exported_functions, private_functions } end - def make_attribute(name, value) do + def make_attribute(name, value, env) do declarator = JS.variable_declarator( JS.identifier(name), - ElixirScript.Translator.translate(value) + ElixirScript.Translator.translate(value, env) ) JS.variable_declaration([declarator], :const) end - defp create__module__(module_name_list) do + defp create__module__(module_name_list, env) do declarator = JS.variable_declarator( JS.identifier(:__MODULE__), - ElixirScript.Translator.translate(List.last(module_name_list)) + ElixirScript.Translator.translate(List.last(module_name_list), env) ) JS.variable_declaration([declarator], :const) diff --git a/lib/elixir_script/translator/primitive.ex b/lib/elixir_script/translator/primitive.ex index 913ab648..d4e409da 100644 --- a/lib/elixir_script/translator/primitive.ex +++ b/lib/elixir_script/translator/primitive.ex @@ -1,6 +1,5 @@ defmodule ElixirScript.Translator.Primitive do @moduledoc false - require Logger alias ESTree.Tools.Builder alias ElixirScript.Translator alias ElixirScript.Translator.Quote @@ -34,13 +33,13 @@ defmodule ElixirScript.Translator.Primitive do ) end - def make_list(ast) when is_list(ast) do + def make_list(ast, env) when is_list(ast) do Builder.call_expression( Builder.member_expression( Builder.identifier("Erlang"), Builder.identifier("list") ), - Enum.map(ast, fn(x) -> Translator.translate(x) end) + Enum.map(ast, fn(x) -> Translator.translate(x, env) end) ) end @@ -64,17 +63,17 @@ defmodule ElixirScript.Translator.Primitive do ) end - def make_tuple({ one, two }) do - make_tuple([one, two]) + def make_tuple({ one, two }, env) do + make_tuple([one, two], env) end - def make_tuple(elements) do + def make_tuple(elements, env) do Builder.call_expression( Builder.member_expression( Builder.identifier("Erlang"), Builder.identifier("tuple") ), - Enum.map(elements, fn(x) -> Translator.translate(x) end) + Enum.map(elements, fn(x) -> Translator.translate(x, env) end) ) end diff --git a/lib/elixir_script/translator/quote.ex b/lib/elixir_script/translator/quote.ex index ae1e5187..b20cbab2 100644 --- a/lib/elixir_script/translator/quote.ex +++ b/lib/elixir_script/translator/quote.ex @@ -42,6 +42,10 @@ defmodule ElixirScript.Translator.Quote do Primitive.make_tuple_quoted(opts, [:unquote, context, params]) end + def make_quote(_, {:alias!, _, [_alias]}) do + _alias + end + def make_quote(_, {:unquote, _, [param]}) do make_unquote(param) end diff --git a/lib/elixir_script/translator/receive.ex b/lib/elixir_script/translator/receive.ex index 9837b62c..e23c4779 100644 --- a/lib/elixir_script/translator/receive.ex +++ b/lib/elixir_script/translator/receive.ex @@ -1,10 +1,9 @@ defmodule ElixirScript.Translator.Receive do @moduledoc false - require Logger alias ESTree.Tools.Builder, as: JS alias ElixirScript.Translator - def make_receive([do: clauses]) do + def make_receive([do: clauses], env) do JS.call_expression( JS.member_expression( JS.member_expression( @@ -19,7 +18,7 @@ defmodule ElixirScript.Translator.Receive do [], JS.block_statement([ JS.return_statement( - ElixirScript.Translator.Case.make_case({:__aliases__, [], [:message]}, clauses) + ElixirScript.Translator.Case.make_case({:__aliases__, [], [:message]}, clauses, env) ) ]) ) @@ -27,7 +26,7 @@ defmodule ElixirScript.Translator.Receive do ) end - def make_receive([do: clauses, after: [{:->, _, [[time], _body]}] = after_clause]) do + def make_receive([do: clauses, after: [{:->, _, [[time], _body]}] = after_clause], env) do JS.call_expression( JS.member_expression( JS.member_expression( @@ -42,12 +41,12 @@ defmodule ElixirScript.Translator.Receive do [], JS.block_statement([ JS.return_statement( - ElixirScript.Translator.Case.make_case({:__aliases__, [], [:message]}, clauses) + ElixirScript.Translator.Case.make_case({:__aliases__, [], [:message]}, clauses, env) ) ]) ), - Translator.translate(time), - ElixirScript.Translator.Function.make_anonymous_function(after_clause) + Translator.translate(time, env), + ElixirScript.Translator.Function.make_anonymous_function(after_clause, env) ] ) end diff --git a/lib/elixir_script/translator/struct.ex b/lib/elixir_script/translator/struct.ex index 4cb7f0c5..f0955735 100644 --- a/lib/elixir_script/translator/struct.ex +++ b/lib/elixir_script/translator/struct.ex @@ -1,6 +1,5 @@ defmodule ElixirScript.Translator.Struct do @moduledoc false - require Logger alias ESTree.Tools.Builder, as: JS alias ElixirScript.Translator diff --git a/lib/elixir_script/translator/try.ex b/lib/elixir_script/translator/try.ex index 2e20ba13..f4e0d5a2 100644 --- a/lib/elixir_script/translator/try.ex +++ b/lib/elixir_script/translator/try.ex @@ -8,7 +8,7 @@ defmodule ElixirScript.Translator.Try do @error_identifier JS.identifier(:e) - def make_try(blocks) do + def make_try(blocks, env) do try_block = Dict.get(blocks, :do) rescue_block = Dict.get(blocks, :rescue) catch_block = Dict.get(blocks, :catch) @@ -21,7 +21,8 @@ defmodule ElixirScript.Translator.Try do processed_rescue_and_catch_blocks = process_rescue_and_catch( process_rescue_block(rescue_block), - process_catch_block(catch_block) + process_catch_block(catch_block), + env ) the_catch = case processed_rescue_and_catch_blocks do @@ -36,10 +37,10 @@ defmodule ElixirScript.Translator.Try do Utils.wrap_in_function_closure( JS.try_statement( Function.return_last_expression( - JS.block_statement(List.wrap(Translator.translate(try_block))) + JS.block_statement(List.wrap(Translator.translate(try_block, env))) ), the_catch, - Function.return_last_expression(process_after_block(after_block)) + Function.return_last_expression(process_after_block(after_block, env)) ) ) end @@ -70,13 +71,13 @@ defmodule ElixirScript.Translator.Try do |> List.flatten end - defp process_after_block(nil) do + defp process_after_block(nil, _) do nil end - defp process_after_block(after_block) do + defp process_after_block(after_block, env) do JS.block_statement(List.wrap( - Translator.translate(after_block) + Translator.translate(after_block, env) )) end @@ -88,14 +89,14 @@ defmodule ElixirScript.Translator.Try do catch_block end - def process_rescue_and_catch([], []) do + def process_rescue_and_catch([], [], _) do nil end - def process_rescue_and_catch(processed_rescue_block, processed_catch_block) do + def process_rescue_and_catch(processed_rescue_block, processed_catch_block, env) do processed_clauses = processed_catch_block ++ processed_rescue_block processed_clauses = processed_clauses ++ [{:->, [], [[], [quote do: throw(e)]]}] - Case.make_case({:e, [], __MODULE__}, processed_clauses) + Case.make_case({:e, [], __MODULE__}, processed_clauses, env) end defp convert_to_struct([module]) do diff --git a/lib/elixir_script/translator/utils.ex b/lib/elixir_script/translator/utils.ex index 744798f8..975bbb90 100644 --- a/lib/elixir_script/translator/utils.ex +++ b/lib/elixir_script/translator/utils.ex @@ -1,6 +1,6 @@ defmodule ElixirScript.Translator.Utils do @moduledoc false - alias ESTree.Tools.Builder + alias ESTree.Tools.Builder, as: JS alias ElixirScript.Translator def inflate_groups(body) do @@ -20,11 +20,11 @@ defmodule ElixirScript.Translator.Utils do end def make_throw_statement(error_name, message) do - Builder.throw_statement( - Builder.new_expression( - Builder.identifier(error_name), + JS.throw_statement( + JS.new_expression( + JS.identifier(error_name), [ - Builder.literal(message) + JS.literal(message) ] ) ) @@ -38,74 +38,74 @@ defmodule ElixirScript.Translator.Utils do Enum.reduce(modules, nil, fn(x, ast) -> case ast do nil -> - Builder.member_expression(Builder.identifier(x), nil, computed) + JS.member_expression(JS.identifier(x), nil, computed) %ESTree.MemberExpression{ property: nil } -> - %{ ast | property: Builder.identifier(x) } + %{ ast | property: JS.identifier(x) } _ -> - Builder.member_expression(ast, Builder.identifier(x), computed) + JS.member_expression(ast, JS.identifier(x), computed) end end) end def make_module_expression_tree(module, _computed) when is_binary(module) or is_atom(module) do - Builder.identifier(module) + JS.identifier(module) end - def make_module_expression_tree(module, _computed) do - Translator.translate(module) + def make_module_expression_tree(module, _computed, env) do + Translator.translate(module, env) end - def make_call_expression_with_ast_params(module_name, function_name, params) do - Builder.call_expression( - make_member_expression(module_name, function_name), + def make_call_expression_with_ast_params(module_name, function_name, params, env) do + JS.call_expression( + make_member_expression(module_name, function_name, env), params ) end - def make_call_expression(module_name, function_name, params) do - Builder.call_expression( - make_member_expression(module_name, function_name), - Enum.map(params, &Translator.translate(&1)) + def make_call_expression(module_name, function_name, params, env) do + JS.call_expression( + make_member_expression(module_name, function_name, env), + Enum.map(params, &Translator.translate(&1, env)) ) end - def make_call_expression(function_name, params) do - Builder.call_expression( - Builder.identifier(function_name), - Enum.map(params, &Translator.translate(&1)) + def make_call_expression(function_name, params, env) do + JS.call_expression( + JS.identifier(function_name), + Enum.map(params, &Translator.translate(&1, env)) ) end - def make_member_expression(module_name, function_name, computed \\ false) do + def make_member_expression(module_name, function_name, env, computed \\ false) do case module_name do modules when is_list(modules) and length(modules) > 1 -> ast = make_module_expression_tree(modules, computed) - Builder.member_expression( + JS.member_expression( ast, build_function_name_ast(function_name), computed ) modules when is_list(modules) and length(modules) == 1 -> - Builder.member_expression( - Builder.identifier(hd(modules)), + JS.member_expression( + JS.identifier(hd(modules)), build_function_name_ast(function_name), computed ) {{:., _, [_module_name, _function_name]}, _, _params } = ast -> - Builder.member_expression( - Translator.translate(ast), + JS.member_expression( + Translator.translate(ast, env), build_function_name_ast(function_name), computed ) {:., _, _} = ast -> - Builder.member_expression( - Translator.translate(ast), + JS.member_expression( + Translator.translate(ast, env), build_function_name_ast(function_name), computed ) _ -> - Builder.member_expression( - Builder.identifier(module_name), + JS.member_expression( + JS.identifier(module_name), build_function_name_ast(function_name), computed ) @@ -113,7 +113,7 @@ defmodule ElixirScript.Translator.Utils do end def build_function_name_ast(function_name) do - Builder.identifier(function_name) + JS.identifier(function_name) end def make_array_accessor_call(name, index) do @@ -128,20 +128,20 @@ defmodule ElixirScript.Translator.Utils do [body] end - Builder.call_expression( - Builder.member_expression( - Builder.function_expression([],[], - Builder.block_statement(the_body) + JS.call_expression( + JS.member_expression( + JS.function_expression([],[], + JS.block_statement(the_body) ), - Builder.identifier("call") + JS.identifier("call") ), - [Builder.identifier("this")] + [JS.identifier("this")] ) end - def make_match(pattern, expr) do - Builder.call_expression( - make_member_expression("Kernel", "match__qmark__"), + def make_match(pattern, expr, env) do + JS.call_expression( + make_member_expression("Kernel", "match__qmark__", env), [ pattern, expr @@ -149,9 +149,9 @@ defmodule ElixirScript.Translator.Utils do ) end - def make_match(pattern, expr, guard) do - Builder.call_expression( - make_member_expression("Kernel", "match__qmark__"), + def make_match(pattern, expr, guard, env) do + JS.call_expression( + make_member_expression("Kernel", "match__qmark__", env), [ pattern, expr, diff --git a/mix.exs b/mix.exs index ac421bf6..008a9d08 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule ElixirScript.Mixfile do def project do [ app: :elixir_script, - version: "0.10.0-dev", + version: "0.10.0", elixir: "~> 1.0", escript: escript_config, deps: deps, @@ -17,7 +17,7 @@ defmodule ElixirScript.Mixfile do def application do [ - applications: [:logger] + applications: [:logger, :inflex, :estree] ] end diff --git a/mix.lock b/mix.lock index 8d77d4ab..d2e6dc25 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,5 @@ %{"earmark": {:hex, :earmark, "0.1.17"}, "estree": {:hex, :estree, "2.0.1"}, - "ex_doc": {:hex, :ex_doc, "0.8.3"}, + "ex_doc": {:hex, :ex_doc, "0.8.4"}, "inflex": {:hex, :inflex, "1.4.1"}, "shouldi": {:hex, :shouldi, "0.2.3"}} diff --git a/priv/javascript/lib/kernel/special_forms.js b/priv/javascript/lib/kernel/special_forms.js index 55e61023..baca8606 100644 --- a/priv/javascript/lib/kernel/special_forms.js +++ b/priv/javascript/lib/kernel/special_forms.js @@ -1,64 +1,18 @@ import Erlang from '../erlang'; -import fun from '../funcy/fun'; let SpecialForms = { __MODULE__: Erlang.atom('SpecialForms'), - case: function(condition, clauses){ - return fun(clauses).call(condition); - }, - - fn: function(clauses){ - return fun(clauses); - }, - - cond: function(clauses){ - for(let clause in clauses){ - if(clause[0]){ - return clause[1](); - } - } - }, - - import: function(module, opts){ - let imported_module = SpecialForms.alias(module); - - if(opts.length === 0){ - return imported_module; - }else if(opts[Erlang.atom("only")]){ - let exported = {}; - for(let item of opts[Erlang.atom("only")]){ - let key = Symbol.keyFor(item.get(0)); - exported[key] = imported_module[key]; - } - - return exported; - }else if(opts[Erlang.atom("except")]){ - let exported = {}; - let except_list = opts[Erlang.atom("except")]; - - for(let [key, value] of imported_module){ - for(let i = 0; i < except_list.length; i++){ - if(except_list[i] === Erlang.atom(key)){ - exported[key] = imported_module[key]; - } - } - } - - return exported; + __DIR__: function(){ + if(__dirname){ + return __dirname; } - }, - - alias: function(module, opts){ - return System.import(module).resolve(); - }, - require: function(module, opts){ - if(module === undefined){ - throw new Error("module is not loaded and could not be found"); + if(document.currentScript){ + return document.currentScript.src; } - SpecialForms.alias(module, opts); + return null; }, receive: function(receive_fun, timeout_in_ms = null, timeout_fn = (time) => true){ diff --git a/test/elixir_script_test.exs b/test/elixir_script_test.exs index 1b8e01c0..d1a7263f 100644 --- a/test/elixir_script_test.exs +++ b/test/elixir_script_test.exs @@ -9,6 +9,7 @@ defmodule ElixirScript.Test do should "parse one module correctly" do js_code = ElixirScript.transpile(""" + defmodule Elephant do @ul JQuery.("#todo-list") @@ -48,8 +49,10 @@ defmodule ElixirScript.Test do end should "parse multiple modules correctly" do + js_code = ElixirScript.transpile(""" defmodule Animals do + use ElixirScript.Using, async: true defmodule Elephant do defstruct trunk: true @@ -61,31 +64,37 @@ defmodule ElixirScript.Test do end defp something_else() do + ElixirScript.Math.squared(1) end end - """) + """, env: make_custom_env) assert_js_matches """ - import Erlang from '__lib/erlang'; - import Enum from '__lib/enum'; - import Kernel from '__lib/kernel'; - import Tuple from '__lib/tuple'; - import fun from '__lib/funcy/fun'; - import Elephant from 'animals/elephant'; - const __MODULE__ = Erlang.atom('Animals'); - - let something_else = fun([[], function() { - return null; - }]); - - let something = fun([[], function() { - return Elephant.defstruct(); - }]); - - export default { - something: something - }; + import Erlang from '__lib/erlang'; + import Enum from '__lib/enum'; + import Kernel from '__lib/kernel'; + import Tuple from '__lib/tuple'; + import fun from '__lib/funcy/fun'; + import Elephant from 'animals/elephant'; + const __MODULE__ = Erlang.atom('Animals'); + + let something_else = fun([[], function() { + return 1 * 1; + }]); + + let something = fun([[], function() { + return Elephant.defstruct(); + }]); + + let sandwich = fun([[], function() { + return null; + }]); + + export default { + something: something, + sandwich: sandwich + }; """, hd(js_code) assert_js_matches """ diff --git a/test/translator/kernel_test.exs b/test/lib/kernel_test.exs similarity index 81% rename from test/translator/kernel_test.exs rename to test/lib/kernel_test.exs index 5358782d..7cdaccb2 100644 --- a/test/translator/kernel_test.exs +++ b/test/lib/kernel_test.exs @@ -1,4 +1,4 @@ -defmodule ElixirScript.Translator.Kernel.Test do +defmodule ElixirScript.Lib.Kernel.Test do use ShouldI import ElixirScript.TestHelper diff --git a/test/translator/pattern_matching_test.exs b/test/pattern_matching/match_test.exs similarity index 98% rename from test/translator/pattern_matching_test.exs rename to test/pattern_matching/match_test.exs index 8b38553b..a36d7d09 100644 --- a/test/translator/pattern_matching_test.exs +++ b/test/pattern_matching/match_test.exs @@ -1,7 +1,7 @@ -defmodule ElixirScript.Translator.PatternMatching.Test do +defmodule ElixirScript.PatternMatching.Match.Test do use ShouldI alias ElixirScript.Translator - alias ElixirScript.Translator.PatternMatching.Match + alias ElixirScript.PatternMatching.Match alias ESTree.Tools.Builder, as: JS def make_list(values) when is_list(values) do diff --git a/test/test_helper.exs b/test/test_helper.exs index 14b6717a..3c25376a 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -2,11 +2,38 @@ exclude = if Node.alive?, do: [], else: [skip: true] ExUnit.start(exclude: exclude, formatters: [ShouldI.CLIFormatter]) +defmodule ElixirScript.Math do + defmacro squared(x) do + quote do + unquote(x) * unquote(x) + end + end +end + +defmodule ElixirScript.Using do + defmacro __using__(_) do + quote do + def sandwich() do + end + end + end +end + defmodule ElixirScript.TestHelper do use ShouldI - + require Logger + + def make_custom_env do + require Logger + require ElixirScript.Math + require ElixirScript.Using + + __ENV__ + end + def ex_ast_to_js(ex_ast) do - ElixirScript.Translator.translate(ex_ast) + + ElixirScript.Translator.translate(ex_ast, make_custom_env) |> ElixirScript.javascript_ast_to_code end diff --git a/test/translator/assignment_test.exs b/test/translator/assignment_test.exs index a236d4d1..b4849bac 100644 --- a/test/translator/assignment_test.exs +++ b/test/translator/assignment_test.exs @@ -15,7 +15,9 @@ defmodule ElixirScript.Translator.Assignment.Test do end should "translate tuple assignment" do - ex_ast = quote do: {a, b} = {1, 2} + ex_ast = quote do + {a, b} = {1, 2} + end js_code = """ let [a, b] = fun.bind(Erlang.tuple(fun.parameter, fun.parameter), Erlang.tuple(1, 2)); let _ref = Erlang.tuple(a, b); diff --git a/test/translator/case_test.exs b/test/translator/case_test.exs index 41e53f58..08835543 100644 --- a/test/translator/case_test.exs +++ b/test/translator/case_test.exs @@ -16,7 +16,7 @@ defmodule ElixirScript.Translator.Case.Test do return value; }], [[Erlang.atom('error')], function() { return null; - }]).call(data) + }]).call(this, data) """ assert_translation(ex_ast, js_code) @@ -34,7 +34,7 @@ defmodule ElixirScript.Translator.Case.Test do return value0; }], [[true], function() { return true; - }]).call(data) + }]).call(this, data) """ assert_translation(ex_ast, js_code) @@ -54,7 +54,7 @@ defmodule ElixirScript.Translator.Case.Test do return value0; }], [[fun.wildcard], function() { return true; - }]).call(data) + }]).call(this, data) """ assert_translation(ex_ast, js_code) @@ -88,7 +88,7 @@ defmodule ElixirScript.Translator.Case.Test do return true; } ] - ).call(data) + ).call(this, data) """ assert_translation(ex_ast, js_code) @@ -107,11 +107,11 @@ defmodule ElixirScript.Translator.Case.Test do js_code = """ fun([[Erlang.atom('ok')], function() { - Logger.info('info'); + console.info('info'); return Todo.add(data); }], [[Erlang.atom('error')], function() { return null; - }]).call(data) + }]).call(this, data) """ assert_translation(ex_ast, js_code) @@ -129,10 +129,10 @@ defmodule ElixirScript.Translator.Case.Test do js_code = """ fun([[Erlang.tuple(fun.parameter, fun.parameter)], function(one, two) { - return Logger.info(one); + return console.info(one); }], [[Erlang.atom('error')], function() { return null; - }]).call(data) + }]).call(this, data) """ assert_translation(ex_ast, js_code) @@ -153,7 +153,7 @@ defmodule ElixirScript.Translator.Case.Test do [ [Erlang.tuple(Erlang.tuple(fun.parameter, fun.parameter), fun.parameter)], function(one, two, three) { - return Logger.info(one); + return console.info(one); } ], [ @@ -162,7 +162,7 @@ defmodule ElixirScript.Translator.Case.Test do return null; } ] - ).call(data) + ).call(this, data) """ assert_translation(ex_ast, js_code) @@ -178,10 +178,10 @@ defmodule ElixirScript.Translator.Case.Test do js_code = """ fun([[Erlang.tuple(fun.parameter, Erlang.tuple(fun.parameter, fun.parameter))], function(one, two, three) { - return Logger.info(one); + return console.info(one); }], [[Erlang.atom('error')], function() { return null; - }]).call(data) + }]).call(this, data) """ assert_translation(ex_ast, js_code) @@ -201,7 +201,7 @@ defmodule ElixirScript.Translator.Case.Test do [ [{'__struct__': Erlang.atom('AStruct'), 'key': {'__struct__': Erlang.atom('BStruct'), 'key2': fun.parameter}}], function(value){ - return Logger.info(value); + return console.info(value); } ], [ @@ -210,7 +210,7 @@ defmodule ElixirScript.Translator.Case.Test do return null; } ] - ).call(data) + ).call(this, data) """ assert_translation(ex_ast, js_code) @@ -230,7 +230,7 @@ defmodule ElixirScript.Translator.Case.Test do [ [{'__struct__': Erlang.atom('AStruct'), 'key': {'__struct__': Erlang.atom('BStruct'), 'key2': fun.parameter, 'key3': {'__struct__': Erlang.atom('CStruct'), 'key4': fun.parameter}}}], function(value,value2){ - return Logger.info(value); + return console.info(value); } ], [ @@ -239,7 +239,7 @@ defmodule ElixirScript.Translator.Case.Test do return null; } ] - ).call(data) + ).call(this, data) """ assert_translation(ex_ast, js_code) diff --git a/test/translator/defmodule_test.exs b/test/translator/defmodule_test.exs index bddb723a..8feba63d 100644 --- a/test/translator/defmodule_test.exs +++ b/test/translator/defmodule_test.exs @@ -350,24 +350,4 @@ defmodule ElixirScript.Translator.Defmodule.Test do assert_translation(ex_ast, js_code) end - - should "ignore standard libs" do - ex_ast = quote do - defmodule Animals do - Kernel.hd([1]) - Kernel.SpecialForms.alias(Dog, []) - end - end - - js_code = """ - const __MODULE__ = Erlang.atom('Animals'); - - Kernel.hd(Erlang.list(1)); - Kernel.SpecialForms.alias(Dog, Erlang.list()); - - export default {}; - """ - - assert_translation(ex_ast, js_code) - end end diff --git a/test/translator/for_test.exs b/test/translator/for_test.exs index bed63bc7..cfb4f308 100644 --- a/test/translator/for_test.exs +++ b/test/translator/for_test.exs @@ -89,7 +89,7 @@ defmodule ElixirScript.Translator.For.Test do (function () { let [_results] = fun.bind(fun.parameter,Erlang.list()); for (let n of Erlang.list(1, 2, 3, 4, 5, 6)) { - if (Kernel.rem(n, 2) == 0) + if (n % 2 == 0) _results = List.append(_results, n); } return _results; diff --git a/test/translator/function_test.exs b/test/translator/function_test.exs index effabfe0..4fff7f75 100644 --- a/test/translator/function_test.exs +++ b/test/translator/function_test.exs @@ -2,6 +2,22 @@ defmodule ElixirScript.Translator.Function.Test do use ShouldI import ElixirScript.TestHelper + should "translate function with a macro" do + ex_ast = quote do + def test1() do + ElixirScript.Math.squared(1) + end + end + + js_code = """ + let test1 = fun([[], function() { + return 1 * 1; + }]); + """ + + assert_translation(ex_ast, js_code) + end + should "translate functions" do ex_ast = quote do def test1() do diff --git a/test/translator/list_test.exs b/test/translator/list_test.exs index 275f7e8f..0360dcaf 100644 --- a/test/translator/list_test.exs +++ b/test/translator/list_test.exs @@ -26,12 +26,12 @@ defmodule ElixirScript.Translator.List.Test do should "concatenate lists" do ex_ast = quote do: [1, 2, 3] ++ [4, 5, 6] - js_code = "List.concat(Erlang.list(1,2,3),Erlang.list(4,5,6))" + js_code = "Erlang.list(1,2,3).concat(Erlang.list(4,5,6))" assert_translation(ex_ast, js_code) ex_ast = quote do: this.list ++ [4, 5, 6] - js_code = "List.concat(Kernel.JS.get_property_or_call_function(this,'list'),Erlang.list(4,5,6))" + js_code = "Kernel.JS.get_property_or_call_function(this,'list').concat(Erlang.list(4,5,6))" assert_translation(ex_ast, js_code) end diff --git a/test/translator/receive_test.exs b/test/translator/receive_test.exs index ae49afff..93705868 100644 --- a/test/translator/receive_test.exs +++ b/test/translator/receive_test.exs @@ -22,7 +22,7 @@ defmodule ElixirScript.Translator.Receive.Test do return value; }], [[fun.wildcard], function() { return IO.puts('Unexpected message received'); - }]).call(message); + }]).call(this, message); }) """ @@ -52,7 +52,7 @@ defmodule ElixirScript.Translator.Receive.Test do return value; }], [[fun.wildcard], function() { return IO.puts('Unexpected message received'); - }]).call(message); + }]).call(this, message); }, 5000, fun([[5000], function() { diff --git a/test/translator/string_test.exs b/test/translator/string_test.exs index bb906edf..9e0bdd7f 100644 --- a/test/translator/string_test.exs +++ b/test/translator/string_test.exs @@ -22,23 +22,22 @@ defmodule ElixirScript.Translator.String.Test do should "translate string interpolation" do ex_ast = quote do: "Hello #{"world"}" - assert_translation(ex_ast, "'Hello ' + Kernel.to_string('world')") - + assert_translation(ex_ast, "'Hello ' + 'world'") ex_ast = quote do: "Hello #{length([])}" - assert_translation(ex_ast, "'Hello ' + Kernel.to_string(Kernel.length(Erlang.list()))") + assert_translation(ex_ast, "'Hello ' + Kernel.to_string(Erlang.list().length)") end should "translate multiline string interpolation" do ex_ast = quote do: """ Hello #{length([])} """ - assert_translation(ex_ast, "'Hello ' + (Kernel.to_string(Kernel.length(Erlang.list())) + '\\n')") + assert_translation(ex_ast, "'Hello ' + (Kernel.to_string(Erlang.list().length) + '\\n')") ex_ast = quote do: """ Hello #{length([])} How are you, #{length([])}? """ - assert_translation(ex_ast, "'Hello ' + (Kernel.to_string(Kernel.length(Erlang.list())) + ('\\nHow are you, ' + (Kernel.to_string(Kernel.length(Erlang.list())) + '?\\n')))") + assert_translation(ex_ast, "'Hello ' + (Kernel.to_string(Erlang.list().length) + ('\\nHow are you, ' + (Kernel.to_string(Erlang.list().length) + '?\\n')))") end end \ No newline at end of file diff --git a/test/translator/try_test.exs b/test/translator/try_test.exs index 4b2cc4cf..49643edd 100644 --- a/test/translator/try_test.exs +++ b/test/translator/try_test.exs @@ -30,7 +30,7 @@ defmodule ElixirScript.Translator.Try.Test do function() { return Kernel.throw(e); } - ]).call(e) + ]).call(this, e) } }.call(this)) """ @@ -68,7 +68,7 @@ defmodule ElixirScript.Translator.Try.Test do return Kernel.throw(e); } ] - ).call(e) + ).call(this, e) } }.call(this)) """ @@ -97,7 +97,7 @@ defmodule ElixirScript.Translator.Try.Test do return Kernel.__in__(x, Erlang.list(ArgumentError.defstruct())); }], [[], function() { return Kernel.throw(e); - }]).call(e) + }]).call(this, e) } }.call(this)) """ @@ -133,7 +133,7 @@ defmodule ElixirScript.Translator.Try.Test do return Kernel.throw(e); } ] - ).call(e) + ).call(this, e) } }.call(this)) """ @@ -178,7 +178,7 @@ defmodule ElixirScript.Translator.Try.Test do return Kernel.throw(e); } ] - ).call(e) + ).call(this, e) } }.call(this)) """ @@ -216,7 +216,7 @@ defmodule ElixirScript.Translator.Try.Test do return Kernel.throw(e); } ] - ).call(e) + ).call(this, e) } finally { return IO.puts('This is printed regardless if it failed or succeed'); } @@ -302,7 +302,7 @@ defmodule ElixirScript.Translator.Try.Test do return Kernel.throw(e); } ] - ).call(e) + ).call(this, e) } }.call(this)) """