Skip to content

Commit 52d1cfb

Browse files
authored
AST: represent concatenation and (dis)equality operators just like in the syntax. (#7248)
* AST: represent concatenation internally as "++" instead of "^". * Represent disequality as `!=` and `!==`. * Represent equality as `==` and `===`. * Update CHANGELOG.md * Adapt core error test. * Add error case for string concatenation.
1 parent 43389c9 commit 52d1cfb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+314
-264
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- AST cleanup: Remove `@res.partial` attribute from the internal representation, and add a flag to untyped and typed ASTs instead. https://github.com/rescript-lang/rescript/pull/7238 https://github.com/rescript-lang/rescript/pull/7240
2929
- AST cleanup: Remove `structure_item_desc.Pstr_class`, `signature_item_desc.Psig_class`, `structure_item_desc.Pstr_class_type`, `signature_item_desc.Psig_class_type`, `structure_item_desc.Tstr_class`, `structure_item_desc.Tstr_class_type`, `signature_item_desc.Tsig_class`, `signature_item_desc.Tsig_class_type` from AST as it is unused. https://github.com/rescript-lang/rescript/pull/7242
3030
- AST cleanup: remove "|." and rename "|." to "->" in the internal representation for the pipe operator. https://github.com/rescript-lang/rescript/pull/7244
31+
- AST cleanup: represent concatenation (`++`) and (dis)equality operators (`==`, `===`, `!=`, `!==`) just like in the syntax. https://github.com/rescript-lang/rescript/pull/7248
3132

3233
# 12.0.0-alpha.7
3334

analysis/examples/larger-project/src/res_comments_table.res

+1-1
Original file line numberDiff line numberDiff line change
@@ -1236,8 +1236,8 @@ and walkExpr = (expr, t, comments) => {
12361236
":="
12371237
| "||"
12381238
| "&&"
1239-
| "="
12401239
| "=="
1240+
| "==="
12411241
| "<"
12421242
| ">"
12431243
| "!="

analysis/src/Xform.ml

+2-2
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,15 @@ module IfThenElse = struct
9393
{
9494
pexp_desc =
9595
Pexp_ident
96-
{txt = Longident.Lident (("=" | "<>") as op)};
96+
{txt = Longident.Lident (("==" | "!=") as op)};
9797
};
9898
args = [(Nolabel, arg1); (Nolabel, arg2)];
9999
};
100100
},
101101
e1,
102102
Some e2 )
103103
when Loc.hasPos ~pos e.pexp_loc -> (
104-
let e1, e2 = if op = "=" then (e1, e2) else (e2, e1) in
104+
let e1, e2 = if op = "==" then (e1, e2) else (e2, e1) in
105105
let mkMatch ~arg ~pat =
106106
let cases =
107107
[

compiler/ml/ast_mapper_from0.ml

+21
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,27 @@ module E = struct
316316
| ( Pexp_ident ({txt = Longident.Lident "|."} as lid),
317317
[(Nolabel, _); (Nolabel, _)] ) ->
318318
{e with pexp_desc = Pexp_ident {lid with txt = Longident.Lident "->"}}
319+
| ( Pexp_ident ({txt = Longident.Lident "^"} as lid),
320+
[(Nolabel, _); (Nolabel, _)] ) ->
321+
{e with pexp_desc = Pexp_ident {lid with txt = Longident.Lident "++"}}
322+
| ( Pexp_ident ({txt = Longident.Lident "<>"} as lid),
323+
[(Nolabel, _); (Nolabel, _)] ) ->
324+
{e with pexp_desc = Pexp_ident {lid with txt = Longident.Lident "!="}}
325+
| ( Pexp_ident ({txt = Longident.Lident "!="} as lid),
326+
[(Nolabel, _); (Nolabel, _)] ) ->
327+
{
328+
e with
329+
pexp_desc = Pexp_ident {lid with txt = Longident.Lident "!=="};
330+
}
331+
| ( Pexp_ident ({txt = Longident.Lident "="} as lid),
332+
[(Nolabel, _); (Nolabel, _)] ) ->
333+
{e with pexp_desc = Pexp_ident {lid with txt = Longident.Lident "=="}}
334+
| ( Pexp_ident ({txt = Longident.Lident "=="} as lid),
335+
[(Nolabel, _); (Nolabel, _)] ) ->
336+
{
337+
e with
338+
pexp_desc = Pexp_ident {lid with txt = Longident.Lident "==="};
339+
}
319340
| _ -> e
320341
in
321342
let process_partial_app_attribute attrs =

compiler/ml/ast_mapper_to0.ml

+15
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,21 @@ module E = struct
327327
| ( Pexp_ident ({txt = Longident.Lident "->"} as lid),
328328
[(Nolabel, _); (Nolabel, _)] ) ->
329329
{e with pexp_desc = Pexp_ident {lid with txt = Longident.Lident "|."}}
330+
| ( Pexp_ident ({txt = Longident.Lident "++"} as lid),
331+
[(Nolabel, _); (Nolabel, _)] ) ->
332+
{e with pexp_desc = Pexp_ident {lid with txt = Longident.Lident "^"}}
333+
| ( Pexp_ident ({txt = Longident.Lident "!="} as lid),
334+
[(Nolabel, _); (Nolabel, _)] ) ->
335+
{e with pexp_desc = Pexp_ident {lid with txt = Longident.Lident "<>"}}
336+
| ( Pexp_ident ({txt = Longident.Lident "!=="} as lid),
337+
[(Nolabel, _); (Nolabel, _)] ) ->
338+
{e with pexp_desc = Pexp_ident {lid with txt = Longident.Lident "!="}}
339+
| ( Pexp_ident ({txt = Longident.Lident "==="} as lid),
340+
[(Nolabel, _); (Nolabel, _)] ) ->
341+
{e with pexp_desc = Pexp_ident {lid with txt = Longident.Lident "=="}}
342+
| ( Pexp_ident ({txt = Longident.Lident "=="} as lid),
343+
[(Nolabel, _); (Nolabel, _)] ) ->
344+
{e with pexp_desc = Pexp_ident {lid with txt = Longident.Lident "="}}
330345
| _ -> e
331346
in
332347
let attrs =

compiler/ml/error_message_utils.ml

+2-1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ let error_expected_type_text ppf type_clash_context =
5555
operator
5656
| Some FunctionReturn ->
5757
fprintf ppf "But this function is expecting you to return:"
58+
| Some StringConcat -> fprintf ppf "But string concatenation is expecting:"
5859
| _ -> fprintf ppf "But it's expected to have type:"
5960

6061
let is_record_type ~extract_concrete_typedecl ~env ty =
@@ -204,7 +205,7 @@ let type_clash_context_from_function sexp sfunct =
204205
in
205206
match sfunct.Parsetree.pexp_desc with
206207
| Pexp_ident
207-
{txt = Lident ("=" | "==" | "<>" | "!=" | ">" | ">=" | "<" | "<=")} ->
208+
{txt = Lident ("==" | "===" | "!=" | "!==" | ">" | ">=" | "<" | "<=")} ->
208209
Some ComparisonOperator
209210
| Pexp_ident {txt = Lident "++"} -> Some StringConcat
210211
| Pexp_ident {txt = Lident (("/." | "*." | "+." | "-.") as operator)} ->

compiler/syntax/src/res_comments_table.ml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1338,7 +1338,7 @@ and walk_expression expr t comments =
13381338
{
13391339
txt =
13401340
Longident.Lident
1341-
( ":=" | "||" | "&&" | "=" | "==" | "<" | ">" | "!="
1341+
( ":=" | "||" | "&&" | "==" | "===" | "<" | ">" | "!="
13421342
| "!==" | "<=" | ">=" | "|>" | "+" | "+." | "-" | "-."
13431343
| "++" | "^" | "*" | "*." | "/" | "/." | "**" | "->"
13441344
| "<>" );

compiler/syntax/src/res_core.ml

+2-7
Original file line numberDiff line numberDiff line change
@@ -391,16 +391,11 @@ let build_longident words =
391391

392392
let make_infix_operator (p : Parser.t) token start_pos end_pos =
393393
let stringified_token =
394-
if token = Token.PlusPlus then "^"
395-
else if token = Token.BangEqual then "<>"
396-
else if token = Token.BangEqualEqual then "!="
397-
else if token = Token.Equal then (
394+
if token = Token.Equal then (
398395
(* TODO: could have a totally different meaning like x->fooSet(y)*)
399396
Parser.err ~start_pos ~end_pos p
400397
(Diagnostics.message "Did you mean `==` here?");
401398
"=")
402-
else if token = Token.EqualEqual then "="
403-
else if token = Token.EqualEqualEqual then "=="
404399
else Token.to_string token
405400
in
406401
let loc = mk_loc start_pos end_pos in
@@ -2327,7 +2322,7 @@ and parse_template_expr ?prefix p =
23272322
in
23282323

23292324
let hidden_operator =
2330-
let op = Location.mknoloc (Longident.Lident "^") in
2325+
let op = Location.mknoloc (Longident.Lident "++") in
23312326
Ast_helper.Exp.ident op
23322327
in
23332328
let concat (e1 : Parsetree.expression) (e2 : Parsetree.expression) =

compiler/syntax/src/res_parens.ml

+2-2
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ let rhs_binary_expr_operand parent_operator rhs =
162162
args = [(_, _left); (_, _right)];
163163
}
164164
when ParsetreeViewer.is_binary_operator operator
165-
&& not (operator_loc.loc_ghost && operator = "^") ->
165+
&& not (operator_loc.loc_ghost && operator = "++") ->
166166
let prec_parent = ParsetreeViewer.operator_precedence parent_operator in
167167
let prec_child = ParsetreeViewer.operator_precedence operator in
168168
prec_parent == prec_child
@@ -180,7 +180,7 @@ let flatten_operand_rhs parent_operator rhs =
180180
args = [(_, _left); (_, _right)];
181181
}
182182
when ParsetreeViewer.is_binary_operator operator
183-
&& not (operator_loc.loc_ghost && operator = "^") ->
183+
&& not (operator_loc.loc_ghost && operator = "++") ->
184184
let prec_parent = ParsetreeViewer.operator_precedence parent_operator in
185185
let prec_child = ParsetreeViewer.operator_precedence operator in
186186
prec_parent >= prec_child || rhs.pexp_attributes <> []

compiler/syntax/src/res_parsetree_viewer.ml

+8-8
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,8 @@ let operator_precedence operator =
270270
| ":=" -> 1
271271
| "||" -> 2
272272
| "&&" -> 3
273-
| "=" | "==" | "<" | ">" | "!=" | "<>" | "!==" | "<=" | ">=" | "|>" -> 4
274-
| "+" | "+." | "-" | "-." | "^" -> 5
273+
| "==" | "===" | "<" | ">" | "!=" | "<>" | "!==" | "<=" | ">=" | "|>" -> 4
274+
| "+" | "+." | "-" | "-." | "++" -> 5
275275
| "*" | "*." | "/" | "/." | "%" -> 6
276276
| "**" -> 7
277277
| "#" | "##" | "->" -> 8
@@ -296,9 +296,9 @@ let is_unary_expression expr =
296296
(* TODO: tweak this to check for ghost ^ as template literal *)
297297
let is_binary_operator operator =
298298
match operator with
299-
| ":=" | "||" | "&&" | "=" | "==" | "<" | ">" | "!=" | "!==" | "<=" | ">="
300-
| "|>" | "+" | "+." | "-" | "-." | "^" | "*" | "*." | "/" | "/." | "**" | "->"
301-
| "<>" | "%" ->
299+
| ":=" | "||" | "&&" | "==" | "===" | "<" | ">" | "!=" | "!==" | "<=" | ">="
300+
| "|>" | "+" | "+." | "-" | "-." | "++" | "*" | "*." | "/" | "/." | "**"
301+
| "->" | "<>" | "%" ->
302302
true
303303
| _ -> false
304304

@@ -314,14 +314,14 @@ let is_binary_expression expr =
314314
args = [(Nolabel, _operand1); (Nolabel, _operand2)];
315315
}
316316
when is_binary_operator operator
317-
&& not (operator_loc.loc_ghost && operator = "^")
317+
&& not (operator_loc.loc_ghost && operator = "++")
318318
(* template literal *) ->
319319
true
320320
| _ -> false
321321

322322
let is_equality_operator operator =
323323
match operator with
324-
| "=" | "==" | "<>" | "!=" -> true
324+
| "==" | "===" | "!=" | "!==" -> true
325325
| _ -> false
326326

327327
let is_rhs_binary_operator operator =
@@ -643,7 +643,7 @@ let is_template_literal expr =
643643
match expr.pexp_desc with
644644
| Pexp_apply
645645
{
646-
funct = {pexp_desc = Pexp_ident {txt = Longident.Lident "^"}};
646+
funct = {pexp_desc = Pexp_ident {txt = Longident.Lident "++"}};
647647
args = [(Nolabel, _); (Nolabel, _)];
648648
}
649649
when has_template_literal_attr expr.pexp_attributes ->

compiler/syntax/src/res_printer.ml

+3-12
Original file line numberDiff line numberDiff line change
@@ -3557,7 +3557,7 @@ and print_template_literal ~state expr cmt_tbl =
35573557
match expr.pexp_desc with
35583558
| Pexp_apply
35593559
{
3560-
funct = {pexp_desc = Pexp_ident {txt = Longident.Lident "^"}};
3560+
funct = {pexp_desc = Pexp_ident {txt = Longident.Lident "++"}};
35613561
args = [(Nolabel, arg1); (Nolabel, arg2)];
35623562
} ->
35633563
let lhs = walk_expr arg1 in
@@ -3662,15 +3662,6 @@ and print_unary_expression ~state expr cmt_tbl =
36623662

36633663
and print_binary_expression ~state (expr : Parsetree.expression) cmt_tbl =
36643664
let print_binary_operator ~inline_rhs operator =
3665-
let operator_txt =
3666-
match operator with
3667-
| "^" -> "++"
3668-
| "=" -> "=="
3669-
| "==" -> "==="
3670-
| "<>" -> "!="
3671-
| "!=" -> "!=="
3672-
| txt -> txt
3673-
in
36743665
let spacing_before_operator =
36753666
if operator = "->" then Doc.soft_line
36763667
else if operator = "|>" then Doc.line
@@ -3683,7 +3674,7 @@ and print_binary_expression ~state (expr : Parsetree.expression) cmt_tbl =
36833674
else Doc.line
36843675
in
36853676
Doc.concat
3686-
[spacing_before_operator; Doc.text operator_txt; spacing_after_operator]
3677+
[spacing_before_operator; Doc.text operator; spacing_after_operator]
36873678
in
36883679
let print_operand ~is_lhs ~is_multiline expr parent_operator =
36893680
let rec flatten ~is_lhs ~is_multiline expr parent_operator =
@@ -3800,7 +3791,7 @@ and print_binary_expression ~state (expr : Parsetree.expression) cmt_tbl =
38003791
match expr.pexp_desc with
38013792
| Pexp_apply
38023793
{
3803-
funct = {pexp_desc = Pexp_ident {txt = Longident.Lident "^"; loc}};
3794+
funct = {pexp_desc = Pexp_ident {txt = Longident.Lident "++"; loc}};
38043795
args = [(Nolabel, _); (Nolabel, _)];
38053796
}
38063797
when loc.loc_ghost ->

runtime/Pervasives.res

+5-5
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,17 @@ external mod: ('a, 'a) => 'a = "%mod"
5555
/* Comparisons */
5656
/* Note: Later comparisons will be converted to unified operations too */
5757

58-
external \"=": ('a, 'a) => bool = "%equal"
59-
external \"<>": ('a, 'a) => bool = "%notequal"
58+
external \"==": ('a, 'a) => bool = "%equal"
59+
external \"!=": ('a, 'a) => bool = "%notequal"
6060
external \"<": ('a, 'a) => bool = "%lessthan"
6161
external \">": ('a, 'a) => bool = "%greaterthan"
6262
external \"<=": ('a, 'a) => bool = "%lessequal"
6363
external \">=": ('a, 'a) => bool = "%greaterequal"
6464
external compare: ('a, 'a) => int = "%compare"
6565
external min: ('a, 'a) => 'a = "%min"
6666
external max: ('a, 'a) => 'a = "%max"
67-
external \"==": ('a, 'a) => bool = "%eq"
68-
external \"!=": ('a, 'a) => bool = "%noteq"
67+
external \"===": ('a, 'a) => bool = "%eq"
68+
external \"!==": ('a, 'a) => bool = "%noteq"
6969

7070
/* Boolean operations */
7171

@@ -230,7 +230,7 @@ let classify_float = (x: float): fpclass =>
230230

231231
/* String and byte sequence operations -- more in modules String and Bytes */
232232

233-
external \"^": (string, string) => string = "%string_concat"
233+
external \"++": (string, string) => string = "%string_concat"
234234

235235
/* Character operations -- more in module Char */
236236

runtime/Pervasives_mini.res

+5-5
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,17 @@ external mod: (int, int) => int = "%modint"
3434
/* Comparisons */
3535
/* Note: Later comparisons will be converted to unified operations too */
3636

37-
external \"=": ('a, 'a) => bool = "%equal"
38-
external \"<>": ('a, 'a) => bool = "%notequal"
37+
external \"==": ('a, 'a) => bool = "%equal"
38+
external \"!=": ('a, 'a) => bool = "%notequal"
3939
external \"<": ('a, 'a) => bool = "%lessthan"
4040
external \">": ('a, 'a) => bool = "%greaterthan"
4141
external \"<=": ('a, 'a) => bool = "%lessequal"
4242
external \">=": ('a, 'a) => bool = "%greaterequal"
4343
external compare: ('a, 'a) => int = "%compare"
4444
external min: ('a, 'a) => 'a = "%min"
4545
external max: ('a, 'a) => 'a = "%max"
46-
external \"==": ('a, 'a) => bool = "%eq"
47-
external \"!=": ('a, 'a) => bool = "%noteq"
46+
external \"===": ('a, 'a) => bool = "%eq"
47+
external \"!==": ('a, 'a) => bool = "%noteq"
4848

4949
/* Boolean operations */
5050

@@ -78,7 +78,7 @@ external \"/.": (float, float) => float = "%divfloat"
7878

7979
/* String operations */
8080

81-
external \"^": (string, string) => string = "%string_concat"
81+
external \"++": (string, string) => string = "%string_concat"
8282

8383
/* Unit operations */
8484

tests/build_tests/super_errors/expected/primitives2.res.expected

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
3 │
88

99
This has type: int
10-
But this function argument is expecting: string
10+
But string concatenation is expecting: string
1111

1212
You can convert int to string with Belt.Int.toString.

tests/build_tests/super_errors/expected/unicode_location.res.expected

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
3 │
99

1010
This has type: int
11-
But this function argument is expecting: string
11+
But string concatenation is expecting: string
1212

1313
You can convert int to string with Belt.Int.toString.

tests/syntax_tests/data/parsing/errors/expressions/expected/block.res.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ let findThreadByIdLinearScan [arity:2]~threads:((threads)[@res.namedArgLoc ])
7575
| Group { id } -> id
7676
| Unknown { id } ->
7777
(unknown.id -> Js.String.make) -> FBID.ofStringUnsafe in
78-
thisId == id)
78+
thisId === id)
7979
[@res.braces ])))
8080
[@res.braces ])
8181
let x = ((loop 0 (Nil -> (push doc)))[@res.braces ])

tests/syntax_tests/data/parsing/errors/structure/expected/gh16B.res.txt

+7-7
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ let wss = Server.make { port = 82 }
1616
let address = wss -> Server.address
1717
let log [arity:1]msg =
1818
Js.log
19-
(((((({js|> Server: |js})[@res.template ]) ^ msg)[@res.template ]) ^
19+
(((((({js|> Server: |js})[@res.template ]) ++ msg)[@res.template ]) ++
2020
(({js||js})[@res.template ]))[@res.template ])
2121
;;log
22-
(((((((((((((({js|Running on: |js})[@res.template ]) ^ address.address)
23-
[@res.template ]) ^ (({js|:|js})[@res.template ]))
24-
[@res.template ]) ^ (address.port -> string_of_int))
25-
[@res.template ]) ^ (({js| (|js})[@res.template ]))
26-
[@res.template ]) ^ address.family)
27-
[@res.template ]) ^ (({js|)|js})[@res.template ]))[@res.template ])
22+
(((((((((((((({js|Running on: |js})[@res.template ]) ++ address.address)
23+
[@res.template ]) ++ (({js|:|js})[@res.template ]))
24+
[@res.template ]) ++ (address.port -> string_of_int))
25+
[@res.template ]) ++ (({js| (|js})[@res.template ]))
26+
[@res.template ]) ++ address.family)
27+
[@res.template ]) ++ (({js|)|js})[@res.template ]))[@res.template ])
2828
module ClientSet =
2929
struct
3030
module T =

tests/syntax_tests/data/parsing/errors/structure/expected/letBinding.res.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@
9999

100100
let rightResource =
101101
(ur.resources).find
102-
(fun [arity:1]r -> r.account_id == ((connection.left).account).id)
102+
(fun [arity:1]r -> r.account_id === ((connection.left).account).id)
103103
let x = ((let field = p -> parseFieldDeclaration in field)[@res.braces ])
104104
let t = ((let (_, _, token) = scanner -> scan in token)[@res.braces ])
105105
let (keyTable : int Belt.Map.String.t) = [%rescript.exprhole ]

tests/syntax_tests/data/parsing/grammar/expressions/expected/arrow.res.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ let f [arity:4]~a:((a)[@res.namedArgLoc ][@attr ]) ((b)[@attrOnB ])
5252
let f [arity:1]list = list ()
5353
;;match colour with
5454
| Red when
55-
(l = l') ||
55+
(l == l') ||
5656
(Clflags.classic.contents &&
57-
((l = Nolabel) && (not (is_optional l'))))
57+
((l == Nolabel) && (not (is_optional l'))))
5858
-> (t1, t2)
5959
| _ -> ()
6060
let arr =

tests/syntax_tests/data/parsing/grammar/expressions/expected/async.res.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
let greetUser async [arity:1]userId =
22
((let name = ((getUserName userId)[@res.await ]) in
3-
({js|Hello |js} ^ name) ^ {js|!|js})
3+
({js|Hello |js} ++ name) ++ {js|!|js})
44
[@res.braces ])
55
;;async fun [arity:1]() -> 123
66
let fetch = ((async fun [arity:1]url -> browserFetch url)[@res.braces ])

0 commit comments

Comments
 (0)