Skip to content

Instantly share code, notes, and snippets.

@cromwellryan
Last active March 29, 2016 19:12
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save cromwellryan/6349503 to your computer and use it in GitHub Desktop.
Save cromwellryan/6349503 to your computer and use it in GitHub Desktop.
JSON Parser in Elixir
defmodule JSON do
import String
def parse( content ) do
case parse_content(content) do
{ value, "" } -> value
{ _, _ } -> raise "crap"
end
end
def parse_content( << m, rest :: binary >> ) when m in ?0..?9, do: parse_number << m, rest :: binary >>
def parse_content( << ?", rest :: binary >> ), do: rest |> parse_string
def parse_content( << ?{, rest :: binary >> ), do: lstrip(rest) |> parse_object
def parse_content( << ?[, rest :: binary >> ), do: lstrip(rest) |> parse_array
def parse_string( << c, rest :: binary >> ), do: rest |> parse_string [c]
def parse_string( << ?", rest :: binary >>, acc ), do: { to_string(Enum.reverse(acc)), rest }
def parse_string( << c, rest :: binary >>, acc ), do: rest |> parse_string [c | acc]
def parse_number( << m, rest :: binary >> ), do: rest |> parse_number [m]
def parse_number( << ?., rest :: binary >>, acc ), do: rest |> parse_float [ "." | acc ]
def parse_number( << m, rest :: binary >>, acc ) when m in ?0..?9, do: rest |> parse_number [ m | acc ]
def parse_number( << rest :: binary >>, acc ), do: { Enum.reverse(acc) |> to_string |> String.to_integer, rest }
def parse_float( << m, rest :: binary >>, acc ) when m in ?0..?9, do: rest |> parse_float [ m | acc ]
def parse_float( << rest :: binary >>, acc ), do: { to_string(Enum.reverse(acc)) |> String.to_float, rest }
def parse_array( content ), do: parse_array([], content)
def parse_array( acc, << rest :: binary >> ) do
{ value, rest } = parse_content rest
acc = [ value | acc ]
case lstrip(rest) do
<< ?], rest :: binary >> -> { Enum.reverse(acc), rest }
<< ?,, rest :: binary >> -> parse_array acc, lstrip(rest)
end
end
def parse_object( content ), do: parse_object(%{}, content)
def parse_object( acc, << rest :: binary >> ) do
{ key, rest } = parse_content rest
{ value, rest } = lstrip(rest) |> parse_object_value
acc = Map.put acc, key, value
case lstrip(rest) do
<< ?}, rest :: binary >> -> { acc, rest }
<< ?,, rest :: binary >> -> parse_object acc, lstrip(rest)
end
end
def parse_object_value( << ?:, rest :: binary >> ), do: lstrip(rest) |> parse_object_value
def parse_object_value( rest ), do: parse_content rest
end
test = fn content ->
val = JSON.parse(content)
IO.puts "-----------------"
IO.puts "#{content} =>"
IO.inspect val
end
test.("\"asdf\"")
test.("0")
test.("1")
test.("10")
test.("18")
test.("1.8")
test.("{ \"a\": \"b\" }")
test.("{ \"x\": 1 }")
test.("{ \"q\": \"r\", \"s\": 10 }")
test.("{ \"q\": \"r\", \"s\": \"10\" }")
test.("{ \"q\": \"r\", \"s\": { \"t\": 99 } }")
test.("[1]")
test.("[1,2]")
test.("[1,2,3]")
test.("{ \"Ages\": [ 6.5, 5, 1, 1 ] }")
test.("{ \"Family\": [ { \"name\": \"Ryan\" }, { \"name\": \"Ben\" } ] }")
family = JSON.parse("{ \"Family\": [ { \"name\": \"Ryan\" }, { \"name\": \"Ben\" } ] }")
Enum.each family["Family"], fn person -> IO.inspect person["name"] end
@kumekay
Copy link

kumekay commented Mar 29, 2016

I know, this sketch is old,
but your code doesn't support negative numbers
Fix is simple: replace ?0..?9 to '0123456789-'
My fixed version https://gist.github.com/pinya/01fa6c490bef8100d55dc158235f6a75

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment