CS 330 Lecture 19 – A Logo REPL Part II

Agenda

  • what ?s
  • comments
  • bugs
  • a REPL
  • variables
  • expressions

General Test Topics

Things on the exam:

  • addressable memory
  • stack vs. heap storage
  • pattern matching with common regex primitives
  • types and type checking
  • declarations vs. definitions
  • basic instructions (pushl, movl, addl, subl) and flow control (cmpl, jmp and friends) in assembly
  • big picture ideas
  • small code exercises
  • questions with gradable answers
  • invocation of shell utilities and pipelines

Things not on the exam:

  • addressing modes
  • Japan’s geopolitical concerns
  • cheese
  • the answers
  • Ruby, per se
  • questions requiring a catalog of knowledge about shell utilities

Code

Logo.g4

grammar Logo;

program
  : statement* EOF
  ;

statement
  : command NEWLINE
  ;

command
  : MOVE expression # Move
  | TURN expression # Turn
  | SLIME (ON | OFF) # Slime
  | ID IS expression # Assignment
  | ID # Echo
  ;

expression
  : INT # Int
  | ID # Reference
  | '(' expression ')' # Parenthesized
  | expression MULTIPLICATIVE_OPERATOR expression # MulDivMod
  | expression ADDITIVE_OPERATOR expression # AddSub
  ;

MULTIPLICATIVE_OPERATOR : ('*'|'/'|'%');
ADDITIVE_OPERATOR : ('+'|'-');
IS : '<-';
MOVE : 'move';
TURN : 'turn';
SLIME : 'slime';
ON : 'on';
OFF : 'off';
INT : '-'? [0-9]+;
NEWLINE : '\r'? '\n';
ID : [A-Za-z_] [A-Za-z0-9_]*;

COMMENT : '#' .*? '\n' -> skip;
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)

all: Interpreter.class

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

square.logo

move 100
turn 90
move 100
turn 90
move 100
turn 90
move 100

varsquar.logo

angle <- 90
distance <- 100
move distance
turn angle
move distance
turn angle
move distance
turn angle
move distance

arithtest.logo

foobar <- 50 * 2 + 100
foobar
move foobar

Interpreter.java

import org.antlr.v4.runtime.*;
import java.util.HashMap;
import java.util.Stack;
import java.util.Scanner;
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 { 
    Interpreter interpreter = new Interpreter();
    interpreter.pause(1000);

    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(interpreter, tree);
  }

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

  private ArrayList<Line2D.Double> segments = new ArrayList<Line2D.Double>();

  private double x = 0.0;
  private double y = 0.0;
  private double theta = 0.0;
  private boolean isLeaking = true;
  private HashMap<String, Integer> variables = new HashMap<String, Integer>();
  private Stack<Integer> stack = new Stack<Integer>();

  public void exitMove(LogoParser.MoveContext context) {
    double distance = stack.pop();

    double newX = x + distance * Math.cos(theta);
    double newY = y + distance * Math.sin(theta);

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

    x = newX;
    y = newY;
  }

  public void exitTurn(LogoParser.TurnContext context) {
    theta += Math.toRadians(stack.pop());
  }

  public void exitAssignment(LogoParser.AssignmentContext context) {
    String name = context.ID().getText();
    variables.put(name, stack.pop());
  }

  public void exitSlime(LogoParser.SlimeContext context) {
    isLeaking = context.ON() != null;
  }

  public void exitInt(LogoParser.IntContext context) {
    stack.push(Integer.parseInt(context.INT().getText())); 
  }

  public void exitReference(LogoParser.ReferenceContext context) {
    String name = context.ID().getText();
    stack.push(variables.get(name));
  }

  public void exitEcho(LogoParser.EchoContext context) {
    String id = context.ID().getText();
    System.out.println(id + ": " + variables.get(id));
  }

  public void exitAddSub(LogoParser.AddSubContext context) {
    int b = stack.pop();
    int a = stack.pop();

    if (context.ADDITIVE_OPERATOR().getText().equals("+")) {
      stack.push(a + b);
    } else {
      stack.push(a - b);
    }
  }

  public void exitMulDivMod(LogoParser.MulDivModContext context) {
    int b = stack.pop();
    int a = stack.pop();

    if (context.MULTIPLICATIVE_OPERATOR().getText().equals("*")) {
      stack.push(a * b);
    } else if (context.MULTIPLICATIVE_OPERATOR().getText().equals("/")) {
      stack.push(a / b);
    } else {
      stack.push(a % b);
    }
  }

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

  public void enterStatement(LogoParser.StatementContext context) {
    System.out.println("about to execute " + context.getText());
  }

  public void exitEveryRule(ParserRuleContext context) {
    pause(500);
    display.repaint();
  }

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

  private LogoDisplay display;

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

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

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

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

    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 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);
    }
  }
}

Haiku

Technology chills?
Machines are impersonal?
My numbers are named

Comments

Leave a Reply

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