2

In Drracket I am being tasked with creating a recursive macro that can take in (edges n1 -> n2 <-> n3) for example, with n1, n2, and n3 being (define-struct node (name edges)). The -> indicates putting the second node into the edges of the first node and the <-> indicates doing it both ways. So n1 would have edges n2 and n2 would have edges n3 and n3 would have edges n2. My problem lies in Drracket recursive macros. When you have a variable with an ellipse after it, ex: (edges n1 -> n2 ...) in the pattern matcher, I don't know how to reference just n2 without also evaluating the ellipse.

(define-syntax edges
  (syntax-rules (-> <->)
[(edges n1 -> n2 ...)
 (begin
     (set-node-edges! n1 (cons (n2 ...) (node-edges n1))) 
   (edges n2 ...))]
[(edges n1 <-> n2 ...)
 (begin
       (begin
         (set-node-edges! n1 (cons (n2 ...) (node-edges n1)))
         (set-node-edges! n2 ... (cons 'n1 (node-edges n2 ...))))
   (edges n2 ...))]
[(edges n1)
 void]))
  • Hey, I just noticed your SO name as well. I also suffer from chronic depression and just wanted you to know that we are here for you. <3 – Leif Andersen Oct 12 '18 at 19:15

1 Answers1

0

The problem is with the patterns you've used, the parens are actually tightly bound with the n2. That is n2 is a list of everything else. What you actually want is a pattern that is something more like:

 n1 -> n2 rest ...

That way, the ... is bound to the rest, rather than the n2.

I also recommend using syntax-parse instead of syntax-rules. It allows for much more clear pattern matching (as well as arbitrary computations. For example, right now you are assuming that n1 and n2 are identifiers, but you aren't checking it. Your macro also requres -> and <-> to be bound outside of that macro. With syntax-parse, the whole thing becomes much cleaner:

#lang racket
(require (for-syntax syntax/parse)) ;; <-- Needed to use syntax-parse
(define-syntax (edges stx)
  (syntax-parse stx
   #:datum-literals (-> <->)
   [(_)
    #'(begin)]
   [(_ n1:id -> n2:id rest ...)
    #'(begin .... your code here ....)]
   [(_ n1:id <-> n2:id rest ...)
    #'(begin .... your code here ....)]))

Inside of the .... your code here .... blocks, you can use n1 to refer to n1:id, you can use n2 to refer to n2:id, and finally, you can use something like (n2 rest ...) to refer to a list starting with n2:id and finishing with rest ....

Also, note that by putting :id at the end of the patterns, you get automatic checking that n1 and n2 are identifiers.

Leif Andersen
  • 21,580
  • 20
  • 67
  • 100