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