loading...
Cover image for Building A Logo Turtle App With Antlr And JavaFX

Building A Logo Turtle App With Antlr And JavaFX

nikos_katsanos profile image Nikos Katsanos ・5 min read

This post was firstly published in my personal blog

This post is about building a very simple application, implementing a subset of the Logo Programming Language along with a UI that visualizes the Logo programms entered by users.
The technologies used were Antlr for creating a parser for a subset of the Logo rules and also JavaFX for creating a UI allowing users to enter Logo programs and providing a drawing visualization of those programs.

The Logo Language

Logo is an educational language, mainly targeted to younger ages. It is a language that I personally had some interactions with back in the junior high school days. It effectively provides a grammar of movement rules (i.e. forward, back, right 90o) along with some control flow (i.e. repeat) allowing the user to produce a set of commands. Those commands, coupled with a visualization software can draw vector graphics, or coupled with robotic devices can move the robot around.

It used to be a nice language for educating kids on programming, nowadays however there are better more advanced ones like Scratch. I picked Logo however, for its simple grammar as my main goal was to refresh some Antlr knowledge, rather than build a real application.

Antlr

Antlr is a great tool for parsing structured text (i.e. imagine regular expressions on steroids). It is a tool that can be used to parse grammatical rules and build applications on top of its features. A common approach is to build custom DSLs using Antlr. I will not describe Antlr in length as the official website has lots of good documentation and also I am no expert on Antlr. Additionally, the book Definitive-ANTLR-4-Reference written by its creator is a good resource.

In this application, effectively I have defined my own set of Logo rules using Antlr's grammar and I relied on Antlr's parsing capabilities to evaluate the Logo programs and give me callbacks of the Logo commands encountered.

JavaFX

Most people would be familiar with JavaFX. Effectively is the next attempt after Java Swing on creating the machinery for building (modern?) UIs using Java. My UI skills are pretty bad, hence I wanted something to force me build a UI. I picked JavaFX, instead of something more standard like HTML5+JS Framework, as I had done some Java Swing in the past and wanted to try JavaFX out of curiosity mainly.

Even though JavaFX is very feature rich and the programming model resembles part of Java Swing and part of C# WPF (which i was a bit familiar back in 2011) I was not impressed by it. It felt cumbersome in ways that I thought the whole programming model was getting into my way, maybe because I am not familiar with it, also maybe because is just not a great programming model justifying the lack of widespread adoption.

Defining The Antlr Grammar

As mentioned above, Antlr needs a grammar definition, which consist of parser and lexer rules. The lexer rules are used to extract tokens out of the text, and the parser rules for extracting meaningful statements.

I decided to only go with a small subset of the Logo features, so the below would be supported:

  • Moving forward
  • Moving backwards
  • Turning left/right
  • Allowing for pen up/down functionality, meaning if pen is up no drawing should appear even if the 'turtle' moves around

Those rules, translated into an Antlr grammar look like Logo.g4

Someone can notice that the above grammar just defines the keyworks (i.e. forward, back, right etc) as lexer rules (a.k.a tokens) and programmar expressions (i.e. forward 50) as parser rules. In the application layer, Antlr generates stubs of listeners for the parser rules, which can be implemented and the user gets callbacks on those rules. Then users can write their logic on top of that.

It is easy to see how helpful Antlr is, doing all the heavylifting for the user. Someone just need to extend the already generated listener, which propagates the events to the user's code.

Wiring Parser Callbacks

As we are mainly interested in the grammar rules that define Logo actions, we can only implement those callbacks. The class that deals with the callback can be made UI agnostic and act as a driver to the underlying implementation. For example we could have various implementations of how to visualize the Logo program:

  • A JavaFX UI
  • A Swing UI
  • A plain standard out program

The below implementation deals with that

public class LogoDriver extends LogoBaseListener {

    private final TurtlePainter painter;

    public LogoDriver(TurtlePainter painter) {
        this.painter = painter;
    }

    @Override
    public void exitForward(final ForwardContext ctx) {
        this.painter.forward(Integer.parseInt(ctx.getChild(1).getText()));
    }

    @Override
    public void exitBack(final BackContext ctx) {
        this.painter.back(Integer.parseInt(ctx.getChild(1).getText()));
    }

    @Override
    public void exitRight(final RightContext ctx) {
        this.painter.right(Integer.parseInt(ctx.getChild(1).getText()));
    }

    @Override
    public void exitLeft(final LeftContext ctx) {
        this.painter.left(Integer.parseInt(ctx.getChild(1).getText()));
    }

    @Override
    public void exitSet(final SetContext ctx) {
        final String[] point = ctx.POINT().getText().split(",");
        final int x = Integer.parseInt(point[0]);
        final int y = Integer.parseInt(point[1]);
        this.painter.set(x, y);
    }

    @Override
    public void exitPenUp(final PenUpContext ctx) {
        this.painter.penUp();
    }

    @Override
    public void exitPenDown(final PenDownContext ctx) {
        this.painter.penDown();
    }

    @Override
    public void exitClearscreen(ClearscreenContext ctx) {
        this.painter.cls();
    }

    @Override
    public void exitResetAngle(ResetAngleContext ctx) {
        this.painter.resetAngle();
    }

    @Override
    public void exitProg(ProgContext ctx) {
        this.painter.finish();
    }
}

The TurtlePainter can be anything, even a program that records the program's commands and asserts them, like a JUnit spy.

The JavaFX UI

In our case, the TurtlePainter is a class that translates the commands into JavaFX constructs and delegates to the UI thread to draw those constrcuts. For example the implementation for the forward command looks like:

    @Override
    public void forward(int points) {
        JavaFXThreadHelper.runOrDefer(() -> {
            final double radian = this.toRadian(this.direction);
            final double x = this.turtle.getCenterX() + points * Math.cos(radian);
            final double y = this.turtle.getCenterY() - points * Math.sin(radian);

            this.validateBounds(x, y);

            this.moveTurtle(x, y);
        });
    }

    private void moveTurtle(final double x, final double y) {
        JavaFXThreadHelper.runOrDefer(() -> {

            final Path path = new Path();
            path.getElements().add(new MoveTo(this.turtle.getCenterX(), this.turtle.getCenterY()));
            path.getElements().add(new LineTo(x, y));

            final PathTransition pathTransition = new PathTransition();
            pathTransition.setDuration(Duration.millis(this.animationDurationMs));
            pathTransition.setPath(path);
            pathTransition.setNode(this.turtle);

            if (this.isPenDown) {
                final Line line = new Line(this.turtle.getCenterX(), this.turtle.getCenterY(), x, y);
                pathTransition.setOnFinished(onFinished -> this.canvas.getChildren().add(line));
            }

            animation.getChildren().add(pathTransition);

            this.paintTurtle(x, y);
        });
    }

Effectively drawing a line to the UI.

A simple Logo program that draws "HELLO WORLD" in the screen can be found here. The result for this one would look like:

Logo Program

The Source Code

Source code is checked into github.

Quite a few enhancements can be made, both on the UI side, but also at the language level side:

  • Implement Logo flow control (i.e. loops)
  • Make the turtle, an actual turtle image, by also showing its facing direction
  • etc...

Feel free to fork or send a PR for any addition :)

Posted on Jun 6 by:

Discussion

markdown guide