Package gnu.expr

Class ANormalize

java.lang.Object
gnu.expr.ExpVisitor<Expression,gnu.expr.ANormalize.Context>
gnu.expr.ExpExpVisitor<gnu.expr.ANormalize.Context>
gnu.expr.ANormalize
All Implemented Interfaces:
SourceLocator, SourceLocator, Locator

public class ANormalize extends ExpExpVisitor<gnu.expr.ANormalize.Context>
A visitor that performs transformation to Administrative Normal Form. This is an adaptation and extension of the A-normalization algoritm described in the paper "The Essence of Compiling with Continuations" by Flanagan et al. and in "A-Normalization: Why and How" by Matt Might (http://matt.might.net/articles/a-normalization/). The algoritm performs a monadic transformation combining three steps:
1. Monadic conversion:
 (+ 1 (if (>= x 0) (f x) 0))
 
becomes:
 (bind (if (>= x 0)  (f x)  (return 0)) (lambda (t) (+ 1 t)))
 
2. The result is interpreted in the Identity monad:
                 (return a)  becomes:  a
 
    (bind a (lambda (x) b))  becomes:  (let ((x a)) b)
 
   (bind (if (>= x 0)
             (f x)
             (return 0))
         (lambda (t) (+ 1 t)))
 
becomes:
   (let ((t (if (>= x 0)
                (f x)
                (return 0))))
        (+ 1 t))
 
3. Nested let are flattened:
 
    (let ((x (let ((y a))               (let ((y a))
               b)))        becomes:          (let ((x b))
      c)                                    c))
 
 
In the actual Java code "return" operation is called "identity", while the "bind" operation is called "normalizeName" as in the Flanagan et al. paper. The ExpVisitor type matching mechanism replaces the role of the "match" in the paper, while the Context class replaces the "k" parameter. Lambdas are simulated with classes for backward compatibility with Java version 7 and lower. Each visit[...]Exp method is called with two parameters, an expression and a context (the very first context is the identity function that returns its argument). If the visit method is called with a non-atomic expression a new context is created and the passed context is called only inside the new one. The new-context is then passed to "normalizeName" that has two purposes: 1. to create a context, that generates a "let" expression to let-bind the expression next to come in the "visiting" process; 2. to call visit() on the passed expression, to continue the syntax tree traversing. This chain will finish when a leaf (an atomic expression) is encountered in the tree, in this case the passed context is invoked (which in turn will invoke the previous context and so on). At this point the chain of context invocations starts to wrap each expression in a "let" binding, processing the expressions backward, and enclosing them step by step in nested "let" expressions. This backward traversing stops when the context called is the identity function. When the expression to normalize is a conditional, "normalizeTerm" is used on each branch expression. Instead of creating a let binding for each branch, as they cannot be evaluated before the test outcome, "normalizeTerm" calls the visit method with the identity context, restarting the normalization in each branch.