1

While I found lots of sources, how to use Elisp in an org-mode document, what I try to do is not related to org-mode at all.

My idea is to embed Elisp functions and calls of Elisp functions within comment blocks in a .cpp file. Which I subsequently want to trigger to generate code within the same buffer, starting from the line after the closing comment (*/).

So, using the *scratch* buffer for testing, I came up with the following code:

(cl-defun c++-generate-fancy-enum
    (enum-name
     values
     &key
     enum-class
     (enum-base-type nil enum-base-type-p)
     (derive-to-string t))
  (cl-labels ((value-name (value)
              (cl-typecase value
                (string value)
                (cons (car value))
                (t (message "~a is expected to be a cons or a string" value)))))
    (interactive)
    (search-forward (concat '(?* ?/)))
    (newline)
    (insert "enum")
    (when enum-class
      (insert " class "))
    (insert enum-name)
    (if (and enum-class enum-base-type-p)
    (insert " : " enum-base-type " {")
      (insert " {"))
    (newline)
    (dolist (value values)
      (cl-typecase value
    (string
     (insert "  " value))
    (cons
     (insert "  " (car value) "=" (number-to-string (cdr value))))
    (t (message "~a is expected to be a cons or a string" value)))
      (insert ",")
      (newline))
    (insert "};")
    (newline)
    (when derive-to-string
      (insert "std::ostream& operator<<(std::ostream& os, " enum-name "& value) {")
      (newline)
      (insert "  switch (value) {")
      (newline)
      (dolist (value values)
    (insert "    case " (value-name value) ": os << \"" enum-name "::" (value-name value) "\"; break;")
    (newline))
      (insert "  }")
      (newline)
      (insert "  return os;")
      (newline))
    (insert "}")
    (newline)))

(c++-generate-fancy-enum "TaskState_t"
             '("New"
               "Pending"
               "Generator"
               "Done"
               "Faulted")
             :enum-class t
             :enum-base-type "uint64_t"
             :derive-to-string t)

It looks for the next closing comment below the snippet, then inserts the generated c++ code into the same buffer.

It works in *scratch* (if you add a trailing "*/" behind the code). But if I try to paste this into a c++ file and a block comment, I would not know, how to execute the code such, that it inserts the generated code into the c++ buffer.

I think I did this before (a longer while back) but I cannot remember how and googling yielded a lot of other stuff but no solution to this problem.

1

2 Answers 2

1

The package alien-el provides the command alien-el-exec.

This command evaluates sexps in comments of the current buffer if they are preceded by the marker elisp-start:.

The region for the output starts directly after the comment with the sexp and ends at the start of a comment that contains elisp-end.

If the sexp evaluates to a string that string replaces the output region after execution. Other types of output are ignored.

An example C++ code after alien-el-exec:

// Setting local variables works.
// The Elisp code starts with the following marker:
// elisp-start: (setq-local fishy t)
// elisp-end
//< The marker for the end of the Elisp form and its output.
int main() {
    // Blocks of line-comments work.
    // If the form evaluates to a string
    // that string is inserted after the comment containing the
    // end of the sexp.
    //
    // elisp-start:
    // (concat
    //  (format
    //    "\tprint(\"Something %s"
    //    (if fishy "fishy" "normal"))
    //  ".\");\n")
    print("Something fishy.");
// elisp-end
    //< Marker for the end of the output.


/*
  Elisp code can also be wrapped by block comments.
  elisp-start:
  (concat
  (format
  "\n\tprint(\"Something %s"
  (if fishy "fishy" "normal"))
  "\");\n")
*/
    print("Something fishy");
/* elisp-end */
} // End of main.
1

TL;DR

Put your cursor after the closing parenthesis at the end of the cl-defun expression (after the ... (newline))) part) and press C-x C-e to evaluate the cl-defun and define the function. Then put it after the closing parenthesis of the second expression (the call to the function) and press C-x C-e again to evaluate the function call. That's it.

The details

How does it work in the *scratch* buffer? If you mean you press C-j after the closing of cl-defun and another C-j after the closing of the function call, then you can do the same thing in any buffer.

C-j is bound to eval-print-last-sexp in the *scratch* buffer (as you can find out by asking emacs for the keybinding with C-h c C-j). eval-print-last-sexp does two things: it evaluates and returns the value of the last sexp (i.e. the sexp right before the cursor) and it prints that result to the current buffer.

While that keybinding is not available in a c++ buffer, the function still exists and you can call it with M-x eval-print-last-sexp. So position the cursor after the end of the cl-defun which will evaluate the cl-defun and define your function and do M-x eval-print-last-sexp and then position the cursor after the function call and say M-x eval-print-last-sexp again which will evaluate the call to your function and add the generated code to your buffer.

But you will notice that when you do eval-print-last-sexp on the function call, not only is your generated code added to the buffer, but you will also see a nil after it. That's because your function returns nil and eval-print-last-sexp prints out the result. The point is that your function operates by inserting what it wants into the buffer (i.e. by side effect): the return value is meaningless, so you don't want it to appear in the buffer.

There is another function, eval-last-sexp, which evaluates the last sexp and returns its value but does not print that value. That's what you really want: instead of M-x eval-print-last-sexp at the end of each of your two sexps, you want to call M-x eval-last-sexp.

You could bind some key to the eval-last-sexp function in a c++ buffer, but you can easily find out if the function is already bound to a key in such a buffer: C-h w eval-last-sexp asks Where is the function 'eval-last-sexp' bound? and the answer is eval-last-sexp is bound to C-x C-e, so you can use C-x C-e in the two places instead. In fact, that keybinding is also available in the *scratch* buffer (try it!), so you can probably use it universally (unless some mode overrides it, but that is unlikely).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.