/*  CSci 658, Software Language Engineering 
    Computer Configuration DSL using Function Sequences
      in Nested Closures 
    H. Conrad Cunningham

1234567890123456789012345678901234567890123456789012345678901234567890

2009-04-11: (V1) Original
2009-04-12: (V1a)
2009-05-13: (V1b) Minor updates to Scala programming style.
2018-02-02: (V2) Changed ComputerBuilder to ComputerBuilderBase to
            to avoid name clash with other DSLs in group.
	    Updated comments. Created compile script.

This is a Scala reimplementation of Martin Fowler's Computer
Configuration DSL given in the Nested Closures chapter of his book
Domain-Specific Languages under the topic "Wrapping a Function
Sequence in a Nested Closure". Fowler's program is in Ruby This
example uses the Object Scoping and Nested Closures (as well as
Expression Builder and Context Variable) DSL patterns.  It also
required a modifed semantic model code from that needed for the
previous Computer Configuration DSL (from the Method Chaining chapter)
that used method chaining without closures.

The overall implementation in Scala can probably be redesigned to
take better advantage of Scala features.

TODO: Explore other structures for building DSL rather than
      subclassing. 
Here is Fowler's example DSL program rewritten in the Scala syntax:

    computer { 
      processor {
        cores(2)
        i386
        processorSpeed(2.2)
      }
      disk {
        size(150)
      }
      disk {
        size(75)
        diskSpeed(7200)
        sata
      }
    }
*/

/* The abstract class ComputerBuilder implements an Expression Builder
   for the configuration of Computers that include a single processor
   and multiple disks.  It is designed according to Object Scoping and
   Nested Closures DSL patterns.
*/

abstract class ComputerBuilderBase {
 
  // Context variables
  protected var currentComputer:  Computer  = null
  protected var currentProcessor: Processor = null
  protected var currentDisk:      Disk      = null

  // Deferred method to hold DSL program in subclass
  def doBuild: Computer

  // DSL statement "computer".  Unlike the other DSL statements,
  // "computer" returns the "Computer" object it creates.
  def computer(compBlock: => Unit): Computer = {
    currentComputer = new Computer()
    compBlock
    currentComputer
  }

  // DSL statement "processor".
  def processor(procBlock: => Unit) { 
    currentProcessor = new Processor()
    currentComputer.setProcessor(currentProcessor)
    procBlock
  }

  // DSL statement "cores".
  def cores(arg: Int) { currentProcessor.setCores(arg) }

  // DSL statement "i386".
  def i386 { currentProcessor.setType(Processor.Type.i386) }

  // DSL statement "processorSpeed".
  def processorSpeed(arg: Double) { currentProcessor.setSpeed(arg) }

  // DSL statement "disk".
  def disk(diskBlock: => Unit) {
    currentDisk = new Disk()
    currentComputer.addDisk(currentDisk)
    diskBlock
  }

  // DSL statement "size".
  def size(arg: Int) { currentDisk.setSize(arg) }

  // DSL statement "diskSpeed".
  def diskSpeed(arg: Int) { currentDisk.setSpeed(arg) }

  // DSL statement "sata".
  def sata { currentDisk.setIface(Disk.Interface.SATA) }

  // Accessor methods
  def getComputer: Computer = currentComputer
}


/* Test the DSL with Fowler's example DSL program.  */

class BasicComputerBuilder extends ComputerBuilderBase {
  def doBuild = 
    // Begin DSL program
      computer { 
        processor {
          cores(2)
          i386
          processorSpeed(2.2)
        }
        disk {
          size(150)
        }
        disk {
          size(75)
          diskSpeed(7200)
          sata
        }
      }
  // End DSL program
}

object CompConfigClosure {

  def main(args: Array[String]) {
    println("\nComputer Configuration DSL test beginning.\n")

    val builder = new BasicComputerBuilder()
    println(builder.doBuild)

    println("Computer Configuration DSL Test ending.")
  }
}
