Skip to content

Commit c249300

Browse files
committed
Support JSON values
1 parent f22e975 commit c249300

File tree

2 files changed

+200
-79
lines changed

2 files changed

+200
-79
lines changed

apps/components_guide_web/lib/components_guide_web/controllers/universal_modules_controller.ex

Lines changed: 199 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,31 @@ defmodule ComponentsGuideWeb.UniversalModulesController do
44
alias ComponentsGuideWeb.UniversalModulesParser, as: Parser
55

66
def index(conn, _params) do
7-
source = "
7+
source = """
88
const pi = 3.14;
99
10-
const answer = 42;
10+
export const answer = 42;
1111
const negativeAnswer = -42;
1212
1313
const isEnabled = true;
1414
const verbose = false;
15-
"
15+
const alwaysNull = null;
16+
17+
export const dateFormat = "YYYY/MM/DD";
18+
19+
const array = [1, 2, 3];
20+
const arrayMultiline = [
21+
4,
22+
5,
23+
6
24+
];
25+
export const flavors = ["vanilla", "chocolate", "caramel", "raspberry"];
26+
27+
const object = { "key": "value" };
28+
29+
function hello() {}
30+
function* gen() {}
31+
"""
1632
# decoded = decode_module(source)
1733
decoded = Parser.decode(source)
1834

@@ -25,123 +41,228 @@ defmodule ComponentsGuideWeb.UniversalModulesController do
2541
end
2642

2743
defmodule ComponentsGuideWeb.UniversalModulesParser do
28-
def switch(submodule, input, result) do
44+
def compose(submodule, input) do
2945
mod = Module.concat(__MODULE__, submodule)
30-
apply(mod, :decode, [input, result])
46+
apply(mod, :decode, [input])
3147
end
3248

49+
# def decode(input), do: Root.decode(input, [])
3350
def decode(input), do: switch(Root, input, [])
3451

52+
defp switch(submodule, input, result) do
53+
mod = Module.concat(__MODULE__, submodule)
54+
apply(mod, :decode, [input, result])
55+
end
56+
3557
defmodule Root do
58+
defdelegate compose(submodule, input), to: ComponentsGuideWeb.UniversalModulesParser
59+
3660
def decode("", result), do: {:ok, Enum.reverse(result)}
3761
def decode(<<"\n", rest::bitstring>>, result), do: decode(rest, result)
3862
def decode(<<" ", rest::bitstring>>, result), do: decode(rest, result)
3963
def decode(<<";", rest::bitstring>>, result), do: decode(rest, result)
4064

41-
def decode(<<"const ", rest::bitstring>>, result) do
42-
ComponentsGuideWeb.UniversalModulesParser.switch(Const, rest, result)
65+
def decode(<<"const ", _::bitstring>> = input, result) do
66+
case compose(Const, input) do
67+
{:ok, term, rest} ->
68+
decode(rest, [term | result])
69+
70+
{:error, reason} ->
71+
{:error, reason}
72+
end
73+
end
74+
75+
def decode(<<"export ", _::bitstring>> = input, result) do
76+
case compose(Export, input) do
77+
{:ok, term, rest} ->
78+
decode(rest, [term | result])
79+
80+
{:error, reason} ->
81+
{:error, reason}
82+
end
83+
end
84+
85+
def decode(<<"function", _::bitstring>> = input, result) do
86+
case compose(Function, input) do
87+
{:ok, term, rest} ->
88+
decode(rest, [term | result])
89+
90+
{:error, reason} ->
91+
{:error, reason}
92+
end
4393
end
4494

4595
def decode(input, result) do
46-
{:err, :unexpected_eof, input, result}
96+
{:error, :unexpected_eof, input, result}
4797
end
4898
end
4999

50-
defmodule Const do
51-
def decode(input, result) do
52-
decode({:expect_identifier, []}, input, result)
100+
defmodule Export do
101+
def decode(<<"export ", rest::bitstring>>) do
102+
case ComponentsGuideWeb.UniversalModulesParser.compose(Const, rest) do
103+
{:ok, term, rest} ->
104+
{:ok, {:export, term}, rest}
105+
106+
{:error, reason} ->
107+
{:error, reason}
108+
end
109+
end
110+
end
111+
112+
defmodule Function do
113+
def decode(<<"function", rest::bitstring>>),
114+
do: decode(%{generator_mark: nil, name: nil, args: nil, body: nil}, rest)
115+
116+
defp decode(context, <<" ", rest::bitstring>>),
117+
do: decode(context, rest)
118+
119+
defp decode(%{generator_mark: nil, name: nil, args: nil} = context, <<"*", rest::bitstring>>),
120+
do: decode(%{context | generator_mark: true}, rest)
121+
122+
defp decode(%{name: nil, args: nil} = context, <<"(", rest::bitstring>>),
123+
do: decode(%{context | args: {:open, []}}, rest)
124+
125+
defp decode(%{name: reverse_name, args: nil} = context, <<"(", rest::bitstring>>) do
126+
name = reverse_name |> Enum.reverse() |> :binary.list_to_bin()
127+
decode(%{context | name: name, args: {:open, []}}, rest)
53128
end
54129

55-
defp decode(
56-
{:expect_identifier, _} = context,
57-
<<" ", rest::bitstring>>,
58-
result
59-
) do
60-
decode(context, rest, result)
130+
defp decode(%{args: {:open, args}} = context, <<")", rest::bitstring>>),
131+
do: decode(%{context | args: {:closed, args}}, rest)
132+
133+
defp decode(%{args: {:closed, _}, body: nil} = context, <<"{", rest::bitstring>>),
134+
do: decode(%{context | body: {:open, []}}, rest)
135+
136+
defp decode(%{args: {:closed, args}, name: name, body: {:open, body_items}} = context, <<"}", rest::bitstring>>) do
137+
case context.generator_mark do
138+
true ->
139+
{:ok, {:generator_function, name, args, body_items}, rest}
140+
141+
nil ->
142+
{:ok, {:function, name, args, body_items}, rest}
143+
end
61144
end
62145

63-
defp decode(
64-
{:expect_identifier, reverse_identifier},
65-
<<"=", rest::bitstring>>,
66-
result
67-
) do
146+
defp decode(%{name: nil, args: nil} = context, <<char::utf8, rest::bitstring>>),
147+
do: decode(%{context | name: [char]}, rest)
148+
149+
defp decode(%{name: name, args: nil} = context, <<char::utf8, rest::bitstring>>) do
150+
name = [char | name]
151+
decode(%{context | name: name}, rest)
152+
end
153+
end
154+
155+
defmodule Yield do
156+
def decode(<<"yield ", rest::bitstring>>) do
157+
case ComponentsGuideWeb.UniversalModulesParser.compose(Expression, rest) do
158+
{:ok, term, rest} ->
159+
{:ok, {:yield, term}, rest}
160+
161+
{:error, reason} ->
162+
{:error, reason}
163+
end
164+
end
165+
end
166+
167+
defmodule Const do
168+
def decode(<<"const ", rest::bitstring>>),
169+
do: decode({:expect_identifier, []}, rest)
170+
171+
defp decode({:expect_identifier, _} = context, <<" ", rest::bitstring>>),
172+
do: decode(context, rest)
173+
174+
defp decode({:expect_identifier, reverse_identifier}, <<"=", rest::bitstring>>) do
68175
identifier = reverse_identifier |> Enum.reverse() |> :binary.list_to_bin()
69-
decode({identifier, :expect_expression, []}, rest, result)
176+
decode({identifier, :expect_expression, []}, rest)
70177
end
71178

72-
defp decode(
73-
{:expect_identifier, reverse_identifier},
74-
<<char::utf8, rest::bitstring>>,
75-
result
76-
) do
77-
decode({:expect_identifier, [char | reverse_identifier]}, rest, result)
179+
defp decode({:expect_identifier, reverse_identifier}, <<char::utf8, rest::bitstring>>) do
180+
decode({:expect_identifier, [char | reverse_identifier]}, rest)
78181
end
79182

80183
# Skip whitespace
81-
defp decode({_, :expect_expression, []} = context, <<" ", rest::bitstring>>, result),
82-
do: decode(context, rest, result)
83-
84-
defp decode(
85-
{identifier, :expect_expression, expression},
86-
<<";", rest::bitstring>>,
87-
result
88-
),
89-
do: Root.decode(rest, [{:const, identifier, expression} | result])
90-
91-
defp decode(
92-
{identifier, :expect_expression, []},
93-
<<"true", rest::bitstring>>,
94-
result
95-
) do
96-
decode({identifier, :expect_expression, [true]}, rest, result)
97-
end
98-
99-
defp decode(
100-
{identifier, :expect_expression, []},
101-
<<"false", rest::bitstring>>,
102-
result
103-
) do
104-
decode({identifier, :expect_expression, [false]}, rest, result)
105-
end
106-
107-
defp decode(
108-
{identifier, :expect_expression, []},
109-
<<"null", rest::bitstring>>,
110-
result
111-
) do
112-
decode({identifier, :expect_expression, [nil]}, rest, result)
113-
end
114-
115-
defp decode(
116-
{identifier, :expect_expression, []} = context,
117-
<<char::utf8, _::bitstring>> = source,
118-
result
119-
)
120-
when char in '0123456789' do
184+
defp decode({_, :expect_expression, []} = context, <<" ", rest::bitstring>>),
185+
do: decode(context, rest)
186+
187+
defp decode({identifier, :expect_expression, expression}, ""),
188+
do: {:ok, {:const, identifier, expression}, ""}
189+
190+
defp decode({identifier, :expect_expression, []}, input) do
191+
case ComponentsGuideWeb.UniversalModulesParser.compose(Expression, input) do
192+
{:ok, expression, rest} ->
193+
{:ok, {:const, identifier, expression}, rest}
194+
195+
{:error, error} ->
196+
{:error, {:invalid_expression, error}}
197+
end
198+
end
199+
end
200+
201+
defmodule Expression do
202+
def decode(input), do: decode([], input)
203+
204+
defp decode(expression, <<";", rest::bitstring>>),
205+
do: {:ok, expression, rest}
206+
207+
defp decode([] = context, <<" ", rest::bitstring>>), do: decode(context, rest)
208+
defp decode([], <<"true", rest::bitstring>>), do: decode(true, rest)
209+
defp decode([], <<"false", rest::bitstring>>), do: decode(false, rest)
210+
defp decode([], <<"null", rest::bitstring>>), do: decode(nil, rest)
211+
212+
# TODO: parse JSON by finding the end character followed by a semicolon + newline.
213+
# JSON strings cannoc contain literal newlines (it’s considered to be a control character),
214+
# so instead it must be encoded as "\n". So we can use this fast to know an actual newline is
215+
# outside the JSON value.
216+
# defp decode([], <<"{", rest::bitstring>>), do: decode([nil], rest)
217+
defp decode([], <<char::utf8, _::bitstring>> = input) when char in [?[, ?{, ?"] do
218+
[encoded_json, rest] = String.split(input, ";\n", parts: 2)
219+
case Jason.decode(encoded_json) do
220+
{:ok, value} ->
221+
{:ok, value, rest}
222+
223+
{:error, error} ->
224+
{:error, error}
225+
end
226+
end
227+
228+
defp decode([] = context, <<char::utf8, _::bitstring>> = source) when char in '0123456789' do
121229
case Float.parse(source) do
122230
:error ->
123-
{:err, context, source, result}
231+
{:error, {:invalid_number, context, source}}
124232

125233
{f, rest} ->
126-
decode({identifier, :expect_expression, [f]}, rest, result)
234+
decode(f, rest)
127235
end
128236
end
129237

130-
defp decode(
131-
{identifier, :expect_expression, []} = context,
132-
<<"-", char::utf8, _::bitstring>> = source,
133-
result
134-
)
238+
defp decode([] = context, <<"-", char::utf8, _::bitstring>> = source)
135239
when char in '0123456789' do
136240
case Float.parse(source) do
137241
:error ->
138-
{:err, context, source, result}
242+
{:error, {:invalid_number, context, source}}
139243

140244
{f, rest} ->
141-
decode({identifier, :expect_expression, [f]}, rest, result)
245+
decode(f, rest)
142246
end
143247
end
144248
end
249+
250+
defmodule KnownIdentifier do
251+
defmodule Symbol do
252+
end
253+
254+
defmodule URL do
255+
end
256+
257+
defmodule URLSearchParams do
258+
end
259+
260+
def decode(<<"Symbol", rest::bitstring>>), do: {:ok, __MODULE__.Symbol, rest}
261+
def decode(<<"URL", rest::bitstring>>), do: {:ok, __MODULE__.URL, rest}
262+
def decode(<<"URLSearchParams", rest::bitstring>>), do: {:ok, __MODULE__.URLSearchParams, rest}
263+
264+
def decode(_), do: {:error, :unknown_identifier}
265+
end
145266
end
146267

147268
defmodule ComponentsGuideWeb.UniversalModulesView do

apps/components_guide_web/lib/components_guide_web/templates/universal_modules/index.html.eex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
<%= @source %>
33
</pre>
44

5-
<pre>
5+
<pre class="whitespace-pre-line">
66
<%= @decoded %>
77
</pre>

0 commit comments

Comments
 (0)