2 * Copyright (c) 2016, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
10 (* If you make changes to the schema that cause it to serialize / deserialize
11 differently, please update this version number *)
12 let full_fidelity_schema_version_number = "2021-03-10-0001"
14 (* TODO: Consider basing the version number on an auto-generated
15 hash of a file rather than relying on people remembering to update it. *)
16 (* TODO: It may be worthwhile to investigate how Thrift describes data types
17 and use that standard. *)
19 include Operator_schema_definition
20 include Token_schema_definition
21 include Schema_definition
24 let add map
({ kind_name
; _
} as schema_node
) =
25 SMap.add kind_name schema_node map
27 List.fold_left
add SMap.empty
30 type_name
= "Token.t";
32 description
= "token";
34 aggregates
= [Expression
; Name
];
41 description
= "error";
53 type transformation
= {
55 func
: schema_node
-> string;
58 type token_transformation
= {
59 token_pattern
: string;
60 token_func
: token_node list
-> string;
63 type trivia_transformation
= {
64 trivia_pattern
: string;
65 trivia_func
: trivia_node list
-> string;
68 type aggregate_transformation
= {
69 aggregate_pattern
: string;
70 aggregate_func
: aggregate_type
-> string;
73 type operator_transformation
= {
74 operator_pattern
: string;
75 operator_func
: operator_node list
-> string;
78 type template_file
= {
81 transformations
: transformation list
;
82 token_transformations
: token_transformation list
;
83 token_no_text_transformations
: token_transformation list
;
84 token_variable_text_transformations
: token_transformation list
;
85 token_given_text_transformations
: token_transformation list
;
86 trivia_transformations
: trivia_transformation list
;
87 aggregate_transformations
: aggregate_transformation list
;
88 operator_transformations
: operator_transformation list
;
91 let make_template_file
92 ?
(transformations
= [])
93 ?
(token_transformations
= [])
94 ?
(token_no_text_transformations
= [])
95 ?
(token_given_text_transformations
= [])
96 ?
(token_variable_text_transformations
= [])
97 ?
(trivia_transformations
= [])
98 ?
(aggregate_transformations
= [])
99 ?
(operator_transformations
= [])
107 token_transformations
;
108 token_no_text_transformations
;
109 token_given_text_transformations
;
110 token_variable_text_transformations
;
111 trivia_transformations
;
112 aggregate_transformations
;
113 operator_transformations
;
116 module Language_flags
= struct
117 let php_and_hack = "php_and_hack"
119 let hack_only = "hack_only"
121 let is_hack_only : string -> bool = function
122 | "php_and_hack" -> false
123 | "hack_only" -> true
124 | f
-> failwith
("Unknown language flag " ^ f ^
" for token.")
127 module LF
= Language_flags
129 module Optional_flags
= struct
132 (* See documentation of token_node.allowed_as_identifier. *)
133 let allowed_as_identifier = "allowed_as_identifier"
135 let is_recognized : string -> bool = function
137 | "allowed_as_identifier" ->
141 let is_xhp : string list
-> bool = (fun flags
-> List.mem
xhp flags
)
143 let is_allowed_as_identifier : string list
-> bool = function
144 | flags
-> List.mem
allowed_as_identifier flags
147 module OF
= Optional_flags
149 let trivia_node_from_list l
=
151 | [trivia_kind
; trivia_text
] -> { trivia_kind
; trivia_text
}
152 | _
-> failwith
"bad trivia schema"
156 trivia_node_from_list
158 ["WhiteSpace"; "whitespace"];
159 ["EndOfLine"; "end_of_line"];
160 ["DelimitedComment"; "delimited_comment"];
161 ["SingleLineComment"; "single_line_comment"];
163 ["IgnoreError"; "ignore_error"];
164 ["FallThrough"; "fall_through"];
165 ["ExtraTokenError"; "extra_token_error"];
168 let escape_token_text t
=
169 (* add one extra backslash because
170 it is removed by Str.replace_first downstream *)
176 let map_and_concat_separated separator f items
=
177 String.concat separator
(List.map f items
)
179 let map_and_concat f items
= map_and_concat_separated "" f items
181 let filter_map_concat p f items
= map_and_concat f
(List.filter p items
)
183 let transform_schema f
= map_and_concat f schema
185 let transform_aggregate f
= map_and_concat f generated_aggregate_types
187 let replace pattern new_text source
=
188 Str.replace_first
(Str.regexp pattern
) new_text source
190 let generate_string template
=
191 let syntax_folder s x
= replace x
.pattern
(transform_schema x
.func
) s
in
192 let tokens_folder token_list s x
=
193 replace x
.token_pattern
(x
.token_func token_list
) s
195 let trivia_folder trivia_list s x
=
196 replace x
.trivia_pattern
(x
.trivia_func trivia_list
) s
198 let aggregate_folder s x
=
199 replace x
.aggregate_pattern
(transform_aggregate x
.aggregate_func
) s
201 let operator_folder operators s x
=
202 replace x
.operator_pattern
(x
.operator_func operators
) s
205 List.fold_left
syntax_folder template
.template template
.transformations
208 List.fold_left
(tokens_folder tokens
) result template
.token_transformations
212 (tokens_folder no_text_tokens
)
214 template
.token_no_text_transformations
218 (tokens_folder given_text_tokens
)
220 template
.token_given_text_transformations
224 (tokens_folder variable_text_tokens
)
226 template
.token_variable_text_transformations
230 (trivia_folder trivia_kinds)
232 template
.trivia_transformations
236 (operator_folder operators
)
238 template
.operator_transformations
241 List.fold_left
aggregate_folder result template
.aggregate_transformations
245 let format_ocaml src path
: string =
246 (* Write the string to a temporary file. *)
247 let tmp_filename = Filename.temp_file
"" (Filename.basename path
) in
249 let open Core_kernel
in
250 let file = Out_channel.create
tmp_filename in
251 Printf.fprintf
file "%s" src
;
252 Out_channel.close
file;
254 let ocamlformat_path =
256 (Sys.getenv_opt
"OCAMLFORMAT_PATH")
257 ~default
:"../tools/third-party/ocamlformat/ocamlformat"
260 (* Run ocamlformat on the file. *)
262 Printf.sprintf
"%s -i --name=%s %s" ocamlformat_path path
tmp_filename
264 ignore
(Sys.command
cmd);
266 (* Read the formatted file, then delete it. *)
268 In_channel.with_file
tmp_filename ~f
:(fun channel
->
269 In_channel.input_all channel
)
271 Sys.remove
tmp_filename;
274 let generate_formatted_string (template
: template_file
) : string =
275 let open Core_kernel
in
276 let s = generate_string template
in
277 let has_suffix s = String.is_suffix template
.filename ~suffix
:s in
278 if has_suffix ".ml" || has_suffix ".mli" then
279 format_ocaml s template
.filename
283 let generate_file (template
: template_file
) : unit =
284 let open Core_kernel
in
285 let filename = template
.filename in
286 let file = Out_channel.create
filename in
287 let s = generate_formatted_string template
in
288 Printf.fprintf
file "%s" s;
289 Out_channel.close
file
291 module GenerateFFJSONSchema
= struct
292 let to_json_trivia { trivia_kind
; trivia_text
} =
294 " { \"trivia_kind_name\" : \"%s\",
295 \"trivia_type_name\" : \"%s\" }"
299 let to_json_given_text x
=
301 " { \"token_kind\" : \"%s\",
302 \"token_text\" : \"%s\" },
305 (escape_token_text x
.token_text
)
307 let to_json_variable_text x
=
309 " { \"token_kind\" : \"%s\",
310 \"token_text\" : null },
314 let to_json_ast_nodes x
=
315 let mapper (f
, _
) = Printf.sprintf
"{ \"field_name\" : \"%s\" }" f
in
316 let fields = String.concat
",\n " (List.map
mapper x
.fields) in
318 " { \"kind_name\" : \"%s\",
319 \"type_name\" : \"%s\",
320 \"description\" : \"%s\",
332 let full_fidelity_json_schema_template =
335 ^
"generated JSON schema of the Hack Full Fidelity Parser AST\",
337 ^
full_fidelity_schema_version_number
344 { \"token_kind\" : \"EndOfFile\",
345 \"token_text\" : null } ],
348 { \"kind_name\" : \"Token\",
349 \"type_name\" : \"token\",
350 \"description\" : \"token\",
353 { \"field_name\" : \"leading\" },
354 { \"field_name\" : \"trailing\" } ] },
355 { \"kind_name\" : \"Missing\",
356 \"type_name\" : \"missing\",
357 \"description\" : \"missing\",
360 { \"kind_name\" : \"SyntaxList\",
361 \"type_name\" : \"syntax_list\",
362 \"description\" : \"syntax_list\",
364 \"fields\" : [ ] } ] }"
366 let full_fidelity_json_schema =
368 ~transformations
:[{ pattern
= "AST_NODES"; func
= to_json_ast_nodes }]
369 ~token_given_text_transformations
:
372 token_pattern
= "GIVEN_TEXT_TOKENS";
373 token_func
= map_and_concat to_json_given_text;
376 ~token_variable_text_transformations
:
379 token_pattern
= "VARIABLE_TEXT_TOKENS";
380 token_func
= map_and_concat to_json_variable_text;
383 ~trivia_transformations
:
386 trivia_pattern
= "TRIVIA_KINDS";
387 trivia_func
= map_and_concat_separated ",\n" to_json_trivia;
390 ~template
:full_fidelity_json_schema_template
391 ~
filename:"hphp/hack/src/parser/js/full_fidelity_schema.json"
395 let schema_as_json () =
396 generate_string GenerateFFJSONSchema.full_fidelity_json_schema