Current section

Files

Jump to
jsonc lib parser.ex
Raw

lib/parser.ex

defmodule JSONC.Parser do
@moduledoc false
import JSONC.Tokenizer
def parse!(content) when is_binary(content) do
case parse(content) do
{:ok, result} ->
result
{:error, reason} ->
raise reason
end
end
def parse(content) when is_binary(content) do
case start_tokenizer(content) do
{:error, reason} ->
{:error, reason}
_ ->
case parse_value(:root) do
{:ok, result} ->
stop_tokenizer()
{:ok, result}
{:error, reason} ->
stop_tokenizer()
{:error, reason}
end
end
end
defp parse_value(context \\ :other) do
case parse_comments() do
comments when is_list(comments) ->
current = next()
case parse_comments() do
new_comments when is_list(new_comments) ->
comments = comments ++ new_comments
value =
case current do
{{:delimiter, {:brace, :open}}, line, column} ->
node = parse_object({line, column})
case node do
{:error, reason} ->
{:error, reason}
_ ->
{node, []}
end
{{:delimiter, {:bracket, :open}}, line, column} ->
node = parse_array({line, column})
case node do
{:error, reason} ->
{:error, reason}
_ ->
{node, []}
end
{{:string, {subtype, value}}, line, column} ->
{%{
type: :string,
subtype: subtype,
value: value,
place: {line, column}
}, comments}
{{:number, {subtype, value}}, line, column} ->
{%{
type: :number,
subtype: subtype,
value: value,
place: {line, column}
}, comments}
{{:boolean, value}, line, column} ->
{%{type: :boolean, value: value, place: {line, column}}, comments}
{nil, line, column} ->
{%{type: nil, place: {line, column}}, comments}
{:error, reason} ->
{:error, reason}
{token, line, column} ->
{:error,
"unexpected token `#{token |> inspect()}` at line #{line} column #{column}"}
:done ->
{:error, "unexpected end of input"}
end
case parse_comments() do
new_comments when is_list(new_comments) ->
comments = comments ++ new_comments
case value do
{:error, reason} ->
{:error, reason}
{value, _} = node ->
case context do
:root ->
case peek() do
:done ->
{:ok, %{type: :root, value: value, comments: comments}}
{token, line, column} ->
{:error,
"unexpected token `#{token |> inspect()}` at line #{line} column #{column}"}
end
_ ->
node
end
end
{:error, reason} ->
{:error, reason}
end
{:error, reason} ->
{:error, reason}
end
{:error, reason} ->
{:error, reason}
end
end
defp parse_object(start, map \\ %{}, comments \\ [])
when is_map(map) and is_list(comments) do
case peek() do
{{:delimiter, {:brace, :close}}, _, _} ->
case parse_comments() do
new_comments when is_list(new_comments) ->
comments = comments ++ new_comments
next()
case parse_comments() do
new_comments when is_list(new_comments) ->
comments = comments ++ new_comments
%{type: :object, value: map, place: start, comments: comments}
{:error, reason} ->
{:error, reason}
end
{:error, reason} ->
{:error, reason}
end
{{:delimiter, :comma} = token, line, column} when map == %{} ->
{:error, "unexpected token `#{token |> inspect()}` at line #{line} column #{column}"}
{{:delimiter, :comma}, _, _} ->
case parse_comments() do
new_comments when is_list(new_comments) ->
comments = comments ++ new_comments
next()
case parse_comments() do
new_comments when is_list(new_comments) ->
comments = comments ++ new_comments
parse_object(start, map, comments)
{:error, reason} ->
{:error, reason}
end
{:error, reason} ->
{:error, reason}
end
_ ->
current = next()
case parse_comments() do
new_comments when is_list(new_comments) ->
comments = comments ++ new_comments
case current do
{{:key, key}, _, _} ->
current = next()
case parse_comments() do
new_comments when is_list(new_comments) ->
comments = comments ++ new_comments
case current do
{{:delimiter, :colon}, _, _} ->
case parse_value() do
{:error, reason} ->
{:error, reason}
{current, value_comments} ->
map = map |> Map.put(key, current)
parse_object(start, map, comments ++ value_comments)
end
{token, line, column} ->
{:error,
"unexpected token `#{token |> inspect()}` at line #{line} column #{column}"}
end
{:error, reason} ->
{:error, reason}
end
{{:string, {subtype, key}}, _, _} when subtype in [:single, :free] ->
case peek() do
{{:delimiter, :colon}, _, _} ->
next()
case parse_comments() do
new_comments when is_list(new_comments) ->
comments = comments ++ new_comments
case parse_value() do
{:error, reason} ->
{:error, reason}
{current, value_comments} ->
map = map |> Map.put(key, current)
parse_object(start, map, comments ++ value_comments)
end
{:error, reason} ->
{:error, reason}
end
{token, line, column} ->
{:error,
"unexpected token `#{token |> inspect()}` at line #{line} column #{column}"}
end
{token, line, column} ->
{:error,
"unexpected token `#{token |> inspect()}` at line #{line} column #{column}"}
end
{:error, reason} ->
{:error, reason}
end
end
end
defp parse_array(start, list \\ [], comments \\ [])
when is_list(list) and is_list(comments) do
case peek() do
{{:delimiter, {:bracket, :close}}, _, _} ->
case parse_comments() do
new_comments when is_list(new_comments) ->
comments = comments ++ new_comments
next()
case parse_comments() do
new_comments when is_list(new_comments) ->
comments = comments ++ new_comments
%{type: :array, value: list, place: start, comments: comments}
{:error, reason} ->
{:error, reason}
end
{:error, reason} ->
{:error, reason}
end
{{:delimiter, :comma} = token, line, column} when list == [] ->
{:error, "unexpected token `#{token |> inspect()}` at line #{line} column #{column}"}
{{:delimiter, :comma}, _, _} ->
case parse_comments() do
new_comments when is_list(new_comments) ->
comments = comments ++ new_comments
next()
case parse_comments() do
new_comments when is_list(new_comments) ->
comments = comments ++ new_comments
parse_array(start, list, comments)
{:error, reason} ->
{:error, reason}
end
{:error, reason} ->
{:error, reason}
end
_ ->
case parse_value() do
{:error, reason} ->
{:error, reason}
{current, value_comments} ->
list = list ++ [current]
parse_array(start, list, comments ++ value_comments)
end
end
end
defp parse_comments(comments \\ []) when is_list(comments) do
case peek() do
{{:comment, {subtype, value}}, line, column} ->
next()
parse_comments(
comments ++ [%{type: :comment, subtype: subtype, value: value, place: {line, column}}]
)
{:error, reason} ->
{:error, reason}
_ ->
comments
end
end
end