Imagine you can manipulate a turtle/robot/drone with a program. In particular, you can do the following to your controllee:
Write a little program to do something interesting. You pick the syntax.
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;
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
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);
}
}
}
Comments