diff --git a/emacs/flan.el b/emacs/flan.el index 441d261..6e16b11 100644 --- a/emacs/flan.el +++ b/emacs/flan.el @@ -2136,34 +2136,44 @@ daemon reads that as stopping on entry instead." ;; question of it: the top-level form point is *in*, which for a bare ;; `(+ 1 1)' written at column 1 is that expression and nothing else. ;; -;; Hardcoded, which is the thing to be careful about: the authority is the -;; declaration arm of `Parse.expr' in lib/parse.ml (the "is a top-level -;; declaration, not an expression" refusal, around line 458), plus the -;; `declare'/`declare-c' arm a few forms below it. This list is that set and -;; has to be changed with it. `defunion' is being renamed `defdata', with -;; `defunion' becoming a C-style untagged union; both spellings are top-level -;; declarations either way, so this list wants `defdata' adding when that -;; lands rather than a swap. +;; Hardcoded, which is the thing to be careful about: the authority is +;; `Parse.decl' in lib/parse.ml — every head it has an arm for is a +;; declaration and belongs here — and this list is that set minus `package', +;; for the reason below. Re-derived whole when it is touched, rather than +;; topped up a name at a time: patching in the one name somebody noticed is +;; exactly how it came to be missing `defdata' and all four of the object and +;; dispatch heads for weeks after they landed. (`Parse.decl's top-level +;; `(do ...)' arm is not a head — it splices the declarations inside it, and +;; the forms this list is matched against are what comes out of that.) ;; -;; `package' is deliberately not here even though flan-mode.el's -;; `flan--definers' has it. This is the set of heads that *fail* when sent to -;; the expression evaluator, which is the bug `C-x C-e' was fixed for, and a -;; `package' form does not fail — `Parse.expr' has no arm refusing it. It is -;; still a declaration, so `C-c C-c' has to keep installing it; see -;; `flan--defun-heads' below, which is this list plus that one head. +;; It used to be derived from somewhere else: the declaration arm of +;; `Parse.expr', the "is a top-level declaration, not an expression" refusal, +;; on the reading that this is the set of heads that *fail* when sent to the +;; expression evaluator. That reading does not survive `declare' and +;; `declare-c', which have been in this list from the start and which +;; `Parse.expr' has no arm for at all: sent as expressions they parse as calls +;; of an unknown name, and the message that comes back is worse than the one +;; the declaration path gives. Which is an argument for installing them, not +;; for leaving them out. So the rule is the plain one — is it a declaration — +;; and `package' is a single named exception rather than the edge of a subtler +;; rule that was never quite true. (defconst flan--declaration-heads - '("defmacro" "defn" "defvar" "defconst" "defstruct" "defunion" "defenum" - "defalias" "import" "declare" "declare-c") + '("defmacro" "defn" "defvar" "defconst" + "defstruct" "defdata" "defunion" "defenum" "defalias" + ;; The object and dispatch heads (lib/parse.ml:1277-1334), which are + ;; declarations in exactly the way `defn' is: each introduces a top-level + ;; name and none of them is an expression. + "defclass" "defgeneric" "defmulti" "defmethod" + "import" "declare" "declare-c") "Heads whose form is a declaration, and never an expression.") -;; The two keys ask *nearly* the same question, and `package' is where the two -;; readings come apart. `C-x C-e' asks "would this fail if I sent it to the -;; expression evaluator?", and a `package' form would not: `Parse.expr' has no -;; arm refusing it, so it falls through to a call of an unknown name and the -;; message would be a worse one than the declaration path gives. `C-c C-c' -;; asks the plain question — "is this a declaration?" — and `package' is one: -;; `Parse.decl' has an arm for it, `C-c C-c' has always installed it, and -;; routing it to the expression evaluator would take that away. +;; `package' is the one declaration `C-x C-e' does not claim, and it is kept +;; out to keep a behaviour rather than to follow a rule: the two keys have +;; always differed here, `C-c C-c' installing a `package' form and `C-x C-e' +;; leaving it to the expression evaluator. Nothing wants changing — a +;; `package' line is written once at the top of a file and is not a thing +;; anybody re-evaluates by hand — so the difference stays, named here where it +;; can be found rather than inferred from a list that is otherwise complete. (defconst flan--defun-heads (cons "package" flan--declaration-heads) "Heads `C-c C-c' recompiles and installs rather than evaluating.") diff --git a/emacs/test-flan.el b/emacs/test-flan.el index c41bbd7..6c218a1 100644 --- a/emacs/test-flan.el +++ b/emacs/test-flan.el @@ -623,12 +623,13 @@ already rely on it — so nothing here is a stand-in for the real thing." (test-flan--check "and draws no value beside it" (null (flan--result-overlays)))) - ;; `package' is the one head the two keys disagree about, and it is a real - ;; disagreement rather than an oversight: `Parse.expr' has no arm refusing - ;; it, so C-x C-e's "would this fail as an expression" says no, while - ;; `Parse.decl' has one and C-c C-c has always installed it. Routing both - ;; off one list would have quietly taken that away. Nothing is sent here — - ;; the claim is only about which path each key picks. + ;; `package' is the one head the two keys disagree about, and the + ;; disagreement is a behaviour being kept rather than a rule being followed: + ;; `C-c C-c' has always installed a `package' form and `C-x C-e' has always + ;; left it to the expression evaluator, and nothing about a line written once + ;; at the top of a file asks for that to change. Routing both keys off one + ;; list would quietly take it away. Nothing is sent here — the claim is only + ;; about which path each key picks. (with-temp-buffer (flan-mode) (insert "(package demo)\n") @@ -642,6 +643,33 @@ already rely on it — so nothing here is a stand-in for the real thing." (test-flan--check "and C-x C-e does not, for the reason it never has" (null (flan--declaration-before-point)))) + ;; And every other head does agree, which is the half that had rotted: the + ;; list was topped up by name and so lost `defdata' and all four object and + ;; dispatch heads for as long as they had existed. A head missing from it + ;; is not a quiet fallback — the form goes to the expression evaluator and + ;; the daemon refuses it with "is a top-level declaration, not an + ;; expression", which is the complaint this is here to keep from coming + ;; back. Read off `Parse.decl', so the check is the derivation. + (with-temp-buffer + (flan-mode) + (dolist (head '("defmacro" "defn" "defvar" "defconst" "defstruct" + "defdata" "defunion" "defenum" "defalias" + "defclass" "defgeneric" "defmulti" "defmethod" + "import" "declare" "declare-c")) + (erase-buffer) + ;; A body neither key needs to be valid: both questions are asked of the + ;; head alone, and a form that parses is a different test. + (insert "(" head " thing [x])\n") + (goto-char (point-min)) + (search-forward "thing") + (test-flan--check (format "C-c C-c installs a %s form" head) + (equal (flan--declaration-head-at + (car (flan--defun-bounds)) flan--defun-heads) + head)) + (goto-char (point-max)) + (test-flan--check (format "and C-x C-e installs one too, for %s" head) + (equal (car (flan--declaration-before-point)) head)))) + ;; A prefix on the expression path is a flag and not a position — there is ;; no inside for one to point at — so both `C-u' and `C-u C-u' mean the one ;; thing, which is `C-u C-x C-e's behaviour reached through the other key.