-
Notifications
You must be signed in to change notification settings - Fork 68
Expand file tree
/
Copy pathffi.ex
More file actions
107 lines (84 loc) · 3.12 KB
/
ffi.ex
File metadata and controls
107 lines (84 loc) · 3.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
defmodule ElixirScript.FFI do
@moduledoc """
The Foreign Function Interface (FFI) for interacting with JavaScript
To define a foreign module, make a new module and add `use ElixirScript.FFI` to it.
To define external functions, use the `defexternal` macro.
Here is an example of a foreign module for a JSON module
```elixir
defmodule MyApp.JSON do
use ElixirScript.FFI
defexternal stringify(map)
defexternal parse(string)
end
```
Foreign modules map to JavaScript files that export functions defined with the `defexternal` macro.
ElixirScript expects JavaScript modules to be in the `priv/elixir_script` directory.
These modules are copied to the output directory upon compilation.
For our example, a JavaScript file must be placed in the `priv/elixir_script` folder.
In our example, it could either be `priv/elixir_script/my_app/json.js` or
`priv/elixir_script/my_app.json.js`. ElixirScript will look for either path
It looks like this
```javascript
export default {
stringify: JSON.stringify,
parse: JSON.parse
}
```
`ElixirScript.FFI` takes the following options
* `global`: If the module is defined in the global state or not. If this is set to `true`,
nothing is imported and instead ElixirScript will use the name of the module to call a module and
function in the global scope.
* `name`: Only applicable with `global` is set to `true`. This will use the name defined here
instead of the module name for calling modules and functions in the global scope
An example using the global option to reference the JSON module in browsers
```elixir
defmodule JSON do
use ElixirScript.FFI, global: true
defexternal stringify(map)
defexternal parse(string)
end
```
The calls above are translated to calls to the `JSON` module in the global scope
An example using global and name options
```elixir
defmodule Console do
use ElixirScript.FFI, global: true, name: :console
defexternal log(term)
end
```
With the above, calls in ElixirScript to `Console.log` will translate to `console.log` in JavaScript
"""
defmacro __using__(opts) do
quote do
import ElixirScript.FFI
Module.register_attribute __MODULE__, :__foreign_info__, persist: true
@__foreign_info__ %{
path: Macro.underscore(__MODULE__),
name: unquote(Keyword.get(opts, :name, nil)),
global: unquote(Keyword.get(opts, :global, false))
}
end
end
@doc """
Defines a JavaScript function to be called from Elixir modules
To define an external function, pass the name and arguments to `defexternal`
```elixir
defexternal my_js_function(arg1, arg2, arg3)
```
"""
defmacro defexternal({name, _, args}) do
args = Enum.map(args, fn
{:\\, meta0, [{name, meta, atom}, value]} ->
name = String.to_atom("_" <> Atom.to_string(name))
{:\\, meta0, [{name, meta, atom}, value]}
{name, meta, atom} ->
name = String.to_atom("_" <> Atom.to_string(name))
{name, meta, atom}
other ->
other
end)
quote do
def unquote(name)(unquote_splicing(args)), do: nil
end
end
end