From 6e006fce270fa8580f460bec25f26028e6178a53 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sat, 26 Sep 2026 06:18:11 +0700 Subject: [PATCH] not is a prefix word in .fln, looser than comparisons and tighter than and, with not(x) still a call --- TODO.org | 4 ---- spec-syntax.md | 2 ++ test/test_syntax.ml | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/TODO.org b/TODO.org index 819d6ee9..a9432e15 100644 --- a/TODO.org +++ b/TODO.org @@ -662,10 +662,6 @@ In a .fln buffer, ~if and(state.paused|)~ then RET leaves ~)~ at the ~if~'s colu the next argument cannot be typed where it belongs. A line inside open brackets, closer-led or not, goes to the continuation column (aligned after the opening bracket). -** NEXT not is a prefix word -Decided 2026-09-26: .fln writes ~not x~ (binding like F#'s ~not~, tighter than -~and~/~or~, looser than comparisons); ~not(x)~ keeps working as a call. - ** NEXT A let takes several bindings on indented lines Decided 2026-09-26: lines indented under a ~let~ that are ~name = v~ or ~name: T = v~ are more bindings of the same let; anything else there stays refused. flan convert writes diff --git a/spec-syntax.md b/spec-syntax.md index b63d9b39..06b1bc75 100644 --- a/spec-syntax.md +++ b/spec-syntax.md @@ -173,6 +173,8 @@ Each item: the proposal, then the reason in one line. `.init-once.counter`; rename it. **Built**, without the rename: it prints and reads back through the fallback, `defonce(.init-once.counter, i64, 7)`. - **`and`, `or`, `not` are words**, since they are Flan's own names. **Built.** + `not` is a prefix word: `not a == b` is `not (a == b)`, `not a and b` is + `(not a) and b`, and `not(x)`, glued, is the call. - **Casts and type-taking builtins are calls:** `i32(x)`, `vec-new(u8)`, `max-value(u8)`, `the([3 f32], [1 2 3.5])`. A pointer cast is the type called: `Ptr(Color)(p)` reads `((Ptr Color) p)`. **Built.** diff --git a/test/test_syntax.ml b/test/test_syntax.ml index e5de70a1..3133f094 100644 --- a/test/test_syntax.ml +++ b/test/test_syntax.ml @@ -524,6 +524,15 @@ let () = reads "chain" "x = a < b < c" "(set x (< a b c))"; reads "left to right" "x = a - b + c" "(set x (+ (- a b) c))"; reads "precedence" "x = a or b and not c == d" "(set x (or a (and b (not (= c d)))))"; + (* not is a prefix word, between and and the comparisons. *) + reads "not over a comparison" "x = not a == b" "(set x (not (= a b)))"; + reads "not under and" "x = not a and b" "(set x (and (not a) b))"; + reads "not twice" "x = not not a" "(set x (not (not a)))"; + reads "not of a group" "x = not (a or b)" "(set x (not (or a b)))"; + reads "not glued is a call" "x = not(a) or b" "(set x (or (not a) b))"; + reads "not as a statement" "if not done\n go()" "(when (not done) (go))"; + refuses "not after a comparison" "x = a == not b" "indent/operator-operand" "nothing is on its left"; + refuses "not in a spaced vector" "x = [not a b]" "indent/separate-elements" "commas"; refuses "not-equal chain" "x = a != b != c" "indent/chained-not-equal" "!=(a, b, c)"; reads "not-equal call" "x = !=(a, b, c)" "(set x (!= a b c))"; refuses "mixed comparison" "x = a < b <= c" "indent/mixed-comparison" "and"; @@ -742,6 +751,12 @@ let () = | exception e -> fail "%s: %s" name (diag_text e) in prints "compound assignment" "(defn f [] () (set x (+ x 1)))" " x += 1"; + prints "not as a word" "(defn f [] () (g (not (= a b)) (and (not x) y)))" + "g(not a == b, not x and y)"; + prints "not of a lower operator in parentheses" "(defn f [] () (g (not (or a b))))" + "g(not (a or b))"; + prints "not under a comparison in parentheses" "(defn f [] () (g (= (not a) b)))" + "g((not a) == b)"; prints "compound update" "(defn f [] () (update (at a (next)) + 1))" " a[next()] += 1"; prints "arm statements" "(defn f [] () (match s 1 (break) _ (return 2)))"