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)
PIECES = $(wildcard *Expression.java *Command.java) LogoBlock.java LVM.java
all: CompilerLoogo.class LVM.class
CompilerLoogo.class: LoogoParser.class makefile CompilerLoogo.java $(PIECES)
javac CompilerLoogo.java
LVM.class: $(PIECES)
javac LVM.java
LoogoParser.class: Loogo.g4 makefile
$(ANTLR) Loogo.g4
javac Loogo*.java
clean:
rm -f *.class *.tokens Logo*Listener.java Logo*Parser.java Logo*Lexer.java
public class AddExpression implements LogoExpression {
private LogoExpression a;
private LogoExpression b;
public AddExpression(LogoExpression a, LogoExpression b) {
this.a = a;
this.b = b;
}
public double evaluate(Environment env) {
return a.evaluate(env) + b.evaluate(env);
}
}
public class AssignmentCommand implements LogoCommand {
private String id;
private LogoExpression expr;
public AssignmentCommand(String id, LogoExpression expr) {
this.id = id;
this.expr = expr;
}
public void execute(Environment env) {
env.variables.put(id, expr.evaluate(env));
}
}
public interface CommandListener {
public void onPostCommand();
}
import org.antlr.v4.runtime.*;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.Stack;
import java.util.List;
import java.util.ArrayList;
import java.io.IOException;
import org.antlr.v4.runtime.tree.*;
public class CompilerLoogo extends LoogoBaseListener {
private Stack<LogoExpression> expressions = new Stack<LogoExpression>();
private Stack<LogoBlock> blocks = new Stack<LogoBlock>();
public void enterBlock(LoogoParser.BlockContext context) {
blocks.push(new LogoBlock());
}
public void exitMove(LoogoParser.MoveContext context) {
blocks.peek().append(new MoveCommand(expressions.pop()));
}
public void exitRepeat(LoogoParser.RepeatContext context) {
int nIterations = Integer.parseInt(context.INT().getText());
RepeatCommand loop = new RepeatCommand(nIterations, blocks.pop());
blocks.peek().append(loop);
}
public void exitIf(LoogoParser.IfContext context) {
LogoExpression condition = expressions.pop();
LogoBlock elseBlock = blocks.pop();
LogoBlock thenBlock = blocks.pop();
blocks.peek().append(new IfCommand(condition, thenBlock, elseBlock));
}
public void exitTurn(LoogoParser.TurnContext context) {
blocks.peek().append(new TurnCommand(expressions.pop()));
}
public void exitAssignment(LoogoParser.AssignmentContext context) {
blocks.peek().append(new AssignmentCommand(context.ID().getText(), expressions.pop()));
}
public void exitSlime(LoogoParser.SlimeContext context) {
blocks.peek().append(new SlimeCommand(context.ON() != null));
}
public void exitDouble(LoogoParser.DoubleContext context) {
expressions.push(new DoubleExpression(Double.parseDouble(context.DOUBLE().getText())));
}
public void exitInt(LoogoParser.IntContext context) {
expressions.push(new IntExpression(Integer.parseInt(context.INT().getText())));
}
public void exitID(LoogoParser.IDContext context) {
expressions.push(new IdExpression(context.ID().getText()));
}
public void exitAddSubtract(LoogoParser.AddSubtractContext context) {
LogoExpression b = expressions.pop();
LogoExpression a = expressions.pop();
if (context.ADDITIVE_OPERATOR().getText().equals("+")) {
expressions.push(new AddExpression(a, b));
} else {
expressions.push(new SubtractExpression(a, b));
}
}
public void exitMulDivMod(LoogoParser.MulDivModContext context) {
LogoExpression b = expressions.pop();
LogoExpression a = expressions.pop();
if (context.MULTIPLICATIVE_OPERATOR().getText().equals("*")) {
expressions.push(new MultiplyExpression(a, b));
} else if (context.MULTIPLICATIVE_OPERATOR().getText().equals("/")) {
expressions.push(new DivideExpression(a, b));
} else {
expressions.push(new ModExpression(a, b));
}
}
public void exitProgram(LoogoParser.ProgramContext context) {
try {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("logo.out"));
out.writeObject(blocks.pop());
out.close();
} catch (IOException e) {
}
}
public static void main(String[] args) throws IOException {
CompilerLoogo compiler = new CompilerLoogo();
ANTLRInputStream input = new ANTLRInputStream(System.in);
LoogoLexer lexer = new LoogoLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
LoogoParser parser = new LoogoParser(tokens);
ParseTree tree = parser.program();
ParseTreeWalker walker = new ParseTreeWalker();
walker.walk(compiler, tree);
}
}
public class DivideExpression implements LogoExpression {
private LogoExpression a;
private LogoExpression b;
public DivideExpression(LogoExpression a, LogoExpression b) {
this.a = a;
this.b = b;
}
public double evaluate(Environment env) {
return a.evaluate(env) / b.evaluate(env);
}
}
public class DoubleExpression implements LogoExpression {
private double value;
public DoubleExpression(double value) {
this.value = value;
}
public double evaluate(Environment env) {
return value;
}
}
import java.util.ArrayList;
import java.util.HashMap;
import java.awt.geom.Line2D;
public class Environment {
public double x = 0.0;
public double y = 0.0;
public double theta = 0.0;
public boolean isLeaking = true;
public HashMap<String, Double> variables = new HashMap<String, Double>();
public ArrayList<Line2D.Double> segments = new ArrayList<Line2D.Double>();
public CommandListener listener = null;
}
public class IdExpression implements LogoExpression {
private String id;
public IdExpression(String id) {
this.id = id;
}
public double evaluate(Environment env) {
Double value = env.variables.get(id);
if (value == null) {
System.err.println("No such variable " + id + "!");
System.exit(1);
}
return value;
}
}
public class IfCommand implements LogoCommand {
private LogoExpression condition;
private LogoBlock thenBlock;
private LogoBlock elseBlock;
public IfCommand(LogoExpression condition, LogoBlock thenBlock, LogoBlock elseBlock) {
this.condition = condition;
this.thenBlock = thenBlock;
this.elseBlock = elseBlock;
}
public void execute(Environment env) {
if (condition.evaluate(env) > 0.0) {
thenBlock.execute(env);
} else {
elseBlock.execute(env);
}
}
}
public class IntExpression implements LogoExpression {
private int value;
public IntExpression(int value) {
this.value = value;
}
public double evaluate(Environment env) {
return value;
}
}
import java.io.Serializable;
import java.util.ArrayList;
public class LogoBlock implements Serializable {
private ArrayList<LogoCommand> commands = new ArrayList<LogoCommand>();
public LogoBlock() {
commands = new ArrayList<LogoCommand>();
}
public void append(LogoCommand command) {
commands.add(command);
}
public void execute(Environment env) {
for (LogoCommand command : commands) {
command.execute(env);
if (env.listener != null) {
env.listener.onPostCommand();
}
}
}
}
import java.io.Serializable;
public interface LogoCommand extends Serializable {
public void execute(Environment env);
}
import java.io.Serializable;
public interface LogoExpression extends Serializable {
public double evaluate(Environment env);
}
import javax.swing.JFrame;
import java.io.ObjectInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
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;
import java.util.HashMap;
public class LVM implements CommandListener {
public static void main(String[] args) throws IOException, ClassNotFoundException {
new LVM(args[0], Integer.parseInt(args[1]));
}
private JFrame display;
private Environment env;
private int delay;
public LVM(String path, int delay) throws IOException, ClassNotFoundException {
this.delay = delay;
ObjectInputStream in = new ObjectInputStream(new FileInputStream(path));
LogoBlock main = (LogoBlock) in.readObject();
in.close();
display = new JFrame("Loogo");
Environment env = new Environment();
env.listener = this;
display.setContentPane(new LoogoDisplay(env));
display.setSize(1200, 700);
display.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
display.setVisible(true);
main.execute(env);
System.out.println("all done");
}
public void onPostCommand() {
pause(delay);
display.repaint();
}
private void pause(int delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
}
}
class LoogoDisplay extends JPanel {
private Line2D.Double arrowTop;
private Line2D.Double arrowBottom;
private Environment env;
public LoogoDisplay(Environment env) {
this.env = env;
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(850, 150);
synchronized (env.segments) {
for (Line2D.Double segment : env.segments) {
g2.draw(segment);
}
}
g2.translate(env.x, env.y);
g2.rotate(env.theta);
g2.draw(arrowTop);
g2.draw(arrowBottom);
}
}
}
public class ModExpression implements LogoExpression {
private LogoExpression a;
private LogoExpression b;
public ModExpression(LogoExpression a, LogoExpression b) {
this.a = a;
this.b = b;
}
public double evaluate(Environment env) {
return a.evaluate(env) % b.evaluate(env);
}
}
import java.awt.geom.Line2D;
public class MoveCommand implements LogoCommand {
private LogoExpression expr;
public MoveCommand(LogoExpression expr) {
this.expr = expr;
}
public void execute(Environment env) {
double distance = expr.evaluate(env);
double newX = env.x + Math.cos(env.theta) * distance;
double newY = env.y + Math.sin(env.theta) * distance;
if (env.isLeaking) {
synchronized (env.segments) {
env.segments.add(new Line2D.Double(env.x, env.y, newX, newY));
}
}
env.x = newX;
env.y = newY;
}
}
public class MultiplyExpression implements LogoExpression {
private LogoExpression a;
private LogoExpression b;
public MultiplyExpression(LogoExpression a, LogoExpression b) {
this.a = a;
this.b = b;
}
public double evaluate(Environment env) {
return a.evaluate(env) * b.evaluate(env);
}
}
public class RepeatCommand implements LogoCommand {
private int nIterations;
private LogoBlock block;
public RepeatCommand(int nIterations, LogoBlock block) {
this.nIterations = nIterations;
this.block = block;
}
public void execute(Environment env) {
for (int i = 0; i < nIterations; ++i) {
block.execute(env);
}
}
}
public class SlimeCommand implements LogoCommand {
private boolean enable;
public SlimeCommand(boolean enable) {
this.enable = enable;
}
public void execute(Environment env) {
env.isLeaking = enable;
}
}
public class SubtractExpression implements LogoExpression {
private LogoExpression a;
private LogoExpression b;
public SubtractExpression(LogoExpression a, LogoExpression b) {
this.a = a;
this.b = b;
}
public double evaluate(Environment env) {
return a.evaluate(env) - b.evaluate(env);
}
}
public class TurnCommand implements LogoCommand {
private LogoExpression expr;
public TurnCommand(LogoExpression expr) {
this.expr = expr;
}
public void execute(Environment env) {
env.theta -= Math.toRadians(expr.evaluate(env));
}
}
grammar Loogo;
program
: block EOF
;
block
: statement*
;
statement
: command NEWLINE
| NEWLINE
;
command
: MOVE expression # Move
| TURN expression # Turn
| SLIME (ON | OFF) # Slime
| ID IS expression # Assignment
| REPEAT INT NEWLINE block END # Repeat
| IF expression NEWLINE block ELSE NEWLINE block END # If
;
expression
: DOUBLE # Double
| INT # Int
| ID # ID
| '(' expression ')' # Parenthesized
| expression MULTIPLICATIVE_OPERATOR expression # MulDivMod
| expression ADDITIVE_OPERATOR expression # AddSubtract
;
MULTIPLICATIVE_OPERATOR : ('*'|'/'|'%');
ADDITIVE_OPERATOR : ('+'|'-');
IS : '<-';
INT : '-'? [0-9]+;
DOUBLE : '-'? [0-9]+ ('.' [0-9]+)?;
MOVE : 'move';
TURN : 'turn';
FUNCTION : 'function';
SLIME : 'slime';
ON : 'on';
OFF : 'off';
END : 'end';
REPEAT : 'repeat';
IF : 'if';
ELSE : 'else';
ID : [a-z]+;
NEWLINE : '\r'? '\n';
COMMENT : '#' .*? '\n' -> skip;
WHITESPACE : [ \t\r]+ -> skip;
a <- 10
repeat 50
move a
turn 90
move a
turn 90
move a
turn 90
move a
a <- a + 4
end
a <- -100
if a
turn 45
else
turn -45
end
move 100
d <- 100
a <- 90
repeat 4
move d
turn a
end
d <- 100
a <- 90
repeat 10
move d
turn a
move d
turn a
move d
turn a
move d
d <- d + 1
a <- a + 10
end
repeat 10
repeat 10
repeat 9
move 10
turn 40
end
slime off
move 30
slime on
end
turn 180
slime off
move 300
turn 90
move 30
turn 90
slime on
end
Comments
“what to do on undeclared variables?”
Don’t you mean “undefined functions”?
Definitions and declarations are different! Only variables and functions are the same thing.
Would it be possible to change the grammar to allow variables to be used in the REPEAT statement?
I invite you to do so and describe the changes here, but still ask you to submit something in todo.logo that doesn’t use your extension.
I’m just running in Eclipse, but I’m not getting any window to open with the drawing. No errors, but nothing good either. Anyone dealt with this yet?
You’ve got to Compile first. And then run with LVM. Are you doing both?
How do you run it from eclipse? Do you use logo folder as workspace? How do you build path when there is no project?
What you use as a workspace does not matter. How does a project come into existence?
I see. I was just running the compiler *face palm*. Now when I hardcode in the values in “new LVM(“todo.logo”, 1);” and run LVM, I get
Exception in thread “main” java.io.StreamCorruptedException: invalid stream header: 61203C2D
My issue is when hardcoding the path in LVM, eclipse is unable to locate my .logo file. My .logo file is located in my root and I have tried using local path names such as (“example.logo”). Also I looked in the example.logo properties and copied the path it provides into the code (Something like “/ProjectName/src/example.logo”).
Anyways, none have worked. Anyone else have a similar issue or know the solution?
The Desktop is always a nice place to drop files.
Eclipse runs your code from /path/to/workspace/your-project. Inside this directory you will see bin and src. If you use relative paths, the files must be in this project directory.
Also LVM doesn’t look at your logo source. It looks at your compiled code, which CompilerLoogo wrote out using an ObjectOutputStream.
Okay, I’m feeling like a derp today and don’t know how to run what we need to get this stuff to work. I have the jar file and the zip file put where they need to, and I am able to view all of the different java files in eclipse. What do I do from there?
Run CompilerLoogo on your Logo source file. It’ll produce a logo.out in your project directory. Run LVM on logo.out. See to TODO comments in both these files to hardcode paths.
I second these questions:
How do you run it from eclipse? Do you use logo folder as workspace? How do you build path when there is no project?
I have been trying to run the makefile, but I am getting 100+ errors all saying “package org.antlr.v4.runtime does not exist”. Where am I supposed to put the antlr jar? Or is there some way to use eclipse to do this as I am currently just running from the thingies.
The ANTLR jar file needs to be on your classpath. That can be set through an environment variable or via the java utility: java -cp /path/to/antlr.jar:. MyJavaClass.
But as I mention above, using Eclipse might be easier since LVM will try to create a window.
I’m getting editor does not contain a main type when I hardcode in the path for the logo file in CompilerLoogo.java. Any suggestions?
I see this error when I try to run something that doesn’t have a main with String[] parameter. Did you remove the String[] parameter on main?
Can you give a explicit example of how I can Compile and RUN src/square.logo ??
I seem to be able to compile it but i cant run it
I tried lots of ways .. I dunno what im doing really.
-bash-4.1$ java LVM logo.out
Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 1
at LVM.main(LVM.java:20)
-bash-4.1$ java LVM
Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 0
at LVM.main(LVM.java:20)
-bash-4.1$ java LVM < logo.out
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at LVM.main(LVM.java:20)
..i dunno.????
-bash-4.1$ java CompilerLoogo src/square.logo
-bash-4.1$ java LVM logo.out 5
Exception in thread “main” java.awt.HeadlessException:
No X11 DISPLAY variable was set, but this program performed an operation which requires it.
at java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:207)
at java.awt.Window.(Window.java:535)
at java.awt.Frame.(Frame.java:420)
at javax.swing.JFrame.(JFrame.java:218)
at LVM.(LVM.java:34)
at LVM.main(LVM.java:20)