Emacs, ruby: convert do end block to curly braces and vice versa - ruby

I often find myself converting code like this:
before do
:something
end
to
before { :something }
Is there a way to automate this task in emacs? I use ruby-mode and rinary, but they're not too helpful here.

ruby-mode in Emacs 24.3 and newer has the command ruby-toggle-block.
The default binding is C-c {.

I am sure it can be made shorter and better, but for now I've got the following:
(defun ruby-get-containing-block ()
(let ((pos (point))
(block nil))
(save-match-data
(save-excursion
(catch 'break
;; If in the middle of or at end of do, go back until at start
(while (and (not (looking-at "do"))
(string-equal (word-at-point) "do"))
(backward-char 1))
;; Keep searching for the containing block (i.e. the block that begins
;; before our point, and ends after it)
(while (not block)
(if (looking-at "do\\|{")
(let ((start (point)))
(ruby-forward-sexp)
(if (> (point) pos)
(setq block (cons start (point)))
(goto-char start))))
(if (not (search-backward-regexp "do\\|{" (point-min) t))
(throw 'break nil))))))
block))
(defun ruby-goto-containing-block-start ()
(interactive)
(let ((block (ruby-get-containing-block)))
(if block
(goto-char (car block)))))
(defun ruby-flip-containing-block-type ()
(interactive)
(save-excursion
(let ((block (ruby-get-containing-block)))
(goto-char (car block))
(save-match-data
(let ((strings (if (looking-at "do")
(cons
(if (= 3 (count-lines (car block) (cdr block)))
"do\\( *|[^|]+|\\)? *\n *\\(.*?\\) *\n *end"
"do\\( *|[^|]+|\\)? *\\(\\(.*\n?\\)+\\) *end")
"{\\1 \\2 }")
(cons
"{\\( *|[^|]+|\\)? *\\(\\(.*\n?\\)+\\) *}"
(if (= 1 (count-lines (car block) (cdr block)))
"do\\1\n\\2\nend"
"do\\1\\2end")))))
(when (re-search-forward (car strings) (cdr block) t)
(replace-match (cdr strings) t)
(delete-trailing-whitespace (match-beginning 0) (match-end 0))
(indent-region (match-beginning 0) (match-end 0))))))))
There are two functions to be bound to keys: ruby-goto-containing-block-start and ruby-flip-containing-block-type.
Either command works anywhere inside a block, and hopefully they can skip blocks that should be skipped - although that shouldn't be an issue if you are converting to a short block format.
The ruby-flip-containing-block-type collapses three line do .. end blocks to single line {} and vice versa. If the blocks are not exactly 3 lines and 1 line long, it should leave them alone.
I am using this on my ruby setup now, so I would appreciate improvements.

You could use a regular expression that crosses newlines.
/do(C-q C-j\?)*(.*)(C-q C-j\?)*end/
and replace with
{\2 }
Something like that could work. You could then customize it until it does exactly what you need and bind it to a macro so that you can whip it out and impress your friends anytime!
I tested the above regexes in vi (my editor of choice) and they worked. So something similar should work for you.
For more information, make sure to checkout the emacs wiki!

Here is a function. I am an elisp beginner. It only goes one way; from do to {. let me know if it works for you.

Related

Standard way to handle quoted symbol in lisp macros in Scheme

For some code I was working I've needed to handle 'x inside macro. What is standard way of handling those values?
I have code like this:
(define (quoted-symbol? x)
(and (pair? x) (eq? (car x) 'quote) (symbol? (cadr x)) (null? (cddr x))))
(define-macro (test x)
(if (quoted-symbol? x)
`(begin
(display ',(cadr x))
(newline))))
(test 'hello) ;; 'hello will be expanded into list (quote hello)
Is this how this should be handled, or is just in macro you don't use quoted symbols?
NOTE: I'm not asking about hygienic macros (I'm asking about real lisp macros), so please no answers with hygienic macros.
EDIT:
My macro works correctly in Guile and BiwaScheme and in my own scheme like lisp in JavaScript. Here is better example:
(define-macro (test x)
(if (quoted-symbol? x)
`',(cadr x)))
(define (example arg)
(list arg (test 'world)))
(example 'hello)
the question was not about display, but about (cadr x).
EDIT2: You've asked so here you go, my macro:
(define-macro (--> expr . code)
"Helper macro that simplify calling methods on objects. It work with chaining
usage: (--> ($ \"body\")
(css \"color\" \"red\")
(on \"click\" (lambda () (print \"click\"))))
(--> document (querySelectorAll \"div\"))
(--> (fetch \"https://jcubic.pl\") (text) (match /<title>([^<]+)<\/title>/) 1)
(--> document (querySelectorAll \".cmd-prompt\") 0 \"innerText\")"
(let ((obj (gensym)))
`(let* ((,obj ,(if (and (symbol? expr) (not (null? (match /\./ (symbol->string expr)))))
`(.. ,expr)
`,expr)))
,#(map (lambda (code)
(let ((name (gensym))
(value (gensym)))
`(let* ((,name ,(cond ((quoted-symbol? code) (symbol->string (cadr code)))
((pair? code) (symbol->string (car code)))
(true code)))
(,value (. ,obj ,name)))
,(if (and (pair? code) (not (quoted-symbol? code)))
`(set! ,obj (,value ,#(cdr code)))
`(set! ,obj ,value)))))
code)
,obj)))
;; ---------------------------------------------------------------------------------------
(define (quoted-symbol? x)
"(quoted-symbol? code)
Helper function that test if value is quoted symbol. To be used in macros
that pass literal code that is transformed by parser.
usage:
(define-macro (test x)
(if (quoted-symbol? x)
`',(cadr x)))
(list 'hello (test 'world))"
(and (pair? x) (eq? (car x) 'quote) (symbol? (cadr x)) (null? (cddr x))))
the macro is used in my scheme like lisp in JavaScript, like the doc string suggest:
(--> document (querySelectorAll ".class") 0 "innerText")
I want to support:
(--> document (querySelectorAll ".class") 0 'innerText)
The code can be tested online at: https://jcubic.github.io/lips/ (You need to copy/paste the code since current version allow only method calls).
To get expansion you can use
(pprint (macroexpand (--> document (querySelector "x"))))
if it don't work (don't expand) it mean that macro is broken somehow.
dot is build in function that get property of an object and .. macro:
(define-macro (.. expr)
"(.. foo.bar.baz)
Macro that gets value from nested object where argument is comma separated symbol"
(if (not (symbol? expr))
expr
(let ((parts (split "." (symbol->string expr))))
(if (single parts)
expr
`(. ,(string->symbol (car parts)) ,#(cdr parts))))))
that can be use to get nested property like (.. document.body.innerHTML)
Scheme doesn't have "real lisp macros". Some implementations has something similar, but the forms have different names and uses. They are not portable at all.
The standard way of handling 'x is to handle it like an expression that gets evaluated in the expansion. Eg.
(define var 'x)
(test 'x)
(test var)
The two test forms should amount to the same even though the macro test gets (quote x) in the first and the symbol var in the second. At the time of the expansion var does not exist since the implementation can expand all the macros before starting.
You implementation of test will not work. Eg. the display might be run one or twice and then each time you call a procedure that uses it it will gfail since the expansion is the undefined value and it might not be fit for evaluation. eg.
(define (example arg)
(list arg (test 'w)))
When this is defined you get 'w or (quote w) printed with a newline and then the procedure it tries to store is:
(define (example arg)
(list arg #<undefined>))
Note that what constitutes the undefined value is chosen by the implementaion, but I know for sure that in many implementaions you cannot evaluate #<undefined>.

How to count the number of if-statements in a separate file of code

I am trying to write a scheme program that counts the number of if-statement a file containing code. I know how to read in the file but I don't know how to go about counting the number of if-statements.
This is very hard without actually implementing reducing the language to a more primitive form. As an example, imagine this:
(count-ifs '(let ((if +))
(if 1 2 3)))
; ==> 0
0 is the correct amount as if is a binding shadowing if and Scheme supports shadowing so the result of that expression is 6 and not 2. let can be rewritten such that you can check this instead:
(count-ifs '((lambda (if)
(if 1 2 3))
+))
; ==> 0
It might not look like an improvement, but here you can actually fix it:
(define (count-ifs expr)
(let helper ((expr expr) (count 0))
(if (or (not (list? expr))
(and (eq? (car expr) 'lambda)
(memq 'if (cadr expr))))
count
(foldl helper
(if (eq? (car expr) 'if)
(add1 count)
count)
expr))))
(count-ifs '((lambda (if)
(if 1 2 3))
(if #t + (if if if))))
; ==> 2
Challenge is to expand the macros. You actually need to make a macro expander to rewrite the code such that the only form making bindings would be lambda. This is the same amount of work as making 80% of a Scheme compiler since once you've dumbed it down the rest is easy.
A simple way to do it could be recursion structure like this:
(define (count-ifs exp)
(+ (if-expression? exp 1 0)))
(if (pair? exp)
(+ (count-ifs (car exp)) (count-ifs (cdr exp))))
0)))
But this might overcount.
A more correct way to do it would be to process the code by checking each type of expression you see - and when you enter a lambda you need to add the variables it binds to a shadowed symbols list.

In Guile or other Scheme, how to print to standard output the nth blank delimited field of lines from the input file or standard input?

If Guile is not the best Scheme for this usage, then which one should I be looking at? I'm basically looking for a Guile equivalent of awk '{print $N}'. If Scheme can't do this, then I'd like to know why not.
Guile changed its I/O a bit between 2.0 and 2.2, so this uses r6rs I/O which (hopefully) works the same in both, but I haven't tested with 2.2.
This can be optimized further.
#!/usr/bin/guile \
-e start -s
!#
(use-modules (rnrs io ports))
;;; Reads one line from current-input-port and prints the field indicated by
;;; field-num. If the line does not have enough fields, it prints a newline.
;;; Returns the field, an empty string, or #f if end of file is reached.
(define (get-field field-num)
(let ((line (get-line (current-input-port))))
(if (eof-object? line)
#f
(let ((fields (string-tokenize line)))
(if (< field-num (length fields))
(let ((field (list-ref fields field-num)))
(put-string (current-output-port)
(string-append field "\n"))
field)
(and (put-string (current-output-port) "\n")
""))))))
;;; Repeat get-field until until end of file is reached
(define (get-all-fields field-num)
(if (get-field field-num)
(get-all-fields field-num)
#f))
(define (start args)
(if (and (> (length args) 1)
(integer? (string->number (list-ref args 1))))
(get-all-fields (1- (string->number (list-ref args 1))))
(display (string-join
`("Usage:" ,(list-ref args 0) "<field-number>\n")
" "))))
At my blog I have an essay giving a set of functions that make it easy to handle delimited text files.

T is a constant, may not be used as a variable

I'm writing common lisp code for a coding challenge, it's an rpg-esque puzzle where you need to calculate the total overkill damage dealt by the warrior. as I'm very new to common lisp, my code is likely pretty bad. Please refrain from posting general common lisp coding tips, unless they are relevant to the error. I plan on posting this code, after the error gets fixed, to codereview
The code runs fine until inside tick (at the bottom) the condition when (> overkill-damage 0) is true. I'm using GNU Clisp 2.49 to run this code.
(defun timer (initialization-time interval)
(list :init initialization-time :interval interval :ready nil :time-passed 0))
(defun tick-timer (timer)
(let ((newtime (1+ (getf timer :time-passed))))
(when (and (not (getf timer :ready)) (>= newtime (getf timer :init)))
(setf (getf timer :ready) t))
(setf (getf timer :time-passed) newtime)))
(defun timer-ready? (timer)
(and
(getf timer :ready)
(= 0 (mod (getf timer :time-passed) (getf timer :interval)))))
(defun weapon (damage timer)
(list :damage damage :timer timer))
(defun weapon-attack (weapon)
(tick-timer (getf weapon :timer))
(if (timer-ready? (getf weapon :timer))
(getf weapon :damage)
0))
(defun attack (character)
(reduce #'(lambda (total weapon) (+ (weapon-attack weapon) total)) (getf character :weapons) :initial-value 0))
(defun attack-monster (monster damage)
(- monster damage))
(defun calculate-overkill-damage (health)
(if (> health 0)
0
(abs health)))
(defparameter *warrior* `(:weapons ,(list (weapon 35 (timer 0 4)))))
(defparameter *mage* `(:weapons ,(list (weapon 80 (timer 2 8)))))
(defparameter *rogue* `(:weapons ,(list (weapon 20 (timer 0 3))
(weapon 30 (timer 0 4)))))
(defparameter *monsters* '(300 600 850 900 1100 3500))
(defparameter *current-monster* 0)
(defparameter *overkill* 0)
(defparameter *game-over* nil)
; I assume, for now, that when a monster dies, they will miss the rest of their attacks
(defun tick ()
(sleep .1)
(let* ((monster (nth *current-monster* *monsters*))
(new-health (attack-monster monster (attack *warrior*)))
(overkill-damage (calculate-overkill-damage new-health)))
(format t "Attacking~%-------~%Attacking monster ~a, which has ~a health." *current-monster* monster)
(format t "~%Dealt ~a overkill damage!" overkill-damage)
(when (> overkill-damage 0)
(do (format t "Dealt ~a overkill damage!" overkill-damage)
(setf *overkill* (+ *overkill* overkill-damage))
(format t "Total overkill damage is now ~a" *overkill*)
(setf *current-monster* (1+ *current-monster*))
(format t "Moving to next monster, ~a" *current-monster*)
(when (= *current-monster* (1- (length *monsters*)))
(setf *game-over* t))))
(let* ((new-health (attack-monster monster (attack *mage*)))
(new-health (attack-monster monster (attack *rogue*))))
(setf (nth *current-monster* *monsters*) new-health)
(format t "~%Monster is now at ~a health~%" (nth *current-monster* *monsters*)))))
(loop for x from 1 until (equal *game-over* t)
do (tick))
The most important part is at the bottom of the code, the tick function. When this code gets run, I get the error *** - LET: T is a constant, may not be used as a variable.
This is what gets printed at execution:
TRUNCATED LOTS OF POINTLESS MESSAGES...
-------
Attacking monster 0, which has 10 health.
Dealt 0 overkill damage!
Monster is now at 10 health
Attacking
-------
Attacking monster 0, which has 10 health.
Dealt 25 overkill damage!
*** - LET: T is a constant, may not be used as a variable
The following restarts are available:
USE-VALUE :R1 Input a value to be used instead.
ABORT :R2 Abort main loop
Break 1 [18]> :w
<1/172> #<SPECIAL-OPERATOR LET>
[170] EVAL frame for form
(LET (FORMAT T "Dealt ~a overkill damage!" OVERKILL-DAMAGE)
(TAGBODY #:LOOP-5923 (IF SETF (GO #:END-5924))
(FORMAT T "Total overkill damage is now ~a" *OVERKILL*)
(SETQ *CURRENT-MONSTER* (1+ *CURRENT-MONSTER*))
(FORMAT T "Moving to next monster, ~a" *CURRENT-MONSTER*)
(WHEN (= *CURRENT-MONSTER* (1- (LENGTH *MONSTERS*))) (SETQ *GAME-OVER* T))
(PSETQ) (GO #:LOOP-5923) #:END-5924
(RETURN-FROM NIL (PROGN *OVERKILL* (+ *OVERKILL* OVERKILL-DAMAGE)))))
Break 1 [18]>
That :w command shows code that isn't even there, I really don't understand what's going on there.
Even if I call macroexpand on tick, the code (LET (FORMAT T "Dealt ~a overkill damage!" OVERKILL-DAMAGE)...... doesn't show up anywhere.
Does anyone know what's going on? Alternatively, if you have any CLISP debugging tips to help me pinpoint the error, please let me know!
Well, I don't understand what the code is supposed to do, but your error comes from DO: http://www.lispworks.com/documentation/HyperSpec/Body/m_do_do.htm
As the documentation says, this is a loop whose first argument is a list of variables:
(do (format t "Dealt ~a overkill damage!" overkill-damage)
This tries to use format, t, "Dealt ~a overkill damage!", and overkill-damage as variables.
If you just want to use multiple forms in the body of when, you don't have to do anything special. when supports this out of the box:
(when (> overkill-damage 0)
(format t "Dealt ~a overkill damage!" overkill-damage)
(setf *overkill* (+ *overkill* overkill-damage))
(format t "Total overkill damage is now ~a" *overkill*)
...)
DO is a macro in Common Lisp. It is one of the older control structures in Lisp, like DOLIST and DOTIMES.
Since it is a macro, it might be difficult to debug. Especially when the DO macro does not do any syntax checking on its own.
For debugging in Lisp we can use the compiler and the interpreter. First let's use the compiler:
[1]> (defun test () (do (format t "hello world") (read)))
TEST
[2]> (compile 'test)
** - Continuable Error
in TEST : Illegal syntax in LET/LET*: "hello world"
The compiler gives an error message about illegal syntax. So there is a syntax error, but it does not make it clear where it is coming from. Since there is no LETor LET* in the code, it must come from some syntax transformation -> macro. DEFUN and DO are macros.
(macro-function 'do) -> #<COMPILED-FUNCTION DO>
The next step is to look at the macro expansion of the DO form:
[4]> (macroexpand-1 '(do (format t "hello world") (read)))
(BLOCK NIL
(LET (FORMAT T "hello world")
(TAGBODY #:LOOP-3239 (IF READ (GO #:END-3240)) (PSETQ) (GO #:LOOP-3239) #:END-3240
(RETURN-FROM NIL (PROGN))))) ;
T
In above form we see the LET and we can see that the binding is wrong. So the DO form is probably wrong.
This is now a perfect time to check the syntax of do: see the HyperSpec entry for DO. Usually that should be clear enough to find the syntax error.
We can also use the CLISP interpreter and step through the example:
[5]> (step (test))
step 1 --> (TEST)
Step 1 [6]> step
step 2 --> (BLOCK NIL (LET (FORMAT T "hello world") (TAGBODY #:LOOP-3210 # # ...)))
Step 2 [7]> step
step 3 --> (LET (FORMAT T "hello world") (TAGBODY #:LOOP-3210 (IF READ #) (PSETQ) ...))
Step 3 [8]> step
*** - LET: T is a constant, may not be used as a variable
Basically we see the code transformation done in each step.
Since Common Lisp has many implementations, there are some with better error messages. For example SBCL:
* (defun test () (do (format t "hello world") (read)))
; in: DEFUN TEST
; (DO (FORMAT
; T
; "hello world")
; (READ))
;
; caught ERROR:
; during macroexpansion of
; (DO (FORMAT
; T
; "hello world")
; (READ)).
; Use *BREAK-ON-SIGNALS* to intercept.
;
; "hello world" is an illegal form for a DO varlist.
;
; compilation unit finished
; caught 1 ERROR condition
That's better.
The first argument to do defines the local variables to the loop, as a let does; you are using it as the start of the body.

How Do For Loops Work In Scheme?

I'm having some difficulty understanding how for loops work in scheme. In particular this code runs but I don't know why
(define (bubblesort alist)
;; this is straightforward
(define (swap-pass alist)
(if (eq? (length alist) 1)
alist
(let ((fst (car alist)) (scnd (cadr alist)) (rest (cddr alist)))
(if (> fst scnd)
(cons scnd (swap-pass (cons fst rest)))
(cons fst (swap-pass (cons scnd rest)))))))
; this is mysterious--what does the 'for' in the next line do?
(let for ((times (length alist))
(val alist))
(if (> times 1)
(for (- times 1) (swap-pass val))
(swap-pass val))))
I can't figure out what the (let for (( is supposed to do here, and the for expression in the second to last line is also a bit off putting--I've had the interpreter complain that for only takes a single argument, but here it appears to take two.
Any thoughts on what's going on here?
That's not a for loop, that's a named let. What it does is create a function called for, then call that; the "looping" behavior is caused by recursion in the function. Calling the function loop is more idiomatic, btw. E.g.
(let loop ((times 10))
(if (= times 0)
(display "stopped")
(begin (display "still looping...")
(loop (- times 1)))))
gets expanded to something like
(letrec ((loop (lambda (times)
(if (= times 0)
(display "stopped")
(begin (display "still looping...")
(loop (- times 1)))))))
(loop 10))
This isn't actually using a for language feature but just using a variation of let that allows you to easily write recursive functions. See this documentation on let (it's the second form on there).
What's going on is that this let form binds the name it's passed (in this case for) to a procedure with the given argument list (times and val) and calls it with the initial values. Uses of the bound name in the body are recursive calls.
Bottom line: the for isn't significant here. It's just a name. You could rename it to foo and it would still work. Racket does have actual for loops that you can read about here.

Resources