CS 330 Lecture 18 – A Logo REPL

Agenda

  • what ?s
  • why write our own languages?
  • program this
  • building an interpreter…
  • …with a REPL

TODO

  • Nada. Though you wish to write more exam questions and post them in the comments. The more questions you come up with, the fewer I’ll come up with.

Program This

Imagine you can manipulate a turtle/robot/drone with a program. In particular, you can do the following to your controllee:

  • move it forward
  • turn it
  • cause it to leave a trail

Write a little program to do something interesting. You pick the syntax.

Code

Logo.g4

grammar Logo;

program
  : statement* EOF
  ;

statement
  : command '\n'
  ;

command
  : MOVE INT           # Move
  | TURN INT           # Turn
  | SLIME (ON | OFF)   # Slime
  ;

MOVE : 'move';
TURN : 'turn';
SLIME : 'slime';
ON : 'on';
OFF : 'off';
INT : '-'? [0-9]+;

WHITESPACE : [ \t\r]+ -> skip;

makefile

Note to copy and pasters: makefile rules need to be indented with real tabs, not spaces.

ANTLR_JAR = $(HOME)/bin/antlr-4.0-complete.jar
ANTLR = java -jar $(ANTLR_JAR)

LogoParser.class: Logo.g4 makefile
  $(ANTLR) Logo.g4
  javac Logo*.java

Interpreter.class: LogoParser.class Interpreter.java
  javac Interpreter.java

clean:
  rm -f *.class Logo*.java *.tokens

Interpreter.java

import org.antlr.v4.runtime.*;
import java.util.ArrayList;
import java.io.IOException;
import org.antlr.v4.runtime.tree.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.AffineTransform;

public class Interpreter extends LogoBaseListener {
  public static void main(String[] args) throws IOException { 
    ANTLRInputStream input = new ANTLRInputStream(System.in);
    LogoLexer lexer = new LogoLexer(input);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    LogoParser parser = new LogoParser(tokens);
    ParseTree tree = parser.program();

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

/* ------------------------------------------------------------------------- */

  private double x = 0.0;
  private double y = 0.0;
  private double theta = 0.0;
  private boolean isLeaking = true;

  public void exitMove(LogoParser.MoveContext context) {
    double distance = Double.parseDouble(context.INT().getText());

    double newX = distance * Math.cos(theta);
    double newY = distance * Math.sin(theta);
  
    display.addSegment(new Line2D.Double(x, y, newX, newY));
    display.step();

    x = newX;
    y = newY;
  }

  public void exitTurn(LogoParser.TurnContext context) {
    double angle = Double.parseDouble(context.INT().getText());
    display.setCursor(0.0, 0.0, angle);
    display.step();
  }

/* ------------------------------------------------------------------------- */

  private LogoDisplay display;
  private int delay;

  public Interpreter(int delay) {
    JFrame frame = new JFrame("Logo");

    display = new LogoDisplay();
    frame.setContentPane(display);

    frame.setSize(400, 300);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);

    this.delay = delay;
    display.step();
  }

  class LogoDisplay extends JPanel {
    private ArrayList<Line2D.Double> segments;
    private Line2D.Double arrowTop;
    private Line2D.Double arrowBottom;
    private double x;
    private double y;
    private double theta;

    public LogoDisplay() {
      segments = new ArrayList<Line2D.Double>();
      arrowTop = new Line2D.Double(-10.0f, -5.0f, 0.0f, 0.0f);
      arrowBottom = new Line2D.Double(-10.0f, 5.0f, 0.0f, 0.0f);
      x = 0.0;
      y = 0.0;
      theta = 0.0;
    }

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

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

      g2.translate(100, 100);
      for (Line2D.Double segment : segments) {
        g2.draw(segment);
      }  

      g2.translate(x, y);
      g2.rotate(theta);
      g2.draw(arrowTop);
      g2.draw(arrowBottom);
    }

    public void step() {
      repaint();
      try {
        Thread.sleep(delay);
      } catch (InterruptedException e) {}
    }

    public void setCursor(double x, double y, double theta) {
      this.x = x;
      this.y = y;
      this.theta = theta;
    }

    public void addSegment(Line2D.Double segment) {
      segments.add(segment);
    }
  }
}

Haiku

Don’t reinvent wheels
Bow before the pioneers
Their third-gen wheels rock

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *