Skip to content

Commit 73ca8c4

Browse files
committed
Add initial dict literal implementation
1 parent 7617b66 commit 73ca8c4

File tree

7 files changed

+341
-12
lines changed

7 files changed

+341
-12
lines changed

jscomp/syntax/src/res_core.ml

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ module LoopProgress = struct
1414
| _ :: rest -> rest
1515
end
1616

17+
type ('a, 'b) spreadInline = Spread of 'a | Inline of 'b
18+
1719
let mkLoc startLoc endLoc =
1820
Location.{loc_start = startLoc; loc_end = endLoc; loc_ghost = false}
1921

@@ -184,6 +186,7 @@ let taggedTemplateLiteralAttr =
184186
(Location.mknoloc "res.taggedTemplate", Parsetree.PStr [])
185187

186188
let spreadAttr = (Location.mknoloc "res.spread", Parsetree.PStr [])
189+
let dictAttr = (Location.mknoloc "res.dict", Parsetree.PStr [])
187190

188191
type argument = {
189192
dotted: bool;
@@ -233,6 +236,7 @@ let getClosingToken = function
233236
| Lbrace -> Rbrace
234237
| Lbracket -> Rbracket
235238
| List -> Rbrace
239+
| Dict -> Rbrace
236240
| LessThan -> GreaterThan
237241
| _ -> assert false
238242

@@ -244,7 +248,7 @@ let rec goToClosing closingToken state =
244248
| GreaterThan, GreaterThan ->
245249
Parser.next state;
246250
()
247-
| ((Token.Lbracket | Lparen | Lbrace | List | LessThan) as t), _ ->
251+
| ((Token.Lbracket | Lparen | Lbrace | List | Dict | LessThan) as t), _ ->
248252
Parser.next state;
249253
goToClosing (getClosingToken t) state;
250254
goToClosing closingToken state
@@ -1055,6 +1059,7 @@ let rec parsePattern ?(alias = true) ?(or_ = true) p =
10551059
ppat_attributes = attrs @ pat.Parsetree.ppat_attributes;
10561060
}))
10571061
| Lbracket -> parseArrayPattern ~attrs p
1062+
(* | Dict -> parseDictPattern ~attrs p *)
10581063
| Lbrace -> parseRecordPattern ~attrs p
10591064
| Underscore ->
10601065
let endPos = p.endPos in
@@ -1921,6 +1926,9 @@ and parseAtomicExpr p =
19211926
| List ->
19221927
Parser.next p;
19231928
parseListExpr ~startPos p
1929+
| Dict ->
1930+
Parser.next p;
1931+
parseDictExpr ~startPos p
19241932
| Module ->
19251933
Parser.next p;
19261934
parseFirstClassModuleExpr ~startPos p
@@ -3876,6 +3884,18 @@ and parseSpreadExprRegionWithLoc p =
38763884
Some (false, parseConstrainedOrCoercedExpr p, startPos, p.prevEndPos)
38773885
| _ -> None
38783886

3887+
and parseSpreadRecordExprRowWithStringKeyRegionWithLoc p =
3888+
let startPos = p.Parser.prevEndPos in
3889+
match p.Parser.token with
3890+
| DotDotDot ->
3891+
Parser.next p;
3892+
let expr = parseConstrainedOrCoercedExpr p in
3893+
Some (Spread expr, startPos, p.prevEndPos)
3894+
| token when Grammar.isExprStart token ->
3895+
parseRecordExprRowWithStringKey p
3896+
|> Option.map (fun parsedRow -> (Inline parsedRow, startPos, p.prevEndPos))
3897+
| _ -> None
3898+
38793899
and parseListExpr ~startPos p =
38803900
let split_by_spread exprs =
38813901
List.fold_left
@@ -3920,6 +3940,105 @@ and parseListExpr ~startPos p =
39203940
loc))
39213941
[(Asttypes.Nolabel, Ast_helper.Exp.array ~loc listExprs)]
39223942

3943+
and parseDictExpr ~startPos p =
3944+
let makeDictRowTuples ~loc idExps =
3945+
idExps
3946+
|> List.map (fun ((id, exp) : Ast_helper.lid * Parsetree.expression) ->
3947+
Ast_helper.Exp.tuple
3948+
[
3949+
Ast_helper.Exp.constant ~loc:id.loc
3950+
(Pconst_string (Longident.last id.txt, None));
3951+
exp;
3952+
])
3953+
|> Ast_helper.Exp.array ~loc
3954+
in
3955+
3956+
let makeSpreadDictRowTuples ~loc spreadDict =
3957+
Ast_helper.Exp.apply ~loc
3958+
(Ast_helper.Exp.ident ~loc ~attrs:[dictAttr]
3959+
(Location.mkloc
3960+
(Longident.Ldot
3961+
(Longident.Ldot (Longident.Lident "Js", "Dict"), "entries"))
3962+
loc))
3963+
[(Asttypes.Nolabel, spreadDict)]
3964+
in
3965+
3966+
let concatManyExpr ~loc listExprs =
3967+
Ast_helper.Exp.apply ~loc
3968+
(Ast_helper.Exp.ident ~loc ~attrs:[spreadAttr]
3969+
(Location.mkloc
3970+
(Longident.Ldot
3971+
(Longident.Ldot (Longident.Lident "Belt", "Array"), "concatMany"))
3972+
loc))
3973+
[(Asttypes.Nolabel, Ast_helper.Exp.array ~loc listExprs)]
3974+
in
3975+
3976+
let makeDictFromRowTuples ~loc arrayEntriesExp =
3977+
Ast_helper.Exp.apply ~loc
3978+
(Ast_helper.Exp.ident ~loc ~attrs:[dictAttr]
3979+
(Location.mkloc
3980+
(Longident.Ldot
3981+
(Longident.Ldot (Longident.Lident "Js", "Dict"), "fromArray"))
3982+
loc))
3983+
[(Asttypes.Nolabel, arrayEntriesExp)]
3984+
in
3985+
let split_by_spread exprs =
3986+
List.fold_left
3987+
(fun acc curr ->
3988+
match (curr, acc) with
3989+
| (Spread expr, startPos, endPos), _ ->
3990+
(* find a spread expression, prepend a new sublist *)
3991+
([], Some expr, startPos, endPos) :: acc
3992+
| ( (Inline fieldExprTuple, startPos, _endPos),
3993+
(no_spreads, spread, _accStartPos, accEndPos) :: acc ) ->
3994+
(* find a non-spread expression, and the accumulated is not empty,
3995+
* prepend to the first sublist, and update the loc of the first sublist *)
3996+
(fieldExprTuple :: no_spreads, spread, startPos, accEndPos) :: acc
3997+
| (Inline fieldExprTuple, startPos, endPos), [] ->
3998+
(* find a non-spread expression, and the accumulated is empty *)
3999+
[([fieldExprTuple], None, startPos, endPos)])
4000+
[] exprs
4001+
in
4002+
let rec getListOfEntryArraysReversed ?(accum = []) ~loc spreadSplit =
4003+
match spreadSplit with
4004+
| [] -> accum
4005+
| (idExps, None, _, _) :: tail ->
4006+
let accum = (idExps |> makeDictRowTuples ~loc) :: accum in
4007+
tail |> getListOfEntryArraysReversed ~loc ~accum
4008+
| ([], Some spread, _, _) :: tail ->
4009+
let accum = (spread |> makeSpreadDictRowTuples ~loc) :: accum in
4010+
tail |> getListOfEntryArraysReversed ~loc ~accum
4011+
| (idExps, Some spread, _, _) :: tail ->
4012+
let accum =
4013+
(spread |> makeSpreadDictRowTuples ~loc)
4014+
:: (idExps |> makeDictRowTuples ~loc)
4015+
:: accum
4016+
in
4017+
tail |> getListOfEntryArraysReversed ~loc ~accum
4018+
in
4019+
4020+
let dictExprsRev =
4021+
parseCommaDelimitedReversedList ~grammar:Grammar.RecordRowsStringKey
4022+
~closing:Rbrace ~f:parseSpreadRecordExprRowWithStringKeyRegionWithLoc p
4023+
in
4024+
Parser.expect Rbrace p;
4025+
let loc = mkLoc startPos p.prevEndPos in
4026+
let arrDictEntries =
4027+
match
4028+
dictExprsRev |> split_by_spread |> getListOfEntryArraysReversed ~loc
4029+
with
4030+
| [] -> Ast_helper.Exp.array ~loc []
4031+
(*empty case*)
4032+
(* TODO: Disallow empty dict? *)
4033+
(* single case*)
4034+
(* multiple case*)
4035+
(* | [] -> makeDictKeyTuplesArray loc [] None *)
4036+
| [singleArrDictEntries] -> singleArrDictEntries
4037+
| multipleArrDictEntries ->
4038+
multipleArrDictEntries |> List.rev |> concatManyExpr ~loc
4039+
in
4040+
makeDictFromRowTuples ~loc arrDictEntries
4041+
39234042
(* Overparse ... and give a nice error message *)
39244043
and parseNonSpreadExp ~msg p =
39254044
let () =

jscomp/syntax/src/res_grammar.ml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ type t =
5656
| TypeConstraint
5757
| AtomicTypExpr
5858
| ListExpr
59+
| DictExpr
5960
| Pattern
6061
| AttributePayload
6162
| TagNames
@@ -114,6 +115,7 @@ let toString = function
114115
| TypeConstraint -> "constraints on a type"
115116
| AtomicTypExpr -> "a type"
116117
| ListExpr -> "an ocaml list expr"
118+
| DictExpr -> "a dict literal expr"
117119
| PackageConstraint -> "a package constraint"
118120
| JsxChild -> "jsx child"
119121
| Pattern -> "pattern"
@@ -168,8 +170,8 @@ let isStructureItemStart = function
168170

169171
let isPatternStart = function
170172
| Token.Int _ | Float _ | String _ | Codepoint _ | Backtick | True | False
171-
| Minus | Plus | Lparen | Lbracket | Lbrace | List | Underscore | Lident _
172-
| Uident _ | Hash | Exception | Lazy | Percent | Module | At ->
173+
| Minus | Plus | Lparen | Lbracket | Lbrace | List | Dict | Underscore
174+
| Lident _ | Uident _ | Hash | Exception | Lazy | Percent | Module | At ->
173175
true
174176
| _ -> false
175177

@@ -267,10 +269,12 @@ let isBlockExprStart = function
267269
let isListElement grammar token =
268270
match grammar with
269271
| ExprList -> token = Token.DotDotDot || isExprStart token
270-
| ListExpr -> token = DotDotDot || isExprStart token
272+
| ListExpr | DictExpr -> token = DotDotDot || isExprStart token
271273
| PatternList -> token = DotDotDot || isPatternStart token
272274
| ParameterList -> isParameterStart token
273-
| StringFieldDeclarations -> isStringFieldDeclStart token
275+
| StringFieldDeclarations ->
276+
print_string "is string field declaration\n";
277+
isStringFieldDeclStart token
274278
| FieldDeclarations -> isFieldDeclStart token
275279
| RecordDecl -> isRecordDeclStart token
276280
| TypExprList -> isTypExprStart token || token = Token.LessThan
@@ -324,3 +328,7 @@ let isListTerminator grammar token =
324328

325329
let isPartOfList grammar token =
326330
isListElement grammar token || isListTerminator grammar token
331+
332+
let isDictElement = isListElement
333+
let isDictTerminator = isListTerminator
334+
let isPartOfDict = isPartOfList

jscomp/syntax/src/res_parsetree_viewer.ml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ let hasAwaitAttribute attrs =
104104
| _ -> false)
105105
attrs
106106

107+
let collectDictExpressions expr =
108+
(* Expresssion is either a spread dict or a tuple *)
109+
match expr.pexp_desc with
110+
| Pexp_tuple [field; expr] -> (Some (field, expr), None)
111+
| _ -> (None, Some expr)
112+
107113
let collectListExpressions expr =
108114
let rec collect acc expr =
109115
match expr.pexp_desc with
@@ -678,6 +684,37 @@ let isSpreadBeltListConcat expr =
678684
hasSpreadAttr expr.pexp_attributes
679685
| _ -> false
680686

687+
let isSpreadBeltArrayConcat expr =
688+
match expr.pexp_desc with
689+
| Pexp_ident
690+
{
691+
txt =
692+
Longident.Ldot
693+
(Longident.Ldot (Longident.Lident "Belt", "Array"), "concatMany");
694+
} ->
695+
hasSpreadAttr expr.pexp_attributes
696+
| _ -> false
697+
698+
let hasDictAttr attrs =
699+
List.exists
700+
(fun attr ->
701+
match attr with
702+
| {Location.txt = "res.dict"}, _ -> true
703+
| _ -> false)
704+
attrs
705+
706+
let isDictFromArray expr =
707+
match expr.pexp_desc with
708+
| Pexp_ident
709+
{
710+
txt =
711+
Longident.Ldot
712+
(Longident.Ldot (Longident.Lident "Js", "Dict"), "fromArray");
713+
} ->
714+
let v = hasDictAttr expr.pexp_attributes in
715+
v
716+
| _ -> false
717+
681718
(* Blue | Red | Green -> [Blue; Red; Green] *)
682719
let collectOrPatternChain pat =
683720
let rec loop pattern chain =

jscomp/syntax/src/res_parsetree_viewer.mli

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ val collectListExpressions :
5050
Parsetree.expression ->
5151
Parsetree.expression list * Parsetree.expression option
5252

53+
val collectDictExpressions :
54+
Parsetree.expression ->
55+
(Parsetree.expression * Parsetree.expression) option
56+
* Parsetree.expression option
57+
5358
type funParamKind =
5459
| Parameter of {
5560
attrs: Parsetree.attributes;
@@ -140,7 +145,11 @@ val isTemplateLiteral : Parsetree.expression -> bool
140145
val isTaggedTemplateLiteral : Parsetree.expression -> bool
141146
val hasTemplateLiteralAttr : Parsetree.attributes -> bool
142147

148+
val hasSpreadAttr : (string Location.loc * 'a) list -> bool
143149
val isSpreadBeltListConcat : Parsetree.expression -> bool
150+
val isSpreadBeltArrayConcat : Parsetree.expression -> bool
151+
val hasDictAttr : (string Location.loc * 'a) list -> bool
152+
val isDictFromArray : Parsetree.expression -> bool
144153

145154
val collectOrPatternChain : Parsetree.pattern -> Parsetree.pattern list
146155

0 commit comments

Comments
 (0)