Skip to content

Commit d7e814f

Browse files
committed
feat: first commit
0 parents  commit d7e814f

20 files changed

+1225
-0
lines changed

.formatter.exs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Used by "mix format"
2+
[
3+
line_length: 80,
4+
inputs: [
5+
"{mix,.formatter}.exs",
6+
"{config,lib,test}/**/*.{ex,exs}"
7+
]
8+
]

.gitignore

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# The directory Mix will write compiled artifacts to.
2+
/_build/
3+
4+
# If you run "mix test --cover", coverage assets end up here.
5+
/cover/
6+
7+
# The directory Mix downloads your dependencies sources to.
8+
/deps/
9+
10+
# Where third-party dependencies like ExDoc output generated docs.
11+
/doc/
12+
13+
# Ignore .fetch files in case you like to edit your project deps locally.
14+
/.fetch
15+
16+
# If the VM crashes, it generates a dump, let's ignore it too.
17+
erl_crash.dump
18+
19+
# Also ignore archive artifacts (built via "mix archive.build").
20+
*.ez
21+
22+
# Ignore package tarball (built via "mix hex.build").
23+
json5-*.tar
24+
25+
# Temporary files, for example, from tests.
26+
/tmp/

README.md

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Json5
2+
3+
Json5 in elixir.
4+
5+
## NOTE
6+
7+
Currently the spec is not entirely implemented, for instance multiline string.
8+
9+
## Usage
10+
11+
```elixir
12+
input = %{test: 1}
13+
Json5.encode(input)
14+
```
15+
16+
```elixir
17+
input = """
18+
{
19+
test: 1
20+
}
21+
"""
22+
Json5.decode(input)
23+
```
24+
25+
## Installation
26+
27+
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
28+
by adding `json5` to your list of dependencies in `mix.exs`:
29+
30+
```elixir
31+
def deps do
32+
[
33+
{:json5, "~> 0.0.1"}
34+
]
35+
end
36+
```
37+
38+
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
39+
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
40+
be found at [https://hexdocs.pm/json5](https://hexdocs.pm/json5).

lib/json5.ex

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
defmodule Json5 do
2+
@moduledoc """
3+
Documentation for `Json5`.
4+
"""
5+
6+
def decode(text, opts \\ []) do
7+
Json5.Decode.parse(text, Map.new(opts))
8+
end
9+
10+
def decode!(text, opts \\ []) do
11+
{:ok, result} = Json5.Decode.parse(text, Map.new(opts))
12+
result
13+
end
14+
15+
def encode(text, opts \\ []) do
16+
Json5.Encode.dump(text, Map.new(opts))
17+
end
18+
end

lib/json5/decode.ex

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
defmodule Json5.Decode do
2+
@moduledoc """
3+
Documentation for `Json5`.
4+
"""
5+
import Combine.Parsers.Base
6+
import Json5.Decode.Helper
7+
8+
alias Json5.Decode
9+
10+
def parse(input, config \\ %{}) do
11+
case Combine.parse(input, parser(config)) do
12+
{:error, error} -> {:error, error}
13+
[val] -> {:ok, val}
14+
end
15+
end
16+
17+
def parser(config) do
18+
ignore_whitespace()
19+
|> json5_value(config)
20+
|> ignore_whitespace()
21+
|> eof()
22+
end
23+
24+
def json5_value(prev \\ nil, config \\ %{}) do
25+
choice(prev, [
26+
json5_null(),
27+
json5_boolean(),
28+
json5_string(),
29+
json5_number(),
30+
json5_array(),
31+
json5_object(config)
32+
])
33+
end
34+
35+
defp json5_null, do: Decode.Null.null()
36+
defp json5_boolean, do: Decode.Boolean.boolean()
37+
defp json5_string, do: Decode.String.string()
38+
defp json5_number, do: Decode.Number.number()
39+
defp json5_array, do: Decode.Array.array()
40+
defp json5_object(config), do: Decode.Object.object(config)
41+
end

lib/json5/decode/array.ex

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
defmodule Json5.Decode.Array do
2+
@moduledoc """
3+
Documentation for `Json5`.
4+
"""
5+
import Combine.Parsers.Base
6+
import Combine.Parsers.Text
7+
import Json5.Decode.Helper
8+
9+
def array do
10+
either(
11+
pipe(
12+
[
13+
ignore_whitespace(),
14+
char("["),
15+
ignore_whitespace(),
16+
char("]"),
17+
ignore_whitespace()
18+
],
19+
fn _ -> [] end
20+
),
21+
pipe(
22+
[
23+
ignore_whitespace(),
24+
ignore(char("[")),
25+
ignore_whitespace(),
26+
array_items(),
27+
ignore_whitespace(),
28+
ignore(char("]")),
29+
ignore_whitespace()
30+
],
31+
fn [expr] -> expr end
32+
)
33+
)
34+
end
35+
36+
defp array_items do
37+
pair_left(array_item(), option(char(",")))
38+
end
39+
40+
defp array_item do
41+
sep_by1(lazy_json5_value(), ignored_comma())
42+
end
43+
end

lib/json5/decode/boolean.ex

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
defmodule Json5.Decode.Boolean do
2+
@moduledoc """
3+
Documentation for `Json5`.
4+
"""
5+
import Combine.Parsers.Base
6+
import Combine.Parsers.Text
7+
8+
def boolean(), do: either(boolean_true(), boolean_false())
9+
10+
defp boolean_true() do
11+
"true"
12+
|> string()
13+
|> label("true")
14+
|> map(fn _ -> true end)
15+
end
16+
17+
defp boolean_false() do
18+
"false"
19+
|> string()
20+
|> label("false")
21+
|> map(fn _ -> false end)
22+
end
23+
end

lib/json5/decode/helper.ex

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# defmodule Json5.Decode.Helper do
2+
# @moduledoc """
3+
# Documentation for `Json5`.
4+
# """
5+
6+
# import NimbleParsec
7+
8+
# def stuff(input) do
9+
# IO.inspect(input)
10+
11+
# input
12+
# :error
13+
# end
14+
15+
# end
16+
17+
defmodule Json5.Decode.Helper do
18+
import Combine.Helpers
19+
import Combine.Parsers.Base
20+
import Combine.Parsers.Text
21+
22+
alias Combine.ParserState
23+
alias Json5.Decode
24+
25+
@multi_line_comment_regex ~R(\/\*[\s\S]*?\*\/)
26+
27+
@elements [
28+
:remove_white_space,
29+
:single_line_comment,
30+
:multi_line_comment
31+
]
32+
@ignore_tags for(
33+
x <- @elements,
34+
y <- @elements,
35+
x != y,
36+
do: [{__MODULE__, x, []}, {__MODULE__, y, []}]
37+
)
38+
|> List.flatten()
39+
|> Enum.dedup()
40+
41+
defparser lazy(%ParserState{status: :ok} = state, generator) do
42+
generator.().(state)
43+
end
44+
45+
def lazy_json5_value() do
46+
lazy(fn -> Decode.json5_value() end)
47+
end
48+
49+
def ignore_whitespace(prev \\ nil) do
50+
prev
51+
|> sequence(Enum.map(@ignore_tags, &apply/1))
52+
# |> many(choice([remove_white_space(), single_line_comment()]))
53+
|> ignore()
54+
end
55+
56+
def ignored_comma(prev \\ nil) do
57+
sequence(prev, [
58+
ignore_whitespace(),
59+
char(","),
60+
ignore_whitespace()
61+
])
62+
end
63+
64+
def remove_white_space do
65+
skip_many(satisfy(char(), &Unicode.Property.white_space?/1))
66+
end
67+
68+
def single_line_comment do
69+
skip(
70+
between(
71+
string("//"),
72+
many(if_not(ecma_line_terminator(), char())),
73+
ecma_line_terminator()
74+
)
75+
)
76+
end
77+
78+
def multi_line_comment do
79+
skip(word_of(@multi_line_comment_regex))
80+
end
81+
82+
defp ecma_line_terminator do
83+
one_of(char(), [
84+
"\u{000A}",
85+
"\u{000D}",
86+
"\u{2028}",
87+
"\u{2029}"
88+
])
89+
end
90+
91+
defp apply({module, func, args}), do: apply(module, func, args)
92+
end

lib/json5/decode/null.ex

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
defmodule Json5.Decode.Null do
2+
@moduledoc """
3+
Documentation for `Json5`.
4+
"""
5+
import Combine.Parsers.Base
6+
import Combine.Parsers.Text
7+
8+
def null() do
9+
"null"
10+
|> string()
11+
|> label("null")
12+
|> map(fn _ -> nil end)
13+
end
14+
end

0 commit comments

Comments
 (0)