views:

113

answers:

2

In kiama a generic "dup" method is defined which copies Product objects and applies a given function to each of the elements of the Product in order to support rewriting of Terms:

/**
 * General product duplication function.  Returns a product that applies
 * the same constructor as the product t, but with the given children            
 * instead of t's children.  Fails if a constructor cannot be found or
 * if one of the children is not of the appropriate type.
 */
private def dup (t : Product, children : Array[AnyRef]) : Product = {

    val ctor = (t.getClass.getConstructors())(0)
    try {
        val ret = ctor.newInstance (children : _*).asInstanceOf[Product]
        ret
                                                                                    } catch {
        case e : java.lang.ClassCastException =>
            error ("dup cast failed: " + t)
        case e : IllegalArgumentException =>
            error ("dup illegal arguments: " + ctor + " (" +
                   children.deep.mkString (",") + "), expects " +
                   ctor.getParameterTypes.length)
    }            
}

Used like this:

private def childProduct (p : Product, i : Int, s : => Strategy) : Option[Term] = {
    val numchildren = p.productArity
    val ct = p.productElement (i-1)
    val children = new Array[AnyRef](numchildren)
    for (j <- 0 until numchildren)
       children (j) = makechild (p.productElement (j))
       s (ct) match {
                case Some (ti) =>
                    children (i-1) = makechild (ti)
                case None      =>
                    return None
           }
    val ret = dup (p, children)
    Some (ret)

This gives some limitations on the usage. I know they are working on replacing this mechanism (Issue 2, Issue 31) but I think it might be interesting in hearing how you guys would do this?

For example will the Positional var(or any other var!) of scala parser combinators not be copied. In fact everything not part of the explict first constructor (and thereby also the default compiler generated unapply method).

I have been experimenting with providing some kind of trait or similar I could add to my terms that the dup/childProduct/etc methods could use, but to no luck yet. (I don't see how case class copy could be used)

+3  A: 

I use a modified version of the Kiama Rewriter module. It adds a Constructable trait that signifies that a value can deconstruct itself (make an array holding its constituents / children) and reconstruct a new instance from an array of constituents. When Kiama encounters a value that implements Constructable in one of the methods dup, child, all or some, it avoids the reflective code and instead uses deconstruct and construct.

In addition to freeing the client code from the requirement that types amenable to rewriting be derived from Product, it allows for a heterogeneous rewrite. In other words, the type coming out of a rewrite need not be precisely identical to the concrete type of the value that was rewritten.

The down-side is that the client code must define those deconstruct and construct methods.

(Lastly and not really germane to your question, I added an optimization that avoids building a new value if the result of transforming the sub-values (the non-atomic ones) were all equal to the original. I added this only in the branch that handles Constructables.)

Randall Schulz
In case the asker doesn't know Kiama: http://code.google.com/p/kiama/
the_great_monkey
Interesting. Is that code available? I have ended up doing lost of jump-through-hoops to include everything that needs to be copied into the default constructor :|
svrist
@the_great_monkey: I do :) I even link to that page as well as parts of the source code
svrist
I can supply my modified version, of course. But some caveats are in order: 1) It's based on a now-superceded version of Kiama; 2) Because I plucked one module (Rewriter.scala) from the Kiama source, I also subjected it to my tender formatting mercies. I did keep a copy of the re-formatted but unmodified version so I can see what deltas I applied. Let me know if you'd like it, I'll put it in a public place.
Randall Schulz
Ahh okay I see! What was I thinking?!
the_great_monkey
@Randall: Sure! it would be interesting.
svrist
+1  A: 

I have created a small GitHub repository, RSchulz/sbtr, containing my modified Kiama Rewriter.scala module. In it you will find these files:

  • src/main/scala/rrs/sbtr/SBTR.scala — My "Strategy-Based Term Rewriting" module
  • src/main/scala/rrs/sbtr/RewriterFmt.alacs — The original code after formatting for my style conventions
  • src/main/scala/rrs/sbtr/Rewriter-0.8.alacs — The slightly updated 0.8 version
  • src/main/scala/rrs/sbtr/Rewriter.alacs — The original version from which I started

Diff-ing the first two will disclose my modifications, which should be easily back-ported into the current Kiama Rewriter.scala code.

The primary difference between the original and the 0.8 versions of the Rewriter.scala source are the addition of queryf, which I did carry over into SBTR. Changes subsequent to 0.8 have not been incorporated.

The suffix .alacs (scala spelled backwards) is used to keep those files from being compiled. To my knowledge, build tools and IDEs cannot be directed to ignore specific files (at least not easily).

Randall Schulz