A pipe into a language word is refused, and a pipe the next operator or an as swallows is refused with the bracketed form as the fix.
This commit is contained in:
parent
5a3d480edf
commit
0c7a53f526
@ -63,6 +63,16 @@ let binops =
|
|||||||
their own. *)
|
their own. *)
|
||||||
("??", 0); ("|>", 0) ]
|
("??", 0); ("|>", 0) ]
|
||||||
|
|
||||||
|
(* The words a statement, clause or declaration starts with, which name no
|
||||||
|
function and so are refused on the right of [|>]. *)
|
||||||
|
let pipe_words =
|
||||||
|
[ "fn"; "fn-"; "def"; "once"; "const"; "struct"; "union"; "data"; "enum";
|
||||||
|
"import"; "if"; "when"; "while"; "until"; "match"; "let"; "for"; "macro";
|
||||||
|
"class"; "generic"; "multi"; "method"; "type"; "return"; "break";
|
||||||
|
"continue"; "defer"; "handler-case"; "handler-bind"; "restart-case";
|
||||||
|
"quote"; "then"; "else"; "elif"; "as"; "in"; "where"; "and"; "or"; "on";
|
||||||
|
"restart" ]
|
||||||
|
|
||||||
let binop_level s = List.assoc_opt s binops
|
let binop_level s = List.assoc_opt s binops
|
||||||
let is_binop s = binop_level s <> None
|
let is_binop s = binop_level s <> None
|
||||||
|
|
||||||
@ -1088,6 +1098,14 @@ and binary p lvl : Form.t * int =
|
|||||||
side: x |> f(a)";
|
side: x |> f(a)";
|
||||||
let i0 = p.i in
|
let i0 = p.i in
|
||||||
let t0 = p.toks.(i0) in
|
let t0 = p.toks.(i0) in
|
||||||
|
(match t0.tok with
|
||||||
|
| NAME w when List.mem w pipe_words ->
|
||||||
|
failk "pipe-keyword" t0.loc
|
||||||
|
"%s is a word of the language, not a function, so it cannot be \
|
||||||
|
the right side of |>. The right side is a function name or a \
|
||||||
|
call: x |> f(a)"
|
||||||
|
w
|
||||||
|
| _ -> ());
|
||||||
let rhs, rl = binary_ p 1 in
|
let rhs, rl = binary_ p 1 in
|
||||||
let head_at (h : Form.t) =
|
let head_at (h : Form.t) =
|
||||||
h.loc.Loc.line = t0.loc.Loc.line && h.loc.Loc.col = t0.loc.Loc.col
|
h.loc.Loc.line = t0.loc.Loc.line && h.loc.Loc.col = t0.loc.Loc.col
|
||||||
@ -1104,8 +1122,48 @@ and binary p lvl : Form.t * int =
|
|||||||
Some (h, args)
|
Some (h, args)
|
||||||
| _ -> None
|
| _ -> None
|
||||||
in
|
in
|
||||||
|
(* [a |> f ?? d] reads [a |> (f ?? d)]: the pipe is loosest. When the
|
||||||
|
leftmost operand of the right side is a target, the likely meaning
|
||||||
|
is the pipe's result under the operator, so the refusal says how
|
||||||
|
to write that. *)
|
||||||
|
(* An operator's form takes its first operand's location, so the
|
||||||
|
head's name is matched against the text too. *)
|
||||||
|
let target_at (f : Form.t) =
|
||||||
|
let starts s pre =
|
||||||
|
String.length s >= String.length pre
|
||||||
|
&& String.sub s 0 (String.length pre) = pre
|
||||||
|
in
|
||||||
|
match f.v, t0.tok with
|
||||||
|
| Form.Sym s, NAME n -> head_at f && s = n && text_of f = n
|
||||||
|
| Form.List (({ v = Form.Sym s; _ } as h) :: _), NAME n ->
|
||||||
|
head_at h && s = n && starts (text_of f) (n ^ "(")
|
||||||
|
| _ -> false
|
||||||
|
in
|
||||||
|
let rec leftmost (f : Form.t) =
|
||||||
|
if target_at f then Some f
|
||||||
|
else match f.v with
|
||||||
|
| Form.List (_ :: a :: _) -> leftmost a
|
||||||
|
| _ -> None
|
||||||
|
in
|
||||||
|
let bracketed () =
|
||||||
|
match leftmost rhs with
|
||||||
|
| None -> None
|
||||||
|
| Some f ->
|
||||||
|
let rt = text_of rhs and ft = text_of f in
|
||||||
|
let n = String.length ft in
|
||||||
|
if String.length rt > n && String.sub rt 0 n = ft then
|
||||||
|
Some (Printf.sprintf "(%s |> %s)%s" (text_of x) ft
|
||||||
|
(String.sub rt n (String.length rt - n)))
|
||||||
|
else None
|
||||||
|
in
|
||||||
(match call with
|
(match call with
|
||||||
| Some (h, args) -> more (mk p l0 (Form.List (h :: x :: args)))
|
| Some (h, args) -> more (mk p l0 (Form.List (h :: x :: args)))
|
||||||
|
| None when bracketed () <> None ->
|
||||||
|
failk "pipe-target" rhs.loc
|
||||||
|
"|> takes the whole expression on its right, so this is %s |> (%s), \
|
||||||
|
and %s is not a function name or a call. To use the pipe's \
|
||||||
|
result there, put the pipe in parentheses:\n\n %s"
|
||||||
|
(text_of x) (text_of rhs) (text_of rhs) (Option.get (bracketed ()))
|
||||||
| None ->
|
| None ->
|
||||||
failk "pipe-target" rhs.loc
|
failk "pipe-target" rhs.loc
|
||||||
"the right side of |> is a function name or a call, and %s is \
|
"the right side of |> is a function name or a call, and %s is \
|
||||||
@ -1500,9 +1558,27 @@ and as_head p (c : Form.t) =
|
|||||||
in
|
in
|
||||||
Some (Form.make (Form.Vec [ g; e ]) c.loc)
|
Some (Form.make (Form.Vec [ g; e ]) c.loc)
|
||||||
| _ ->
|
| _ ->
|
||||||
|
(* [a |> f? as g] would test [f] alone: a compound test is bracketed. *)
|
||||||
|
let t = text_of c in
|
||||||
|
let compound =
|
||||||
|
let depth = ref 0 and quoted = ref false and hit = ref false in
|
||||||
|
String.iteri
|
||||||
|
(fun i ch ->
|
||||||
|
if !quoted then
|
||||||
|
(if ch = '"' && (i = 0 || t.[i - 1] <> '\\') then quoted := false)
|
||||||
|
else
|
||||||
|
match ch with
|
||||||
|
| '"' -> quoted := true
|
||||||
|
| '(' | '[' | '{' -> incr depth
|
||||||
|
| ')' | ']' | '}' -> decr depth
|
||||||
|
| ' ' when !depth = 0 -> hit := true
|
||||||
|
| _ -> ())
|
||||||
|
t;
|
||||||
|
!hit
|
||||||
|
in
|
||||||
failk "as-test" at.loc
|
failk "as-test" at.loc
|
||||||
"as names what a test found, and %s is not one. Write %s? as name"
|
"as names what a test found, and %s is not one. Write %s? as name"
|
||||||
(text_of c) (text_of c))
|
t (if compound then "(" ^ t ^ ")" else t))
|
||||||
| _ -> None
|
| _ -> None
|
||||||
|
|
||||||
(* The if an [if let] head was read into, rewritten to (if-let [P v] then
|
(* The if an [if let] head was read into, rewritten to (if-let [P v] then
|
||||||
|
|||||||
@ -176,11 +176,16 @@ Each item: the proposal, then the reason in one line.
|
|||||||
It is the loosest operator, so each side is a whole expression:
|
It is the loosest operator, so each side is a whole expression:
|
||||||
`a + 1 |> f()` is `f(a + 1)`, `a ?? b |> f()` is `f(a ?? b)`,
|
`a + 1 |> f()` is `f(a + 1)`, `a ?? b |> f()` is `f(a ?? b)`,
|
||||||
`a == b |> f()` is `f(a == b)`, `not x |> f()` is `f(not x)` and
|
`a == b |> f()` is `f(a == b)`, `not x |> f()` is `f(not x)` and
|
||||||
`a or b |> f()` is `f(a or b)`. The left side is evaluated once, before
|
`a or b |> f()` is `f(a or b)`. When the right side names a function, the
|
||||||
the call's other arguments, as the first argument of any call is. The
|
left side is evaluated once, before the call's other arguments. A macro
|
||||||
|
receives the left side unevaluated as its first argument, as it would in
|
||||||
|
the written call: `x |> set(5)` is `set(x, 5)`, which assigns to `x`. The
|
||||||
right side is a name or a call to one; anything else — `x |> 3`,
|
right side is a name or a call to one; anything else — `x |> 3`,
|
||||||
`x |> a + b`, `x |> a.b`, `x |> f(a).x`, `x |> f(a)(b)` — is refused. A
|
`x |> a + b`, `x |> a.b`, `x |> f(a).x`, `x |> f(a)(b)`, a word such as
|
||||||
pipe line under a statement is indented past it:
|
`if`, `let` or `fn` — is refused. Since the pipe binds loosest,
|
||||||
|
`a |> f ?? d` is `a |> (f ?? d)` and is refused with the bracketed form
|
||||||
|
`(a |> f) ?? d` as the fix; likewise a test, `(a |> f)? as v`. A pipe line
|
||||||
|
under a statement is indented past it:
|
||||||
|
|
||||||
```
|
```
|
||||||
let cell = get(grid, r, c)
|
let cell = get(grid, r, c)
|
||||||
|
|||||||
@ -1441,12 +1441,27 @@ let () =
|
|||||||
reads "|> into a call with a block" "xs |> each(1):\n print(2)" "(each xs 1 (print 2))";
|
reads "|> into a call with a block" "xs |> each(1):\n print(2)" "(each xs 1 (print 2))";
|
||||||
reads "|>( is a call" "y = |>(a, b)" "(set y (|> a b))";
|
reads "|>( is a call" "y = |>(a, b)" "(set y (|> a b))";
|
||||||
refuses "|> into a number" "y = x |> 3" "indent/pipe-target" "3 is neither";
|
refuses "|> into a number" "y = x |> 3" "indent/pipe-target" "3 is neither";
|
||||||
refuses "|> into arithmetic" "y = x |> a + b" "indent/pipe-target" "a + b is neither";
|
refuses "|> into arithmetic" "y = x |> a + b" "indent/pipe-target" "\n (x |> a) + b";
|
||||||
refuses "|> into a field" "y = x |> a.b" "indent/pipe-target" "a.b is neither";
|
refuses "|> into a field" "y = x |> a.b" "indent/pipe-target" "(x |> a).b";
|
||||||
refuses "|> into a field of a call" "y = x |> f(a).x" "indent/pipe-target" "f(a).x";
|
refuses "|> into a field of a call" "y = x |> f(a).x" "indent/pipe-target" "f(a).x";
|
||||||
refuses "|> into a call's result" "y = x |> f(a)(b)" "indent/pipe-target" "f(a)(b)";
|
refuses "|> into a call's result" "y = x |> f(a)(b)" "indent/pipe-target" "f(a)(b)";
|
||||||
refuses "|> into an index" "y = x |> a[1]" "indent/pipe-target" "a[1]";
|
refuses "|> into an index" "y = x |> a[1]" "indent/pipe-target" "a[1]";
|
||||||
refuses "|> into a lambda" "y = x |> fn(a) => g(a)" "indent/pipe-target" "is neither";
|
refuses "|> into a lambda" "y = x |> fn(a) => g(a)" "indent/pipe-keyword" "fn is a word";
|
||||||
|
refuses "|> into if" "y = 1 |> if" "indent/pipe-keyword" "if is a word";
|
||||||
|
refuses "|> into let" "y = 1 |> let" "indent/pipe-keyword" "let is a word";
|
||||||
|
refuses "|> into fn" "y = 1 |> fn" "indent/pipe-keyword" "fn is a word";
|
||||||
|
reads "|> into not" "y = x |> not" "(set y (not x))";
|
||||||
|
refuses "|> before ??" "y = a |> f ?? d" "indent/pipe-target" "\n (a |> f) ?? d";
|
||||||
|
refuses "|> before or" "y = a |> f or c" "indent/pipe-target" "\n (a |> f) or c";
|
||||||
|
refuses "|> before a test" "y = a |> f?" "indent/pipe-target" "\n (a |> f)?";
|
||||||
|
refuses "|> chained before ==" "y = a |> b |> f(1) == 2" "indent/pipe-target"
|
||||||
|
"\n (a |> b |> f(1)) == 2";
|
||||||
|
refuses "|> before as" "if a |> f(1) as v\n v" "indent/as-test" "Write (a |> f(1))? as name";
|
||||||
|
refuses "|> before as over a pattern" "if a |> f as Some(v)\n v" "indent/as-test"
|
||||||
|
"Write (a |> f)? as name";
|
||||||
|
refuses "a call before as is not bracketed" "if f(x) as v\n v" "indent/as-test"
|
||||||
|
"Write f(x)? as name";
|
||||||
|
reads "|> into a macro passes the place" "y = x |> set(5)" "(set y (set x 5))";
|
||||||
refuses "|> into parentheses" "y = x |> (f)" "indent/pipe-target" "(f) is neither";
|
refuses "|> into parentheses" "y = x |> (f)" "indent/pipe-target" "(f) is neither";
|
||||||
refuses "|> unspaced on the right" "y = x |>[f]" "indent/unspaced-operator" "x |> f(a)";
|
refuses "|> unspaced on the right" "y = x |>[f]" "indent/unspaced-operator" "x |> f(a)";
|
||||||
refuses "|> at a statement's column" "f(x)\n|> g()" "indent/continuation" "starts with the operator |>";
|
refuses "|> at a statement's column" "f(x)\n|> g()" "indent/continuation" "starts with the operator |>";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user