@@ -4,15 +4,31 @@ defmodule ComponentsGuideWeb.UniversalModulesController do
4
4
alias ComponentsGuideWeb.UniversalModulesParser , as: Parser
5
5
6
6
def index ( conn , _params ) do
7
- source = "
7
+ source = """
8
8
const pi = 3.14;
9
9
10
- const answer = 42;
10
+ export const answer = 42;
11
11
const negativeAnswer = -42;
12
12
13
13
const isEnabled = true;
14
14
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
+ """
16
32
# decoded = decode_module(source)
17
33
decoded = Parser . decode ( source )
18
34
@@ -25,123 +41,228 @@ defmodule ComponentsGuideWeb.UniversalModulesController do
25
41
end
26
42
27
43
defmodule ComponentsGuideWeb.UniversalModulesParser do
28
- def switch ( submodule , input , result ) do
44
+ def compose ( submodule , input ) do
29
45
mod = Module . concat ( __MODULE__ , submodule )
30
- apply ( mod , :decode , [ input , result ] )
46
+ apply ( mod , :decode , [ input ] )
31
47
end
32
48
49
+ # def decode(input), do: Root.decode(input, [])
33
50
def decode ( input ) , do: switch ( Root , input , [ ] )
34
51
52
+ defp switch ( submodule , input , result ) do
53
+ mod = Module . concat ( __MODULE__ , submodule )
54
+ apply ( mod , :decode , [ input , result ] )
55
+ end
56
+
35
57
defmodule Root do
58
+ defdelegate compose ( submodule , input ) , to: ComponentsGuideWeb.UniversalModulesParser
59
+
36
60
def decode ( "" , result ) , do: { :ok , Enum . reverse ( result ) }
37
61
def decode ( << "\n " , rest :: bitstring >> , result ) , do: decode ( rest , result )
38
62
def decode ( << " " , rest :: bitstring >> , result ) , do: decode ( rest , result )
39
63
def decode ( << ";" , rest :: bitstring >> , result ) , do: decode ( rest , result )
40
64
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
43
93
end
44
94
45
95
def decode ( input , result ) do
46
- { :err , :unexpected_eof , input , result }
96
+ { :error , :unexpected_eof , input , result }
47
97
end
48
98
end
49
99
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 )
53
128
end
54
129
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
61
144
end
62
145
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
68
175
identifier = reverse_identifier |> Enum . reverse ( ) |> :binary . list_to_bin ( )
69
- decode ( { identifier , :expect_expression , [ ] } , rest , result )
176
+ decode ( { identifier , :expect_expression , [ ] } , rest )
70
177
end
71
178
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 )
78
181
end
79
182
80
183
# 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
121
229
case Float . parse ( source ) do
122
230
:error ->
123
- { :err , context , source , result }
231
+ { :error , { :invalid_number , context , source } }
124
232
125
233
{ f , rest } ->
126
- decode ( { identifier , :expect_expression , [ f ] } , rest , result )
234
+ decode ( f , rest )
127
235
end
128
236
end
129
237
130
- defp decode (
131
- { identifier , :expect_expression , [ ] } = context ,
132
- << "-" , char :: utf8 , _ :: bitstring >> = source ,
133
- result
134
- )
238
+ defp decode ( [ ] = context , << "-" , char :: utf8 , _ :: bitstring >> = source )
135
239
when char in '0123456789' do
136
240
case Float . parse ( source ) do
137
241
:error ->
138
- { :err , context , source , result }
242
+ { :error , { :invalid_number , context , source } }
139
243
140
244
{ f , rest } ->
141
- decode ( { identifier , :expect_expression , [ f ] } , rest , result )
245
+ decode ( f , rest )
142
246
end
143
247
end
144
248
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
145
266
end
146
267
147
268
defmodule ComponentsGuideWeb.UniversalModulesView do
0 commit comments