Current section

Files

Jump to
orb lib orb i32 string.ex
Raw

lib/orb/i32/string.ex

defmodule Orb.I32.String do
@moduledoc """
Custom type for a nul-terminated ASCII string.
"""
# TODO: should this be called Orb.I32.ASCII? Or just Orb.ASCII?
@behaviour Orb.CustomType
@behaviour Access
@impl Orb.CustomType
def wasm_type(), do: :i32
# TODO: is the byte count of the pointer, or the items?
@impl Orb.CustomType
def byte_count(), do: 1
@impl Access
def fetch(%Orb.VariableReference{} = var_ref, at!: offset) do
# ast = Orb.Instruction.i32(:load8_u, Orb.Numeric.Add.optimized(Orb.I32, var_ref, offset))
ast = Orb.Memory.load!(Orb.I32.U8, Orb.Numeric.Add.optimized(Orb.I32, var_ref, offset))
{:ok, ast}
end
@impl Access
def get_and_update(_data, _key, _function) do
raise UndefinedFunctionError, module: __MODULE__, function: :get_and_update, arity: 3
end
@impl Access
def pop(_data, _key) do
raise UndefinedFunctionError, module: __MODULE__, function: :pop, arity: 2
end
use Orb
Memory.pages(1)
# TODO: remove all of these funcs. It will live in SilverOrb instead.
# Plus strings will likely become a Memory.Range instead, so we know the length statically.
defw streq(address_a: I32, address_b: I32),
I32,
i: I32,
byte_a: I32,
byte_b: I32 do
loop EachByte, result: I32 do
byte_a = Memory.load!(I32.U8, I32.add(address_a, i))
byte_b = Memory.load!(I32.U8, I32.add(address_b, i))
# I32.match byte_a do
# 0 ->
# return(byte_b === 0)
# byte_b ->
# i = i + 1
# EachByte.continue()
# _ ->
# return(0x0)
# end
if I32.eqz(byte_a) do
return(I32.eqz(byte_b))
end
if I32.eq(byte_a, byte_b) do
i = I32.add(i, 1)
EachByte.continue()
end
return(0x0)
end
end
defw strlen(string_ptr: I32.String), I32, count: I32 do
# while (string_ptr[count] != 0) {
# count++;
# }
# loop EachChar, while: memory32_8![count] do
loop EachChar do
if Memory.load!(I32.U8, I32.add(string_ptr, count)) do
# FIXME: remove memory32_8!
# if Memory.load!(I32.U8, I32.add(string_ptr, count)) do
count = I32.add(count, 1)
EachChar.continue()
end
end
count
end
defmacro __using__(_opts) do
quote do
import unquote(__MODULE__), only: [streq: 2, strlen: 1]
import Orb
Orb.include(unquote(__MODULE__))
end
end
# TODO: is it safe to call this empty() ?
def null(), do: {:i32_const, 0}
def streq(address_a, address_b), do: Orb.DSL.typed_call(I32, :streq, [address_a, address_b])
def strlen(string_ptr), do: Orb.DSL.typed_call(I32, :strlen, [string_ptr])
defmacro match(value, do: transform) do
statements =
for {:->, _, [input, target]} <- transform do
case input do
# _ ->
# like an else clause
[{:_, _, _}] ->
quote do
Orb.InstructionSequence.new(unquote(Orb.__get_block_items(target)))
end
[match] ->
quote do
Orb.IfElse.new(
streq(unquote(value), unquote(match)),
Orb.InstructionSequence.new([
unquote(Orb.__get_block_items(target)),
Orb.Control.break(:i32_string_match)
])
)
end
end
end
# catchall = for {:->, _, [[{:_, _, _}], _]} <- transform, do: true
has_catchall? = Enum.any?(transform, &match?({:->, _, [[{:_, _, _}], _]}, &1))
final_instruction =
case has_catchall? do
false -> quote do: %Orb.Unreachable{}
true -> quote do: Orb.InstructionSequence.empty()
end
quote do
Orb.Control.block :i32_string_match, I32 do
unquote(statements)
unquote(final_instruction)
end
end
end
end