Arc Forumnew | comments | leaders | submitlogin
2 points by akkartik 4206 days ago | link | parent

"how do I manipulate quotes in macroexpansions, as needed in qq.wart?"

Yes, you're right that I need to bring back my definitions of quote, etc for qq.wart. But there was more to it than that. Here is -- I think -- a relatively faithful port of qq-expand including the optimizer: http://gist.github.com/3971932#file_080qq.wart. I say 'relatively' because wart doesn't support multiple args to quote/unquote/quasiquote, so a lot of your hardest tests become moot. Also, the backquote doesn't expand to a call to quasiquote in wart.

Sorry this took so long. Even when you provided such thorough tests it was hard to make myself focus on this exercise; there's always alternatives with more instant gratification. But in the end it was a fun exercise in recreating the 'theory' of a (tiny) codebase (http://alistair.cockburn.us/ASD+book+extract%3A+%22Naur,+Ehn...)

---

Rather than start with your port I went back to your arc sources and tried to track my process in an effort to generalize lessons. I was able to split your recursive functions into clauses without much effort[1]. For example:

  ; Arc
  (def qq-cons (expr1 expr2)
    ; assume expr2 is non-splicing
    (let operator (if (splicing expr1) 'dotted-list 'cons)
      (if (no optimize-cons*)
           (list operator expr1 expr2)
          (and (~splicing expr1) (literal expr1) (no expr2))
           (list 'quote (list (eval expr1)))
          (no expr2)
           (list 'list expr1)
          (atom expr2)
           (list operator expr1 expr2)
          (caris expr2 'list)
           (dotted-list 'list expr1 (cdr expr2))
          (and (quoted-non-splice expr1) (quoted-non-splice expr2))
           (list 'quote (cons (cadr expr1) (cadr expr2)))
          (list operator expr1 expr2))))

  # Wart; an early incorrect version
  def qq_cons(expr1 expr2)
    # assume expr2 is non-splicing
    let operator (if splicing?.expr1 'dotted_list 'cons)
      (list operator expr1 expr2)

  def qq_cons(expr1 expr2) :case (and Optimize_cons quoted_non_splice?.expr1
                                                    quoted_non_splice?.expr2)
    (list quote (list cdr.expr1 cdr.expr2))

  def qq_cons(expr1 expr2) :case (and Optimize_cons (carif.expr2 = 'list))
    (dotted_list 'list expr1 cdr.expr2)

  def qq_cons(expr1 expr2) :case (and Optimize_cons atom?.expr2)
    let operator (if splicing?.expr1 'dotted_list 'cons)
      (list operator expr1 expr2)

  def qq_cons(expr1 expr2) :case (and Optimize_cons no.expr2)
    (list 'list expr1)

  def qq_cons(expr1 expr2) :case (and Optimize_cons ~splicing?.expr1
                                                    literal?.expr1
                                                    no.expr2)
    (list quote (list eval.expr1))
The key was to peel the cases off in reverse order.

As in the past, wart is more verbose than arc, but in exchange you get to move the cases around. For example, all but the first clause can be in a separate file so you don't care about them when Optimize_cons is unset.

---

But there was more to this than a simple transliterated port, because quote/unquote/backquote were represented differently in wart than other lisps. I had to track down and update all the places in your version that made this foundational assumption.

Since the backquote is baked into wart's reader rather than expanding to quasiquote like in other lisps, I traced through all top-level calls to qq-expand as a baseline to compare against:

  ; change in original arc version
  (mac quasiquote (expr)
    (ret ans qq-expand.expr
      (ero expr " => " ans)))
Once I had this output in hand I could start porting your tests. I started with a pass at just visually catching as many cases of treating the cdr of quote, quasiquote, etc. as a list as I could[2]; you might enjoy seeing what I was missing at that point, the 5 tokens that took a week of debugging :)

Two forms turned out to be quite useful in my debugging:

  # http://arclanguage.org/item?id=11140
  after_fn qq_expand_list(expr)
    (prn "qq_expand_list " expr "
  => " result)
  # ..and so on for qq_transform, qq_cons, qq_append.

  # Easily turn debug prints on and off.
  def xprn args
    nil
---

References are to snapshots of the same gist:

[1] https://gist.github.com/3971932/f85f4da34d79d7d6cb1c9b01ce60.... Try diffing this version against your port.

[2] https://gist.github.com/3971932/72d159184afaa68e42920801b75e...