teaching machines

CS 330 Lecture 13 – Control Statements

February 23, 2015 by . Filed under cs330, lectures, spring 2015.

Agenda

TODO

Code

Logo.g

grammar Logo;

program
  : block EOF
  ;

block
  : (statement? NEWLINE)*
  ;

statement
  : MOVE expr # Move
  | ROTATE expr # Rotate
  | IF expr NEWLINE block ELSE block END # If
  | TO IDENTIFIER IDENTIFIER* NEWLINE block END # FunctionDefine
  | IDENTIFIER expr* # FunctionCall
  | REPEAT expr NEWLINE block END # Repeat
  | IDENTIFIER ASSIGNMENT expr # Assignment
  ;

expr
  : LEFT_PARENTHESIS expr RIGHT_PARENTHESIS # Grouped
  | NEGATIVE expr # Negate
  | expr POWER expr # Power
  | expr MULTIPLICATIVE_OPERATOR expr # Multiply
  | expr ADDITIVE_OPERATOR expr # Add
  | NUMBER # Literal
  | IDENTIFIER # Identifier
  ;

IF: 'if';
ELSE: 'else';
TO: 'to';
MOVE: 'move';
ROTATE: 'rotate';
REPEAT: 'repeat';
END: 'end';
LEFT_PARENTHESIS: '(';
RIGHT_PARENTHESIS: ')';
POWER: '^';
NEGATIVE: '-';
MULTIPLICATIVE_OPERATOR: [*/%];
ADDITIVE_OPERATOR: '+' | '- ';
ASSIGNMENT: '=';
IDENTIFIER: [a-z]+ [a-z_]*;
NUMBER: '-'? [0-9]+ ('.' [0-9]+)?;

NEWLINE: '\n';
WHITESPACE: [ \t]+ -> skip;

Block.java

import java.util.ArrayList;

public class Block {
  private ArrayList<Statement> statements;

  public Block() {
    statements = new ArrayList<>();
  }

  public void add(Statement s) {
    statements.add(s);
  }

  public int size() {
    return statements.size();
  }

  public void execute(Environment env) {
    for (Statement s : statements) {
      s.executeWithTrigger(env);
    }
  }
}

Function.java

import java.util.ArrayList;

public class Function {
  private String id;
  private ArrayList<String> formalParameters;
  private Block body;

  public Function(String id, ArrayList<String> formalParameters, Block body) {
    this.id = id;
    this.formalParameters = formalParameters;
    this.body = body;
  }

  public String getID() {
    return id;
  }

  public ArrayList<String> getFormalParameters() {
    return formalParameters;
  }

  public Block getBody() {
    return body;
  }
}

Statement.java

public abstract class Statement {
  protected abstract void execute(Environment env);

  protected void executeWithTrigger(Environment env) {
    execute(env);
    if (env.listener != null) {
      env.listener.onPostExecute();
    }
  } 
}

Environment.java

import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.HashMap;

public class Environment {
  public double x = 0.0;
  public double y = 0.0;
  public double heading = 0.0;
  public boolean isDrawing = true;

  public HashMap<String, Double> locals = new HashMap<String, Double>();
  public HashMap<String, Function> functions = new HashMap<String, Function>();

  public ArrayList<Line2D.Double> segments = new ArrayList<Line2D.Double>();
  public StatementListener listener = null;
}

Expr.java

public abstract class Expr {
  public abstract double evaluate(Environment env);
}

ExprAdd.java

public class ExprAdd extends Expr {
  private Expr a;
  private Expr b;

  public ExprAdd(Expr a, Expr b) {
    this.a = a;
    this.b = b;
  }

  public double evaluate(Environment env) {
    return a.evaluate(env) + b.evaluate(env);
  }
}

ExprDivide.java

public class ExprDivide extends Expr {
  private Expr a;
  private Expr b;

  public ExprDivide(Expr a, Expr b) {
    this.a = a;
    this.b = b;
  }

  public double evaluate(Environment env) {
    return a.evaluate(env) / b.evaluate(env);
  }
}

ExprIdentifier.java

public class ExprIdentifier extends Expr {
  private String id;

  public ExprIdentifier(String id) {
    this.id = id;
  }

  public double evaluate(Environment env) {
    return env.locals.get(id);
  }
}

ExprLiteral.java

public class ExprLiteral extends Expr {
  public double i;

  public ExprLiteral(double i) {
    this.i = i;
  }
  
  public double evaluate(Environment env) {
    return i;
  }
}

ExprMultiply.java

public class ExprMultiply extends Expr {
  private Expr a;
  private Expr b;

  public ExprMultiply(Expr a, Expr b) {
    this.a = a;
    this.b = b;
  }

  public double evaluate(Environment env) {
    return a.evaluate(env) * b.evaluate(env);
  }
}

ExprNegate.java

public class ExprNegate extends Expr {
  private Expr a;

  public ExprNegate(Expr a) {
    this.a = a;
  }

  public double evaluate(Environment env) {
    return -a.evaluate(env);
  }
}

ExprPower.java

public class ExprPower extends Expr {
  private Expr a;
  private Expr b;

  public ExprPower(Expr a, Expr b) {
    this.a = a;
    this.b = b;
  }

  public double evaluate(Environment env) {
    return Math.pow(a.evaluate(env), b.evaluate(env));
  }
}

ExprRemainder.java

public class ExprRemainder extends Expr {
  private Expr a;
  private Expr b;

  public ExprRemainder(Expr a, Expr b) {
    this.a = a;
    this.b = b;
  }

  public double evaluate(Environment env) {
    return a.evaluate(env) % b.evaluate(env);
  }
}

ExprSubtract.java

public class ExprSubtract extends Expr {
  private Expr a;
  private Expr b;

  public ExprSubtract(Expr a, Expr b) {
    this.a = a;
    this.b = b;
  }

  public double evaluate(Environment env) {
    return a.evaluate(env) - b.evaluate(env);
  }
}

Statement.java

public abstract class Statement {
  protected abstract void execute(Environment env);

  protected void executeWithTrigger(Environment env) {
    execute(env);
    if (env.listener != null) {
      env.listener.onPostExecute();
    }
  } 
}

StatementAssignment.java

public class StatementAssignment extends Statement {
  private String id;
  private Expr expr;

  public StatementAssignment(String id, Expr expr) {
    this.id = id;
    this.expr = expr;
  }

  public void execute(Environment env) {
    double value = expr.evaluate(env);
    env.locals.put(id, value);
  }
}

StatementFunctionCall.java

import java.util.ArrayList;
import java.util.HashMap;

public class StatementFunctionCall extends Statement {
  private String id;
  private ArrayList<Expr> actualParameters;

  public StatementFunctionCall(String id, ArrayList<Expr> actualParameters) {
    this.id = id;
    this.actualParameters = actualParameters;
  }

  @Override
  protected void execute(Environment env) {
    Function f = env.functions.get(id);

    HashMap<String, Double> outerLocals = env.locals;

    HashMap<String, Double> innerLocals = new HashMap<String, Double>();
    for (int i = 0; i < actualParameters.size(); ++i) {
      innerLocals.put(f.getFormalParameters().get(i), actualParameters.get(i).evaluate(env));
    }

    env.locals = innerLocals;
    f.getBody().execute(env);
    env.locals = outerLocals;
  }
}

StatementFunctionDefine.java

import java.util.ArrayList;

public class StatementFunctionDefine extends Statement {
  private Function f;

  public StatementFunctionDefine(String id, ArrayList<String> formalParameters, Block body) {
    f = new Function(id, formalParameters, body);
  }

  @Override
  protected void execute(Environment env) {
    env.functions.put(f.getID(), f);
  }
}

StatementIf.java

import java.awt.geom.Line2D;

public class StatementIf extends Statement {
  private Expr expr;
  private Block thenBlock;
  private Block elseBlock;

  public StatementIf(Expr expr, Block thenBlock, Block elseBlock) {
    this.expr = expr;
    this.thenBlock = thenBlock;
    this.elseBlock = elseBlock;
  }

  protected void execute(Environment env) {
    int condition = (int) expr.evaluate(env);
    if (condition == 0) {
      elseBlock.execute(env);
    } else {
      thenBlock.execute(env);
    }
  }
}

StatementListener.java

public interface StatementListener {
  public void onPostExecute();
}

StatementMove.java

import java.awt.geom.Line2D;

public class StatementMove extends Statement {
  private Expr expr;

  public StatementMove(Expr expr) {
    this.expr = expr;
  }

  protected void execute(Environment env) {
    double distance = expr.evaluate(env);
    double newX = env.x + distance * Math.cos(env.heading);
    double newY = env.y + distance * Math.sin(env.heading);

    synchronized (env.segments) {
      env.segments.add(new Line2D.Double(env.x, env.y, newX, newY));
    }

    env.x = newX;
    env.y = newY;
  }
}

StatementRepeat.java

import java.awt.geom.Line2D;

public class StatementRepeat extends Statement {
  private Expr expr;
  private Block block;

  public StatementRepeat(Expr expr, Block block) {
    this.expr = expr;
    this.block = block;
  }

  protected void execute(Environment env) {
    int n = (int) expr.evaluate(env);
    for (int i = 0; i < n; ++i) {
      block.execute(env);
    }
  }
}

StatementRotate.java

import java.awt.geom.Line2D;

public class StatementRotate extends Statement {
  private Expr expr;

  public StatementRotate(Expr expr) {
    this.expr = expr;
  }

  protected void execute(Environment env) {
    env.heading += Math.toRadians(expr.evaluate(env));
  }
}

LogoBaseListener.java

// Generated from Logo.g by ANTLR 4.5

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;

/**
 * This class provides an empty implementation of {@link LogoListener},
 * which can be extended to create a listener which only needs to handle a subset
 * of the available methods.
 */
public class LogoBaseListener implements LogoListener {
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterProgram(LogoParser.ProgramContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitProgram(LogoParser.ProgramContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterBlock(LogoParser.BlockContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitBlock(LogoParser.BlockContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterMove(LogoParser.MoveContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitMove(LogoParser.MoveContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterRotate(LogoParser.RotateContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitRotate(LogoParser.RotateContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterIf(LogoParser.IfContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitIf(LogoParser.IfContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterFunctionDefine(LogoParser.FunctionDefineContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitFunctionDefine(LogoParser.FunctionDefineContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterFunctionCall(LogoParser.FunctionCallContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitFunctionCall(LogoParser.FunctionCallContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterRepeat(LogoParser.RepeatContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitRepeat(LogoParser.RepeatContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterAssignment(LogoParser.AssignmentContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitAssignment(LogoParser.AssignmentContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterAdd(LogoParser.AddContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitAdd(LogoParser.AddContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterIdentifier(LogoParser.IdentifierContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitIdentifier(LogoParser.IdentifierContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterLiteral(LogoParser.LiteralContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitLiteral(LogoParser.LiteralContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterMultiply(LogoParser.MultiplyContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitMultiply(LogoParser.MultiplyContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterNegate(LogoParser.NegateContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitNegate(LogoParser.NegateContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterGrouped(LogoParser.GroupedContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitGrouped(LogoParser.GroupedContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterPower(LogoParser.PowerContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitPower(LogoParser.PowerContext ctx) { }

  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void enterEveryRule(ParserRuleContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void exitEveryRule(ParserRuleContext ctx) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void visitTerminal(TerminalNode node) { }
  /**
   * {@inheritDoc}
   *
   * <p>The default implementation does nothing.</p>
   */
  @Override public void visitErrorNode(ErrorNode node) { }
}

LogoLexer.java

// Generated from Logo.g by ANTLR 4.5
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.*;

@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
public class LogoLexer extends Lexer {
  static { RuntimeMetaData.checkVersion("4.5", RuntimeMetaData.VERSION); }

  protected static final DFA[] _decisionToDFA;
  protected static final PredictionContextCache _sharedContextCache =
    new PredictionContextCache();
  public static final int
    IF=1, ELSE=2, TO=3, MOVE=4, ROTATE=5, REPEAT=6, END=7, LEFT_PARENTHESIS=8, 
    RIGHT_PARENTHESIS=9, POWER=10, NEGATIVE=11, MULTIPLICATIVE_OPERATOR=12, 
    ADDITIVE_OPERATOR=13, ASSIGNMENT=14, IDENTIFIER=15, NUMBER=16, NEWLINE=17, 
    WHITESPACE=18;
  public static String[] modeNames = {
    "DEFAULT_MODE"
  };

  public static final String[] ruleNames = {
    "IF", "ELSE", "TO", "MOVE", "ROTATE", "REPEAT", "END", "LEFT_PARENTHESIS", 
    "RIGHT_PARENTHESIS", "POWER", "NEGATIVE", "MULTIPLICATIVE_OPERATOR", "ADDITIVE_OPERATOR", 
    "ASSIGNMENT", "IDENTIFIER", "NUMBER", "NEWLINE", "WHITESPACE"
  };

  private static final String[] _LITERAL_NAMES = {
    null, "'if'", "'else'", "'to'", "'move'", "'rotate'", "'repeat'", "'end'", 
    "'('", "')'", "'^'", "'-'", null, null, "'='", null, null, "'\n'"
  };
  private static final String[] _SYMBOLIC_NAMES = {
    null, "IF", "ELSE", "TO", "MOVE", "ROTATE", "REPEAT", "END", "LEFT_PARENTHESIS", 
    "RIGHT_PARENTHESIS", "POWER", "NEGATIVE", "MULTIPLICATIVE_OPERATOR", "ADDITIVE_OPERATOR", 
    "ASSIGNMENT", "IDENTIFIER", "NUMBER", "NEWLINE", "WHITESPACE"
  };
  public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);

  /**
   * @deprecated Use {@link #VOCABULARY} instead.
   */
  @Deprecated
  public static final String[] tokenNames;
  static {
    tokenNames = new String[_SYMBOLIC_NAMES.length];
    for (int i = 0; i < tokenNames.length; i++) {
      tokenNames[i] = VOCABULARY.getLiteralName(i);
      if (tokenNames[i] == null) {
        tokenNames[i] = VOCABULARY.getSymbolicName(i);
      }

      if (tokenNames[i] == null) {
        tokenNames[i] = "<INVALID>";
      }
    }
  }

  @Override
  @Deprecated
  public String[] getTokenNames() {
    return tokenNames;
  }

  @Override

  public Vocabulary getVocabulary() {
    return VOCABULARY;
  }


  public LogoLexer(CharStream input) {
    super(input);
    _interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
  }

  @Override
  public String getGrammarFileName() { return "Logo.g"; }

  @Override
  public String[] getRuleNames() { return ruleNames; }

  @Override
  public String getSerializedATN() { return _serializedATN; }

  @Override
  public String[] getModeNames() { return modeNames; }

  @Override
  public ATN getATN() { return _ATN; }

  public static final String _serializedATN =
    "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2\24~\b\1\4\2\t\2\4"+
    "\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t"+
    "\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
    "\4\23\t\23\3\2\3\2\3\2\3\3\3\3\3\3\3\3\3\3\3\4\3\4\3\4\3\5\3\5\3\5\3\5"+
    "\3\5\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\b\3\b\3"+
    "\b\3\b\3\t\3\t\3\n\3\n\3\13\3\13\3\f\3\f\3\r\3\r\3\16\3\16\3\16\5\16W"+
    "\n\16\3\17\3\17\3\20\6\20\\\n\20\r\20\16\20]\3\20\7\20a\n\20\f\20\16\20"+
    "d\13\20\3\21\5\21g\n\21\3\21\6\21j\n\21\r\21\16\21k\3\21\3\21\6\21p\n"+
    "\21\r\21\16\21q\5\21t\n\21\3\22\3\22\3\23\6\23y\n\23\r\23\16\23z\3\23"+
    "\3\23\2\2\24\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r\31\16"+
    "\33\17\35\20\37\21!\22#\23%\24\3\2\7\5\2\'\',,\61\61\3\2c|\4\2aac|\3\2"+
    "\62;\4\2\13\13\"\"\u0085\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2"+
    "\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25"+
    "\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2"+
    "\2\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2\3\'\3\2\2\2\5*\3\2\2\2\7/\3\2\2"+
    "\2\t\62\3\2\2\2\13\67\3\2\2\2\r>\3\2\2\2\17E\3\2\2\2\21I\3\2\2\2\23K\3"+
    "\2\2\2\25M\3\2\2\2\27O\3\2\2\2\31Q\3\2\2\2\33V\3\2\2\2\35X\3\2\2\2\37"+
    "[\3\2\2\2!f\3\2\2\2#u\3\2\2\2%x\3\2\2\2\'(\7k\2\2()\7h\2\2)\4\3\2\2\2"+
    "*+\7g\2\2+,\7n\2\2,-\7u\2\2-.\7g\2\2.\6\3\2\2\2/\60\7v\2\2\60\61\7q\2"+
    "\2\61\b\3\2\2\2\62\63\7o\2\2\63\64\7q\2\2\64\65\7x\2\2\65\66\7g\2\2\66"+
    "\n\3\2\2\2\678\7t\2\289\7q\2\29:\7v\2\2:;\7c\2\2;<\7v\2\2<=\7g\2\2=\f"+
    "\3\2\2\2>?\7t\2\2?@\7g\2\2@A\7r\2\2AB\7g\2\2BC\7c\2\2CD\7v\2\2D\16\3\2"+
    "\2\2EF\7g\2\2FG\7p\2\2GH\7f\2\2H\20\3\2\2\2IJ\7*\2\2J\22\3\2\2\2KL\7+"+
    "\2\2L\24\3\2\2\2MN\7`\2\2N\26\3\2\2\2OP\7/\2\2P\30\3\2\2\2QR\t\2\2\2R"+
    "\32\3\2\2\2SW\7-\2\2TU\7/\2\2UW\7\"\2\2VS\3\2\2\2VT\3\2\2\2W\34\3\2\2"+
    "\2XY\7?\2\2Y\36\3\2\2\2Z\\\t\3\2\2[Z\3\2\2\2\\]\3\2\2\2][\3\2\2\2]^\3"+
    "\2\2\2^b\3\2\2\2_a\t\4\2\2`_\3\2\2\2ad\3\2\2\2b`\3\2\2\2bc\3\2\2\2c \3"+
    "\2\2\2db\3\2\2\2eg\7/\2\2fe\3\2\2\2fg\3\2\2\2gi\3\2\2\2hj\t\5\2\2ih\3"+
    "\2\2\2jk\3\2\2\2ki\3\2\2\2kl\3\2\2\2ls\3\2\2\2mo\7\60\2\2np\t\5\2\2on"+
    "\3\2\2\2pq\3\2\2\2qo\3\2\2\2qr\3\2\2\2rt\3\2\2\2sm\3\2\2\2st\3\2\2\2t"+
    "\"\3\2\2\2uv\7\f\2\2v$\3\2\2\2wy\t\6\2\2xw\3\2\2\2yz\3\2\2\2zx\3\2\2\2"+
    "z{\3\2\2\2{|\3\2\2\2|}\b\23\2\2}&\3\2\2\2\13\2V]bfkqsz\3\b\2\2";
  public static final ATN _ATN =
    new ATNDeserializer().deserialize(_serializedATN.toCharArray());
  static {
    _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
    for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) {
      _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
    }
  }
}

LogoListener.java

// Generated from Logo.g by ANTLR 4.5
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.tree.ParseTreeListener;

/**
 * This interface defines a complete listener for a parse tree produced by
 * {@link LogoParser}.
 */
public interface LogoListener extends ParseTreeListener {
  /**
   * Enter a parse tree produced by {@link LogoParser#program}.
   * @param ctx the parse tree
   */
  void enterProgram(LogoParser.ProgramContext ctx);
  /**
   * Exit a parse tree produced by {@link LogoParser#program}.
   * @param ctx the parse tree
   */
  void exitProgram(LogoParser.ProgramContext ctx);
  /**
   * Enter a parse tree produced by {@link LogoParser#block}.
   * @param ctx the parse tree
   */
  void enterBlock(LogoParser.BlockContext ctx);
  /**
   * Exit a parse tree produced by {@link LogoParser#block}.
   * @param ctx the parse tree
   */
  void exitBlock(LogoParser.BlockContext ctx);
  /**
   * Enter a parse tree produced by the {@code Move}
   * labeled alternative in {@link LogoParser#statement}.
   * @param ctx the parse tree
   */
  void enterMove(LogoParser.MoveContext ctx);
  /**
   * Exit a parse tree produced by the {@code Move}
   * labeled alternative in {@link LogoParser#statement}.
   * @param ctx the parse tree
   */
  void exitMove(LogoParser.MoveContext ctx);
  /**
   * Enter a parse tree produced by the {@code Rotate}
   * labeled alternative in {@link LogoParser#statement}.
   * @param ctx the parse tree
   */
  void enterRotate(LogoParser.RotateContext ctx);
  /**
   * Exit a parse tree produced by the {@code Rotate}
   * labeled alternative in {@link LogoParser#statement}.
   * @param ctx the parse tree
   */
  void exitRotate(LogoParser.RotateContext ctx);
  /**
   * Enter a parse tree produced by the {@code If}
   * labeled alternative in {@link LogoParser#statement}.
   * @param ctx the parse tree
   */
  void enterIf(LogoParser.IfContext ctx);
  /**
   * Exit a parse tree produced by the {@code If}
   * labeled alternative in {@link LogoParser#statement}.
   * @param ctx the parse tree
   */
  void exitIf(LogoParser.IfContext ctx);
  /**
   * Enter a parse tree produced by the {@code FunctionDefine}
   * labeled alternative in {@link LogoParser#statement}.
   * @param ctx the parse tree
   */
  void enterFunctionDefine(LogoParser.FunctionDefineContext ctx);
  /**
   * Exit a parse tree produced by the {@code FunctionDefine}
   * labeled alternative in {@link LogoParser#statement}.
   * @param ctx the parse tree
   */
  void exitFunctionDefine(LogoParser.FunctionDefineContext ctx);
  /**
   * Enter a parse tree produced by the {@code FunctionCall}
   * labeled alternative in {@link LogoParser#statement}.
   * @param ctx the parse tree
   */
  void enterFunctionCall(LogoParser.FunctionCallContext ctx);
  /**
   * Exit a parse tree produced by the {@code FunctionCall}
   * labeled alternative in {@link LogoParser#statement}.
   * @param ctx the parse tree
   */
  void exitFunctionCall(LogoParser.FunctionCallContext ctx);
  /**
   * Enter a parse tree produced by the {@code Repeat}
   * labeled alternative in {@link LogoParser#statement}.
   * @param ctx the parse tree
   */
  void enterRepeat(LogoParser.RepeatContext ctx);
  /**
   * Exit a parse tree produced by the {@code Repeat}
   * labeled alternative in {@link LogoParser#statement}.
   * @param ctx the parse tree
   */
  void exitRepeat(LogoParser.RepeatContext ctx);
  /**
   * Enter a parse tree produced by the {@code Assignment}
   * labeled alternative in {@link LogoParser#statement}.
   * @param ctx the parse tree
   */
  void enterAssignment(LogoParser.AssignmentContext ctx);
  /**
   * Exit a parse tree produced by the {@code Assignment}
   * labeled alternative in {@link LogoParser#statement}.
   * @param ctx the parse tree
   */
  void exitAssignment(LogoParser.AssignmentContext ctx);
  /**
   * Enter a parse tree produced by the {@code Add}
   * labeled alternative in {@link LogoParser#expr}.
   * @param ctx the parse tree
   */
  void enterAdd(LogoParser.AddContext ctx);
  /**
   * Exit a parse tree produced by the {@code Add}
   * labeled alternative in {@link LogoParser#expr}.
   * @param ctx the parse tree
   */
  void exitAdd(LogoParser.AddContext ctx);
  /**
   * Enter a parse tree produced by the {@code Identifier}
   * labeled alternative in {@link LogoParser#expr}.
   * @param ctx the parse tree
   */
  void enterIdentifier(LogoParser.IdentifierContext ctx);
  /**
   * Exit a parse tree produced by the {@code Identifier}
   * labeled alternative in {@link LogoParser#expr}.
   * @param ctx the parse tree
   */
  void exitIdentifier(LogoParser.IdentifierContext ctx);
  /**
   * Enter a parse tree produced by the {@code Literal}
   * labeled alternative in {@link LogoParser#expr}.
   * @param ctx the parse tree
   */
  void enterLiteral(LogoParser.LiteralContext ctx);
  /**
   * Exit a parse tree produced by the {@code Literal}
   * labeled alternative in {@link LogoParser#expr}.
   * @param ctx the parse tree
   */
  void exitLiteral(LogoParser.LiteralContext ctx);
  /**
   * Enter a parse tree produced by the {@code Multiply}
   * labeled alternative in {@link LogoParser#expr}.
   * @param ctx the parse tree
   */
  void enterMultiply(LogoParser.MultiplyContext ctx);
  /**
   * Exit a parse tree produced by the {@code Multiply}
   * labeled alternative in {@link LogoParser#expr}.
   * @param ctx the parse tree
   */
  void exitMultiply(LogoParser.MultiplyContext ctx);
  /**
   * Enter a parse tree produced by the {@code Negate}
   * labeled alternative in {@link LogoParser#expr}.
   * @param ctx the parse tree
   */
  void enterNegate(LogoParser.NegateContext ctx);
  /**
   * Exit a parse tree produced by the {@code Negate}
   * labeled alternative in {@link LogoParser#expr}.
   * @param ctx the parse tree
   */
  void exitNegate(LogoParser.NegateContext ctx);
  /**
   * Enter a parse tree produced by the {@code Grouped}
   * labeled alternative in {@link LogoParser#expr}.
   * @param ctx the parse tree
   */
  void enterGrouped(LogoParser.GroupedContext ctx);
  /**
   * Exit a parse tree produced by the {@code Grouped}
   * labeled alternative in {@link LogoParser#expr}.
   * @param ctx the parse tree
   */
  void exitGrouped(LogoParser.GroupedContext ctx);
  /**
   * Enter a parse tree produced by the {@code Power}
   * labeled alternative in {@link LogoParser#expr}.
   * @param ctx the parse tree
   */
  void enterPower(LogoParser.PowerContext ctx);
  /**
   * Exit a parse tree produced by the {@code Power}
   * labeled alternative in {@link LogoParser#expr}.
   * @param ctx the parse tree
   */
  void exitPower(LogoParser.PowerContext ctx);
}

LogoParser.java

// Generated from Logo.g by ANTLR 4.5
import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.misc.*;
import org.antlr.v4.runtime.tree.*;
import java.util.List;
import java.util.Iterator;
import java.util.ArrayList;

@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
public class LogoParser extends Parser {
  static { RuntimeMetaData.checkVersion("4.5", RuntimeMetaData.VERSION); }

  protected static final DFA[] _decisionToDFA;
  protected static final PredictionContextCache _sharedContextCache =
    new PredictionContextCache();
  public static final int
    IF=1, ELSE=2, TO=3, MOVE=4, ROTATE=5, REPEAT=6, END=7, LEFT_PARENTHESIS=8, 
    RIGHT_PARENTHESIS=9, POWER=10, NEGATIVE=11, MULTIPLICATIVE_OPERATOR=12, 
    ADDITIVE_OPERATOR=13, ASSIGNMENT=14, IDENTIFIER=15, NUMBER=16, NEWLINE=17, 
    WHITESPACE=18;
  public static final int
    RULE_program = 0, RULE_block = 1, RULE_statement = 2, RULE_expr = 3;
  public static final String[] ruleNames = {
    "program", "block", "statement", "expr"
  };

  private static final String[] _LITERAL_NAMES = {
    null, "'if'", "'else'", "'to'", "'move'", "'rotate'", "'repeat'", "'end'", 
    "'('", "')'", "'^'", "'-'", null, null, "'='", null, null, "'\n'"
  };
  private static final String[] _SYMBOLIC_NAMES = {
    null, "IF", "ELSE", "TO", "MOVE", "ROTATE", "REPEAT", "END", "LEFT_PARENTHESIS", 
    "RIGHT_PARENTHESIS", "POWER", "NEGATIVE", "MULTIPLICATIVE_OPERATOR", "ADDITIVE_OPERATOR", 
    "ASSIGNMENT", "IDENTIFIER", "NUMBER", "NEWLINE", "WHITESPACE"
  };
  public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);

  /**
   * @deprecated Use {@link #VOCABULARY} instead.
   */
  @Deprecated
  public static final String[] tokenNames;
  static {
    tokenNames = new String[_SYMBOLIC_NAMES.length];
    for (int i = 0; i < tokenNames.length; i++) {
      tokenNames[i] = VOCABULARY.getLiteralName(i);
      if (tokenNames[i] == null) {
        tokenNames[i] = VOCABULARY.getSymbolicName(i);
      }

      if (tokenNames[i] == null) {
        tokenNames[i] = "<INVALID>";
      }
    }
  }

  @Override
  @Deprecated
  public String[] getTokenNames() {
    return tokenNames;
  }

  @Override

  public Vocabulary getVocabulary() {
    return VOCABULARY;
  }

  @Override
  public String getGrammarFileName() { return "Logo.g"; }

  @Override
  public String[] getRuleNames() { return ruleNames; }

  @Override
  public String getSerializedATN() { return _serializedATN; }

  @Override
  public ATN getATN() { return _ATN; }

  public LogoParser(TokenStream input) {
    super(input);
    _interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
  }
  public static class ProgramContext extends ParserRuleContext {
    public BlockContext block() {
      return getRuleContext(BlockContext.class,0);
    }
    public TerminalNode EOF() { return getToken(LogoParser.EOF, 0); }
    public ProgramContext(ParserRuleContext parent, int invokingState) {
      super(parent, invokingState);
    }
    @Override public int getRuleIndex() { return RULE_program; }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterProgram(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitProgram(this);
    }
  }

  public final ProgramContext program() throws RecognitionException {
    ProgramContext _localctx = new ProgramContext(_ctx, getState());
    enterRule(_localctx, 0, RULE_program);
    try {
      enterOuterAlt(_localctx, 1);
      {
      setState(8);
      block();
      setState(9);
      match(EOF);
      }
    }
    catch (RecognitionException re) {
      _localctx.exception = re;
      _errHandler.reportError(this, re);
      _errHandler.recover(this, re);
    }
    finally {
      exitRule();
    }
    return _localctx;
  }

  public static class BlockContext extends ParserRuleContext {
    public List<TerminalNode> NEWLINE() { return getTokens(LogoParser.NEWLINE); }
    public TerminalNode NEWLINE(int i) {
      return getToken(LogoParser.NEWLINE, i);
    }
    public List<StatementContext> statement() {
      return getRuleContexts(StatementContext.class);
    }
    public StatementContext statement(int i) {
      return getRuleContext(StatementContext.class,i);
    }
    public BlockContext(ParserRuleContext parent, int invokingState) {
      super(parent, invokingState);
    }
    @Override public int getRuleIndex() { return RULE_block; }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterBlock(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitBlock(this);
    }
  }

  public final BlockContext block() throws RecognitionException {
    BlockContext _localctx = new BlockContext(_ctx, getState());
    enterRule(_localctx, 2, RULE_block);
    int _la;
    try {
      enterOuterAlt(_localctx, 1);
      {
      setState(17);
      _errHandler.sync(this);
      _la = _input.LA(1);
      while ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << IF) | (1L << TO) | (1L << MOVE) | (1L << ROTATE) | (1L << REPEAT) | (1L << IDENTIFIER) | (1L << NEWLINE))) != 0)) {
        {
        {
        setState(12);
        _la = _input.LA(1);
        if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << IF) | (1L << TO) | (1L << MOVE) | (1L << ROTATE) | (1L << REPEAT) | (1L << IDENTIFIER))) != 0)) {
          {
          setState(11);
          statement();
          }
        }

        setState(14);
        match(NEWLINE);
        }
        }
        setState(19);
        _errHandler.sync(this);
        _la = _input.LA(1);
      }
      }
    }
    catch (RecognitionException re) {
      _localctx.exception = re;
      _errHandler.reportError(this, re);
      _errHandler.recover(this, re);
    }
    finally {
      exitRule();
    }
    return _localctx;
  }

  public static class StatementContext extends ParserRuleContext {
    public StatementContext(ParserRuleContext parent, int invokingState) {
      super(parent, invokingState);
    }
    @Override public int getRuleIndex() { return RULE_statement; }
   
    public StatementContext() { }
    public void copyFrom(StatementContext ctx) {
      super.copyFrom(ctx);
    }
  }
  public static class AssignmentContext extends StatementContext {
    public TerminalNode IDENTIFIER() { return getToken(LogoParser.IDENTIFIER, 0); }
    public TerminalNode ASSIGNMENT() { return getToken(LogoParser.ASSIGNMENT, 0); }
    public ExprContext expr() {
      return getRuleContext(ExprContext.class,0);
    }
    public AssignmentContext(StatementContext ctx) { copyFrom(ctx); }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterAssignment(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitAssignment(this);
    }
  }
  public static class RotateContext extends StatementContext {
    public TerminalNode ROTATE() { return getToken(LogoParser.ROTATE, 0); }
    public ExprContext expr() {
      return getRuleContext(ExprContext.class,0);
    }
    public RotateContext(StatementContext ctx) { copyFrom(ctx); }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterRotate(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitRotate(this);
    }
  }
  public static class MoveContext extends StatementContext {
    public TerminalNode MOVE() { return getToken(LogoParser.MOVE, 0); }
    public ExprContext expr() {
      return getRuleContext(ExprContext.class,0);
    }
    public MoveContext(StatementContext ctx) { copyFrom(ctx); }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterMove(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitMove(this);
    }
  }
  public static class RepeatContext extends StatementContext {
    public TerminalNode REPEAT() { return getToken(LogoParser.REPEAT, 0); }
    public ExprContext expr() {
      return getRuleContext(ExprContext.class,0);
    }
    public TerminalNode NEWLINE() { return getToken(LogoParser.NEWLINE, 0); }
    public BlockContext block() {
      return getRuleContext(BlockContext.class,0);
    }
    public TerminalNode END() { return getToken(LogoParser.END, 0); }
    public RepeatContext(StatementContext ctx) { copyFrom(ctx); }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterRepeat(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitRepeat(this);
    }
  }
  public static class FunctionCallContext extends StatementContext {
    public TerminalNode IDENTIFIER() { return getToken(LogoParser.IDENTIFIER, 0); }
    public List<ExprContext> expr() {
      return getRuleContexts(ExprContext.class);
    }
    public ExprContext expr(int i) {
      return getRuleContext(ExprContext.class,i);
    }
    public FunctionCallContext(StatementContext ctx) { copyFrom(ctx); }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterFunctionCall(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitFunctionCall(this);
    }
  }
  public static class IfContext extends StatementContext {
    public TerminalNode IF() { return getToken(LogoParser.IF, 0); }
    public ExprContext expr() {
      return getRuleContext(ExprContext.class,0);
    }
    public TerminalNode NEWLINE() { return getToken(LogoParser.NEWLINE, 0); }
    public List<BlockContext> block() {
      return getRuleContexts(BlockContext.class);
    }
    public BlockContext block(int i) {
      return getRuleContext(BlockContext.class,i);
    }
    public TerminalNode ELSE() { return getToken(LogoParser.ELSE, 0); }
    public TerminalNode END() { return getToken(LogoParser.END, 0); }
    public IfContext(StatementContext ctx) { copyFrom(ctx); }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterIf(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitIf(this);
    }
  }
  public static class FunctionDefineContext extends StatementContext {
    public TerminalNode TO() { return getToken(LogoParser.TO, 0); }
    public List<TerminalNode> IDENTIFIER() { return getTokens(LogoParser.IDENTIFIER); }
    public TerminalNode IDENTIFIER(int i) {
      return getToken(LogoParser.IDENTIFIER, i);
    }
    public TerminalNode NEWLINE() { return getToken(LogoParser.NEWLINE, 0); }
    public BlockContext block() {
      return getRuleContext(BlockContext.class,0);
    }
    public TerminalNode END() { return getToken(LogoParser.END, 0); }
    public FunctionDefineContext(StatementContext ctx) { copyFrom(ctx); }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterFunctionDefine(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitFunctionDefine(this);
    }
  }

  public final StatementContext statement() throws RecognitionException {
    StatementContext _localctx = new StatementContext(_ctx, getState());
    enterRule(_localctx, 4, RULE_statement);
    int _la;
    try {
      setState(60);
      switch ( getInterpreter().adaptivePredict(_input,4,_ctx) ) {
      case 1:
        _localctx = new MoveContext(_localctx);
        enterOuterAlt(_localctx, 1);
        {
        setState(20);
        match(MOVE);
        setState(21);
        expr(0);
        }
        break;
      case 2:
        _localctx = new RotateContext(_localctx);
        enterOuterAlt(_localctx, 2);
        {
        setState(22);
        match(ROTATE);
        setState(23);
        expr(0);
        }
        break;
      case 3:
        _localctx = new IfContext(_localctx);
        enterOuterAlt(_localctx, 3);
        {
        setState(24);
        match(IF);
        setState(25);
        expr(0);
        setState(26);
        match(NEWLINE);
        setState(27);
        block();
        setState(28);
        match(ELSE);
        setState(29);
        block();
        setState(30);
        match(END);
        }
        break;
      case 4:
        _localctx = new FunctionDefineContext(_localctx);
        enterOuterAlt(_localctx, 4);
        {
        setState(32);
        match(TO);
        setState(33);
        match(IDENTIFIER);
        setState(37);
        _errHandler.sync(this);
        _la = _input.LA(1);
        while (_la==IDENTIFIER) {
          {
          {
          setState(34);
          match(IDENTIFIER);
          }
          }
          setState(39);
          _errHandler.sync(this);
          _la = _input.LA(1);
        }
        setState(40);
        match(NEWLINE);
        setState(41);
        block();
        setState(42);
        match(END);
        }
        break;
      case 5:
        _localctx = new FunctionCallContext(_localctx);
        enterOuterAlt(_localctx, 5);
        {
        setState(44);
        match(IDENTIFIER);
        setState(48);
        _errHandler.sync(this);
        _la = _input.LA(1);
        while ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << LEFT_PARENTHESIS) | (1L << NEGATIVE) | (1L << IDENTIFIER) | (1L << NUMBER))) != 0)) {
          {
          {
          setState(45);
          expr(0);
          }
          }
          setState(50);
          _errHandler.sync(this);
          _la = _input.LA(1);
        }
        }
        break;
      case 6:
        _localctx = new RepeatContext(_localctx);
        enterOuterAlt(_localctx, 6);
        {
        setState(51);
        match(REPEAT);
        setState(52);
        expr(0);
        setState(53);
        match(NEWLINE);
        setState(54);
        block();
        setState(55);
        match(END);
        }
        break;
      case 7:
        _localctx = new AssignmentContext(_localctx);
        enterOuterAlt(_localctx, 7);
        {
        setState(57);
        match(IDENTIFIER);
        setState(58);
        match(ASSIGNMENT);
        setState(59);
        expr(0);
        }
        break;
      }
    }
    catch (RecognitionException re) {
      _localctx.exception = re;
      _errHandler.reportError(this, re);
      _errHandler.recover(this, re);
    }
    finally {
      exitRule();
    }
    return _localctx;
  }

  public static class ExprContext extends ParserRuleContext {
    public ExprContext(ParserRuleContext parent, int invokingState) {
      super(parent, invokingState);
    }
    @Override public int getRuleIndex() { return RULE_expr; }
   
    public ExprContext() { }
    public void copyFrom(ExprContext ctx) {
      super.copyFrom(ctx);
    }
  }
  public static class AddContext extends ExprContext {
    public List<ExprContext> expr() {
      return getRuleContexts(ExprContext.class);
    }
    public ExprContext expr(int i) {
      return getRuleContext(ExprContext.class,i);
    }
    public TerminalNode ADDITIVE_OPERATOR() { return getToken(LogoParser.ADDITIVE_OPERATOR, 0); }
    public AddContext(ExprContext ctx) { copyFrom(ctx); }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterAdd(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitAdd(this);
    }
  }
  public static class IdentifierContext extends ExprContext {
    public TerminalNode IDENTIFIER() { return getToken(LogoParser.IDENTIFIER, 0); }
    public IdentifierContext(ExprContext ctx) { copyFrom(ctx); }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterIdentifier(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitIdentifier(this);
    }
  }
  public static class LiteralContext extends ExprContext {
    public TerminalNode NUMBER() { return getToken(LogoParser.NUMBER, 0); }
    public LiteralContext(ExprContext ctx) { copyFrom(ctx); }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterLiteral(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitLiteral(this);
    }
  }
  public static class MultiplyContext extends ExprContext {
    public List<ExprContext> expr() {
      return getRuleContexts(ExprContext.class);
    }
    public ExprContext expr(int i) {
      return getRuleContext(ExprContext.class,i);
    }
    public TerminalNode MULTIPLICATIVE_OPERATOR() { return getToken(LogoParser.MULTIPLICATIVE_OPERATOR, 0); }
    public MultiplyContext(ExprContext ctx) { copyFrom(ctx); }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterMultiply(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitMultiply(this);
    }
  }
  public static class NegateContext extends ExprContext {
    public TerminalNode NEGATIVE() { return getToken(LogoParser.NEGATIVE, 0); }
    public ExprContext expr() {
      return getRuleContext(ExprContext.class,0);
    }
    public NegateContext(ExprContext ctx) { copyFrom(ctx); }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterNegate(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitNegate(this);
    }
  }
  public static class GroupedContext extends ExprContext {
    public TerminalNode LEFT_PARENTHESIS() { return getToken(LogoParser.LEFT_PARENTHESIS, 0); }
    public ExprContext expr() {
      return getRuleContext(ExprContext.class,0);
    }
    public TerminalNode RIGHT_PARENTHESIS() { return getToken(LogoParser.RIGHT_PARENTHESIS, 0); }
    public GroupedContext(ExprContext ctx) { copyFrom(ctx); }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterGrouped(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitGrouped(this);
    }
  }
  public static class PowerContext extends ExprContext {
    public List<ExprContext> expr() {
      return getRuleContexts(ExprContext.class);
    }
    public ExprContext expr(int i) {
      return getRuleContext(ExprContext.class,i);
    }
    public TerminalNode POWER() { return getToken(LogoParser.POWER, 0); }
    public PowerContext(ExprContext ctx) { copyFrom(ctx); }
    @Override
    public void enterRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).enterPower(this);
    }
    @Override
    public void exitRule(ParseTreeListener listener) {
      if ( listener instanceof LogoListener ) ((LogoListener)listener).exitPower(this);
    }
  }

  public final ExprContext expr() throws RecognitionException {
    return expr(0);
  }

  private ExprContext expr(int _p) throws RecognitionException {
    ParserRuleContext _parentctx = _ctx;
    int _parentState = getState();
    ExprContext _localctx = new ExprContext(_ctx, _parentState);
    ExprContext _prevctx = _localctx;
    int _startState = 6;
    enterRecursionRule(_localctx, 6, RULE_expr, _p);
    try {
      int _alt;
      enterOuterAlt(_localctx, 1);
      {
      setState(71);
      switch (_input.LA(1)) {
      case NEGATIVE:
        {
        _localctx = new NegateContext(_localctx);
        _ctx = _localctx;
        _prevctx = _localctx;

        setState(63);
        match(NEGATIVE);
        setState(64);
        expr(6);
        }
        break;
      case LEFT_PARENTHESIS:
        {
        _localctx = new GroupedContext(_localctx);
        _ctx = _localctx;
        _prevctx = _localctx;
        setState(65);
        match(LEFT_PARENTHESIS);
        setState(66);
        expr(0);
        setState(67);
        match(RIGHT_PARENTHESIS);
        }
        break;
      case NUMBER:
        {
        _localctx = new LiteralContext(_localctx);
        _ctx = _localctx;
        _prevctx = _localctx;
        setState(69);
        match(NUMBER);
        }
        break;
      case IDENTIFIER:
        {
        _localctx = new IdentifierContext(_localctx);
        _ctx = _localctx;
        _prevctx = _localctx;
        setState(70);
        match(IDENTIFIER);
        }
        break;
      default:
        throw new NoViableAltException(this);
      }
      _ctx.stop = _input.LT(-1);
      setState(84);
      _errHandler.sync(this);
      _alt = getInterpreter().adaptivePredict(_input,7,_ctx);
      while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
        if ( _alt==1 ) {
          if ( _parseListeners!=null ) triggerExitRuleEvent();
          _prevctx = _localctx;
          {
          setState(82);
          switch ( getInterpreter().adaptivePredict(_input,6,_ctx) ) {
          case 1:
            {
            _localctx = new PowerContext(new ExprContext(_parentctx, _parentState));
            pushNewRecursionContext(_localctx, _startState, RULE_expr);
            setState(73);
            if (!(precpred(_ctx, 5))) throw new FailedPredicateException(this, "precpred(_ctx, 5)");
            setState(74);
            match(POWER);
            setState(75);
            expr(6);
            }
            break;
          case 2:
            {
            _localctx = new MultiplyContext(new ExprContext(_parentctx, _parentState));
            pushNewRecursionContext(_localctx, _startState, RULE_expr);
            setState(76);
            if (!(precpred(_ctx, 4))) throw new FailedPredicateException(this, "precpred(_ctx, 4)");
            setState(77);
            match(MULTIPLICATIVE_OPERATOR);
            setState(78);
            expr(5);
            }
            break;
          case 3:
            {
            _localctx = new AddContext(new ExprContext(_parentctx, _parentState));
            pushNewRecursionContext(_localctx, _startState, RULE_expr);
            setState(79);
            if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)");
            setState(80);
            match(ADDITIVE_OPERATOR);
            setState(81);
            expr(4);
            }
            break;
          }
          } 
        }
        setState(86);
        _errHandler.sync(this);
        _alt = getInterpreter().adaptivePredict(_input,7,_ctx);
      }
      }
    }
    catch (RecognitionException re) {
      _localctx.exception = re;
      _errHandler.reportError(this, re);
      _errHandler.recover(this, re);
    }
    finally {
      unrollRecursionContexts(_parentctx);
    }
    return _localctx;
  }

  public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) {
    switch (ruleIndex) {
    case 3:
      return expr_sempred((ExprContext)_localctx, predIndex);
    }
    return true;
  }
  private boolean expr_sempred(ExprContext _localctx, int predIndex) {
    switch (predIndex) {
    case 0:
      return precpred(_ctx, 5);
    case 1:
      return precpred(_ctx, 4);
    case 2:
      return precpred(_ctx, 3);
    }
    return true;
  }

  public static final String _serializedATN =
    "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3\24Z\4\2\t\2\4\3\t"+
    "\3\4\4\t\4\4\5\t\5\3\2\3\2\3\2\3\3\5\3\17\n\3\3\3\7\3\22\n\3\f\3\16\3"+
    "\25\13\3\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\7"+
    "\4&\n\4\f\4\16\4)\13\4\3\4\3\4\3\4\3\4\3\4\3\4\7\4\61\n\4\f\4\16\4\64"+
    "\13\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\5\4?\n\4\3\5\3\5\3\5\3\5\3\5"+
    "\3\5\3\5\3\5\3\5\5\5J\n\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\7\5U\n\5"+
    "\f\5\16\5X\13\5\3\5\2\3\b\6\2\4\6\b\2\2e\2\n\3\2\2\2\4\23\3\2\2\2\6>\3"+
    "\2\2\2\bI\3\2\2\2\n\13\5\4\3\2\13\f\7\2\2\3\f\3\3\2\2\2\r\17\5\6\4\2\16"+
    "\r\3\2\2\2\16\17\3\2\2\2\17\20\3\2\2\2\20\22\7\23\2\2\21\16\3\2\2\2\22"+
    "\25\3\2\2\2\23\21\3\2\2\2\23\24\3\2\2\2\24\5\3\2\2\2\25\23\3\2\2\2\26"+
    "\27\7\6\2\2\27?\5\b\5\2\30\31\7\7\2\2\31?\5\b\5\2\32\33\7\3\2\2\33\34"+
    "\5\b\5\2\34\35\7\23\2\2\35\36\5\4\3\2\36\37\7\4\2\2\37 \5\4\3\2 !\7\t"+
    "\2\2!?\3\2\2\2\"#\7\5\2\2#\'\7\21\2\2$&\7\21\2\2%$\3\2\2\2&)\3\2\2\2\'"+
    "%\3\2\2\2\'(\3\2\2\2(*\3\2\2\2)\'\3\2\2\2*+\7\23\2\2+,\5\4\3\2,-\7\t\2"+
    "\2-?\3\2\2\2.\62\7\21\2\2/\61\5\b\5\2\60/\3\2\2\2\61\64\3\2\2\2\62\60"+
    "\3\2\2\2\62\63\3\2\2\2\63?\3\2\2\2\64\62\3\2\2\2\65\66\7\b\2\2\66\67\5"+
    "\b\5\2\678\7\23\2\289\5\4\3\29:\7\t\2\2:?\3\2\2\2;<\7\21\2\2<=\7\20\2"+
    "\2=?\5\b\5\2>\26\3\2\2\2>\30\3\2\2\2>\32\3\2\2\2>\"\3\2\2\2>.\3\2\2\2"+
    ">\65\3\2\2\2>;\3\2\2\2?\7\3\2\2\2@A\b\5\1\2AB\7\r\2\2BJ\5\b\5\bCD\7\n"+
    "\2\2DE\5\b\5\2EF\7\13\2\2FJ\3\2\2\2GJ\7\22\2\2HJ\7\21\2\2I@\3\2\2\2IC"+
    "\3\2\2\2IG\3\2\2\2IH\3\2\2\2JV\3\2\2\2KL\f\7\2\2LM\7\f\2\2MU\5\b\5\bN"+
    "O\f\6\2\2OP\7\16\2\2PU\5\b\5\7QR\f\5\2\2RS\7\17\2\2SU\5\b\5\6TK\3\2\2"+
    "\2TN\3\2\2\2TQ\3\2\2\2UX\3\2\2\2VT\3\2\2\2VW\3\2\2\2W\t\3\2\2\2XV\3\2"+
    "\2\2\n\16\23\'\62>ITV";
  public static final ATN _ATN =
    new ATNDeserializer().deserialize(_serializedATN.toCharArray());
  static {
    _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
    for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) {
      _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
    }
  }
}

LogoVirtualMachine.java

import java.awt.Dimension;
import javax.swing.JButton;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.io.IOException;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class LogoVirtualMachine implements StatementListener {
  private JFrame display;
  private LogoDisplay panel;
  private int delay = 100;

  public LogoVirtualMachine(final Block main) {
    display = new JFrame("Logo");
    panel = new LogoDisplay();
    JScrollPane scroller = new JScrollPane(panel);
    display.add(scroller);

    JButton runButton = new JButton("Run");
    runButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        Environment env = new Environment();
        panel.setEnvironment(env);
        env.listener = LogoVirtualMachine.this;
        main.execute(env);
      }
    });
    display.add(runButton, BorderLayout.NORTH);

    panel.setPreferredSize(new Dimension(3000, 3000));
    panel.setSize(panel.getPreferredSize());

    display.setSize(1200, 700);
    display.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    display.setVisible(true);
  }

  public void onPostExecute() {
    pause();
    panel.paintImmediately(0, 0, panel.getWidth(), panel.getHeight());
  }

  private void pause() {
    try {
      Thread.sleep(delay);
    } catch (InterruptedException e) {
    }
  }

  class LogoDisplay extends JPanel {
    private Line2D.Double arrowTop;
    private Line2D.Double arrowBottom;
    private Environment env;

    public LogoDisplay() {
      arrowTop = new Line2D.Double(-10.0f, -5.0f, 0.0f, 0.0f);
      arrowBottom = new Line2D.Double(-10.0f, 5.0f, 0.0f, 0.0f);
    }

    public void setEnvironment(Environment env) {
      this.env = env;
    }

    public void paintComponent(Graphics g) {
      super.paintComponent(g);

      if (env == null) {
        return;
      }

      Graphics2D g2 = (Graphics2D) g;
      AffineTransform xform = g2.getTransform();

      g2.translate(1200 / 2, 700 / 2);

      synchronized (env.segments) {
        for (Line2D.Double segment : env.segments) {
          g2.draw(segment);
        }
      }

      g2.translate(env.x, env.y);
      g2.rotate(env.heading);
      g2.draw(arrowTop);
      g2.draw(arrowBottom);

      g2.setTransform(xform);
    }
  }
}

Interpreter.java

import java.util.HashMap;
import java.util.ArrayList;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.File;
import javax.swing.JFileChooser;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ANTLRInputStream;
import java.util.Stack;

public class Interpreter extends LogoBaseListener {
  private Stack<Expr> operands;
  private Stack<Block> blocks;

  public Interpreter() {
    operands = new Stack<>();
    blocks = new Stack<>();
  }

  @Override
  public void exitProgram(LogoParser.ProgramContext ctx) {
    Block main = blocks.pop();
    new LogoVirtualMachine(main);
  }

  @Override
  public void enterBlock(LogoParser.BlockContext ctx) {
    blocks.push(new Block());
  }

  @Override
  public void exitMove(LogoParser.MoveContext ctx) {
    blocks.peek().add(new StatementMove(operands.pop()));
  }

  @Override
  public void exitRepeat(LogoParser.RepeatContext ctx) {
    Block body = blocks.pop();
    blocks.peek().add(new StatementRepeat(operands.pop(), body));
  }

  @Override
  public void exitIf(LogoParser.IfContext ctx) {
    Block elseBlock = blocks.pop();
    Block thenBlock = blocks.pop();
    blocks.peek().add(new StatementIf(operands.pop(), thenBlock, elseBlock));
  }

  @Override
  public void exitAssignment(LogoParser.AssignmentContext ctx) {
    blocks.peek().add(new StatementAssignment(ctx.IDENTIFIER().getText(), operands.pop()));
  }

  @Override
  public void exitRotate(LogoParser.RotateContext ctx) {
    blocks.peek().add(new StatementRotate(operands.pop()));
  }

  @Override
  public void exitFunctionDefine(LogoParser.FunctionDefineContext ctx) {
    Block body = blocks.pop();
    ArrayList<String> formals = new ArrayList<String>();
    for (int i = 1; i < ctx.IDENTIFIER().size(); ++i) {
      formals.add(ctx.IDENTIFIER(i).getText());
    }
    blocks.peek().add(new StatementFunctionDefine(ctx.IDENTIFIER(0).getText(), formals, body));
  }

  @Override
  public void exitFunctionCall(LogoParser.FunctionCallContext ctx) {
    ArrayList<Expr> actuals = new ArrayList<Expr>();
    for (int i = 0; i < ctx.expr().size(); ++i) {
      actuals.add(0, operands.pop());
    }
    blocks.peek().add(new StatementFunctionCall(ctx.IDENTIFIER().getText(), actuals));
  }

  @Override
  public void exitLiteral(LogoParser.LiteralContext ctx) {
    String digitsAsString = ctx.NUMBER().getText(); 
    double digits = Double.parseDouble(digitsAsString);
    operands.push(new ExprLiteral(digits));
  }

  @Override
  public void exitAdd(LogoParser.AddContext ctx) {
    Expr b = operands.pop();
    Expr a = operands.pop();
    if (ctx.ADDITIVE_OPERATOR().getText().equals("+")) {
      operands.push(new ExprAdd(a, b));
    } else {
      operands.push(new ExprSubtract(a, b));
    }
  }

  @Override
  public void exitPower(LogoParser.PowerContext ctx) {
    Expr b = operands.pop();
    Expr a = operands.pop();
    operands.push(new ExprPower(a, b));
  }

  @Override
  public void exitNegate(LogoParser.NegateContext ctx) {
    Expr a = operands.pop();
    operands.push(new ExprNegate(a));
  }

  @Override
  public void exitMultiply(LogoParser.MultiplyContext ctx) {
    Expr b = operands.pop();
    Expr a = operands.pop();
    if (ctx.MULTIPLICATIVE_OPERATOR().getText().equals("*")) {
      operands.push(new ExprMultiply(a, b));
    } else if (ctx.MULTIPLICATIVE_OPERATOR().getText().equals("/")) {
      operands.push(new ExprDivide(a, b));
    } else {
      operands.push(new ExprRemainder(a, b));
    }
  }

  @Override
  public void exitIdentifier(LogoParser.IdentifierContext ctx) {
    operands.push(new ExprIdentifier(ctx.IDENTIFIER().getText()));
  }

  public static void main(String[] args) throws IOException {
    File f = new File(args[0]);

    if (!f.exists()) {
      JFileChooser chooser = new JFileChooser();
      int ok = chooser.showOpenDialog(null);
      if (ok == JFileChooser.APPROVE_OPTION) {
        f = chooser.getSelectedFile();
      }
    }

    ANTLRInputStream ais = new ANTLRInputStream(new FileInputStream(f));
    LogoLexer lexer = new LogoLexer(ais);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    LogoParser parser = new LogoParser(tokens);
    ParseTree tree = parser.program();

    ParseTreeWalker walker = new ParseTreeWalker();
    walker.walk(new Interpreter(), tree);
  }
}

square2.logo

move 50
rotate 90
move 50
rotate 90
move 50
rotate 90
move 50

square2_with_loop.logo

a = 50
repeat 20
  move a
  rotate 90
  move a
  rotate 90
  move a
  rotate 90
  move a
  a = a + 10
end

pyramid.logo

to pyramid level
  if level
    move 20
    rotate -90
    move 20
    rotate 90
    pyramid level - 1
  else
  end
end

pyramid 10

Haiku

on differential geometry
I’m A-to-B-ing
The direct routeĀ costs the most
‘Cuz I think the least