[MRQL-96] Develop a MRQL debugger
diff --git a/core/src/main/java/org/apache/mrql/Bag.java b/core/src/main/java/org/apache/mrql/Bag.java
index 7a8acec..33424d0 100644
--- a/core/src/main/java/org/apache/mrql/Bag.java
+++ b/core/src/main/java/org/apache/mrql/Bag.java
@@ -87,6 +87,8 @@
         mode = Modes.STREAMED;
         iterator = i;
         consumed = false;
+        if (Config.debug)
+            materialize();
     }
 
     /** is the Bag stored in an ArrayList? */
diff --git a/core/src/main/java/org/apache/mrql/Debugger.gen b/core/src/main/java/org/apache/mrql/Debugger.gen
new file mode 100644
index 0000000..266594d
--- /dev/null
+++ b/core/src/main/java/org/apache/mrql/Debugger.gen
@@ -0,0 +1,282 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.mrql;
+
+import org.apache.mrql.gen.*;
+import java.util.Enumeration;
+import java.io.*;
+import javax.swing.*;
+import javax.swing.JTree.*;
+import javax.swing.tree.*;
+import javax.swing.event.*;
+import java.awt.*;
+import java.awt.event.*;
+
+
+/* The MRQL debugger that uses the lineage generated in Provenance.gen */
+public class Debugger extends JPanel implements TreeSelectionListener {
+    final MRData result;
+    final JTree tree;
+    final JTextField search = new JTextField(20);
+    private static volatile boolean exit = false;
+    Trees exprs;
+
+    /** wrapped strings to be colored red */
+    private final static class TaggedString {
+        public String value;
+        TaggedString ( String value ) { this.value = value; }
+        public String toString () { return value; }
+    }
+
+    public Debugger ( MRData result_value, Trees exprs ) {
+        super(new GridLayout(1,0));
+        result = result_value;
+        this.exprs = exprs;
+        JToolBar toolBar = new JToolBar("");
+        toolBar.setPreferredSize(new Dimension(1000,40));
+        JButton button = new JButton();
+        button.setActionCommand("Previous");
+        button.setText("prev");
+        button.addActionListener(new ActionListener() {
+                public void actionPerformed ( ActionEvent e ) {
+                }
+            });
+        button.setVisible(false);
+        toolBar.add(button);
+        button = new JButton();
+        button.setActionCommand("Next");
+        button.setText("next");
+        button.addActionListener(new ActionListener() {
+                public void actionPerformed ( ActionEvent e ) {
+                }
+            });
+        button.setVisible(false);
+        toolBar.add(button);
+        toolBar.add(Box.createRigidArea(new Dimension(400,0)));
+        button = new JButton();
+        button.setActionCommand("Clear");
+        button.setText("clear");
+        button.addActionListener(new ActionListener() {
+                public void actionPerformed ( ActionEvent e ) {
+                    search.setText("");
+                    DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
+                    DefaultMutableTreeNode root = createNode(result,"","");
+                    root.setUserObject("results");
+                    model.setRoot(root);
+                    model.reload(root);
+                }
+            });
+        toolBar.add(button);
+        toolBar.add(search);
+        button = new JButton();
+        button.setActionCommand("SearchOutput");
+        button.setText("Search Output");
+        button.addActionListener(new ActionListener() {
+                public void actionPerformed ( ActionEvent e ) {
+                    if (!search.getText().equals("")) {
+                        DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
+                        DefaultMutableTreeNode root = createNode(result,search.getText(),"");
+                        root.setUserObject("search output results");
+                        model.setRoot(root);
+                        model.reload(root);
+                    }
+                }
+            });
+        toolBar.add(button);
+        button = new JButton();
+        button.setActionCommand("SearchInput");
+        button.setText("Search Input");
+        button.addActionListener(new ActionListener() {
+                public void actionPerformed ( ActionEvent e ) {
+                    if (!search.getText().equals("")) {
+                        DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
+                        DefaultMutableTreeNode root = createNode(result,"",search.getText());
+                        root.setUserObject("search input results");
+                        model.setRoot(root);
+                        model.reload(root);
+                    }
+                }
+            });
+        toolBar.add(button);
+        tree = new JTree(createNode(result,"",""));
+        JScrollPane sp = new JScrollPane(tree,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+                                         JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+        sp.setPreferredSize(new Dimension(1000,2000));
+        sp.getVerticalScrollBar().setPreferredSize(new Dimension(20,0));
+        tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+        Font currentFont = tree.getFont();
+        tree.setFont(new Font(currentFont.getName(),
+                              currentFont.getStyle(),
+                              (int)(currentFont.getSize()*1.5)));
+        DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer() {
+                public Component getTreeCellRendererComponent
+                    ( JTree tree, Object value, boolean sel, boolean exp,
+                      boolean leaf, int row, boolean hasFocus ) {
+                    super.getTreeCellRendererComponent(tree,value,sel,exp,leaf,row,hasFocus);
+                    // tagged strings are red
+                    if (((DefaultMutableTreeNode)value).getUserObject() instanceof TaggedString)
+                        setForeground(Color.red);
+                    return this;
+                }
+            };
+        renderer.setLeafIcon(null);
+        renderer.setOpenIcon(null);
+        renderer.setClosedIcon(null);
+        tree.setCellRenderer(renderer);
+        tree.addTreeSelectionListener(this);
+        setLayout(new BorderLayout());
+        add(toolBar,BorderLayout.NORTH);
+        add(sp,BorderLayout.CENTER);
+    }
+
+    public void valueChanged ( TreeSelectionEvent e ) { }
+
+    private String exprNode ( Tree e ) {
+        match e {
+        case call(source,_,`path,...):
+            return "source: "+path;
+        case call(`f,...):
+            return f.toString();
+        case cmap(lambda(`v,_),_):
+            return "cmap "+v;
+        case groupBy(_):
+            return "groupBy";
+        case coGroup(...):
+            return "coGroup";
+        case reduce(`m,...):
+            return "reduce("+m+")";
+        case project(`v,`a):
+            return exprNode(v)+"."+a;
+        case nth(`x,`n):
+            return exprNode(x)+"#"+n;
+        case `f(...):
+            return f;
+        };
+        return e.toString();
+    }
+
+    private DefaultMutableTreeNode create_node ( MRData value, int n, String inputSearch ) {
+        if (value instanceof Tuple) {
+            DefaultMutableTreeNode node = new DefaultMutableTreeNode("input "+n);
+            DefaultMutableTreeNode child = provenanceNode(value,inputSearch);
+            if (child.getUserObject() instanceof TaggedString)
+                node.setUserObject(new TaggedString("input "+n));
+            node.add(child);
+            return node;
+        } else if (value instanceof Bag) {
+            DefaultMutableTreeNode node = new DefaultMutableTreeNode("input "+n);
+            boolean matched = false;
+            for ( MRData e: (Bag)value ) {
+                DefaultMutableTreeNode child = provenanceNode(e,inputSearch);
+                matched |= child.getUserObject() instanceof TaggedString;
+                node.add(child);
+            };
+            if (matched)
+                node.setUserObject(new TaggedString("input "+n));
+            return node;
+        } else return new DefaultMutableTreeNode(value.toString());
+    }
+
+    private DefaultMutableTreeNode provenanceNode ( MRData value, String inputSearch ) {
+        Tuple p = ((Tuple)value);
+        MRData v = p.get(1);
+        DefaultMutableTreeNode node = new DefaultMutableTreeNode(v.toString());
+        Tree opr = exprs.nth(((MR_int)p.get(0)).get());
+        node.add(new DefaultMutableTreeNode(exprNode(opr)));
+        boolean matched = false;
+        for ( int i = 2; i < p.size(); i++ ) {
+            DefaultMutableTreeNode child = create_node(p.get(i),i-1,inputSearch);
+            node.add(child);
+            matched |= child.getUserObject() instanceof TaggedString;
+        };
+        if (matched)
+            node.setUserObject(new TaggedString(v.toString()));
+        else match opr {
+        case call(source,...):
+            if (!inputSearch.equals("") && v.toString().contains(inputSearch))
+                node.setUserObject(new TaggedString(v.toString()));
+        };
+        return node;
+    }
+
+    private DefaultMutableTreeNode createNode ( MRData value, String outputSearch, String inputSearch ) {
+        if (value instanceof Bag) {
+            DefaultMutableTreeNode node = new DefaultMutableTreeNode("results");
+            for ( MRData e: (Bag)value )
+                if (outputSearch.equals("") || ((Tuple)e).get(0).toString().contains(outputSearch)) {
+                    DefaultMutableTreeNode child = provenanceNode(((Tuple)e).get(1),inputSearch);
+                    if (inputSearch.equals("") || (child.getUserObject() instanceof TaggedString))
+                        node.add(child);
+                };
+            return node;
+        } else if (value instanceof MR_dataset) {
+            DefaultMutableTreeNode node = new DefaultMutableTreeNode("results");
+            for ( MRData e: ((MR_dataset)value).dataset().take(Config.max_bag_size_print) )
+                if (outputSearch.equals("") || ((Tuple)e).get(0).toString().contains(outputSearch)) {
+                    DefaultMutableTreeNode child = provenanceNode(((Tuple)e).get(1),inputSearch);
+                    if (inputSearch.equals("") || (child.getUserObject() instanceof TaggedString))
+                        node.add(child);
+                };
+            return node;
+        } else {
+            DefaultMutableTreeNode node = new DefaultMutableTreeNode(((Tuple)value).get(0).toString());
+            node.add(provenanceNode(((Tuple)value).get(1),inputSearch));
+            return node;
+        }
+    }
+    
+    private static void createAndShowGUI ( MRData lineage, Tree tp, Trees exprs ) {
+        final JFrame frame = new JFrame("MRQL debugger");
+        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+ 	frame.addWindowListener(new WindowListener() {
+                public void windowClosing ( WindowEvent e ) {
+                    frame.setVisible(false);
+                    frame.dispose();
+                    exit = true;
+                }
+                public void windowActivated ( WindowEvent e ) {}
+                public void windowClosed ( WindowEvent e ) {}
+                public void windowDeactivated ( WindowEvent e ) {}
+                public void windowDeiconified ( WindowEvent e ) {}
+                public void windowIconified ( WindowEvent e ) {}
+                public void windowOpened ( WindowEvent e ) {}
+            });
+        frame.add(new Debugger(lineage,exprs));
+        frame.pack();
+        frame.setVisible(true);
+    }
+
+    /** use the MRQL debugger on a query result
+     * @param lineage the query result extended with lineage
+     * @param type the result type
+     * @param exprs the expr terms used in the lineage
+     */
+    public static void debug ( final MRData lineage, final Tree type, final Trees exprs ) {
+        try {
+            SwingUtilities.invokeLater(new Runnable() {
+                    public void run () {
+                        createAndShowGUI(lineage,type,exprs);
+                    }
+                });
+            exit = false;
+            while (!exit)
+                Thread.sleep(1000);
+        } catch (Exception ex) {
+        }
+    }
+}
diff --git a/core/src/main/java/org/apache/mrql/Interpreter.gen b/core/src/main/java/org/apache/mrql/Interpreter.gen
index e7dac63..37e2147 100644
--- a/core/src/main/java/org/apache/mrql/Interpreter.gen
+++ b/core/src/main/java/org/apache/mrql/Interpreter.gen
@@ -28,6 +28,7 @@
 public class Interpreter extends TypeInference {
 
     public final static Tree identity_mapper = #<lambda(x,bag(x))>;
+    public static boolean debug_mode = false;
 
     protected static Environment global_env = null;
 
@@ -615,13 +616,17 @@
             return new Bag();
         case provenance(`x,`tp,...s):
             MRData value = evalE(x,env);
-            Provenance.display(value,tp,#[...s]);
+            if (Config.debug)
+                Debugger.debug(value,tp,#[...s]);
+            else Provenance.display(value,tp,#[...s]);
             return new Tuple(0);
         case Provenance(`x,`tp,...s):
             MRData value = (Config.hadoop_mode)
                 ? new MR_dataset(Evaluator.evaluator.eval(x,env,"-"))
                 : evalS(x,env);
-            Provenance.display(value,tp,#[...s]);
+            if (Config.debug)
+                Debugger.debug(value,tp,#[...s]);
+            else Provenance.display(value,tp,#[...s]);
             return new Tuple(0);
         case Lineage(...as):
             return evalEE(#<tuple(...as)>,env);
@@ -914,7 +919,7 @@
                 if (Config.trace)
                     System.out.println("After provenance injection:\n"+ne.pretty(0));
             } else if (Config.debug) {
-                ne = Provenance.embed_provenance(ne,true);
+                ne = Provenance.embed_provenance(ne,debug_mode);
                 if (Config.trace)
                     System.out.println("After provenance injection:\n"+ne.pretty(0));
             };
diff --git a/core/src/main/java/org/apache/mrql/Provenance.gen b/core/src/main/java/org/apache/mrql/Provenance.gen
index 513d629..62cae72 100644
--- a/core/src/main/java/org/apache/mrql/Provenance.gen
+++ b/core/src/main/java/org/apache/mrql/Provenance.gen
@@ -57,7 +57,7 @@
     }
 
     /** The nodes of the query AST */
-    private static Trees exprs = #[];
+    static Trees exprs = #[];
 
     /** Construct a provenance tuple
      * @param expr the AST that corresponds to this value
@@ -68,6 +68,8 @@
     private static Tree prov ( Tree expr, Tree value, Trees provenance ) {
         exprs = exprs.append(expr);
         int loc = exprs.length()-1;
+        if (value.is_variable())
+            return #<tuple(`value,Lineage(`loc,`value,...provenance))>;
         Tree nv = new_var();
         return #<let(`nv,`value,tuple(`nv,Lineage(`loc,`nv,...provenance)))>;
     }
@@ -111,13 +113,11 @@
             Tree nw = new_var();
             Tree ex = embedB(x);
             Tree nb = subst(v,#<nth(`nv,0)>,b);
-            return #<cmap(lambda(`nv,cmap(lambda(`nw,bag(tuple(`nw,nth(`nv,1)))),
-                                          `nb)),
-                                 `ex)>;
+            Tree p = prov(e,nw,#<nth(`nv,1)>);
+            return #<cmap(lambda(`nv,cmap(lambda(`nw,bag(`p)),`nb)),`ex)>;
         case cmap(lambda(`v,`b),`x):
             Tree nv = new_var();
             Tree nw = new_var();
-            Tree y = new_var();
             Tree ex = embedB(x);
             Tree ef = lift_var(v,nv,#<nth(`nv,0)>,embedB(b));
             Tree p = prov(e,#<nth(`nw,0)>,#<nth(`nw,1)>);
@@ -136,13 +136,15 @@
             return #<cmap(lambda(`nv,bag(`p)),orderBy(`ex))>;
         case coGroup(`x,`y):
             Tree nv = new_var();
+            Tree xv = new_var();
+            Tree yv = new_var();
             Tree ex = flip(embedB(x));
             Tree ey = flip(embedB(y));
-            Tree val = #<tuple(nth(`nv,0),tuple(`(first(#<nth(nth(`nv,1),0)>)),
-                                                `(first(#<nth(nth(`nv,1),1)>))))>;
-            Tree p = prov(e,val,#[`(second(#<nth(nth(`nv,1),0)>)),
-                                  `(second(#<nth(nth(`nv,1),1)>))]);
-            return #<cmap(lambda(`nv,bag(`p)),coGroup(`ex,`ey))>;
+            Tree val = #<tuple(nth(`nv,0),tuple(`(first(xv)),`(first(yv))))>;
+            Tree p = prov(e,val,#[`(second(xv)),`(second(yv))]);
+            return #<cmap(lambda(`nv,bag(let(`xv,nth(nth(`nv,1),0),
+                                             let(`yv,nth(nth(`nv,1),1),`p)))),
+                          coGroup(`ex,`ey))>;
         case call(source,...):
             Tree nv = new_var();
             Tree p = prov(e,nv,#[ ]);
@@ -196,12 +198,22 @@
 
     /** Lift the expression e of type t to (t,provenance) */
     public static Tree embedP ( Tree e ) {
+        match TypeInference.type_inference(e) {
+        case `T(_):
+            if (!is_collection(T))
+                fail;
+            Tree nv = new_var();
+            Tree ex = embedB(e);
+            Tree p = prov(e,first(nv),second(nv));
+            return #<let(`nv,`ex,`p)>;
+        };
         match e {
         case reduce(`m,`x):
             Tree nv = new_var();
             Tree ex = embedB(x);
             Tree p = prov(e,#<reduce(`m,`(first(nv)))>,second(nv));
-            return #<Let(`nv,`ex,`p)>;
+            return #<let(`nv,`ex,`p)>;
+            //return #<Let(`nv,`ex,`p)>;
         case tuple(...as):
             Tree nv = new_var();
             Trees es = #[ ];
@@ -277,15 +289,6 @@
                     return prov(e,e,#[ ]);
                 else return v;
         };
-        match TypeInference.type_inference(e) {
-        case `T(_):
-            if (!is_collection(T))
-                fail;
-            Tree nv = new_var();
-            Tree ex = embedB(e);
-            Tree p = prov(e,first(nv),second(nv));
-            return #<Let(`nv,`ex,`p)>;
-        };
         return prov(e,e,#[ ]);
     }
 
@@ -328,21 +331,20 @@
             Bag s = new Bag();
             for ( int i = 2; i < p.size() && s.size() < Config.max_bag_size_print; i++ )
                 for ( MRData e: collect_lineage(p.get(i)) )
-                    if (!member(s,e))
-                        s.add(e);
+                    s.add(e);
             return s;
         } else if (value instanceof Bag) {
             Bag s = new Bag();
             ((Bag)value).materialize();
             for ( MRData e: (Bag)value )
                 for ( MRData x: collect_lineage(e) )
-                    if (s.size() < Config.max_bag_size_print && !member(s,x))
+                    if (s.size() < Config.max_bag_size_print)
                         s.add(x);
             return s;
         } else return new Bag();
     }
 
-    /** Print the the data sources that contribute to the output to the output */
+    /** Print the data sources that contribute to the output */
     public static void display ( MRData value, Tree tp, Trees prov_exprs ) {
         exprs = prov_exprs;
         match tp {
diff --git a/core/src/main/java/org/apache/mrql/Streaming.gen b/core/src/main/java/org/apache/mrql/Streaming.gen
index 53034a9..c4ff10f 100644
--- a/core/src/main/java/org/apache/mrql/Streaming.gen
+++ b/core/src/main/java/org/apache/mrql/Streaming.gen
@@ -854,15 +854,13 @@
             Tree vy = new_var();
             Tree cx = convert_to_algebra(x);
             Tree cy = convert_to_algebra(y);
-            Tree mx = new_var();
-            Tree my = new_var();
             return #<join(lambda(`vx,nth(`vx,0)),lambda(`vy,nth(`vy,0)),
                           lambda(`v,bag(tuple(call(join_key,nth(`v,0),nth(`v,1)),
                                               tuple(cmap(lambda(`vx,bag(nth(`vx,1))),nth(`v,0)),
                                                     cmap(lambda(`vy,bag(nth(`vy,1))),nth(`v,1)))))),
                           `cx, `cy)>;
-      case reduce(`aggr,`s):
-            return convert_reduce(aggr,convert_to_algebra(s));
+        case reduce(`aggr,`s):
+          return convert_reduce(aggr,convert_to_algebra(s));
         case `f(...as):
             Trees bs = #[ ];
             for ( Tree a: as )
diff --git a/core/src/main/java/org/apache/mrql/TopLevel.gen b/core/src/main/java/org/apache/mrql/TopLevel.gen
index 769099a..b4f1c3a 100644
--- a/core/src/main/java/org/apache/mrql/TopLevel.gen
+++ b/core/src/main/java/org/apache/mrql/TopLevel.gen
@@ -379,23 +379,25 @@
             };
         case lineage(`e):
             Config.lineage = true;
-            boolean quiet = Config.quiet_execution;
-            Config.quiet_execution = true;
+            if (Config.hadoop_mode)
+                Config.write(Plan.conf);
             long t = System.currentTimeMillis();
             if (expression(e,false) != null && !Config.quiet_execution)
                 System.out.println("Run time: "+(System.currentTimeMillis()-t)/1000.0+" secs");
-            Config.lineage = false;
-            Config.quiet_execution = quiet;
-        case debug(`path,`e):
+            if (Config.hadoop_mode)
+                Config.write(Plan.conf);
+        case debug(`mode,`e):
             Config.debug = true;
             boolean quiet = Config.quiet_execution;
             Config.quiet_execution = true;
-            long t = System.currentTimeMillis();
-            dump(path.stringValue(),e);
-            if (!Config.quiet_execution)
-                System.out.println("Run time: "+(System.currentTimeMillis()-t)/1000.0+" secs");
+            if (Config.hadoop_mode)
+                Config.write(Plan.conf);
+            debug_mode = mode.equals(#<fine>);
+            expression(e,false);
             Config.debug = false;
             Config.quiet_execution = quiet;
+            if (Config.hadoop_mode)
+                Config.write(Plan.conf);
         case dump(`s,`e):
             long t = System.currentTimeMillis();
             dump(s.stringValue(),e);
diff --git a/core/src/main/java/org/apache/mrql/mrql.cgen b/core/src/main/java/org/apache/mrql/mrql.cgen
index 6e09902..795243a 100644
--- a/core/src/main/java/org/apache/mrql/mrql.cgen
+++ b/core/src/main/java/org/apache/mrql/mrql.cgen
@@ -35,7 +35,7 @@
         sym.STEP, sym.LIMIT, sym.LET, sym.ATSYM, sym.EXCLAMATION,
         sym.Variable, sym.Integer, sym.Double, sym.String, sym.Decimal,
         sym.START_TEMPLATE, sym.END_TEMPLATE, sym.TEXT, sym.TRACE, sym.INCR,
-        sym.LINEAGE, sym.DEBUG
+        sym.LINEAGE, sym.DEBUG, sym.DEBUGALL
     };
 
     static String[] token_names = {
@@ -51,7 +51,7 @@
         "step", "limit", "let", "@", "!",
         "Variable", "Integer", "Double", "String", "Decimal",
         "[|", "|]", "Text", "trace", "incr",
-        "lineage", "debug"
+        "lineage", "debug", "debug all"
     };
 
     public static String print ( Symbol s ) {
@@ -101,7 +101,7 @@
          ORDER, GROUP, BY, ASCENDING, DESCENDING, UMINUS, FUNCTION, DISTINCT, BSLASH,
          SOME, ALL, GTR, SEP, STORE, TYPE, DATA, CASE, ATSYM, XPATH, REPEAT, STEP, LIMIT,
          LET, IMPORT, PARSER, AGGREGATION, INCLUDE, EXCLAMATION, MACRO, DUMP, TRACE, INCR,
-         LINEAGE, DEBUG;
+         LINEAGE, DEBUG, DEBUGALL;
 
 terminal String         Variable;
 terminal Long           Integer;
@@ -147,7 +147,8 @@
                 |   DUMP String:s FROM expr:e           {: RESULT = #<dump_text(`(new StringLeaf(s)),`e)>; :}
                 |   INCR expr:e                         {: RESULT = #<incr(`e)>; :}
                 |   LINEAGE expr:e                      {: RESULT = #<lineage(`e)>; :}
-                |   DEBUG String:s expr:e               {: RESULT = #<debug(`s,`e)>; :}
+                |   DEBUG expr:e                        {: RESULT = #<debug(coarse,`e)>; :}
+                |   DEBUGALL expr:e                     {: RESULT = #<debug(fine,`e)>; :}
                 |   TYPE var:v EQ type:t                {: RESULT = #<typedef(`v,`t)>; :}
                 |   DATA var:v EQ data_binds:nl         {: RESULT = #<datadef(`v,union(...nl))>; :}
                 |   FUNCTION var:f LP
diff --git a/core/src/main/java/org/apache/mrql/mrql.lex b/core/src/main/java/org/apache/mrql/mrql.lex
index 868eb05..39cd33d 100644
--- a/core/src/main/java/org/apache/mrql/mrql.lex
+++ b/core/src/main/java/org/apache/mrql/mrql.lex
@@ -186,6 +186,7 @@
 <YYINITIAL> "trace"	        { return symbol(sym.TRACE); }
 <YYINITIAL> "lineage"	        { return symbol(sym.LINEAGE); }
 <YYINITIAL> "debug"	        { return symbol(sym.DEBUG); }
+<YYINITIAL> "debug all"	        { return symbol(sym.DEBUGALL); }
 
 <YYINITIAL> {ID}		{ return symbol(sym.Variable,yytext()); }