Last active
March 29, 2016 19:12
-
-
Save cromwellryan/6349503 to your computer and use it in GitHub Desktop.
JSON Parser in Elixir
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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