FullFormEmitter.java Source code

Java tutorial

Introduction

Here is the source code for FullFormEmitter.java

Source

/*
 * Copyright (c) 2015, Robert Jacobson
 * All rights reserved. 
 * 
 * Licensed under the BSD license. See LICENSE.txt for details.
 * 
 * Author(s): Robert Jacobson
 * 
 * Description: This class emits the FullForm representation of an expression.
 * 
 */

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;

import java.util.List;
import java.util.HashMap;
//import java.util.Map;

public class FullFormEmitter extends FoxySheepBaseVisitor<String> {

    public String getFullForm(ParseTree e) {
        if (e instanceof TerminalNode) {
            return e.getText();
        }
        return visit(e);
    }

    public String makeHead(String head, ParseTree e) {
        StringBuilder val = new StringBuilder(head);
        val.append("[");
        val.append(getFullForm(e));
        val.append("]");
        return val.toString();
    }

    public String makeHead(String head, ParseTree e1, ParseTree e2) {
        StringBuilder val = new StringBuilder(head);
        val.append("[");
        val.append(getFullForm(e1));
        val.append(",");
        val.append(getFullForm(e2));
        val.append("]");
        return val.toString();
    }

    public String makeHead(String head, ParseTree e1, ParseTree e2, ParseTree e3) {
        StringBuilder val = new StringBuilder(head);
        val.append("[");
        val.append(getFullForm(e1));
        val.append(",");
        val.append(getFullForm(e2));
        val.append(",");
        val.append(getFullForm(e3));
        val.append("]");
        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>Makes a "head" expression with arguments from List e. When passing in
     * ctx.children for e, one must remove the TerminalNode representing the
     * operator from ctx.children first. Alternatively, pass ctx.expr().</p>
     */
    public String makeHeadList(String head, List e) {
        StringBuilder val = new StringBuilder(head);
        val.append("[");
        for (int i = 0; i < e.size(); i++) {
            val.append(getFullForm((ParseTree) e.get(i)));
            if (i < e.size() - 1) {
                val.append(",");
            }
        }
        val.append("]");
        return val.toString();
    }

    public static void main(String[] args) throws Exception {
        FoxySheep.main(args);
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitUnset(FoxySheepParser.UnsetContext ctx) {
        return makeHead("Unset", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitCondition(FoxySheepParser.ConditionContext ctx) {
        return makeHead("Condition", ctx.expr(0), ctx.expr(1));
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitImplies(FoxySheepParser.ImpliesContext ctx) {
        return makeHeadList("Implies", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitCompoundExpression(FoxySheepParser.CompoundExpressionContext ctx) {
        /* Since we need to check for ending in "Null" anyway, we might as
         * well not bother with makeHeadList(). 
         */
        StringBuilder val = new StringBuilder("CompoundExpression[");
        val.append(getFullForm(ctx.getChild(0)));
        for (int i = 2; i < ctx.getChildCount(); i += 2) {
            val.append(",");
            val.append(getFullForm(ctx.getChild(i)));
        }
        if (ctx.getChildCount() % 2 == 0) {
            //An even number of children means we ended in a semicolon.
            val.append(",Null]");
        } else {
            val.append("]");
        }
        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitVerticalBar(FoxySheepParser.VerticalBarContext ctx) {
        if (ctx.VERTICALBAR() != null) {
            return makeHeadList("VerticalBar", ctx.expr());
        }
        if (ctx.NOTVERTICALBAR() != null) {
            return makeHeadList("NotVerticalBar", ctx.expr());
        }
        if (ctx.DOUBLEVERTICALBAR() != null) {
            return makeHeadList("DoubleVerticalBar", ctx.expr());
        }
        //if(ctx.NOTDOUBLEVERTICALBAR() != null){
        return makeHeadList("NotDoubleVerticalBar", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitVerticalTilde(FoxySheepParser.VerticalTildeContext ctx) {
        return makeHeadList("VerticalTilde", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitVee(FoxySheepParser.VeeContext ctx) {
        return makeHeadList("Vee", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitCross(FoxySheepParser.CrossContext ctx) {
        return makeHeadList("Cross", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitStringJoin(FoxySheepParser.StringJoinContext ctx) {
        return makeHeadList("StringJoin", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitComparison(FoxySheepParser.ComparisonContext ctx) {
        /*
         * This is a rather complicated construct, because:
         *       "x==y"       -> Equal[x,y]
         *       "x==y>z"    -> Inequality[x, Equal, y, Greater, z]
         *       "x==y==z      -> Equal[x,y,z]
         *       "x>y>z"      -> Greater[x,y,z]
         * So we need to flatten many different operators at once
         * if necessary.
         * 
         * We solve this problem by postprocessing the syntax tree
         * to flatten the tree where appropriate.  
         */

        HashMap<Integer, String> opText = new HashMap<Integer, String>(6);
        opText.put(FoxySheepParser.EqualSymbol, "Equal");
        opText.put(FoxySheepParser.NotEqualSymbol, "Unequal");
        opText.put(FoxySheepParser.GREATER, "Greater");
        opText.put(FoxySheepParser.GreaterEqualSymbol, "GreaterEqual");
        opText.put(FoxySheepParser.LESS, "Less");
        opText.put(FoxySheepParser.LessEqualSymbol, "LessEqual");

        boolean allSame = true;
        int opType = ((TerminalNode) ctx.getChild(1)).getSymbol().getType();
        for (int i = 3; i < ctx.getChildCount(); i += 2) {
            allSame = allSame && (opType == ((TerminalNode) ctx.getChild(i)).getSymbol().getType());
        }

        //If all operators are the same, make a "head" with that operator. 
        if (allSame) {
            return makeHeadList((String) opText.get(opType), ctx.expr());
        }

        //All operators are not the same. We need to create an Inequality[].
        TerminalNode op;
        StringBuilder val = new StringBuilder("Inequality[");
        val.append(getFullForm(ctx.expr(0)));
        for (int i = 1; i < ctx.getChildCount(); i += 2) {
            val.append(",");
            op = (TerminalNode) ctx.getChild(i);
            val.append((String) opText.get(op.getSymbol().getType()));
            val.append(",");
            val.append(getFullForm(ctx.getChild(i + 1)));
        }
        val.append("]");
        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitCirclePlus(FoxySheepParser.CirclePlusContext ctx) {
        return makeHeadList("CirclePlus", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitTee(FoxySheepParser.TeeContext ctx) {
        if (ctx.LEFTTEE() != null) {
            return makeHeadList("LeftTee", ctx.expr());
        }
        if (ctx.DOUBLELEFTTEE() != null) {
            return makeHeadList("DoubleLeftTee", ctx.expr());
        }
        if (ctx.UPTEE() != null) {
            return makeHeadList("UpTee", ctx.expr());
        }
        return makeHeadList("DownTee", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitIntersection(FoxySheepParser.IntersectionContext ctx) {
        return makeHeadList("Intersection", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitIncrement(FoxySheepParser.IncrementContext ctx) {
        if (ctx.DOUBLEMINUS() == null) {
            return makeHead("Increment", ctx.expr());
        }
        return makeHead("Decrement", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSlot(FoxySheepParser.SlotContext ctx) {
        return getFullForm(ctx.slotExpression());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSet(FoxySheepParser.SetContext ctx) {
        if (ctx.EQUAL() != null) {
            return makeHeadList("Set", ctx.expr());
        }
        if (ctx.COLONEQUAL() != null) {
            return makeHeadList("SetDelayed", ctx.expr());
        }
        if (ctx.CARETEQUAL() != null) {
            return makeHeadList("UpSet", ctx.expr());
        }
        if (ctx.CARETCOLONEQUAL() != null) {
            return makeHeadList("UpSetDelayed", ctx.expr());
        }
        //if(ctx.FUNCTIONARROW() != null)
        StringBuilder val = new StringBuilder("Function[{");

        val.append(getFullForm(ctx.expr(0)));
        val.append("},");
        val.append(getFullForm(ctx.expr(1)));
        val.append("]");

        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitXor(FoxySheepParser.XorContext ctx) {
        if (ctx.XOR() != null) {
            return makeHeadList("Xor", ctx.expr());
        }
        return makeHeadList("Xnor", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitComposition(FoxySheepParser.CompositionContext ctx) {
        return makeHeadList("Composition", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitOut(FoxySheepParser.OutContext ctx) {
        return getFullForm(ctx.outExpression());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitIntegrate(FoxySheepParser.IntegrateContext ctx) {
        return makeHeadList("Integrate", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitAngleBracket(FoxySheepParser.AngleBracketContext ctx) {
        return makeHead("AngleBracket", ctx.expressionList());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitAccessor(FoxySheepParser.AccessorContext ctx) {
        return makeHead("Part", ctx.expr(), ctx.accessExpression());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitHeadExpression(FoxySheepParser.HeadExpressionContext ctx) {
        return makeHead(getFullForm(ctx.expr()), ctx.expressionList());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSquare(FoxySheepParser.SquareContext ctx) {
        return makeHead("Square", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitTherefore(FoxySheepParser.ThereforeContext ctx) {
        return makeHeadList("Therefore", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitPut(FoxySheepParser.PutContext ctx) {
        if (ctx.DOUBLEGREATER() != null) {
            return makeHead("Put", ctx.expr(), ctx.StringLiteral());
        }
        return makeHead("PutAppend", ctx.expr(), ctx.StringLiteral());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitPlusOp(FoxySheepParser.PlusOpContext ctx) {
        if (ctx.BINARYPLUS() != null) {
            return makeHeadList("Plus", ctx.expr());
        }
        if (ctx.BINARYMINUS() != null) {
            //Mathematica interprets x-y as Plus[x,Times[-1,y]].
            StringBuilder val = new StringBuilder("Plus[");
            val.append(getFullForm(ctx.expr(0)));
            for (int i = 1; i < ctx.expr().size(); i++) {
                val.append(",Times[-1,");
                val.append(getFullForm(ctx.expr(i)));
                val.append("]");
            }
            val.append("]");

            return val.toString();
        }
        if (ctx.BINARYPLUSMINUS() != null) {
            return makeHeadList("PlusMinus", ctx.expr());
        }
        //if(ctx.BINARYMINUSPLUS() != null){
        return makeHeadList("MinusPlus", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitPower(FoxySheepParser.PowerContext ctx) {
        return makeHeadList("Power", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitVerticalSeparator(FoxySheepParser.VerticalSeparatorContext ctx) {
        return makeHeadList("VerticalSeparator", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitUnaryPlusMinus(FoxySheepParser.UnaryPlusMinusContext ctx) {
        if (ctx.MINUS() != null) {
            StringBuilder val = new StringBuilder("Times[-1,");
            val.append(getFullForm(ctx.expr()));
            val.append("]");
            return val.toString();
        }
        if (ctx.PLUS() != null) {
            return makeHead("Plus", ctx.expr());
        }
        if (ctx.PLUSMINUS() != null) {
            return makeHead("PlusMinus", ctx.expr());
        }
        //if(ctx.MINUSPLUS() != null)
        return makeHead("MinusPlus", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitUnion(FoxySheepParser.UnionContext ctx) {
        return makeHeadList("Union", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitPrefix(FoxySheepParser.PrefixContext ctx) {
        return makeHead(getFullForm(ctx.expr(0)), ctx.expr(1));
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitPreIncrement(FoxySheepParser.PreIncrementContext ctx) {
        if (ctx.DOUBLEMINUS() == null) {
            return makeHead("PreIncrement", ctx.expr());
        }
        return makeHead("PreDecrement", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitProg(FoxySheepParser.ProgContext ctx) {
        StringBuilder val = new StringBuilder(getFullForm(ctx.expr(0)));

        for (int i = 1; i < ctx.expr().size(); i++) {
            val.append("\n\n");
            val.append(getFullForm(ctx.expr(i)));
        }

        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitPatternExp(FoxySheepParser.PatternExpContext ctx) {
        //symb:expr
        return makeHead("Pattern", ctx.symbol(), ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    //   @Override public String visitPatternForm(FoxySheepParser.PatternFormContext ctx) {
    //      return ctx.getText();
    //   }
    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitBecause(FoxySheepParser.BecauseContext ctx) {
        return makeHead("Because", ctx.expr(0), ctx.expr(1));
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitBracketingBar(FoxySheepParser.BracketingBarContext ctx) {
        return makeHead("BracketingBar", ctx.expressionList());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitDoubleBracketingBar(FoxySheepParser.DoubleBracketingBarContext ctx) {
        return makeHead("DoubleBracketingBar", ctx.expressionList());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitOr(FoxySheepParser.OrContext ctx) {
        if (ctx.NOR() != null) {
            return makeHeadList("Nor", ctx.expr());
        }
        return makeHeadList("Or", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitRightTee(FoxySheepParser.RightTeeContext ctx) {
        if (ctx.RIGHTTEE() != null) {
            return makeHeadList("RightTee", ctx.expr());
        }
        return makeHeadList("DoubleRightTee", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitCup(FoxySheepParser.CupContext ctx) {
        return makeHeadList("Cup", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitBoxParen(FoxySheepParser.BoxParenContext ctx) {
        return ctx.getText();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitStringExpression(FoxySheepParser.StringExpressionContext ctx) {
        return makeHeadList("StringExpression", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitPostfix(FoxySheepParser.PostfixContext ctx) {
        StringBuilder val = new StringBuilder();

        val.append(getFullForm(ctx.expr(1)));
        val.append("[");
        val.append(getFullForm(ctx.expr(0)));
        val.append("]");

        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitAnd(FoxySheepParser.AndContext ctx) {
        if (ctx.NAND() != null) {
            return makeHeadList("Nand", ctx.expr());
        }
        return makeHeadList("And", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitOptional(FoxySheepParser.OptionalContext ctx) {
        return makeHeadList("Optional", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitRightComposition(FoxySheepParser.RightCompositionContext ctx) {
        return makeHeadList("RightComposition", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitPatternTest(FoxySheepParser.PatternTestContext ctx) {
        return makeHeadList("PatternTest", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitNonCommutativeMultiply(FoxySheepParser.NonCommutativeMultiplyContext ctx) {
        return makeHeadList("NonCommutativeMultiply", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>This is code factored out of both visitSpanA and visitSpanB.</p>
     */
    public String visitSpan(ParserRuleContext ctx) {
        StringBuilder val = new StringBuilder("Span[");
        int curChild = 0;

        //Because this SpanA might have been created by a subtree rewrite, we
        //cannot guarantee it begins with an expr.
        if (ctx.getChild(curChild).getText().equals(";;")) {
            //Begins with ";;", implicit start of 1.
            val.append("1");
            curChild++;
        } else {
            //Begins with expr
            val.append(getFullForm(ctx.getChild(curChild)));
            curChild += 2;
        }
        //Cursor now points to one past the first ";;"
        if (curChild < ctx.children.size() && !ctx.getChild(curChild).getText().equals(";;")) {
            //The middle expr has not been omitted
            val.append(",");
            val.append(getFullForm(ctx.getChild(curChild)));
            curChild++;
        } else {
            //The middle expr has been omitted.
            val.append(",All");
        }

        //Cursor now points to either the second ";;" or past the end of the expr.
        if (curChild < ctx.children.size() && ctx.getChild(curChild).getText().equals(";;")) {
            //There is a skip amount.
            val.append(",");
            val.append(getFullForm(ctx.getChild(curChild + 1)));
        }

        val.append("]");
        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSpanA(FoxySheepParser.SpanAContext ctx) {
        return visitSpan(ctx);
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSpanB(FoxySheepParser.SpanBContext ctx) {
        return visitSpan(ctx);
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitInfix(FoxySheepParser.InfixContext ctx) {
        return makeHead(getFullForm(ctx.expr(1)), ctx.expr(0), ctx.expr(2));
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitParentheses(FoxySheepParser.ParenthesesContext ctx) {
        return getFullForm(ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitFactorial(FoxySheepParser.FactorialContext ctx) {
        if (ctx.BANG() != null) {
            return makeHead("Factorial", ctx.expr());
        }
        return makeHead("Factorial2", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitFloor(FoxySheepParser.FloorContext ctx) {
        return makeHead("Floor", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSuchThat(FoxySheepParser.SuchThatContext ctx) {
        return makeHeadList("SuchThat", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitNumber(FoxySheepParser.NumberContext ctx) {
        /*
         * Frustratingly, number literals have no FullForm[] in Mathematica.
         * Mathematica will automatically compute the value of a number
         * literal. Since we do no computation in the parser, the only
         * "correct" option for us is to reproduce the number form as-is. 
         */
        return ctx.getText();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSetContainment(FoxySheepParser.SetContainmentContext ctx) {
        if (ctx.ELEMENT() != null) {
            return makeHeadList("Element", ctx.expr());
        }
        if (ctx.NOTELEMENT() != null) {
            return makeHeadList("NotElement", ctx.expr());
        }
        if (ctx.SUBSET() != null) {
            return makeHeadList("Subset", ctx.expr());
        }
        //if(ctx.SUPERSET() != null)
        return makeHeadList("Superset", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitCircleDot(FoxySheepParser.CircleDotContext ctx) {
        return makeHeadList("CircleDot", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitWedge(FoxySheepParser.WedgeContext ctx) {
        return makeHeadList("Wedge", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitDel(FoxySheepParser.DelContext ctx) {
        return makeHead("Del", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitColon(FoxySheepParser.ColonContext ctx) {
        return makeHeadList("Colon", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitBoxConstructor(FoxySheepParser.BoxConstructorContext ctx) {
        return ctx.getText();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitCap(FoxySheepParser.CapContext ctx) {
        return makeHeadList("Cap", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitOpEquals(FoxySheepParser.OpEqualsContext ctx) {
        if (ctx.PLUSEQUAL() != null) {
            return makeHead("AddTo", ctx.expr(0), ctx.expr(1));
        }
        if (ctx.MINUSEQUAL() != null) {
            return makeHead("SubtractFrom", ctx.expr(0), ctx.expr(1));
        }
        if (ctx.ASTERISKEQUAL() != null) {
            return makeHead("TimesBy", ctx.expr(0), ctx.expr(1));
        }
        //if(ctx.SLASHEQUAL() != null){
        return makeHead("DivideBy", ctx.expr(0), ctx.expr(1));
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitAlternatives(FoxySheepParser.AlternativesContext ctx) {
        return makeHeadList("Alternatives", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitReplaceAll(FoxySheepParser.ReplaceAllContext ctx) {
        if (ctx.SLASHDOT() != null) {
            return makeHeadList("ReplaceAll", ctx.expr());
        }
        return makeHeadList("ReplaceRepeated", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitDiamond(FoxySheepParser.DiamondContext ctx) {
        return makeHeadList("Diamond", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitDivide(FoxySheepParser.DivideContext ctx) {
        if (ctx.DIVIDE() != null) {
            return makeHead("Divide", ctx.expr(0), ctx.expr(1));
        }
        //Mathematica treats x/y as Times[x,Power[y,-1]].
        StringBuilder val = new StringBuilder("Times[");
        val.append(getFullForm(ctx.expr(0)));
        val.append(",Power[");
        val.append(getFullForm(ctx.expr(1)));
        val.append(",-1]]");
        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitList(FoxySheepParser.ListContext ctx) {
        return makeHead("List", ctx.expressionList());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitMapApply(FoxySheepParser.MapApplyContext ctx) {
        //expr (MAP | MAPALL | DOUBLEAT | TRIPPLEAT) expr
        if (ctx.MAP() != null) {
            return makeHead("Map", ctx.expr(0), ctx.expr(1));
        }
        if (ctx.MAPALL() != null) {
            return makeHead("MapAll", ctx.expr(0), ctx.expr(1));
        }
        if (ctx.DOUBLEAT() != null) {
            return makeHead("Apply", ctx.expr(0), ctx.expr(1));
        }
        //if(ctx.TRIPPLEAT()!=null)
        //We can't use makeHead because the third argument isn't a ParseTree.
        //Apply[expr1,expr2,{1}]
        StringBuilder val = new StringBuilder("Apply[");
        val.append(getFullForm(ctx.expr(0)));
        val.append(",");
        val.append(getFullForm(ctx.expr(1)));
        val.append(",List[1]]");
        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitCenterDot(FoxySheepParser.CenterDotContext ctx) {
        return makeHeadList("CenterDot", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitCeiling(FoxySheepParser.CeilingContext ctx) {
        return makeHead("Ceiling", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitConjugate(FoxySheepParser.ConjugateContext ctx) {
        if (ctx.CONJUGATE() != null) {
            return makeHead("Conjugate", ctx.expr());
        }
        if (ctx.TRANSPOSE() != null) {
            return makeHead("Transpose", ctx.expr());
        }
        //The other two are the same.
        //if(ctx.CONJUGATETRANSPOSE()!=null)
        //if(ctx.HERMITIANCONJUGATE()!=null)
        return makeHead("ConjugateTranspose", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitStringLiteral(FoxySheepParser.StringLiteralContext ctx) {
        return ctx.getText();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitEquivalent(FoxySheepParser.EquivalentContext ctx) {
        return makeHeadList("Equivalent", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitCoproduct(FoxySheepParser.CoproductContext ctx) {
        return makeHeadList("Coproduct", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSmallCircle(FoxySheepParser.SmallCircleContext ctx) {
        return makeHeadList("SmallCircle", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitMessage(FoxySheepParser.MessageContext ctx) {
        //One string literal.
        if (ctx.StringLiteral().size() == 1) {
            return makeHead("MessageName", ctx.expr(), ctx.StringLiteral(0));
        }
        //Two string literals.
        return makeHead("MessageName", ctx.expr(), ctx.StringLiteral(0), ctx.StringLiteral(1));
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitFunction(FoxySheepParser.FunctionContext ctx) {
        return makeHead("Function", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitTimes(FoxySheepParser.TimesContext ctx) {
        return makeHeadList("Times", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitTagSet(FoxySheepParser.TagSetContext ctx) {
        if (ctx.EQUAL() != null) {
            return makeHead("TagSet", ctx.symbol(), ctx.expr(0), ctx.expr(1));
        }
        //Must be TagSetDelated.
        return makeHead("TagSetDelayed", ctx.symbol(), ctx.expr(0), ctx.expr(1));
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitTagUnset(FoxySheepParser.TagUnsetContext ctx) {
        return makeHead("TagUnset", ctx.symbol(), ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSymbolLiteral(FoxySheepParser.SymbolLiteralContext ctx) {
        //FullForm of a symbol is itself.
        return ctx.getText();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitRule(FoxySheepParser.RuleContext ctx) {
        if (ctx.MINUSGREATER() != null || ctx.RARROW() != null) {
            return makeHeadList("Rule", ctx.expr());
        }
        return makeHeadList("RuleDelayed", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitBackslash(FoxySheepParser.BackslashContext ctx) {
        return makeHead("Backslash", ctx.expr(0), ctx.expr(1));
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitNot(FoxySheepParser.NotContext ctx) {
        return makeHead("Not", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitDot(FoxySheepParser.DotContext ctx) {
        return makeHeadList("Dot", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitCircleTimes(FoxySheepParser.CircleTimesContext ctx) {
        return makeHeadList("CircleTimes", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSame(FoxySheepParser.SameContext ctx) {
        if (ctx.TRIPPLEEQUAL() != null) {
            return makeHeadList("SameQ", ctx.expr());
        }
        return makeHeadList("UnsameQ", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitStar(FoxySheepParser.StarContext ctx) {
        return makeHeadList("Star", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitDerivative(FoxySheepParser.DerivativeContext ctx) {
        StringBuilder val = new StringBuilder("Derivative[");
        val.append(Integer.toString(ctx.SINGLEQUOTE().size()));
        val.append("][");
        val.append(getFullForm(ctx.expr()));
        val.append("]");
        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitGet(FoxySheepParser.GetContext ctx) {
        return makeHead("Get", ctx.StringLiteral());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitRepeated(FoxySheepParser.RepeatedContext ctx) {
        if (ctx.DOUBLEDOT() != null) {
            return makeHead("Repeated", ctx.expr());
        }
        return makeHead("RepeatedNull", ctx.expr());
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitCircleMinus(FoxySheepParser.CircleMinusContext ctx) {
        return makeHead("CircleMinus", ctx.expr(0), ctx.expr(1));
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitContextName(FoxySheepParser.ContextNameContext ctx) {
        return ctx.getText();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSimpleContext(FoxySheepParser.SimpleContextContext ctx) {
        return ctx.getText();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitCompoundContext(FoxySheepParser.CompoundContextContext ctx) {
        return ctx.getText();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitPatternBlanks(FoxySheepParser.PatternBlanksContext ctx) {
        StringBuilder val = new StringBuilder();

        if (ctx.TRIPPLEBLANK() != null) {
            val.append("BlankNullSequence[");
            if (ctx.expr() != null)
                val.append(getFullForm(ctx.expr()));
            val.append("]");
        }
        if (ctx.DOUBLEBLANK() != null) {
            val.append("BlankSequence[");
            if (ctx.expr() != null)
                val.append(getFullForm(ctx.expr()));
            val.append("]");
        }
        if (ctx.BLANK() != null) {
            val.append("Blank[");
            if (ctx.expr() != null)
                val.append(getFullForm(ctx.expr()));
            val.append("]");
        }

        // If there is a symbol, we wrap the whole expression in Pattern[]
        if (ctx.symbol() != null) {
            StringBuilder wrap = new StringBuilder("Pattern[");
            wrap.append(getFullForm(ctx.symbol()));
            wrap.append(",");
            wrap.append(val);
            wrap.append("]");
            val = wrap;
        }

        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitPatternBlankDot(FoxySheepParser.PatternBlankDotContext ctx) {
        StringBuilder val = new StringBuilder();
        if (ctx.symbol() != null) {
            val.append("Optional[Pattern[");
            val.append(getFullForm(ctx.symbol()));
            val.append(",Blank[]]]");
        } else {
            val.append("Optional[Blank[]]");
        }
        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitOutNumbered(FoxySheepParser.OutNumberedContext ctx) {
        StringBuilder val = new StringBuilder("Out[");
        val.append(ctx.getText().substring(1));
        val.append("]");
        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitOutUnnumbered(FoxySheepParser.OutUnnumberedContext ctx) {
        int textLen = ctx.getText().length();
        if (textLen == 1) {
            return "Out[]";
        }
        StringBuilder val = new StringBuilder("Out[-");
        val.append(textLen);
        val.append("]");
        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSlotDigits(FoxySheepParser.SlotDigitsContext ctx) {
        StringBuilder val = new StringBuilder("Slot[");
        val.append(ctx.getText().substring(1));
        val.append("]");
        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSlotNamed(FoxySheepParser.SlotNamedContext ctx) {
        StringBuilder val = new StringBuilder("Slot[");
        val.append(ctx.getText().substring(1));
        val.append("]");
        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSlotSequenceDigits(FoxySheepParser.SlotSequenceDigitsContext ctx) {
        StringBuilder val = new StringBuilder("SlotSequence[");
        val.append(ctx.getText().substring(2));
        val.append("]");
        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSlotSequence(FoxySheepParser.SlotSequenceContext ctx) {
        return "SlotSequence[1]";
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitSlotUnnamed(FoxySheepParser.SlotUnnamedContext ctx) {
        return "Slot[1]";
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitExpressionListed(FoxySheepParser.ExpressionListedContext ctx) {
        //expressionList can be empty. 
        if (ctx.children == null || ctx.children.size() == 0)
            return "";

        StringBuilder val = new StringBuilder();
        ParseTree child;
        int exprCounter = 0;

        for (int childCounter = 0; childCounter < ctx.children.size(); childCounter++) {
            //Separate with a comma.
            if (childCounter > 0)
                val.append(",");

            child = ctx.children.get(childCounter);
            if (exprCounter < ctx.expr().size() && child == ctx.expr(exprCounter)) {
                val.append(getFullForm(child));
                exprCounter++;
                childCounter++; //The next child is a comma (or end of list) which we skip.
            } else {
                //Must have been a comma indicating Null.
                val.append("Null");
            }
        }

        //If the last child is a comma, it needs to be followed by a Null, too.
        if (!(ctx.getChild(ctx.children.size() - 1) instanceof FoxySheepParser.ExprContext)) {
            val.append(",Null");
        }

        return val.toString();
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitAccessExpressionA(FoxySheepParser.AccessExpressionAContext ctx) {
        return getFullForm(ctx.expressionList()); //ctx.expressionList().accept(this);
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitAccessExpressionB(FoxySheepParser.AccessExpressionBContext ctx) {
        return getFullForm(ctx.expressionList()); //ctx.expressionList().accept(this);
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public String visitBox(FoxySheepParser.BoxContext ctx) {
        return ctx.getText();
    }
}