Skip to content

Commit 9a55919

Browse files
committed
Handle dict literal syntax
Closes #6545
1 parent 0b0b5c2 commit 9a55919

File tree

11 files changed

+210
-13
lines changed

11 files changed

+210
-13
lines changed

jscomp/others/belt_HashSetInt.resi

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
2424

2525
/***
26-
This module is [`Belt.HashSet`]() specialized with key type to be a primitive type.
26+
This module is [`Belt.HashSet`]() specialized with key type to be a primitive type.
2727
28-
It is more efficient in general, the API is the same with [`Belt.HashSet`]() except its key type is fixed,
29-
and identity is not needed(using the built-in one)
28+
It is more efficient in general, the API is the same with [`Belt.HashSet`]() except its key type is fixed,
29+
and identity is not needed(using the built-in one)
3030
31-
**See** [`Belt.HashSet`]()
31+
**See** [`Belt.HashSet`]()
3232
*/
3333

3434
type key = int

jscomp/others/belt_HashSetString.resi

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
2424

2525
/***
26-
This module is [`Belt.HashSet`]() specialized with key type to be a primitive type.
26+
This module is [`Belt.HashSet`]() specialized with key type to be a primitive type.
2727
28-
It is more efficient in general, the API is the same with [`Belt.HashSet`]() except its key type is fixed,
29-
and identity is not needed(using the built-in one)
28+
It is more efficient in general, the API is the same with [`Belt.HashSet`]() except its key type is fixed,
29+
and identity is not needed(using the built-in one)
3030
31-
**See** [`Belt.HashSet`]()
31+
**See** [`Belt.HashSet`]()
3232
*/
3333

3434
type key = string

jscomp/syntax/src/res_core.ml

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ let getClosingToken = function
229229
| Lbrace -> Rbrace
230230
| Lbracket -> Rbracket
231231
| List -> Rbrace
232+
| Dict -> Rbrace
232233
| LessThan -> GreaterThan
233234
| _ -> assert false
234235

@@ -240,7 +241,7 @@ let rec goToClosing closingToken state =
240241
| GreaterThan, GreaterThan ->
241242
Parser.next state;
242243
()
243-
| ((Token.Lbracket | Lparen | Lbrace | List | LessThan) as t), _ ->
244+
| ((Token.Lbracket | Lparen | Lbrace | List | Dict | LessThan) as t), _ ->
244245
Parser.next state;
245246
goToClosing (getClosingToken t) state;
246247
goToClosing closingToken state
@@ -1918,6 +1919,9 @@ and parseAtomicExpr p =
19181919
| List ->
19191920
Parser.next p;
19201921
parseListExpr ~startPos p
1922+
| Dict ->
1923+
Parser.next p;
1924+
parseDictExpr ~startPos p
19211925
| Module ->
19221926
Parser.next p;
19231927
parseFirstClassModuleExpr ~startPos p
@@ -3130,6 +3134,20 @@ and parseRecordExprRow p =
31303134
| _ -> None)
31313135
| _ -> None
31323136

3137+
and parseDictExprRow p =
3138+
match p.Parser.token with
3139+
| String s -> (
3140+
let loc = mkLoc p.startPos p.endPos in
3141+
Parser.next p;
3142+
let field = Location.mkloc (Longident.Lident s) loc in
3143+
match p.Parser.token with
3144+
| Colon ->
3145+
Parser.next p;
3146+
let fieldExpr = parseExpr p in
3147+
Some (field, fieldExpr)
3148+
| _ -> Some (field, Ast_helper.Exp.ident ~loc:field.loc field))
3149+
| _ -> None
3150+
31333151
and parseRecordExprWithStringKeys ~startPos firstRow p =
31343152
let rows =
31353153
firstRow
@@ -3923,6 +3941,30 @@ and parseListExpr ~startPos p =
39233941
loc))
39243942
[(Asttypes.Nolabel, Ast_helper.Exp.array ~loc listExprs)]
39253943

3944+
and parseDictExpr ~startPos p =
3945+
let exprs =
3946+
parseCommaDelimitedRegion ~grammar:Grammar.DictRows ~closing:Rbrace
3947+
~f:parseDictExprRow p
3948+
in
3949+
let loc = mkLoc startPos p.prevEndPos in
3950+
let toKeyValuePair recordItem =
3951+
match recordItem with
3952+
| {Location.txt = Longident.Lident key}, valueExpr ->
3953+
Some
3954+
(Ast_helper.Exp.tuple ~loc
3955+
[Ast_helper.Exp.constant ~loc (Pconst_string (key, None)); valueExpr])
3956+
| _ -> None
3957+
in
3958+
let keyValuePairs = List.filter_map toKeyValuePair exprs in
3959+
Parser.expect Rbrace p;
3960+
Ast_helper.Exp.apply ~loc
3961+
(Ast_helper.Exp.ident ~loc
3962+
(Location.mkloc
3963+
(Longident.Ldot
3964+
(Longident.Ldot (Longident.Lident "Js", "Dict"), "fromArray"))
3965+
loc))
3966+
[(Asttypes.Nolabel, Ast_helper.Exp.array ~loc keyValuePairs)]
3967+
39263968
and parseArrayExp p =
39273969
let startPos = p.Parser.startPos in
39283970
Parser.expect Lbracket p;

jscomp/syntax/src/res_grammar.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type t =
5959
| Pattern
6060
| AttributePayload
6161
| TagNames
62+
| DictRows
6263

6364
let toString = function
6465
| OpenDescription -> "an open description"
@@ -120,6 +121,7 @@ let toString = function
120121
| ExprFor -> "a for expression"
121122
| AttributePayload -> "an attribute payload"
122123
| TagNames -> "tag names"
124+
| DictRows -> "rows of a dict"
123125

124126
let isSignatureItemStart = function
125127
| Token.At | Let | Typ | External | Exception | Open | Include | Module | AtAt
@@ -219,6 +221,10 @@ let isModExprStart = function
219221
true
220222
| _ -> false
221223

224+
let isDictRowStart = function
225+
| Token.String _ -> true
226+
| _ -> false
227+
222228
let isRecordRowStart = function
223229
| Token.DotDotDot -> true
224230
| Token.Uident _ | Lident _ -> true
@@ -278,6 +284,7 @@ let isListElement grammar token =
278284
| FunctorArgs -> isFunctorArgStart token
279285
| ModExprList -> isModExprStart token
280286
| TypeParameters -> isTypeParameterStart token
287+
| DictRows -> isDictRowStart token
281288
| RecordRows -> isRecordRowStart token
282289
| RecordRowsStringKey -> isRecordRowStringKeyStart token
283290
| ArgumentList -> isArgumentStart token

jscomp/syntax/src/res_printer.ml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,16 @@ let isValidNumericPolyvarNumber (x : string) =
427427
| _ -> false)
428428
else a >= 48
429429

430+
let isTupleArray (expr : Parsetree.expression) =
431+
let isPlainTuple (expr : Parsetree.expression) =
432+
match expr with
433+
| {pexp_desc = Pexp_tuple _} -> true
434+
| _ -> false
435+
in
436+
match expr with
437+
| {pexp_desc = Pexp_array items} -> List.for_all isPlainTuple items
438+
| _ -> false
439+
430440
(* Exotic identifiers in poly-vars have a "lighter" syntax: #"ease-in" *)
431441
let printPolyVarIdent txt =
432442
(* numeric poly-vars don't need quotes: #644 *)
@@ -1377,6 +1387,45 @@ and printRecordDeclaration ~state (lds : Parsetree.label_declaration list)
13771387
Doc.rbrace;
13781388
])
13791389

1390+
and printLiteralDictExpr ~state (e : Parsetree.expression) cmtTbl =
1391+
let forceBreak =
1392+
e.pexp_loc.loc_start.pos_lnum < e.pexp_loc.loc_end.pos_lnum
1393+
in
1394+
let tupleToRow (e : Parsetree.expression) =
1395+
match e with
1396+
| {
1397+
pexp_desc =
1398+
Pexp_tuple
1399+
[
1400+
{pexp_desc = Pexp_constant (Pconst_string (name, _)); pexp_loc}; value;
1401+
];
1402+
} ->
1403+
Some (Location.mkloc (Longident.Lident name) pexp_loc, value)
1404+
| _ -> None
1405+
in
1406+
let rows =
1407+
match e with
1408+
| {pexp_desc = Pexp_array expressions} ->
1409+
List.filter_map tupleToRow expressions
1410+
| _ -> []
1411+
in
1412+
Doc.breakableGroup ~forceBreak
1413+
(Doc.concat
1414+
[
1415+
Doc.indent
1416+
(Doc.concat
1417+
[
1418+
Doc.softLine;
1419+
Doc.join
1420+
~sep:(Doc.concat [Doc.text ","; Doc.line])
1421+
(List.map
1422+
(fun row -> printBsObjectRow ~state row cmtTbl)
1423+
rows);
1424+
]);
1425+
Doc.trailingComma;
1426+
Doc.softLine;
1427+
])
1428+
13801429
and printConstructorDeclarations ~state ~privateFlag
13811430
(cds : Parsetree.constructor_declaration list) cmtTbl =
13821431
let forceBreak =
@@ -3956,6 +4005,24 @@ and printPexpApply ~state expr cmtTbl =
39564005
| [] -> doc
39574006
| attrs -> Doc.group (Doc.concat [printAttributes ~state attrs cmtTbl; doc])
39584007
)
4008+
| Pexp_apply
4009+
( {
4010+
pexp_desc =
4011+
Pexp_ident
4012+
{
4013+
txt =
4014+
Longident.Ldot
4015+
(Longident.Ldot (Lident "Js", "Dict"), "fromArray");
4016+
};
4017+
},
4018+
[(Nolabel, keyValues)] )
4019+
when isTupleArray keyValues ->
4020+
Doc.concat
4021+
[
4022+
Doc.text "dict{";
4023+
printLiteralDictExpr ~state keyValues cmtTbl;
4024+
Doc.rbrace;
4025+
]
39594026
| Pexp_apply
39604027
( {pexp_desc = Pexp_ident {txt = Longident.Ldot (Lident "Array", "get")}},
39614028
[(Nolabel, parentExpr); (Nolabel, memberExpr)] )

jscomp/syntax/src/res_scanner.ml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,16 @@ let scanIdentifier scanner =
195195
let str =
196196
(String.sub [@doesNotRaise]) scanner.src startOff (scanner.offset - startOff)
197197
in
198-
if '{' == scanner.ch && str = "list" then (
198+
match (scanner, str) with
199+
| {ch = '{'}, "list" ->
199200
next scanner;
200201
(* TODO: this isn't great *)
201-
Token.lookupKeyword "list{")
202-
else Token.lookupKeyword str
202+
Token.lookupKeyword "list{"
203+
| {ch = '{'}, "dict" ->
204+
next scanner;
205+
(* TODO: this isn't great *)
206+
Token.lookupKeyword "dict{"
207+
| _ -> Token.lookupKeyword str
203208

204209
let scanDigits scanner ~base =
205210
if base <= 10 then

jscomp/syntax/src/res_token.ml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ type t =
8888
| PercentPercent
8989
| Comment of Comment.t
9090
| List
91+
| Dict
9192
| TemplateTail of string * Lexing.position
9293
| TemplatePart of string * Lexing.position
9394
| Backtick
@@ -198,6 +199,7 @@ let toString = function
198199
| PercentPercent -> "%%"
199200
| Comment c -> "Comment" ^ Comment.toString c
200201
| List -> "list{"
202+
| Dict -> "dict{"
201203
| TemplatePart (text, _) -> text ^ "${"
202204
| TemplateTail (text, _) -> "TemplateTail(" ^ text ^ ")"
203205
| Backtick -> "`"
@@ -222,6 +224,7 @@ let keywordTable = function
222224
| "include" -> Include
223225
| "let" -> Let
224226
| "list{" -> List
227+
| "dict{" -> Dict
225228
| "module" -> Module
226229
| "mutable" -> Mutable
227230
| "of" -> Of
@@ -240,7 +243,7 @@ let keywordTable = function
240243
let isKeyword = function
241244
| Await | And | As | Assert | Constraint | Else | Exception | External | False
242245
| For | If | In | Include | Land | Let | List | Lor | Module | Mutable | Of
243-
| Open | Private | Rec | Switch | True | Try | Typ | When | While ->
246+
| Open | Private | Rec | Switch | True | Try | Typ | When | While | Dict ->
244247
true
245248
| _ -> false
246249

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// empty dict
2+
let x = dict{}
3+
4+
// one value
5+
let x = dict{"foo": "bar"}
6+
7+
// two values
8+
let x = dict{"foo": "bar", "bar": "baz"}
9+
10+
let baz = "foo"
11+
let x = dict{"foo": "bar", "bar": "baz", "baz": baz}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
let x = Js.Dict.fromArray [||]
2+
let x = Js.Dict.fromArray [|("foo", {js|bar|js})|]
3+
let x = Js.Dict.fromArray [|("foo", {js|bar|js});("bar", {js|baz|js})|]
4+
let baz = {js|foo|js}
5+
let x =
6+
Js.Dict.fromArray
7+
[|("foo", {js|bar|js});("bar", {js|baz|js});("baz", baz)|]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// empty dict
2+
let x = dict{}
3+
4+
// one value
5+
let x = dict{"foo": "bar"}
6+
7+
// two values
8+
let x = dict{"foo": "bar", "bar": "baz"}
9+
10+
let baz = "foo"
11+
let x = dict{"foo": "bar", "bar": "baz", "baz": baz}
12+
13+
// multiline
14+
let x = dict{
15+
"foo": "bar",
16+
"bar": "baz",
17+
"baz": baz
18+
}
19+
20+
let x = Js.Dict.fromArray([("foo", "bar"), ("bar", "baz")])
21+
let x = Js.Dict.fromArray([("foo", "bar"), ("bar", "baz"), ("baz", baz)])
22+
23+
let x = Js.Dict.fromArray([
24+
("foo", "bar"),
25+
("bar", "baz"),
26+
("baz", baz)
27+
])
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// empty dict
2+
let x = dict{}
3+
4+
// one value
5+
let x = dict{"foo": "bar"}
6+
7+
// two values
8+
let x = dict{"foo": "bar", "bar": "baz"}
9+
10+
// punning
11+
let baz = "foo"
12+
let x = dict{"foo": "bar", "bar": "baz", "baz": baz}
13+
14+
// multiline
15+
let x = dict{
16+
"foo": "bar",
17+
"bar": "baz",
18+
"baz": baz,
19+
}
20+
21+
let x = dict{"foo": "bar", "bar": "baz"}
22+
let x = dict{"foo": "bar", "bar": "baz", "baz": baz}
23+
24+
let x = dict{
25+
"foo": "bar",
26+
"bar": "baz",
27+
"baz": baz,
28+
}

0 commit comments

Comments
 (0)