Groovy web console

subscribe to the feed Subscribe
to this
site
LispBuilder (via #groovywebconsole)
tweet this snippet Tweet
this
script

LispBuilder

Published 9 years ago by uehaj with tags LispBuilder Lisp Interpreter DSL
Actions  ➤ Edit in console Back to console Show/hide line numbers View recent scripts
ArrayList.metaClass.asType = { Class c->
  if (c == LispList) {
    if (delegate.size() == 0) {
      return null
    }
    return new Cons(delegate.first(), delegate.tail() as LispList)
  }
  else if (c == Cons) {
    return new Cons(delegate[0], delegate[1])
  }
}

String.metaClass.getIsSymbol = { false } /* set closure through metaclass for class */

String.metaClass.eval = { env ->
  if (delegate.isSymbol) { /* the String is lisp symbol */
    if (env.containsKey(delegate)) {
      return env[delegate]
    }
    throw new Exception("Symbol not found: "+delegate)
  }
  return delegate
}

Object.metaClass.eval = { env ->
  delegate
}

class LispList {
}

class Cons extends LispList {
  private carPart

  def getCar() {
    if (carPart instanceof Closure) {
      carPart = carPart.call()
    }
    carPart
  }

  void replaceCar_(a) {
    carPart = a
  }

  private cdrPart

  def getCdr() {
    if (cdrPart instanceof Closure) {
      cdrPart = cdrPart.call()
    }
    cdrPart
  }

  void replaceCdr_(a) {
    cdrPart = a
  }

  Cons(a, b) {
    carPart = a;
    cdrPart = b;
  }

  def getAt(n) {
    assert n >= 0
    n==0 ? car : cdr[n-1]
  }

  String toStringHelper(HashSet hashSet) {
    if (hashSet.contains(this)) {
      return "..."
    }
    hashSet.add(this)
    def result = new StringBuilder("(")
    def list
    for (list=this; list instanceof LispList; list=list.cdr) {
      def elem = list.car
      if (elem == null) {
        result.append("<nil>")
      }
      else if (elem instanceof Cons) {
        result.append(elem.toStringHelper(hashSet)+(list.cdr == null?"":" "))
      }
      else {
        result.append(elem.toString()+(list.cdr == null?"":" "))
      }
    }
    if (list != null) {
      result.append(". " + list.toString());
    }
    result.append(")");
  }


  String toString() {
    def set = new HashSet()
    return toStringHelper(set)
  }


  int size() {
    if (this.cdr == null) {
      1
    }
    else {
      1 + this.cdr.size()
    }
  }

  LispList plus(LispList a) {
    if (this.size() == 0) { /* this condition is impossible in this List implementation */
      a
    }
    else if(this.size() == 1) {
      [car, a] as Cons
    }
    else {
      [car, cdr+a] as Cons
    }
  }

  LispList append_(a) { /* destructive append */
    if (this.size() == 0) { /* this condition is impossible in this List implementation */
      a
    }
    else if(this.size() == 1) {
      replaceCdr_(new Cons(a, null))
    }
    else {
      cdr.append_(a)
    }
  }

  boolean equals(LispList a) {
    if (this.car != a.car) {
      return false
    }
    if (this.cdr == null && a.cdr != null) {
      return false
    }
    if (this.cdr != null && a.cdr == null) {
      return false
    }
    if (this.cdr == null && a.cdr == null) {
      return true
    }
    return this.cdr == a.cdr
  }

  Iterator iterator() {
    return new LispListIterator(this)
  }

  def applyLambda(lambda, args, env) {
    def pseudoArgList = lambda.cdr.car
    def body = lambda.cdr.cdr.car
    if (args == null && pseudoArgList != null
        || args != null && pseudoArgList == null
        || args.size() != pseudoArgList.size()) {
      throw new Error("Arguments number mismatch: required '$pseudoArgList' but value is '$args'")
    }
    def localEnv = new Env(env)
    pseudoArgList.eachWithIndex { it, idx ->
      localEnv[it] = args[idx]
    }
    body.eval(localEnv)
  }

  def apply(func, args, env) {

    if (func instanceof String && !env.containsKey(func)) {
      throw new Error("Undefined function: "+func)
    }
    def entry = func.eval(env)
    if (entry instanceof Closure) {
      /* the function is Groovy closure */
      if (entry.maximumNumberOfParameters != 3) {
        /* evaluate arguments and call the Groovy closure(SUBR) */
        args = args*.eval(env) as LispList
        return entry.call(args, env)
      }
      else {
        /* 3 argument closure means special form,
           so call without argument evaluation(FSUBR) */
        return entry.call(args, env, "no_automatic_eval_arg")
      }
    }
    else if (entry instanceof Cons) {
      /* the function is list (lambda function). (EXPR) */
      if (entry.car == "lambda" && entry.car.isSymbol == true) {
        args = args*.eval(env) as LispList
        return applyLambda(entry, args, env)
      }
    }
    return entry
  }

  def eval(env = new Env()) {
    def func = car
    def args = cdr
    def result = apply(func, args, env)
    result
  }

}

class LispListIterator implements Iterator {
  LispList cursor

  LispListIterator(LispList list) {
    cursor = list
  }

  boolean hasNext() {
    cursor != null
  }

  Object next() {
    def result = cursor.car
    cursor = cursor.cdr
    return result
  }

  void remove() {
    throw new UnsupportedOperationException("remove not supported");
  }
}

class Env {
  Map localEnv = [:]
  def parentEnv

  Env(env = null) {
    if (env == null) {
      parentEnv = [:]
      Functions.registerFunctions(parentEnv)
    }
    else {
      this.parentEnv = env
    }
  }


  def getProperty(String key) {
    if (localEnv.containsKey(key)) {
      return localEnv[key]
    }
    if (parentEnv != null) {
      return parentEnv[key]
    }
    null
  }

  void setProperty(String key, value) {
    localEnv[key] = value
  }

  def containsKey(key) {
    if (localEnv.containsKey(key)) {
      return true
    }
    if (parentEnv.containsKey(key)) {
      return true
    }
    return false
  }

  String toString() {
    "local:" + localEnv.toString() + "parent:" + parentEnv.toString()
  }

  def eval(Closure c) {
    def bx = new LispBuilder()
    return bx.build(c).eval(this)
  }

}

class Functions {

  private static registerClosureFunctions(map) {
    map.with {

      eq = { args, env ->
             assert args.size() == 2
             if (args[0] == null && args[1] == null) {
               return true
             }
             if ((args[0] == null && args[1] != null)
                 ||(args[0] != null && args[1] == null)
                 ) {
               return false
             }
             if (args[0].class != args[1].class) {
               return false
             }
             if (args[0] instanceof Number
                 || args[0] instanceof String) {
               return args[0] == args[1]
             }
             args[0].is(args[1])
      }

      IF = { args, env, no_automatic_eval_arg ->
        assert args.size() in 2..3
        def cond = args[0].eval(env)
        def thenPart = args[1]
        if (cond) {
          return thenPart.eval(env)
        }
        else if (args.size() == 3) {
          def elsePart = args[2]
          return elsePart.eval(env)
        }
        return false
      }

      quote = { args, env, no_automatic_eval_arg ->
        assert args.size() == 1
        return args[0]
      }

      TRUE = true

      FALSE = false

      nil = null

      car = { args, env ->
              assert args.size() == 1
              args[0].car }

      cdr = { args, env ->
              assert args.size() == 1
              args[0].cdr }

      cons = { args, env ->
               assert args.size() == 2
               new Cons(args[0], args[1]) }

      setq = { args, env, no_automatic_eval_arg ->
               assert args.size() == 2
               env[args[0]] = args[1].eval(env)
      }

      and =  { args, env ->
               def result = true
               args.each {
                 result = result && it
               }
               result
      }

      or =  { args, env ->
              def result = false
              args.each {
                result = result || it
              }
              result
      }

      progn = {args, env, no_automatic_eval_arg ->
               def last
               args.each {
                 last = it.eval(env)
               }
               last
      }

      equal = { args, env ->
                assert args.size() == 2
                args[0] == args[1]
      }

      lt = { args, env ->
             assert args.size() == 2
             args[0] < args[1]
      }

      le = { args, env ->
             lt.call(args, env) || equal.call(args, env)
      }

      gt = { args, env ->
             !le.call(args, env)
      }

      ge = { args, env ->
             !lt.call(args, env)
      }


      add = { args, env ->
              assert args.size() >= 2
              def result = 0
              args.each {
                result += it.eval(env)
              }
              result
      }

      sub = { args, env ->
              assert args.size() >= 2
              def result = args[0]
              args.cdr.each {
                result -= it.eval(env)
              }
              result
      }

      mul = { args, env ->
              assert args.size() >= 2
              def result = args[0]
              args.cdr.each {
                result *= it.eval(env)
              }
              result
      }

      div = { args, env ->
              assert args.size() >= 2
              def result = args[0]
              args.cdr.each {
                result /= it.eval(env)
              }
              result
      }

      defun = {args, env, no_automatic_eval_arg ->
               assert args.size() == 3
               def sym_lambda = LispBuilder.makeSymbol("lambda");
               def sym_quote = LispBuilder.makeSymbol("quote");
               def new_args = [args[0],
                               [sym_quote,
                                [ sym_lambda, args[1], args[2] ]  as LispList
                                ] as LispList
                               ] as LispList
               setq.call(new_args, env, "no_automatic_eval_arg")
      }

      // invoke Java(groovy) level method
      call = {args, env ->
              assert args.size() >= 2
              def new_args = []
              for (int i=2; i<args.size(); i++) {
                new_args += args[i]
              }
              args[0].invokeMethod(args[1], new_args.size()==0 ? null : new_args)
      }

      print = {args, env ->
               args.each {
                 print it
               }
      }

      println = {args, env ->
                 print.call(args, env)
                 println ""
      }
    }
  }

  private static registerLambdaFunctions(map) {
    def bx = new LispBuilder()
    map.with {

      not = bx.build{lambda
                     ${x}
                     ${IF; x; FALSE; TRUE}
      }

      neq = bx.build{lambda
             ${x; y}
             ${not; ${eq; x; y}}
      }

      nullp = bx.build {lambda
                        ${x}
                        ${eq; nil; x}
      }

      append = bx.build {lambda
                         ${a; b}
                         ${IF;
                           ${nullp; a}
                           b;
                           ${cons; ${car; a}; ${append; ${cdr; a}; b}}}
      }

      reverse = bx.build {lambda
                          ${x}
                          ${IF
                            ${nullp; x}
                            x
                            ${append
                              ${reverse
                                ${cdr; x}}
                              ${cons; ${car; x}; nil}}}
      }
    }
  }

  static registerFunctions(map) {
    registerClosureFunctions(map)
    registerLambdaFunctions(map)
  }

}

class LispBuilder {
  private LispList readBuffer

  def readStart() {
    readBuffer = null
  }

  def readResult() {
    readBuffer
  }

  def build(Closure c) {
    readStart()
    c.delegate = this
    c.resolveStrategy = Closure.DELEGATE_FIRST;
//    c.resolveStrategy = Closure.DELEGATE_ONLY;
    c.call()
    return readResult()
  }

  def invokeMethod(String m, args) {
    try {
      if (m != '$') {
        getProperty(m)
      }
      def elem = args[0]
      if (elem instanceof Closure) {
        LispBuilder reader = new LispBuilder()
        elem = reader.build(elem)
      }
      if (elem != null) {
        if (readBuffer == null) {
          readBuffer = [elem] as LispList
        }
        else {
          readBuffer.append_(elem)
        }
      }
    }
    catch (Exception e) {
      e.printStackTrace()
    }
  }

  static makeSymbol(s) {
    def result = new String(s) /* new String instance is reqired (countermasure of 'interned' equality check) */
    result.metaClass.getIsSymbol = { true } /* set closure through metaclass for instance */
    result
  }

  def getProperty(String p) {
    try {
      def value = p
      if (p.startsWith('$')) {
        value = Integer.parseInt(p.substring(1,p.size()))
      }
      else {
        value = makeSymbol(value)
      }
      if (readBuffer == null) {
        readBuffer = [value] as LispList
      }
      else {
        readBuffer.append_(value)
      }
    }
    catch (Exception e) {
      e.printStackTrace()
    }
  }

}

println([1,2,3] as LispList)
println([1,2] as Cons)

def bx = new LispBuilder()

// Fibonacci number by recursive call
println bx.build{progn
                ${defun; fib; ${n}
                  ${IF; ${or; ${equal; n; $1}; ${equal; n; $2}}
                    $1
                    ${add; ${fib; ${add; n; $(-1)}}
                      ${fib; ${add; n; $(-2)}}}}}
                ${fib; $10}
}.eval()

// Square Root calcuration by newton method
def env = new Env()
env.eval{defun; abs; ${x}
         ${IF; ${lt; x; $0}; ${mul; x; $(-1)}; x}}

env.eval{defun; average; ${x; y}
         ${div; ${add; x; y}; $2}}

env.eval{progn
         ${defun; improve; ${y; x}
           ${average; y; ${div; x; y}}}

         ${defun; good_enoughp; ${y; x}
           ${le; ${abs; ${sub; ${mul; y; y}; x}}; $(0.000001)}}

         ${defun; sqrt_iter; ${y; x}
           ${IF; ${good_enoughp; y; x}
             y
             ${sqrt_iter; ${improve; y; x}
               x}}}

         ${defun; sqrt; ${x}
           ${sqrt_iter; $(1.0); x}}}


println env.eval{sqrt; $(3.0)}