* SMIE: if-elseif-else constructs
@ 2020-10-03 15:16 Andreas Matthias
2020-10-03 16:24 ` Stefan Monnier
0 siblings, 1 reply; 4+ messages in thread
From: Andreas Matthias @ 2020-10-03 15:16 UTC (permalink / raw)
To: help-gnu-emacs
I'm trying to teach SMIE to handle if-elseif-else constructs correctly.
In the following example the indentation of the last line, i.e. "xoxox;",
is wrong. I suppose something's wrong with my definition of elseif
in the grammar. But what is it? How can I fix it?
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
if a > 5 then
foo;
bar;
else
foo;
bar;
end;
xoxox; // correct indentation
if a > 5 then
foo;
bar;
elseif a > 5 then
foo;
bar;
else
foo;
bar;
end;
xoxox; // wrong indentation
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Here is the code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'smie)
(setq foobar-grammar
(smie-prec2->grammar
(smie-bnf->prec2
`((insts (insts ";" insts)
(inst))
(inst (if))
(exp)
(if ("if" exp "then" else "end"))
(else (insts)
(insts "else" insts)
(insts "elseif" exp "then" else)))
'((assoc ";")
(assoc "then" "end")
))))
(defun foobar-rules (kind token)
(message "rule: (%s . %s) at point %d" kind token (point))
(pcase (cons kind token)
(`(:before . "then") (smie-rule-parent 0))
(`(:before . "else") (smie-rule-parent 0))
(`(:after . "else") (smie-rule-parent 4))
(`(:before . "elseif") (smie-rule-parent 0))
(`(:after . "elseif") (smie-rule-parent 4))
(`(:before . "end") (smie-rule-parent 0))
))
(define-derived-mode foobar-mode prog-mode "foobar"
:syntax-table nil
(modify-syntax-entry ?/ ". 124")
(modify-syntax-entry ?* ". 23b")
(modify-syntax-entry ?\n ">")
(setq comment-start "//")
(smie-setup foobar-grammar #'foobar-rules)
(font-lock-add-keywords
nil
`((,(regexp-opt '("if" "then" "elseif" "else" "end")) .
font-lock-keyword-face)))
(font-lock-mode)
(font-lock-ensure (point-min) (point-max)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: SMIE: if-elseif-else constructs
2020-10-03 15:16 SMIE: if-elseif-else constructs Andreas Matthias
@ 2020-10-03 16:24 ` Stefan Monnier
2020-10-06 13:29 ` Andreas Matthias
0 siblings, 1 reply; 4+ messages in thread
From: Stefan Monnier @ 2020-10-03 16:24 UTC (permalink / raw)
To: help-gnu-emacs
> (assoc "then" "end")
This says that
a then b then c then d
is acceptable and that `a`, `b`, and `c` should be considered as
siblings (i.e. at the same depth level). Same for `end`.
It's clearly not what you want and is the source of your problem.
Stefan
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: SMIE: if-elseif-else constructs
2020-10-03 16:24 ` Stefan Monnier
@ 2020-10-06 13:29 ` Andreas Matthias
2020-10-06 14:04 ` Stefan Monnier
0 siblings, 1 reply; 4+ messages in thread
From: Andreas Matthias @ 2020-10-06 13:29 UTC (permalink / raw)
To: help-gnu-emacs
Stefan Monnier wrote:
>
> > (assoc "then" "end")
>
> This says that
>
> a then b then c then d
>
> is acceptable and that `a`, `b`, and `c` should be considered as
> siblings (i.e. at the same depth level). Same for `end`.
I see. Using assoc seemed to be the easy way but was not reasonable.
I think I've found a better way. I revised the grammar and adjusted
the indentation rules.
The following seems to work.
(require 'smie)
(setq foobar-grammar
(smie-prec2->grammar
(smie-bnf->prec2
`((insts (insts ";" insts)
(inst))
(inst (if))
(exp)
(if ("if" if-body1 "end"))
(if-body1 (exp "then" if-body2))
(if-body2 (insts "elseif" if-body1)
(insts "else" insts)
(insts)))
'((assoc ";")
))))
(defun foobar-rules (kind token)
(message "rule: (%s . %s) at point %d" kind token (point))
(pcase (cons kind token)
(`(:elem . basic) foobar-indent-basic)
(`(:before . "then") 0)
(`(:after . "then") foobar-indent-basic)
(`(:before . "else") 0)
(`(:after . "else") foobar-indent-basic)
(`(:before . "elseif") 0)
(`(:after . "elseif") foobar-indent-basic)
))
(setq foobar-indent-basic 4)
(define-derived-mode foobar-mode prog-mode "foobar"
:syntax-table nil
(modify-syntax-entry ?/ ". 124")
(modify-syntax-entry ?* ". 23b")
(modify-syntax-entry ?\n ">")
(setq comment-start "//")
(smie-setup foobar-grammar #'foobar-rules)
(font-lock-add-keywords
nil
`((,(regexp-opt '("if" "then" "elseif" "else" "end")) .
font-lock-keyword-face)))
(font-lock-mode)
(font-lock-ensure (point-min) (point-max)))
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: SMIE: if-elseif-else constructs
2020-10-06 13:29 ` Andreas Matthias
@ 2020-10-06 14:04 ` Stefan Monnier
0 siblings, 0 replies; 4+ messages in thread
From: Stefan Monnier @ 2020-10-06 14:04 UTC (permalink / raw)
To: help-gnu-emacs
> I think I've found a better way. I revised the grammar and adjusted
> the indentation rules.
> The following seems to work.
It looks OK, now (tho I think it implies that "elsif" is nested within
the right hand side of "then", which I think is wrong in the sense that
it doesn't reflect the natural shape of the abstract syntax tree).
It seems similar to the approach I've used in such cases, except I would
have used something like:
(inst ("if" if-body "end"))
(if-body (exp "then" insts)
(if-body "elseif" if-body)
(if-body "else" exp))
with an ((assoc "elseif") (nonassoc "else")). I believe this better
reflects the intended abstract syntax tree, so it should behave a bit
better w.r.t indentation and navigation.
BTW, another way to define the grammar can be:
[...]
(inst ("if" exp "then" insts "end")
("if" exp "then" insts "else" insts "end")
("if" exp "then" insts "elsif" exp "then" insts "else" insts "end")
[...]
You don't need to list all the (infinite number of) combinations, the
above is sufficient for SMIE to figure out the needed relative
constraints between the precedences of the keywords. But IIRC this
approach tends to behave less robustly in cases of syntax error (by
which I mean either when the source code is incorrect or when SMIE
parses incorrectly because its grammar or lexer isn't good enough).
Stefan
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2020-10-06 14:04 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-10-03 15:16 SMIE: if-elseif-else constructs Andreas Matthias
2020-10-03 16:24 ` Stefan Monnier
2020-10-06 13:29 ` Andreas Matthias
2020-10-06 14:04 ` Stefan Monnier
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).