unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
* first steps in elisp
@ 2016-11-24 23:00 Mark Piffer
  2016-11-25  5:39 ` Marcin Borkowski
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Mark Piffer @ 2016-11-24 23:00 UTC (permalink / raw)
  To: help-gnu-emacs

I am trying to write some helper functions which should ease documentation of C as per the method which my customers require (mostly repetition of parameters as Doxygen-enabled comments - I don't think that that's a good idea, but the customer wants it). I coudn't find a package that was primitive enough to help me with the parsing - the code is embedded C and quite non-standard with respect to compiler extensions. So I tried to walk the extra mile and code a little elisp for fun. Which things are especially bad or unusual concerning both, Lisp and emacs?

(defun ignore-multiline-comment (nlines)
  "assumes point is inside a C multiline comment /*. Advances
until end of comment */ or nlines becomes 0"
  (if (zerop nlines)
      nil
    (if (looking-at ".*?\\*/")
                (progn
                  (goto-char (match-end 0))
                  (ignore-line-comments nlines))
      (beginning-of-line 2)
      (ignore-multiline-comment (1- nlines)))))
 

(defun ignore-line-comments (nlines)
  "return the text starting at point as a list, going nlines lines down, stripped of 
all C comments (except pathological cases w/ string literals)"
  (if (zerop nlines)
      nil    
    (setq ml-e (if (looking-at "\\(.*?\\)/\\*") ;; test on /* comment
                                 (match-end 1)
                               nil))
    (setq sl-e (if (looking-at "\\(.*?\\)//") ;; test on // comment
                                  (match-end 1)
                               nil))
    (if (or sl-e ml-e) ;; any comment on line?
                (if (and ml-e (or (not sl-e) (< ml-e sl-e)))  ;; is /* the only or first comment?
                    (progn
                      (setq r (buffer-substring-no-properties (point) ml-e))
                      (goto-char ml-e)
                      (cons r (ignore-multiline-comment nlines)))
                 (setq r (buffer-substring-no-properties (point) sl-e))
                  (beginning-of-line 2)
                  (cons r (ignore-line-comments (1- nlines))))
      (looking-at ".*$")
      (setq r (buffer-substring-no-properties (car (match-data)) (cadr (match-data))) )
      (beginning-of-line 2)
      (cons r (ignore-line-comments (1- nlines))))))


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: first steps in elisp
  2016-11-24 23:00 first steps in elisp Mark Piffer
@ 2016-11-25  5:39 ` Marcin Borkowski
  2016-11-25  8:00 ` Joost Kremers
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Marcin Borkowski @ 2016-11-25  5:39 UTC (permalink / raw)
  To: Mark Piffer; +Cc: help-gnu-emacs


On 2016-11-25, at 00:00, Mark Piffer <mark.piffer@gmail.com> wrote:

> I am trying to write some helper functions which should ease documentation of C as per the method which my customers require (mostly repetition of parameters as Doxygen-enabled comments - I don't think that that's a good idea, but the customer wants it). I coudn't find a package that was primitive enough to help me with the parsing - the code is embedded C and quite non-standard with respect to compiler extensions. So I tried to walk the extra mile and code a little elisp for fun. Which things are especially bad or unusual concerning both, Lisp and emacs?

After just a quick glance:

(if CONDITION (progn THEN-CLAUSES...) nil) == (if CONDITION (progn THEN-CLAUSES...)) == (when CONDITION THEN-CLAUSES...)

(if CONDITION nil ELSE-CLAUSES...) == (unless CONDITION ELSE-CLAUSES...)

Also, instead of using setq and global variables, it's much better to
use let.

Keep up the good work of learning Elisp;-)!

-- 
Marcin Borkowski



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: first steps in elisp
  2016-11-24 23:00 first steps in elisp Mark Piffer
  2016-11-25  5:39 ` Marcin Borkowski
@ 2016-11-25  8:00 ` Joost Kremers
  2016-12-21 17:57 ` Thien-Thi Nguyen
  2017-04-12  6:26 ` Steve
  3 siblings, 0 replies; 5+ messages in thread
From: Joost Kremers @ 2016-11-25  8:00 UTC (permalink / raw)
  To: Mark Piffer; +Cc: help-gnu-emacs


On Thu, Nov 24 2016, Mark Piffer wrote:
> (defun ignore-multiline-comment (nlines)

It's a good habit to get into to always provide a prefix for any 
Elisp functions and variables that you write, even if it's just 
for your own personal code. For personal code, most people use 
their initials, or simply `my-`.

>   "assumes point is inside a C multiline comment /*. Advances
> until end of comment */ or nlines becomes 0"

There are some doc string conventions (which can be found in the 
Elisp manual: C-h i m Elisp RET m Documentation Tips RET), the 
most important of which is that the first line of a doc string 
should be a sentence on its own. For personal code, it doesn't 
matter as much, of course, but again, it's a good habit to get 
into. Also, the doc string should say what a function does, i.e., 
what it expects as input (or context) and what it returns or what 
side effects it has. So, e.g.,:

      "Move point past the end of a C multiline comment.
This function assumes that point is inside the comment.  This is a 
recursive function, its stop condition is met when NLINES is 0."

>   (if (zerop nlines)
>       nil
>     (if (looking-at ".*?\\*/")
>                 (progn
>                   (goto-char (match-end 0))
>                   (ignore-line-comments nlines))
>       (beginning-of-line 2)
>       (ignore-multiline-comment (1- nlines)))))

Personally, I normally wouldn't explicitly write nil as a return 
value but instead rely on the function structure to handle that. 
So I'd do something like:


```
(defun my-ignore-multiline-comment (nlines)
  "assumes point is inside a C multiline comment /*. Advances
until end of comment */ or nlines becomes 0"
  (when (not (zerop nlines))
    (if (looking-at ".*?\\*/")
        (progn
          (goto-char (match-end 0))
          (my-ignore-line-comments nlines))
      (beginning-of-line 2)
      (my-ignore-multiline-comment (1- nlines)))))
```

But in this case the nil return value might make sense to indicate 
that the stop condition is met. Personally, I would probably write 
that in the doc string, but YMMV.

> (defun ignore-line-comments (nlines)
>   "return the text starting at point as a list, going nlines 
>   lines down, stripped of
> all C comments (except pathological cases w/ string literals)"
>   (if (zerop nlines)
>       nil
>     (setq ml-e (if (looking-at "\\(.*?\\)/\\*") ;; test on /* 
>     comment
>                                  (match-end 1)
>                                nil))
>     (setq sl-e (if (looking-at "\\(.*?\\)//") ;; test on // 
>     comment
>                                   (match-end 1)
>                                nil))

Here, you should definitely use local variables for ml-e and sl-e. 
Note that if you use setq on a symbol that hasn't been let-bound, 
you automatically create a global variable that stays around until 
Emacs is shut down. So unless you need to store some global state, 
always use let.

Also, I would use longer names for variables, so it's easier to 
see what they're used for. But again, YMMV.

>     (if (or sl-e ml-e) ;; any comment on line?
>                 (if (and ml-e (or (not sl-e) (< ml-e sl-e)))  ;; 
>                 is /* the only or first comment?
>                     (progn
>                       (setq r (buffer-substring-no-properties 
>                       (point) ml-e))
>                       (goto-char ml-e)
>                       (cons r (ignore-multiline-comment 
>                       nlines)))
>                  (setq r (buffer-substring-no-properties (point) 
>                  sl-e))
>                   (beginning-of-line 2)
>                   (cons r (ignore-line-comments (1- nlines))))
>       (looking-at ".*$")
>       (setq r (buffer-substring-no-properties (car (match-data)) 
>       (cadr (match-data))) )
>       (beginning-of-line 2)
>       (cons r (ignore-line-comments (1- nlines))))))

So with let, this would become:

```
(defun my-ignore-line-comments (nlines)
  "return the text starting at point as a list, going nlines lines 
  down, stripped of 
all C comments (except pathological cases w/ string literals)"
  (when (not (zerop nlines)) 
    (let ((ml-e (if (looking-at "\\(.*?\\)/\\*") ;; test on /* 
    comment
                    (match-end 1)
                  nil))
          (sl-e (if (looking-at "\\(.*?\\)//") ;; test on // 
          comment
                    (match-end 1)
                  nil))
          r)
      (if (or sl-e ml-e) ;; any comment on line?
          (if (and ml-e (or (not sl-e) (< ml-e sl-e)))  ;; is /* 
          the only or first comment?
              (progn
                (setq r (buffer-substring-no-properties (point) 
                ml-e))
                (goto-char ml-e)
                (cons r (my-ignore-multiline-comment nlines)))
            (setq r (buffer-substring-no-properties (point) sl-e))
            (beginning-of-line 2)
            (cons r (my-ignore-line-comments (1- nlines))))
        (looking-at ".*$")
        (setq r (buffer-substring-no-properties (car (match-data)) 
        (cadr (match-data))))
        (beginning-of-line 2)
        (cons r (my-ignore-line-comments (1- nlines)))))))
```

It's perfectly fine to use setq on a variable once it's been 
let-bound. If you cannot provide an initial value for a variable, 
just leave out the value as done here for `r` and setq it later.

BTW, I find it useful to run flycheck in my Elisp buffers, helps 
one to develop good habits. :-)

HTH 

--
Joost Kremers
Life has its moments



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: first steps in elisp
  2016-11-24 23:00 first steps in elisp Mark Piffer
  2016-11-25  5:39 ` Marcin Borkowski
  2016-11-25  8:00 ` Joost Kremers
@ 2016-12-21 17:57 ` Thien-Thi Nguyen
  2017-04-12  6:26 ` Steve
  3 siblings, 0 replies; 5+ messages in thread
From: Thien-Thi Nguyen @ 2016-12-21 17:57 UTC (permalink / raw)
  To: Mark Piffer; +Cc: help-gnu-emacs

[-- Attachment #1: Type: text/plain, Size: 1026 bytes --]


() Mark Piffer <mark.piffer@gmail.com>
() Thu, 24 Nov 2016 15:00:59 -0800 (PST)

   (defun ignore-multiline-comment (nlines)
     DOCSTRING
     (if (zerop nlines)
         nil
       (if (looking-at ".*?\\*/")
                   (progn
                     (goto-char (match-end 0))
                     (ignore-line-comments nlines))
         (beginning-of-line 2)
         (ignore-multiline-comment (1- nlines)))))

In addition to suggestions made by others, i would replace the
(current) recursion w/ iteration, so as to future-proof against
large ‘nlines’ (maybe the text has short multiline comments now,
but you never know what weirdness may inspire the programmer to
wax eloquent in the future :-D).

-- 
Thien-Thi Nguyen -----------------------------------------------
 (defun responsep (type via)
   (case type
     (technical (eq 'mailing-list via))
     ...))                              748E A0E8 1CB8 A748 9BFA
--------------------------------------- 6CE4 6703 2224 4C80 7502


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: first steps in elisp
  2016-11-24 23:00 first steps in elisp Mark Piffer
                   ` (2 preceding siblings ...)
  2016-12-21 17:57 ` Thien-Thi Nguyen
@ 2017-04-12  6:26 ` Steve
  3 siblings, 0 replies; 5+ messages in thread
From: Steve @ 2017-04-12  6:26 UTC (permalink / raw)
  To: help-gnu-emacs

Mark Piffer <mark.piffer@gmail.com> writes:

< I am trying to write some helper functions which should ease
< documentation of C as per the method which my customers require
< (mostly repetition of parameters as Doxygen-enabled comments - I don't
< think that that's a good idea, but the customer wants it). I coudn't
< find a package that was primitive enough to help me with the parsing -
< the code is embedded C and quite non-standard with respect to compiler
< extensions.

I have some terrible ideas, but you might find them useful or
interesting.

using grep as a shell command process. I would try looking at
`grep-process-startup' or `shell-command on region'. You don't need to
use an external program (shell script). also, emacs has the
`combine-and-quote-strings' function which is very nice. Also
`looking-at-p' is a newer one, does not load match-data.

I'm sure the doxygen docstrings are parsable with emacs - they are font
locked when I check some C buffers; doxygen is not something I know much
about.

check in site-lisp/progmodes/cc-mode.el - has some functions to
determine if your inside a string or comment.

< Which things are especially bad or unusual concerning both, Lisp and
< emacs?

Concerning lisp, I often hear that lisp programmers are considered a tad
bit goofy :) they seem to like to write everything from scratch until it
becomes a command interpreter; the course of just a few bytecodes...

They used to say emacs and vi are religions; these days they are
starting to seem like latin.

< [ ... ]

>
< (defun ignore-line-comments (nlines)
<   "return the text starting at point as a list,
< going nlines lines down, stripped of 
< all C comments (except pathological cases w/ string literals)"
<   (if (zerop nlines)
<       nil    
<     (setq ml-e (if (looking-at "\\(.*?\\)/\\*") ;; test on /* comment
<                                  (match-end 1)
<                                nil))

using setq will bind the variable with dynamic scope. to get lexical
scope youe use a lambda function (lambda function usually means `let'.

(defun ignore-line-comments (nlines)
  "return [ ... ] "
;; new
 (let ((ml-e nil))

 ;; now setq will bind ml-e lexically for the function
 
  (if (zerop nlines)
       nil    
     (setq ml-e (if (looking-at "\\(.*?\\)/\\*") ;; test on /* comment
                                  (match-end 1)
                                nil))


Hops this helps.

P.S. in another article about the `serial-term' ; Emacs serial term is
woring great with a parallax propeller. The trick is to use pst#NL (that
is insert newline and then form feed. Arg... just had to get that off my
chest.


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2017-04-12  6:26 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-11-24 23:00 first steps in elisp Mark Piffer
2016-11-25  5:39 ` Marcin Borkowski
2016-11-25  8:00 ` Joost Kremers
2016-12-21 17:57 ` Thien-Thi Nguyen
2017-04-12  6:26 ` Steve

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).