Packages
orb
0.0.48
0.2.2
0.2.1
0.2.0
0.1.1
0.1.0
0.0.51
0.0.49
0.0.48
0.0.47
0.0.46
0.0.45
0.0.44
0.0.43
0.0.42
0.0.41
0.0.40
0.0.39
0.0.38
0.0.37
0.0.36
0.0.35
0.0.34
0.0.33
0.0.32
0.0.31
0.0.30
0.0.28
0.0.27
0.0.26
0.0.25
0.0.24
0.0.23
0.0.22
0.0.21
0.0.20
0.0.19
0.0.18
0.0.17
0.0.16
0.0.15
0.0.14
0.0.13
0.0.12
retired
0.0.11
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
DSL for WebAssembly
Current section
Files
Jump to
Current section
Files
lib/orb/i32.ex
defmodule Orb.I32 do
@moduledoc """
Type for 32-bit integer.
"""
import Kernel, except: [and: 2, or: 2]
require alias Orb.Ops
alias Orb.Instruction
alias Orb.InstructionSequence
with @behaviour Orb.CustomType do
@impl Orb.CustomType
def wasm_type, do: :i32
end
def const(n) when is_integer(n), do: Instruction.wrap_constant!(:i32, n)
def add(a, b)
def sub(a, b)
def mul(a, b)
def div_u(a, divisor)
def div_s(a, divisor)
def rem_u(a, divisor)
def rem_s(a, divisor)
def unquote(:or)(a, b)
def xor(a, b)
def shl(a, b)
def shr_u(a, b)
def shr_s(a, b)
def rotl(a, b)
def rotr(a, b)
for op <- Ops.i32(1) do
def unquote(op)(a) do
Instruction.i32(unquote(op), a)
end
end
for op <- Ops.i32(2) do
case op do
:eq ->
# TODO: is this the best place for this optimization?
# Perhaps it should be done at `def a === b`
# Otherwise it would justify adding optimization for `0 AND 1 --> 0` etc too.
def eq(0, n), do: Instruction.i32(:eqz, n)
def eq(n, 0), do: Instruction.i32(:eqz, n)
def eq(a, b), do: Instruction.i32(:eq, a, b)
:and ->
def band(a, b) do
Instruction.i32(:and, a, b)
end
_ ->
def unquote(op)(a, b) do
Instruction.i32(unquote(op), a, b)
end
end
end
def sum!([]), do: const(0)
def sum!(items) when is_list(items) do
items
|> Enum.map(&Orb.TypeNarrowable.type_narrow_to(&1, __MODULE__))
|> Enum.reduce(&add/2)
end
def in?(value, list) when is_list(list) do
instructions =
for {item, index} <- Enum.with_index(list) do
case index do
0 ->
eq(value, item)
_ ->
# InstructionSequence.new(:i32, [eq(value, item), Instruction.i32(:or)])
Instruction.i32(:or, eq(value, item))
end
end
InstructionSequence.new(:i32, instructions)
end
@doc "Converts an ASCII little-endian 4-byte string to an 32-bit integer."
def from_4_byte_ascii(<<int::little-size(32)>>), do: int
# def to_4_byte_ascii(i32) when is_integer(i32), do: <<i32::little-size(32)>>
defmacro match(value, do: transform) do
statements =
for {:->, _, [input, result]} <- transform do
case input do
# _ ->
# like an else clause
[{:_, _, _}] ->
quote do: Orb.InstructionSequence.new(unquote(__get_block_items(result)))
[match] ->
quote do
Orb.IfElse.new(
nil,
Orb.I32.eq(unquote(value), unquote(match)),
Orb.InstructionSequence.new(
:i32,
unquote(__get_block_items(result)) ++ [Orb.Control.break(:i32_match)]
),
nil
)
end
matches ->
quote do
Orb.IfElse.new(
nil,
Orb.I32.in?(unquote(value), unquote(matches)),
Orb.InstructionSequence.new(
:i32,
unquote(__get_block_items(result)) ++ [Orb.Control.break(:i32_match)]
),
nil
)
end
end
end
has_catchall? = Enum.any?(transform, &match?({:->, _, [[{:_, _, _}], _]}, &1))
final_instruction =
case has_catchall? do
false -> quote do: %Orb.Unreachable{debug_reason: "Orb.I32.match/2 catchall"}
true -> quote do: Orb.InstructionSequence.empty()
end
quote do
with do
require Orb.Control
Orb.Control.block :i32_match, Orb.I32 do
Orb.InstructionSequence.new(nil, unquote(statements))
unquote(final_instruction)
end
end
end
end
defmacro cond(do: transform) do
statements =
for {:->, _, [input, target]} <- transform do
case input do
# true ->
# like an else clause
[true] ->
target
[match] ->
quote do
Orb.IfElse.new(
unquote(match),
Orb.InstructionSequence.new(
unquote(__get_block_items(target)) ++ [Orb.Control.break(:i32_map)]
)
)
end
end
end
has_catchall? = Enum.any?(transform, &match?({:->, _, [[{:_, _, _}], _]}, &1))
final_instruction =
case has_catchall? do
false -> quote do: %Orb.Unreachable{debug_reason: "Orb.I32.cond/1 catchall"}
true -> quote do: Orb.InstructionSequence.empty()
end
quote do
with do
require Orb.Control
Orb.Control.block :i32_map, Orb.I32 do
Orb.InstructionSequence.new(unquote(statements))
unquote(final_instruction)
end
end
end
end
defp __get_block_items(block) do
case block do
nil -> nil
{:__block__, _meta, block_items} -> block_items
single -> [single]
end
end
def __global_value(value) when is_integer(value), do: const(value)
def __global_value(false), do: const(0)
def __global_value(true), do: const(1)
# TODO: stash away which module so we can do smart stuff like with local types
def __global_value(mod) when is_atom(mod),
do: const(mod.initial_i32())
# TODO: remove
defmacro global(mutability \\ :mutable, list)
when mutability in ~w{readonly mutable}a do
quote do
@wasm_globals (for {key, value} <- unquote(list) do
Orb.Global.new(
:i32,
key,
unquote(mutability),
:internal,
Orb.I32.__global_value(value)
)
end)
end
end
# TODO: remove
defmacro export_global(mutability, list)
when mutability in ~w{readonly mutable}a do
quote do
@wasm_globals (for {key, value} <- unquote(list) do
Orb.Global.new(
:i32,
key,
unquote(mutability),
:exported,
Orb.I32.__global_value(value)
)
end)
end
end
defmacro export_enum(keys, offset \\ 0) do
quote do
unquote(__MODULE__).export_global(
:readonly,
Enum.with_index(unquote(keys), unquote(offset))
)
end
end
defmacro enum(keys, offset \\ 0) do
quote do
unquote(__MODULE__).global(:readonly, Enum.with_index(unquote(keys), unquote(offset)))
end
end
end