Skip to content

Commit bd40ea9

Browse files
committed
Updated functions in ElixirScript module
Renamed the parse functions to transpile.
1 parent 8ef9956 commit bd40ea9

34 files changed

+216
-161
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ priv/javascript/scratchpad.js
1414
sample/dest
1515
fprof.trace
1616
index.js
17+
/doc

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
# v0.7.0-dev
1+
# v0.6.5
22
* Enhancements
33
* Now using the JS code generator from elixir-estree for code generation, improving speed of transpilation
4+
* the parse functions in the ElixirScript module have been renamed to transpile
45

56
# v0.6.0
67
* Enhancements

README.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
ElixirScript
22
============
33

4-
The goal is to convert a subset (or full set) of Elixir code to JavaScript, providing the ability to write JavaScript in Elixir. This is done by taking the Elixir AST and converting it into [Spider Monkey AST](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Parser_API). From there, it uses [escodegen](https://github.com/estools/escodegen) to convert the Spider Monkey AST to JavaScript.
4+
The goal is to convert a subset (or full set) of Elixir code to JavaScript, providing the ability to write JavaScript in Elixir. This is done by taking the Elixir AST and converting it into JavaScript AST and then to JavaScript code. This is done using the [Elixir-ESTree](https://github.com/bryanjos/elixir-estree) library.
55

66
It also includes an escript CLI utility named ex2js. This takes files or Elixir code strings as input and emits Spider Monkey AST or JavaScript code. The results may be sent to standard output or files, based on the options selected.
77

8+
ElixirScript is now in hex. There are transpile functions in th ElixirScript module. There is also a mix task, `mix ex2js` that works exactly like the cli client.
9+
810
Requirements
911
===========
1012
* Elixir
11-
* Node or io.js
13+
* Node or io.js (only for development)
1214

1315

1416
Development
@@ -37,7 +39,7 @@ To build distributable tarball
3739

3840
`ex2js-version-tar.gz` will be in the `dist` folder
3941

40-
Installation
42+
Installation of the CLI client
4143
==============
4244

4345
* uncompress `ex2js.tar.gz`.
@@ -55,12 +57,9 @@ $ ex2js -h
5557
the elixir code string if the -ex flag is used
5658
5759
options:
58-
5960
-o --output [path] places output at the given path
60-
-t --ast shows only produced spider monkey ast
6161
-ex --elixir read input as elixir code string
62-
-st --stdio reads from stdio
63-
--lib writes the standard lib js to standard out
62+
-r --root [path] root path for standard libs
6463
-h --help this message
6564
```
6665

lib/elixir_script.ex

Lines changed: 92 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,129 @@
11
defmodule ElixirScript do
2+
alias ElixirScript.Translator
23
alias ElixirScript.Translator.JSModule
34
alias ESTree.Tools.Builder
45
alias ESTree.Tools.Generator
56

7+
@moduledoc """
8+
Transpiles Elixir into JavaScript.
9+
10+
All transpile functions return a list of
11+
transpiled javascript code or a tuple consisting of
12+
the file name for the code and the transpiled javascript code.
13+
14+
All transpile functions also take an optional opts parameter
15+
that controls transpiler output.
16+
17+
Available options are:
18+
* include_path: a boolean controlling whether to return just the JavaScript code
19+
or a tuple of the file name and the JavaScript code
20+
21+
* root: a binary path prepended to the path of the standard lib imports if needed
22+
"""
23+
624
@doc """
7-
Parses Elixir code string into JavaScript AST
25+
Transpiles the given Elixir code string
826
"""
9-
@spec parse_elixir(binary) :: {binary, ESTree.Node.t}
10-
def parse_elixir(elixir_code) do
27+
@spec transpile(binary, Dict.t) :: [binary | { binary, binary }]
28+
def transpile(elixir_code, opts \\ []) do
1129
elixir_code
1230
|> Code.string_to_quoted!
13-
|> parse_quoted
31+
|> transpile_quoted(opts)
1432
end
1533

1634
@doc """
17-
Parses Elixir code in it's quoted form into JavaScript AST
35+
Transpiles the given Elixir code in quoted form
1836
"""
19-
@spec parse_quoted(Macro.t) :: {binary, ESTree.Node.t}
20-
def parse_quoted(quoted) do
21-
ElixirScript.Translator.translate(quoted)
37+
@spec transpile_quoted(Macro.t, Dict.t) :: [binary | { binary, binary }]
38+
def transpile_quoted(quoted, opts \\ []) do
39+
include_path = Dict.get(opts, :include_path, false)
40+
root = Dict.get(opts, :root)
41+
42+
case Translator.translate(quoted) do
43+
modules when is_list(modules) ->
44+
List.flatten(modules)
45+
|> Enum.map(fn(x) ->
46+
convert_to_code(x, root, include_path)
47+
end)
48+
module ->
49+
List.wrap(
50+
convert_to_code(module, root, include_path)
51+
)
52+
end
2253
end
2354

2455
@doc """
25-
Parses Elixir code files into JavaScript AST
56+
Transpiles the elixir files found at the given path
2657
"""
27-
@spec parse_elixir_files(binary) :: [{binary, ESTree.Node.t}]
28-
def parse_elixir_files(path) do
58+
@spec transpile_path(binary, Dict.t) :: [binary | { binary, binary }]
59+
def transpile_path(path, opts \\ []) do
60+
include_path = Dict.get(opts, :include_path, false)
61+
root = Dict.get(opts, :root)
62+
2963
path
3064
|> Path.wildcard
31-
|> Enum.map(fn(x) ->
65+
|> Enum.map(fn(x) ->
3266
File.read!(x)
3367
|> Code.string_to_quoted!
34-
|> ElixirScript.Translator.translate
68+
|> Translator.translate
3569
end)
3670
|> List.flatten
71+
|> Enum.map(fn(x) ->
72+
convert_to_code(x, root, include_path)
73+
end)
3774
end
3875

39-
def process_module(module, root) do
76+
@doc """
77+
Copies the javascript that makes up the ElixirScript standard libs
78+
to the specified location in a "/__lib" folder
79+
"""
80+
def copy_standard_libs_to_destination(destination) do
81+
File.cp_r!(operating_path <> "/lib", destination <> "/__lib")
82+
end
83+
84+
defp convert_to_code(js_ast, root, include_path) do
85+
js_ast
86+
|> process_module(root)
87+
|> javascript_ast_to_code
88+
|> process_include_path(include_path)
89+
end
90+
91+
defp process_module(%JSModule{} = module, root) do
4092
file_path = create_file_name(module)
4193

4294
program = ElixirScript.Translator.Module.create_standard_lib_imports(module.stdlibs, root) ++ module.body
4395
|> ESTree.Tools.Builder.program
44-
45-
{ file_path, program }
96+
97+
{ file_path, program }
4698
end
4799

48-
defp create_file_name(%ElixirScript.Translator.JSModule{ name: module_list }) do
49-
"#{ElixirScript.Translator.Import.make_file_path(module_list)}.js"
100+
defp process_module(module, _root) do
101+
{ "", module }
50102
end
51103

52-
@doc """
53-
Converts JavaScript AST into JavaScript code
54-
"""
55-
@spec javascript_ast_to_code(ESTree.Node.t) :: {:ok, binary} | {:error, binary}
56-
def javascript_ast_to_code(js_ast) do
57-
js_ast = prepare_js_ast(js_ast)
58-
{:ok, Generator.generate(js_ast) }
104+
defp create_file_name(%JSModule{ name: module_list }) do
105+
name = ElixirScript.Translator.Import.make_file_path(module_list)
106+
"#{name}.js"
59107
end
60108

61-
@doc """
62-
Same as javascript_ast_to_code but throws an error
63-
"""
64-
@spec javascript_ast_to_code!(ESTree.Node.t) :: binary
65-
def javascript_ast_to_code!(js_ast) do
66-
case javascript_ast_to_code(js_ast) do
67-
{:ok, js_code } ->
68-
js_code
69-
{:error, error } ->
70-
raise ElixirScript.ParseError, message: error
71-
end
109+
defp process_include_path({ path, code } = pair, true) do
110+
pair
111+
end
112+
113+
defp process_include_path({ _, code }, false) do
114+
code
115+
end
116+
117+
@doc false
118+
def javascript_ast_to_code({ path, js_ast }) do
119+
js_code = javascript_ast_to_code(js_ast)
120+
{ path, js_code }
121+
end
122+
123+
@doc false
124+
def javascript_ast_to_code(js_ast) do
125+
prepare_js_ast(js_ast)
126+
|> Generator.generate
72127
end
73128

74129
defp prepare_js_ast(js_ast) do
@@ -83,24 +138,7 @@ defmodule ElixirScript do
83138
end
84139
end
85140

86-
@doc """
87-
Writes output to file
88-
"""
89-
def write_to_file({ file_path, js_code }, destination) do
90-
file_name = Path.join([destination, file_path])
91-
92-
if !File.exists?(Path.dirname(file_name)) do
93-
File.mkdir_p!(Path.dirname(file_name))
94-
end
95-
96-
File.write!(file_name, js_code)
97-
end
98-
99-
def copy_standard_libs_to_destination(destination) do
100-
File.cp_r!(operating_path <> "/lib", destination <> "/__lib")
101-
end
102-
103-
def operating_path() do
141+
defp operating_path() do
104142
try do
105143
Mix.Project.build_path <> "/lib/elixir_script/priv/javascript"
106144
rescue
@@ -111,14 +149,6 @@ defmodule ElixirScript do
111149
Path.join(replaced_path)
112150
end
113151
end
114-
115-
def post_process_js_ast(modules, root) when is_list(modules) do
116-
Enum.map(modules, &process_module(&1, root))
117-
end
118-
119-
def post_process_js_ast(js_ast) do
120-
js_ast
121-
end
122152

123153

124154
end

lib/elixir_script/cli.ex

Lines changed: 36 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
defmodule ElixirScript.CLI do
2+
@moduledoc false
3+
24
def main(argv) do
35
argv
46
|> parse_args
@@ -7,11 +9,13 @@ defmodule ElixirScript.CLI do
79

810
def parse_args(args) do
911
switches = [
10-
output: :binary, ast: :boolean, elixir: :boolean,
11-
help: :boolean, root: :binary
12+
output: :binary, elixir: :boolean, root: :binary,
13+
help: :boolean
1214
]
1315

14-
aliases = [ o: :output, t: :ast, ex: :elixir, h: :help, r: :root ]
16+
aliases = [
17+
o: :output, ex: :elixir, h: :help, r: :root
18+
]
1519

1620
parse = OptionParser.parse(args, switches: switches, aliases: aliases)
1721

@@ -29,9 +33,8 @@ defmodule ElixirScript.CLI do
2933
the elixir code string if the -ex flag is used
3034
options:
3135
-o --output [path] places output at the given path
32-
-t --ast shows only produced spider monkey ast
3336
-ex --elixir read input as elixir code string
34-
-r --root root path for standard libs
37+
-r --root [path] root path for standard libs
3538
-h --help this message
3639
"""
3740
end
@@ -45,67 +48,51 @@ defmodule ElixirScript.CLI do
4548
end
4649

4750
def do_process(input, options) do
48-
js_ast = case options[:elixir] do
51+
transpile_opts = [
52+
root: options[:root],
53+
include_path: options[:output] != nil
54+
]
55+
56+
transpile_output = case options[:elixir] do
4957
true ->
50-
ElixirScript.parse_elixir(input)
58+
ElixirScript.transpile(input, transpile_opts)
5159
_ ->
52-
ElixirScript.parse_elixir_files(input)
60+
ElixirScript.transpile_path(input, transpile_opts)
5361
end
5462

55-
js_ast = ElixirScript.post_process_js_ast(js_ast, options[:root])
56-
57-
handle_output(js_ast, options)
58-
end
59-
60-
def handle_output(js_ast, options) when is_list(js_ast) do
61-
parse_result = case options[:ast] do
62-
true ->
63-
Enum.map(js_ast, fn({path, code}) ->
64-
{path, Poison.encode!(code) }
65-
end)
66-
_ ->
67-
Enum.map(js_ast, fn({path, code}) ->
68-
{path, ElixirScript.javascript_ast_to_code!(code)}
69-
end)
70-
end
71-
72-
7363
case options[:output] do
7464
nil ->
75-
Enum.each(parse_result, fn({path, code})-> IO.write(code) end)
65+
Enum.each(transpile_output,
66+
fn
67+
({_path, code})-> IO.write(code)
68+
(code)-> IO.write(code)
69+
end)
7670
output_path ->
77-
Enum.each(parse_result, fn(x) ->
78-
ElixirScript.write_to_file(x, output_path)
71+
Enum.each(transpile_output, fn(x) ->
72+
write_to_file(x, output_path)
7973
end)
8074

8175
ElixirScript.copy_standard_libs_to_destination(output_path)
82-
83-
end
84-
end
85-
86-
def handle_output(js_ast, options) do
87-
parse_result = case options[:ast] do
88-
true ->
89-
Poison.encode!(js_ast)
90-
_ ->
91-
ElixirScript.javascript_ast_to_code!(js_ast)
92-
end
93-
94-
case options[:output] do
95-
nil ->
96-
IO.write(parse_result)
97-
output_path ->
98-
ElixirScript.write_to_file(parse_result, output_path)
9976
end
10077
end
10178

102-
def options_contains_unknown_values(options) do
103-
Enum.any?(options, fn({key, value}) ->
104-
if key in [:output, :ast, :elixir, :root] do
79+
defp options_contains_unknown_values(options) do
80+
Enum.any?(options, fn({key, _value}) ->
81+
if key in [:output, :elixir, :root, :help] do
10582
false
10683
else
10784
true
10885
end
10986
end)
11087
end
88+
89+
def write_to_file({ file_path, js_code }, destination) do
90+
file_name = Path.join([destination, file_path])
91+
92+
if !File.exists?(Path.dirname(file_name)) do
93+
File.mkdir_p!(Path.dirname(file_name))
94+
end
95+
96+
File.write!(file_name, js_code)
97+
end
11198
end

lib/elixir_script/parse_error.ex

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)