emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: smie-next-sexp vs associative operators


From: Stephen Leake
Subject: Re: smie-next-sexp vs associative operators
Date: Sat, 20 Oct 2012 05:15:02 -0400
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.2 (windows-nt)

Stefan Monnier <address@hidden> writes:

>> if A then
>>    B;
>> elsif C then
>>    D;
>> elsif
>>   E then
>>    F;
>> else
>>   G;
>> end if;
>
> I'm not completely sure how to interpret the above.
> Is it an example of good indentation, or does it present a problem
> case?

This is the prefered indentation. I'm using it as an example of a case
when calling smie-backward-sexp is necessary, when computing the
indentation of F.

That's what modula-2-mode does as well; smie-indent-after-keyword calls
smie-indent-virtual to compute the indentation for "then"; that calls
smie-indent-keyword, which calls (smie-backward-sexp token), with
`token' = "then". This parses all the way back to "if".

> In which way is it related to the select...do case?

smie-backward-sexp ought to treat them similarly, as you agree below.
But it does not, because smie-backward-sexp treats associative keywords
specially.

>> Here the internal keywords "then", "elsif" are not associative; "else" is
>> associative.
>
> That doesn't sound right.  "then" should not be associative,
> but "elseif" should be.

There's a comment on smie--associative-p that says tokens have the same
left and right levels if they are optional; that makes sense, and
applies in this case. "else" is optional, "elsif  then" is optional, but
"elsif" on its own is not optional.

> More specifically, with point right after an "elsif", I'd expect C-M-b
> to stop right after the previous "elsif".

How is that useful for indentation?

What about C-M-b after "else"; should that stop on the preceding "then"?
or "elsif"? (In fact, it goes nowhere). I don't understand the general
rule you are expressing here.

The attached example_grammars.el, has several grammars for
"if/then/else/endif", and in the comments, I show what
smie-backward-sexp does for several points in the "if" statement, with
different values of halfsexp. C-M-b calls (smie-backward-sexp
'halfsexp). In most cases, it does move to after the previous keyword.
But not in all, so I don't see how it is generally useful. On the other
hand, the behavior of (smie-backward-sexp token) is quite reliable.

>> In the analogous situation with "select or", (smie-backward-sexp "or")
>> stops at the previous "or".
>
> The "or" in select should largely behave like the "elsif" above.

That is precisely what I'd like to have, but it is not happening. You
are saying it is the "elsif" that's behaving incorrectly, but the main
point is that they are different. In addition, the way I've defined
"elsif" in Ada mode is the same as "elsif" in modula-2-mode.

> You might like to use rules like:
>
>    (exp ("if" expthenexp "end")
>         ("if" expthenexp "else" exp "end")
>         ("if" expthenexp "elsif" expthenexp "end")
>         ("if" expthenexp "elsif" expthenexp "end")
>    (expthenexp (exp "then" exp))

I tried that (see the attached example_grammars.el, grammar-3). It does
change the levels assigned by the grammar compiler. But "then" does not
have equal left and right levels, and the behavior of smie-backward-sexp
is no more useful, as far as I can tell.

>> Which suggests another way to state my core complaint; since
>> smie-next-sexp will skip an arbitrary number of "transitive pairs",
>
> That's annoying.
>
>> it should also skip an arbitrary number of "transitive singles".  Or at
>> least, provide an option to do so.
>
> No, you got it backwards: you want indentation to walk back as little
> as possible.  E.g. you don't want to walk all the way back to "if" if
> you can indent relative to the closest "elsif".

But walking back to "if" is what modula-2-mode does, via
smie-indent-keyword.

Looking at the various grammars in example_grammars.el, I don't see any
consistent patterns, except that (smie-backward-sexp token) always goes
to "if" when token is one of the statement keywords. That's why I use
it, and I assume that's why smie-indent-keyword uses it.

I could fuss with the grammar of each statement to get slightly shorter
parses in some cases. I don't have the time, and that would be premature
optimization.

It comes down to trading indentation code complexity & programmer time vs CPU 
time.
The modula-2 grammar has 63 keywords, only a few refined, including many 
operators.
So far, my Ada grammar has 124 keywords, most refined, no operators.
modula2.el is 617 lines for the entire mode. ada-indent.el is 3153 lines
and counting just for indentation (mostly refinement code); Ada is a
much bigger language.

So I need reliable, simple patterns to make this practical.
(smie-backward-sexp token) is very reliable, except for associative
keywords. That behaviour should be controlled by the language
implementor, not dictated by smie.

I've verified that the patch shown below allows me to make
smie-backward-sexp behave the same for the Ada "select/or" and
"if/elsif" statements. 

--
-- Stephe

--- /Apps/emacs-24.2/lisp/emacs-lisp/smie.el    2012-09-14 04:44:57.521167100 
-0400
+++ smie.el     2012-10-17 20:33:06.616977700 -0400
@@ -672,6 +672,8 @@
   ;; has to be careful to distinguish those different cases.
   (eq (smie-op-left toklevels) (smie-op-right toklevels)))

+(defvar smie-skip-associative nil)
+
 (defun smie-next-sexp (next-token next-sexp op-forw op-back halfsexp)
   "Skip over one sexp.
 NEXT-TOKEN is a function of no argument that moves forward by one
@@ -770,7 +772,8 @@
                    ;; The new operator is associative.  Two cases:
                    ;; - it's really just an associative operator (like + or ;)
                    ;;   in which case we should have stopped right before.
-                   ((and lastlevels
+                   ((and (not smie-skip-associative)
+                        lastlevels
                          (smie--associative-p (car lastlevels)))
                     (throw 'return
                            (prog1 (list (or (car toklevels) t) (point) token)

Attachment: example_grammars.el
Description: application/emacs-lisp


reply via email to

[Prev in Thread] Current Thread [Next in Thread]