Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compiler)!: Allow infix operators on new line #2136

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 71 additions & 73 deletions compiler/src/formatting/fmt.re
Original file line number Diff line number Diff line change
Expand Up @@ -181,49 +181,6 @@ let precedence = expr => {
};
};

let infixop = op => {
switch (op.[0]) {
| '+'
| '-'
| '*'
| '/'
| '%'
| '='
| '^'
| '<'
| '>'
| '&'
| '|'
| '?' => true
| _ when op == "is" => true
| _ when op == "isnt" => true
| _ when String.starts_with(~prefix="!=", op) => true
| _
| exception _ => false
};
};

let is_infix_op = expr => {
switch (expr.pexp_desc) {
| PExpId({txt: Identifier.IdentName({txt: op})}) => infixop(op)
| _ => false
};
};

let prefixop = op =>
switch (op.[0]) {
| '!' => true
| _
| exception _ => false
};

let is_prefix_op = expr => {
switch (expr.pexp_desc) {
| PExpId({txt: Identifier.IdentName({txt: op})}) => prefixop(op)
| _ => false
};
};

let is_keyword_function = expr => {
switch (expr.pexp_desc) {
| PExpId({txt: Identifier.IdentName({txt: "assert" | "throw" | "fail"})}) =>
Expand All @@ -236,13 +193,17 @@ let needs_grouping = (~parent, ~side: infix_side, expr) => {
switch (expr.pexp_desc, side) {
| (PExpIf(_), _) => ParenGrouping
| (PExpApp(fn1, _), Left)
when is_infix_op(fn1) && precedence(fn1) < precedence(parent) =>
when
Parser_utils.is_infix_op(fn1)
&& precedence(fn1) < precedence(parent) =>
ParenGrouping
| (PExpApp(fn1, _), Right)
when is_infix_op(fn1) && precedence(fn1) <= precedence(parent) =>
when
Parser_utils.is_infix_op(fn1)
&& precedence(fn1) <= precedence(parent) =>
ParenGrouping
| (PExpApp(fn1, _), _) =>
if (is_infix_op(fn1)) {
if (Parser_utils.is_infix_op(fn1)) {
if ((!is_math_op(parent) && !is_logic_op(parent))
&& !is_same_op(fn1, parent)) {
ParenGrouping;
Expand Down Expand Up @@ -871,7 +832,7 @@ let print_pattern = (fmt, {ppat_desc, ppat_loc}) => {
};

let print_ident_string = (fmt, ident) =>
if (infixop(ident) || prefixop(ident)) {
if (Parser_utils.infixop(ident) || Parser_utils.prefixop(ident)) {
parens(string(ident));
} else {
string(ident);
Expand Down Expand Up @@ -923,7 +884,7 @@ let print_grouped_access_expression = (fmt, expr) =>
| PExpBlock(_)
| PExpArray(_)
| PExpList(_) => fmt.print_expression(fmt, expr)
| PExpApp(func, _) when is_infix_op(func) =>
| PExpApp(func, _) when Parser_utils.is_infix_op(func) =>
parens(indent(break ++ fmt.print_expression(fmt, expr)) ++ break)
| PExpApp(_) => fmt.print_expression(fmt, expr)
| _ => parens(indent(break ++ fmt.print_expression(fmt, expr)) ++ break)
Expand Down Expand Up @@ -1515,7 +1476,7 @@ let print_expression = (fmt, ~infix_wrap=d => group(indent(d)), expr) => {
indent(
concat_map(
~lead=
first =>
((_, first)) =>
fmt.print_comment_range(
fmt,
~none=hardline,
Expand All @@ -1525,7 +1486,7 @@ let print_expression = (fmt, ~infix_wrap=d => group(indent(d)), expr) => {
first.pexp_loc,
),
~sep=
(prev, next) =>
((_, prev), (_, next)) =>
fmt.print_comment_range(
fmt,
~none=
Expand All @@ -1543,7 +1504,7 @@ let print_expression = (fmt, ~infix_wrap=d => group(indent(d)), expr) => {
next.pexp_loc,
),
~trail=
last =>
((_, last)) =>
fmt.print_comment_range(
fmt,
~block_end=true,
Expand All @@ -1552,13 +1513,21 @@ let print_expression = (fmt, ~infix_wrap=d => group(indent(d)), expr) => {
enclosing_end_location(expr.pexp_loc),
),
~f=
(~final, e) =>
(~final, (i, e)) =>
if (has_disable_formatting_comment(fmt.comments, e.pexp_loc)) {
fmt.print_original_code(fmt, e.pexp_loc);
} else {
fmt.print_expression(fmt, e);
let include_parens =
i != 0
&& Parser_utils.starts_with_negative_value(
~bypass_parens=false,
e,
);
let format_expr =
include_parens ? parens(~wrap=Fun.id) : Fun.id;
format_expr(fmt.print_expression(fmt, e));
},
exprs,
List.mapi((i, expr) => (i, expr.pblk_expr), exprs),
),
)
++ hardline,
Expand Down Expand Up @@ -1602,7 +1571,7 @@ let print_expression = (fmt, ~infix_wrap=d => group(indent(d)), expr) => {
~f=(~final, vb) => fmt.print_value_binding(fmt, vb),
vbs,
)
| PExpApp(fn, [arg]) when is_prefix_op(fn) =>
| PExpApp(fn, [arg]) when Parser_utils.is_prefix_op(fn) =>
fmt.print_infix_prefix_op(fmt, fn)
++ fmt.print_comment_range(fmt, fn.pexp_loc, arg.paa_loc)
++ (
Expand All @@ -1617,7 +1586,7 @@ let print_expression = (fmt, ~infix_wrap=d => group(indent(d)), expr) => {
| None => fmt.print_application_argument(fmt, arg)
}
)
| PExpApp(fn, [lhs, rhs]) when is_infix_op(fn) =>
| PExpApp(fn, [lhs, rhs]) when Parser_utils.is_infix_op(fn) =>
// To ensure adequate grouping/breaking of subexpressions, chains of
// binops are included in a single Doc.group, with new groups inserted
// where necessary. By default, this group indents when breaking. This
Expand Down Expand Up @@ -1647,19 +1616,18 @@ let print_expression = (fmt, ~infix_wrap=d => group(indent(d)), expr) => {
)
++ fmt.print_comment_range(
fmt,
~none=space,
~none=breakable_space,
~lead=space,
~trail=space,
~allow_breaks=false,
~trail=breakable_space,
lhs.paa_loc,
fn.pexp_loc,
)
++ fmt.print_infix_prefix_op(fmt, fn)
++ fmt.print_comment_range(
fmt,
~none=breakable_space,
~none=space,
~lead=space,
~trail=breakable_space,
~trail=space,
fn.pexp_loc,
rhs.paa_loc,
)
Expand Down Expand Up @@ -3319,6 +3287,30 @@ let print_include_declaration =
);
};

let rec exprs_with_include_parens = (stmts, prev_is_expr, acc) => {
switch (stmts) {
| [first, ...rest] =>
let wrap_in_parens =
switch (first.ptop_desc) {
| PTopExpr(expr) =>
Parser_utils.starts_with_negative_value(~bypass_parens=false, expr)
| _ => false
};
let curr_is_expr =
switch (first.ptop_desc) {
| PTopExpr(_)
| PTopLet(_) => true
| _ => false
};
exprs_with_include_parens(
rest,
curr_is_expr,
[(first, wrap_in_parens), ...acc],
);
| [] => acc
};
};

let print_module_declaration = (fmt, {pmod_name, pmod_stmts, pmod_loc}) => {
string("module")
++ fmt.print_comment_range(
Expand All @@ -3337,7 +3329,7 @@ let print_module_declaration = (fmt, {pmod_name, pmod_stmts, pmod_loc}) => {
indent(
concat_map(
~lead=
next =>
((next, _)) =>
fmt.print_comment_range(
fmt,
~none=hardline,
Expand All @@ -3347,7 +3339,7 @@ let print_module_declaration = (fmt, {pmod_name, pmod_stmts, pmod_loc}) => {
next.ptop_loc,
),
~sep=
(prev, next) =>
((prev, _), (next, _)) =>
fmt.print_comment_range(
fmt,
~none=
Expand All @@ -3365,21 +3357,23 @@ let print_module_declaration = (fmt, {pmod_name, pmod_stmts, pmod_loc}) => {
next.ptop_loc,
),
~trail=
prev =>
((prev, _)) =>
fmt.print_comment_range(
fmt,
~lead=space,
prev.ptop_loc,
enclosing_end_location(pmod_loc),
),
~f=
(~final, s) =>
(~final, (s, include_parens)) =>
if (has_disable_formatting_comment(fmt.comments, s.ptop_loc)) {
fmt.print_original_code(fmt, s.ptop_loc);
} else {
fmt.print_toplevel_stmt(fmt, s);
let format_expr =
include_parens ? parens(~wrap=Fun.id) : Fun.id;
format_expr(fmt.print_toplevel_stmt(fmt, s));
},
pmod_stmts,
List.rev(exprs_with_include_parens(pmod_stmts, false, [])),
),
)
++ hardline,
Expand Down Expand Up @@ -3999,7 +3993,7 @@ let print_program = (fmt, parsed_program) => {
| _ =>
concat_map(
~lead=
first =>
((first, _)) =>
fmt.print_comment_range(
fmt,
~none=hardline ++ hardline,
Expand All @@ -4009,7 +4003,7 @@ let print_program = (fmt, parsed_program) => {
first.ptop_loc,
),
~sep=
(prev, next) => {
((prev, _), (next, _)) => {
fmt.print_comment_range(
fmt,
~none=
Expand All @@ -4028,7 +4022,7 @@ let print_program = (fmt, parsed_program) => {
)
},
~trail=
last =>
((last, _)) =>
fmt.print_comment_range(
fmt,
~block_end=true,
Expand All @@ -4038,13 +4032,17 @@ let print_program = (fmt, parsed_program) => {
)
++ hardline,
~f=
(~final, s) =>
(~final, (s, include_parens)) =>
if (has_disable_formatting_comment(fmt.comments, s.ptop_loc)) {
fmt.print_original_code(fmt, s.ptop_loc);
} else {
fmt.print_toplevel_stmt(fmt, s);
let format_expr =
include_parens ? parens(~wrap=Fun.id) : Fun.id;
format_expr(fmt.print_toplevel_stmt(fmt, s));
},
parsed_program.statements,
List.rev(
exprs_with_include_parens(parsed_program.statements, false, []),
),
)
};

Expand Down
4 changes: 4 additions & 0 deletions compiler/src/parsing/ast_helper.re
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ module Expression = {
pexp_attributes: attributes,
pexp_loc: loc,
pexp_core_loc: core_loc,
// Placehoder; will be corrected later in parsing
pexp_in_parens: false,
};
};
let ident = (~loc, ~core_loc, ~attributes=?, a) =>
Expand Down Expand Up @@ -430,6 +432,8 @@ module Toplevel = {
ptop_attributes: attributes,
ptop_loc: loc,
ptop_core_loc: core_loc,
// Placehoder; will be corrected later in parsing
ptop_ends_semi: false,
};
};
let include_ = (~loc, ~core_loc, ~attributes=?, i) =>
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/parsing/ast_helper.rei
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ module Expression: {
~loc: loc,
~core_loc: loc,
~attributes: attributes=?,
list(expression)
list(block_expression)
) =>
expression;
let ignore: expression => expression;
Expand Down
7 changes: 6 additions & 1 deletion compiler/src/parsing/ast_mapper.re
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,12 @@ module E = {
},
)
| PExpBlock(el) =>
block(~loc, ~core_loc, ~attributes, List.map(sub.expr(sub), el))
block(
~loc,
~core_loc,
~attributes,
List.map(e => {...e, pblk_expr: sub.expr(sub, e.pblk_expr)}, el),
)
| PExpConstraint(e, t) =>
constraint_(
~loc,
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/parsing/lexer.re
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,8 @@ let rec token = lexbuf => {
| "*" => positioned(STAR)
| "/" => positioned(SLASH)
| "|" => positioned(PIPE)
| "-" => positioned(DASH)
| ("-", blank | newlines) => positioned(DASHWHITESPACE)
| "-" => positioned(DASHNOWHITESPACE)
| "->" => positioned(ARROW)
| "=>" => positioned(THICKARROW)
| "type" => positioned(TYPE)
Expand Down
Loading
Loading