Packages
orb
0.0.49
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/stack.ex
defmodule Orb.Stack do
@moduledoc """
Functions for working with the WebAssembly function stack.
Particularly useful for inlined algorithms which can push a value to the stack and later pop it, avoiding the need to declare another local.
"""
defmodule Drop do
@moduledoc false
defstruct instruction: nil, count: 0, pop_type: nil, push_type: nil
require alias Orb.Ops
def new(instruction) do
case Ops.pop_push_of(instruction) do
{nil, nil} ->
raise ArgumentError,
message: "Cannot drop instruction pushing nothing to the stack."
{nil, type} when is_atom(type) ->
%__MODULE__{
instruction: instruction,
count: 1
}
{nil, type} when is_tuple(type) ->
%__MODULE__{
instruction: instruction,
count: tuple_size(type)
}
{pop, _} ->
raise ArgumentError,
message: "Cannot drop instruction that is already popping #{pop} from the stack."
end
end
defimpl Orb.ToWat do
def to_wat(
%Orb.Stack.Drop{instruction: instruction, count: count},
indent
) do
[
Orb.ToWat.to_wat(instruction, indent),
for _ <- 1..count do
["\n", indent, "drop"]
end
]
end
end
end
defmodule Pop do
@moduledoc false
defstruct pop_type: nil, push_type: nil, count: 0
require alias Orb.Ops
def new(type) do
count = type |> Ops.type_stack_count()
%__MODULE__{
# This instruction struct is weird. It essentially does nothing
# as WebAssembly automatically pops from the stack if it needs.
# So we pretend to pop and push the same type: effectively a nop.
# Other parts of the system need to see the type, say
# `Orb.Instruction.get_operand_type/1` so we fulfill that contract.
pop_type: type,
push_type: type,
count: count
}
end
defimpl Orb.ToWat do
alias Orb.ToWat.Helpers
# def to_wat(%Orb.Stack.Pop{}, _), do: []
def to_wat(%Orb.Stack.Pop{pop_type: type}, _), do: ["(;", Helpers.do_type(type), ";)"]
end
end
@doc """
Executes the passed instruction and immediately drops its result(s) from the stack, effectively ignoring the resulting value.
"""
def drop(instruction), do: Drop.new(instruction)
@doc """
Pops the last value from the stack, useful for assigning it to a local.
"""
def pop(type), do: Pop.new(type)
@doc """
Pushes a value onto the current stack.
"""
def push(value)
def push(%Orb.Instruction{operation: {:local_set, identifier, type}, operands: [value]}) do
Orb.Instruction.local_tee(type, identifier, value)
end
def push(%Orb.Instruction{} = instruction), do: instruction
def push(%Orb.VariableReference{} = ref), do: ref
@doc """
Push value then run the block. Useful for when you mutate a variable but want its previous value.
"""
defmacro push(value, do: block) do
quote do
Orb.InstructionSequence.concat(
Orb.InstructionSequence.new([unquote(value)]),
Orb.InstructionSequence.new(unquote(Orb.__get_block_items(block)))
)
end
end
end