.fln has a pipe, x |> f(a), that passes x as the first argument.
This commit is contained in:
commit
21c2e78808
5
TODO.org
5
TODO.org
@ -15,6 +15,11 @@ Found porting the prelude (2026-09-26): =declare= has no statement form; a quasi
|
||||
in the middle of an expression needs =quasiquote(...)=; =if not x= plus a block reads as
|
||||
=when=, so a template wanting =(if c (do …))= keeps call syntax; a spliced let binding
|
||||
list needs =let([…]):=. Each needs a .fln spelling now that .fln is the only syntax.
|
||||
** DONE x |> f(a) is f(x, a)
|
||||
CLOSED: [2026-09-26]
|
||||
Decided (137): the pipe feeds the first argument, as Elixir's does, since Flan puts the
|
||||
thing operated on first. Rules out F#'s last-argument pipe. Below =or=; the right side is
|
||||
a name or a call to one, and the reader writes the plain call.
|
||||
** CANCELLED rand-int returns i64
|
||||
Decided 2026-09-26 (134): rand-int stays u64; write rand-int-range(lo, hi) for a signed
|
||||
range.
|
||||
|
||||
@ -77,7 +77,7 @@ fine here. Brackets and strings are still paired."
|
||||
;; that starts with one, or follows a line that ends with one, continues the
|
||||
;; line above.
|
||||
(defconst flan-fln--binops
|
||||
'("or" "and" "==" "!=" "??" "<" "<=" ">" ">=" "||" "^^" "&&" "<<" ">>" "+" "-"
|
||||
'("|>" "or" "and" "==" "!=" "??" "<" "<=" ">" ">=" "||" "^^" "&&" "<<" ">>" "+" "-"
|
||||
"*" "/" "%"))
|
||||
|
||||
(defconst flan-fln--binop-re (regexp-opt flan-fln--binops))
|
||||
@ -1492,12 +1492,36 @@ lambda's block: it ends that block, and goes to the opening line's column."
|
||||
((save-excursion (back-to-indentation) (looking-at flan-fln--clause-re))
|
||||
(or (flan-fln--clause-columns (match-string-no-properties 1) (point))
|
||||
(flan-fln--block-levels (point))))
|
||||
((flan-fln--starts-with-pipe-p (point))
|
||||
(flan-fln--pipe-columns prev))
|
||||
((or (flan-fln--starts-with-op-p (point))
|
||||
(flan-fln--ends-in-op-p prev))
|
||||
(list (+ (flan-fln--indent-at (flan-fln--logical-start prev))
|
||||
flan-fln-indent-offset)))
|
||||
(t (flan-fln--block-levels (point))))))))))
|
||||
|
||||
(defun flan-fln--starts-with-pipe-p (pos)
|
||||
"Non-nil if POS's line starts with the pipe `|>'."
|
||||
(save-excursion
|
||||
(goto-char pos)
|
||||
(back-to-indentation)
|
||||
(looking-at "|>\\(?:[ \t]\\|$\\)")))
|
||||
|
||||
(defun flan-fln--pipe-columns (prev)
|
||||
"Columns for a line starting with `|>' whose code line above is PREV.
|
||||
Under the pipe above it when PREV starts with one. Else under the value
|
||||
PREV's statement binds -- `let t = x', then `|> f()' under the `x' -- and
|
||||
one level in from the statement."
|
||||
(if (flan-fln--starts-with-pipe-p prev)
|
||||
(list (flan-fln--indent-at prev))
|
||||
(let* ((start (flan-fln--logical-start prev))
|
||||
(in (+ (flan-fln--indent-at start) flan-fln-indent-offset))
|
||||
(v (flan-fln--value-start start))
|
||||
(vcol (and v
|
||||
(< v (save-excursion (goto-char start) (line-end-position)))
|
||||
(save-excursion (goto-char v) (current-column)))))
|
||||
(if (and vcol (> vcol in)) (list vcol in) (list in)))))
|
||||
|
||||
(defun flan-fln-indent-line ()
|
||||
"Indent the line to a block column.
|
||||
A line with text at a valid column stays there: its column is its meaning,
|
||||
@ -1891,6 +1915,8 @@ lambda or a `Fn(...)' type, and not after a match arm's."
|
||||
;; The optional marks: `x ?? d', the unwrap of `x!' and the `?' of a
|
||||
;; chain, `a?.b' and `a?[i]'. A type's `T?' stays the type's.
|
||||
("[ \t]\\(\\?\\?\\)[ \t]" 1 font-lock-keyword-face)
|
||||
;; The pipe, `x |> f(a)', inside a line or starting one.
|
||||
("\\(?:^[ \t]*\\|[ \t]\\)\\(|>\\)\\(?:[ \t]\\|$\\)" 1 font-lock-keyword-face)
|
||||
("[])[:alnum:]_]\\(!\\)\\(?:[^=]\\|$\\)" 1 font-lock-keyword-face)
|
||||
("[])[:alnum:]_]\\(\\?\\)[.[]" 1 font-lock-keyword-face)
|
||||
(,(concat "\\_<" (regexp-opt flan--constants t) "\\_>")
|
||||
|
||||
@ -950,6 +950,31 @@ defconst(k, 3)
|
||||
(forward-line 2)
|
||||
(test-flan--check "a line starting with ?? continues"
|
||||
(flan-fln--continuation-p (point))))
|
||||
;; The pipe: a line starting with `|>' goes under the value the statement
|
||||
;; binds, then under the pipe above it. The first `|' of each text is point.
|
||||
(test-flan-fln--is "a first |> goes under the let's value"
|
||||
(test-flan-fln--tabs "let total = get(g)\n||> f()" 1) 12)
|
||||
(test-flan-fln--is "and a second TAB one level in from the let"
|
||||
(test-flan-fln--tabs "let total = get(g)\n||> f()" 2) 2)
|
||||
(with-temp-buffer
|
||||
(insert "let total = get(g)\n |> f()\n|> g()")
|
||||
(flan-fln-mode)
|
||||
(flan-fln-indent-line)
|
||||
(test-flan-fln--is "a |> under a |> lines up with it" (current-indentation) 12))
|
||||
(test-flan-fln--is "a |> under a bare statement is one level in"
|
||||
(test-flan-fln--tabs "fn f() -> ()\n get(g)\n||> f()" 1) 4)
|
||||
(test-flan-fln--in "|let y = x |> f()\nz = a\n |> g\nw = b |>\n h\n"
|
||||
(font-lock-ensure)
|
||||
(test-flan-fln--is "a |> inside a line is marked"
|
||||
(save-excursion (search-forward "x |>") (get-text-property (- (point) 2) 'face))
|
||||
'font-lock-keyword-face)
|
||||
(test-flan-fln--is "a |> starting a line is marked"
|
||||
(save-excursion (search-forward " |>") (get-text-property (- (point) 2) 'face))
|
||||
'font-lock-keyword-face)
|
||||
(forward-line 2)
|
||||
(test-flan--check "a line starting with |> continues" (flan-fln--continuation-p (point)))
|
||||
(forward-line 2)
|
||||
(test-flan--check "a line after a trailing |> continues it" (flan-fln--continuation-p (point))))
|
||||
(test-flan-fln--is "else goes to its if's column, whatever the depth"
|
||||
(test-flan-fln--tabs "if a\n if b\n c\n |else" 1) 2)
|
||||
(test-flan-fln--is "and a second TAB to the outer if's"
|
||||
|
||||
@ -479,7 +479,7 @@ and list f h args =
|
||||
((if t <> "" && t.[0] = '~' then "~(" ^ t ^ ")" else "~" ^ t), 13)
|
||||
| Form.Sym "unquote-splicing", [ x ] -> ("~@" ^ at 13 x, 13)
|
||||
| Form.Sym s, _ :: _ :: _
|
||||
when R.is_binop (infix_op s) && s <> "==" && s <> "??"
|
||||
when R.is_binop (infix_op s) && s <> "==" && s <> "??" && s <> "|>"
|
||||
&& not (s = "!=" && List.length args > 2) ->
|
||||
let op = infix_op s in
|
||||
let lvl = Option.get (R.binop_level op) in
|
||||
|
||||
@ -57,10 +57,21 @@ let binops =
|
||||
("==", 4); ("!=", 4); ("<", 4); ("<=", 4); (">", 4); (">=", 4);
|
||||
("||", 5); ("^^", 6); ("&&", 7);
|
||||
("<<", 8); (">>", 8); ("+", 9); ("-", 9); ("*", 10); ("/", 10); ("%", 10);
|
||||
(* [??] is here for the line rules — a line ending in it, or one starting
|
||||
with it, continues — and is read by [operand] between 4 and 5, so no
|
||||
level of [binary]'s is its own. *)
|
||||
("??", 0) ]
|
||||
(* [??] and [|>] are here for the line rules — a line ending in one, or
|
||||
starting with one, continues. [operand] reads [??] between 4 and 5,
|
||||
and [binary] reads [|>] below [or]; no level of [binary_]'s climb is
|
||||
their own. *)
|
||||
("??", 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 is_binop s = binop_level s <> None
|
||||
@ -1066,7 +1077,107 @@ let and_rewrite (xs : Form.t list) ops =
|
||||
separated only by whitespace. *)
|
||||
let rec expr p : Form.t * int = binary p 1
|
||||
|
||||
(* [x |> f(a)] is [f(x, a)] and [x |> f] is [f(x)] (decision 137): the value
|
||||
is the first argument, as Flan puts the thing operated on first. It is
|
||||
below [or], so each side is a whole level-1 expression, and it reads to
|
||||
the plain call, so nothing after the reader knows a pipe was written.
|
||||
The right side is a name or a call to one and nothing else: those are the
|
||||
only shapes the first-argument rewrite has one meaning for. *)
|
||||
and binary p lvl : Form.t * int =
|
||||
if lvl <> 1 then binary_ p lvl
|
||||
else
|
||||
let l0 = (peek p).loc in
|
||||
let ((first, _) as fp) = binary_ p 1 in
|
||||
let rec more (x : Form.t) =
|
||||
match (peek p).tok with
|
||||
| NAME "|>" when not ((peek_at p 1).tok = LP && not (peek_at p 1).sp) ->
|
||||
let ot = advance p in
|
||||
if not (ot.sp && (peek p).sp) then
|
||||
failk "unspaced-operator" ot.loc
|
||||
"|> is an operator here, and a binary operator has a space on each \
|
||||
side: x |> f(a)";
|
||||
let i0 = p.i 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 head_at (h : Form.t) =
|
||||
h.loc.Loc.line = t0.loc.Loc.line && h.loc.Loc.col = t0.loc.Loc.col
|
||||
in
|
||||
let glued_paren () =
|
||||
i0 + 1 < Array.length p.toks
|
||||
&& p.toks.(i0 + 1).tok = LP && not p.toks.(i0 + 1).sp
|
||||
in
|
||||
let call =
|
||||
match t0.tok, rhs.v with
|
||||
| NAME _, Form.Sym _ when rl = 13 && p.i = i0 + 1 -> Some (rhs, [])
|
||||
| NAME _, Form.List (({ v = Form.Sym _; _ } as h) :: args)
|
||||
when rl = 12 && head_at h && (last p).tok = RP && glued_paren () ->
|
||||
Some (h, args)
|
||||
| _ -> None
|
||||
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
|
||||
| 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 ->
|
||||
failk "pipe-target" rhs.loc
|
||||
"the right side of |> is a function name or a call, and %s is \
|
||||
neither. The value on the left becomes the first argument:\n\n\
|
||||
\ x |> f(a, b) is f(x, a, b)\n\
|
||||
\ x |> f is f(x)"
|
||||
(if t0.tok = LP && rl = 13 then "(" ^ text_of rhs ^ ")"
|
||||
else text_of rhs))
|
||||
| _ -> x
|
||||
in
|
||||
let r = more first in
|
||||
if r == first then fp else (r, 1)
|
||||
|
||||
and binary_ p lvl : Form.t * int =
|
||||
if lvl = 3 then not_ p
|
||||
else if lvl > 10 then unary p
|
||||
else
|
||||
@ -1447,9 +1558,27 @@ and as_head p (c : Form.t) =
|
||||
in
|
||||
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
|
||||
"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
|
||||
|
||||
(* The if an [if let] head was read into, rewritten to (if-let [P v] then
|
||||
|
||||
@ -122,7 +122,7 @@ Each item: the proposal, then the reason in one line.
|
||||
a lambda's block there, laid out as at the top level against the column its
|
||||
line starts at, and the block ends where the enclosing bracket closes.*
|
||||
- **Continuation outside brackets:** a line that starts with a spaced infix
|
||||
operator (`+`, `and`, `==`, …) continues the previous line; so does a line
|
||||
operator (`+`, `and`, `==`, `|>`, …) continues the previous line; so does a line
|
||||
after one that ends in a spaced infix operator. (F# `LexFilter.fs` 360-380,
|
||||
1850-1870, 2345-2360.) No `\` continuation. **Built** (`=` does not
|
||||
continue: `let x =` plus a block is a block value). A continuation line must
|
||||
@ -163,11 +163,38 @@ Each item: the proposal, then the reason in one line.
|
||||
|
||||
### Expressions
|
||||
|
||||
- **Precedence**, low to high: `or` < `and` < `not` < comparisons
|
||||
- **Precedence**, low to high: `|>` < `or` < `and` < `not` < comparisons
|
||||
(`== != < <= > >=`) < `??` < `||` < `^^` < `&&` < `<< >>` < `+ -` < `* / %` <
|
||||
prefix `-` and `~~` < postfix (call, index, field, `!`, `?`, `?.`). **Built.** An operator
|
||||
glued to `(` is always a call. The bit operators sit where Python and Rust
|
||||
put them, so `x && mask == 0` is `(x && mask) == 0`.
|
||||
- **The pipe** (decision 137): `x |> f(a, b)` reads `(f x a b)`; `x |> f()`
|
||||
and `x |> f` read `(f x)`. The value on the left is the first argument. A
|
||||
chain runs left to right: `get(grid, r, c) |> or-else(empty) |> is-empty-cell()`
|
||||
reads `(is-empty-cell (or-else (get grid r c) empty))`. The name may be
|
||||
qualified, `x |> m/f(a)`, and the call may take a block, `xs |> each():`.
|
||||
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 == b |> f()` is `f(a == b)`, `not x |> f()` is `f(not x)` and
|
||||
`a or b |> f()` is `f(a or b)`. When the right side names a function, 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`,
|
||||
`x |> a + b`, `x |> a.b`, `x |> f(a).x`, `x |> f(a)(b)`, a word such as
|
||||
`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)
|
||||
|> or-else(empty)
|
||||
|> is-empty-cell()
|
||||
```
|
||||
|
||||
The reader writes the plain call, so nothing after it knows a pipe was
|
||||
written. `|>(a, b)`, glued, is a call to a function named `|>`. **Built.**
|
||||
- **The bit operators** are `a && b`, `a || b`, `a ^^ b` and `~~a`, reading
|
||||
`(bit-and a b)`, `(bit-or a b)`, `(bit-xor a b)` and `(bit-not a)`. They take
|
||||
integers; `and`, `or` and `not` are the logical ones. `~~` is one token, so a
|
||||
|
||||
42
test/programs/pipe.fln
Normal file
42
test/programs/pipe.fln
Normal file
@ -0,0 +1,42 @@
|
||||
;; The pipe x |> f(a) is f(x, a): the value on the left is the first argument,
|
||||
;; evaluated before the others. Typed, dyn, chained, multi-line and qualified.
|
||||
|
||||
import shape "pkgs/shape"
|
||||
import area "pkgs/area"
|
||||
|
||||
fn add(a: i32, b: i32) -> i32 = a + b
|
||||
fn scale(a: i32, k: i32) -> i32 = a * k
|
||||
fn twice(a: i32) -> i32 = a * 2
|
||||
fn sub3(a: i32, b: i32, c: i32) -> i32 = a - b - c
|
||||
|
||||
;; Prints its argument, to show the order the arguments run in.
|
||||
fn note(n: i32) -> i32
|
||||
println(n)
|
||||
n
|
||||
|
||||
fn held-or(o: i32?, d: i32) -> i32 = o ?? d
|
||||
|
||||
fn bump(x) = x + 1
|
||||
|
||||
fn main()
|
||||
println(3 |> add(4))
|
||||
println(3 |> twice)
|
||||
println(3 |> twice())
|
||||
println(1 |> add(2) |> scale(10) |> twice)
|
||||
;; Below or and the arithmetic: the whole left side is piped.
|
||||
println(2 + 3 |> scale(2))
|
||||
let n: i32? = None
|
||||
println(n ?? 4 |> twice)
|
||||
println(1 == 1 |> not)
|
||||
let total = 10
|
||||
|> add(5)
|
||||
|> scale(2)
|
||||
println(total)
|
||||
let other = 10 |>
|
||||
sub3(1, 2)
|
||||
println(other)
|
||||
println(Some(7) |> held-or(0), n |> held-or(-1))
|
||||
println(shape/box(3, 2) |> area/of)
|
||||
println(5 |> bump |> bump)
|
||||
;; The left side runs first, then the other arguments in order: 1 2 3.
|
||||
println(note(1) |> sub3(note(2), note(3)))
|
||||
@ -2276,6 +2276,12 @@ let () =
|
||||
[ ("optionals.fln", optionals_out); ("optionals-dyn.fln", optionals_dyn_out);
|
||||
(* x? tests and narrows, e? as g names what it found (decision 133). *)
|
||||
("presence.fln", "true false true\n6\n-1\n3\n101 209 0\n11\n42\n2\nabsent\n6\nfalse true\n3\n6\n15\n"); ("presence-dyn.fln", "true false\n103 209 0\nno pet\nann\n3 2\n") ];
|
||||
(* The pipe (decision 137): chains, multi-line, qualified, dyn, and the
|
||||
left side run before the other arguments (the last 1 2 3). *)
|
||||
(let want = "7\n6\n6\n60\n10\n8\nfalse\n30\n7\n7 -1\n6\n7\n1\n2\n3\n-4\n" in
|
||||
outputs "pipe.fln" "programs/pipe.fln" want;
|
||||
outputs ~opt:"-O0" "pipe.fln, -O0" "programs/pipe.fln" want;
|
||||
outputs ~x86:true "pipe.fln, --x86" "programs/pipe.fln" want);
|
||||
(* x! over nothing traps at its site and names the expression. *)
|
||||
List.iter
|
||||
(fun (x86, arg, want) ->
|
||||
|
||||
@ -1417,6 +1417,61 @@ let () =
|
||||
reads "while e? as g" "while pop(s)? as x\n f(x)"
|
||||
"(while true (if-let [x (pop s)] (do (f x)) (break)))";
|
||||
refuses "as after no test" "if x as y\n y" "indent/as-test" "Write x? as name";
|
||||
(* Decision 137: x |> f(a) is f(x, a), below or. *)
|
||||
reads "|> into a call" "y = x |> f(1, 2)" "(set y (f x 1 2))";
|
||||
reads "|> into an empty call" "y = x |> f()" "(set y (f x))";
|
||||
reads "|> into a name" "y = x |> f" "(set y (f x))";
|
||||
reads "|> into a case" "y = x |> Some" "(set y (Some x))";
|
||||
reads "|> chains left to right"
|
||||
"y = get(grid, r, c) |> or-else(empty) |> is-empty-cell()"
|
||||
"(set y (is-empty-cell (or-else (get grid r c) empty)))";
|
||||
reads "|> into a qualified name" "y = x |> m/f(a) |> m/g" "(set y (m/g (m/f x a)))";
|
||||
reads "|> is below arithmetic" "y = a + 1 |> f()" "(set y (f (+ a 1)))";
|
||||
reads "|> is below ??" "y = a ?? b |> f()" "(set y (f (?? a b)))";
|
||||
reads "|> is below comparisons" "y = a == b |> f()" "(set y (f (= a b)))";
|
||||
reads "|> is below not" "y = not x |> f()" "(set y (f (not x)))";
|
||||
reads "|> is below or" "y = a or b and c |> f()" "(set y (f (or a (and b c))))";
|
||||
reads "|> in an argument" "g(x |> f, 2)" "(g (f x) 2)";
|
||||
reads "|> in a condition" "if x |> is-empty() then 1 else 2" "(if (is-empty x) 1 2)";
|
||||
reads "|> starting lines" "let y = get(g, r)\n |> or-else(0)\n |> f()\ny"
|
||||
"(def y dyn (f (or-else (get g r) 0)))\ny";
|
||||
reads "|> ending a line" "let y = get(g, r) |>\n or-else(0)\ny"
|
||||
"(def y dyn (or-else (get g r) 0))\ny";
|
||||
reads "|> under a statement" "x\n |> f(1)\n |> g" "(g (f x 1))";
|
||||
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))";
|
||||
refuses "|> into a number" "y = x |> 3" "indent/pipe-target" "3 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" "(x |> a).b";
|
||||
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 an index" "y = x |> a[1]" "indent/pipe-target" "a[1]";
|
||||
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 "|> 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 "|> in a list separated by spaces" "y = [x |> f() 2]" "indent/separate-elements" "commas";
|
||||
(match read "y = |>(a, b)" with
|
||||
| forms ->
|
||||
let got = Indent_printer.program ~source:"y = |>(a, b)" forms in
|
||||
if not (Test_support.contains got "|>(a, b)") then
|
||||
fail "a call to |> prints as a call: %S" got
|
||||
| exception e -> fail "a call to |> prints as a call: %s" (diag_text e));
|
||||
refused "present-i32.fln" "fn main()\n let x = 5\n println(x?)\n"
|
||||
[ "x is i32, which always holds a value, so x? has nothing to test";
|
||||
"a yes-or-no name starts with is- or has-, as in is-x" ];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user