Skip to content

Instantly share code, notes, and snippets.

@devinus
Created February 9, 2014 09:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devinus/c8fc12b63f1c73aedbae to your computer and use it in GitHub Desktop.
Save devinus/c8fc12b63f1c73aedbae to your computer and use it in GitHub Desktop.
defmodule StringIO do
def new(string) when is_binary(string) do
spawn_link(fn -> loop(string) end)
end
defp loop(buf) do
receive do
{ :io_request, from, reply_as, req } ->
{ reply, buf } = io_request(req, buf)
send from, { :io_reply, reply_as, reply }
loop(buf)
:stop ->
loop(buf)
end
end
defp io_request({ :put_chars, chars }, buf) do
{ :ok, buf <> chars }
end
defp io_request({ :put_chars, mod, fun, args }, buf) do
io_request({ :put_chars, apply(mod, fun, args) }, buf)
end
defp io_request({ :put_chars, _enc, chars }, buf) do
io_request({ :put_chars, chars }, buf)
end
defp io_request({ :put_chars, _enc, mod, fun, args }, buf) do
io_request({ :put_chars, mod, fun, args }, buf)
end
defp io_request({ :get_chars, _prompt, n }, buf) when n >= 0 do
get_chars(buf, n)
end
defp io_request({ :get_chars, _enc, prompt, n }, buf) when n >= 0 do
io_request({ :get_chars, prompt, n }, buf)
end
defp io_request({ :get_line, _prompt }, buf) do
get_line(buf)
end
defp io_request({ :get_line, _enc, prompt}, buf) do
io_request({ :get_line, prompt }, buf)
end
defp io_request({ :get_until, _enc, prompt, mod, fun, args}, buf) do
io_request({ :get_until, prompt, mod, fun, args}, buf)
end
defp io_request({ :get_until, _prompt, mod, fun, args }, buf) do
get_until(mod, fun, args, buf)
end
defp io_request({ :setopts, _opts }, buf) do
{ { :error, :enotsup }, buf }
end
defp io_request(:getopts, buf) do
{ [], buf }
end
defp io_request({ :get_geometry, _geom }, buf) do
{ { :error, :enotsup }, buf }
end
defp io_request(_, buf) do
{ { :error, :request }, buf }
end
defp get_chars(buf, n) do
case buf do
"" ->
{ :eof, "" }
_ ->
<< part :: [binary, size(n)], rest :: binary >> = buf
{ part, rest }
end
end
defp get_line(buf) do
case buf do
"" -> { :eof, "" }
_ ->
case :binary.split(buf, "\n") do
[ line ] ->
{ line, "" }
[ line, rest ] ->
{ line <> "\n", rest }
end
end
end
defp get_until(mod, fun, args, buf) do
get_until_step(mod, fun, args, [], String.to_char_list!(buf))
end
defp get_until_apply(mod, fun, args, cont, []) do
apply(mod, fun, [cont, :eof | args])
end
defp get_until_apply(mod, fun, args, cont, chars) do
apply(mod, fun, [cont, chars | args])
end
defp get_until_step(mod, fun, args, cont, chars) do
case get_until_apply(mod, fun, args, cont, chars) do
{ :done, :eof, _rest } ->
{ :eof, "" }
{ :done, result, rest } ->
{ String.from_char_list!(result),
String.from_char_list!(rest) }
{ :more, cont } ->
get_until_loop(mod, fun, args, cont)
end
end
defp get_until_loop(mod, fun, args, cont) do
receive do
{ :io_request, _from, _reply_as, { :put_chars, chars } } ->
get_until_continue(mod, fun, args, cont, chars)
{ :io_request, _from, _reply_as, { :put_chars, _enc, chars } } ->
get_until_continue(mod, fun, args, cont, chars)
{ :io_request, _from, _reply_as, { :put_chars, rmod, rfun, rargs } } ->
chars = apply(rmod, rfun, rargs)
get_until_continue(mod, fun, args, cont, chars)
{ :io_request, _from, _reply_as, { :put_chars, _enc, rmod, rfun, rargs } } ->
chars = apply(rmod, rfun, rargs)
get_until_continue(mod, fun, args, cont, chars)
end
end
defp get_until_continue(mod, fun, args, cont, chars) do
get_until_step(mod, fun, args, cont, String.to_char_list!(chars))
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment