Этот пост был впервые опубликован в моем личном блоге
Этот пост посвящен созданию очень простого приложения, реализующего подмножество языка программирования Logo вместе с пользовательским интерфейсом, который визуализирует программы с логотипами, введенные пользователями. Использовались технологии Antlr для создания синтаксического анализатора для подмножества правил логотипа, а также JavaFX для создания пользовательского интерфейса, позволяющего пользователям вводить программы с логотипами и предоставлять визуализацию этих программ.
Язык Логотипа
Логотип – это образовательный язык, предназначенный в основном для детей младшего возраста. Это язык, с которым я лично общался еще в младших классах средней школы. Он эффективно предоставляет грамматику правил движения (т.е. вперед, назад, вправо 90 o ) наряду с некоторым потоком управления (т.е. повторением), позволяющим пользователю создавать набор команд. Эти команды в сочетании с программным обеспечением для визуализации могут рисовать векторную графику или в сочетании с роботизированными устройствами могут перемещать робота.
Раньше это был хороший язык для обучения детей программированию, однако в настоящее время существуют более продвинутые языки, такие как Scratch . Однако я выбрал логотип из-за его простой грамматики, поскольку моей главной целью было освежить некоторые знания Antlr, а не создать реальное приложение.
Антлр
Antlr – отличный инструмент для синтаксического анализа структурированного текста (т.Е. Представьте себе регулярные выражения на стероидах). Это инструмент, который можно использовать для анализа грамматических правил и создания приложений на основе его функций. Распространенный подход заключается в создании пользовательских DSL с использованием Antlr. Я не буду подробно описывать Antlr, так как на официальном сайте много хорошей документации, а также я не являюсь экспертом по Antlr. Кроме того, книга Definitive-ANTLR-4-Reference , написанная ее создателем, является хорошим ресурсом.
В этом приложении я фактически определил свой собственный набор правил логотипа, используя грамматику Antlr, и я полагался на возможности анализа Antlr для оценки программ с логотипами и предоставления мне обратных вызовов встреченных команд логотипа.
Большинство людей были бы знакомы с JavaFX. Эффективно ли следующая попытка после Java Swing создать оборудование для создания (современного?) Пользовательские интерфейсы, использующие Java. Мои навыки работы с пользовательским интерфейсом довольно плохи, поэтому я хотел, чтобы что-то заставило меня создать пользовательский интерфейс. Я выбрал JavaFX вместо чего-то более стандартного, такого как фреймворк HTML5 + JS, так как в прошлом я немного занимался Java Swing и хотел попробовать JavaFX в основном из любопытства.
Несмотря на то, что JavaFX очень богат функциями, а модель программирования напоминает часть Java Swing и часть C # WPF (с которой я был немного знаком еще в 2011 году) На меня это не произвело впечатления. Это казалось громоздким в том смысле, что я думал, что вся модель программирования мешает мне, может быть, потому, что я с ней не знаком, а также, может быть, потому, что это просто не очень хорошая модель программирования, оправдывающая отсутствие широкого распространения.
Определение Грамматики Antlr
Как упоминалось выше, Antlr нуждается в определении грамматики, состоящем из правил синтаксического анализа и лексера. Правила лексера используются для извлечения токенов из текста, а правила синтаксического анализа – для извлечения значимых утверждений.
Я решил использовать только небольшое подмножество функций логотипа, поэтому будет поддерживаться следующее:
- Движение вперед
- Движение назад
- Поворот влево/вправо
- Возможность использования функции пера вверх/вниз, что означает, что при движении пера вверх рисунок не должен появляться, даже если “черепаха” перемещается
Эти правила, переведенные в грамматику Antlr, выглядят как Logo.g4
Кто-то может заметить, что приведенная выше грамматика просто определяет ключевые работы (т.е. вперед
, назад
, вправо
и т. Д.) В качестве правил лексера (он же токены) и выражения программиста (т.е. вперед 50
) в качестве правил синтаксического анализа. На прикладном уровне Antlr генерирует заглушки прослушивателей для правил синтаксического анализа, которые могут быть реализованы, и пользователь получает обратные вызовы по этим правилам. Затем пользователи могут написать свою логику поверх этого.
Легко понять, насколько полезен Antlr, выполняющий всю тяжелую работу за пользователя. Кому-то просто нужно расширить уже сгенерированный прослушиватель, который распространяет события на код пользователя.
Подключение обратных вызовов анализатора
Поскольку нас в основном интересуют правила грамматики, определяющие действия с логотипом, мы можем реализовать только эти обратные вызовы. Класс, который имеет дело с обратным вызовом, может быть сделан независимым от пользовательского интерфейса и выступать в качестве драйвера для базовой реализации. Например, у нас могли бы быть различные реализации того, как визуализировать программу логотипа:
- Пользовательский ИНТЕРФЕЙС JavaFX
- Пользовательский ИНТЕРФЕЙС Swing
- Простая стандартная программа
Приведенная ниже реализация касается этого
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(); } }
Художник Черепаха
может быть чем угодно, даже программой, которая записывает команды программы и утверждает их, как шпион JUnit.
Пользовательский интерфейс JavaFX
В нашем случае Turtle Painter
– это класс, который преобразует команды в конструкции JavaFX и делегирует потоку пользовательского интерфейса для рисования этих конструкций. Например, реализация команды вперед
выглядит следующим образом:
@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); }); }
Эффективное рисование линии в пользовательском интерфейсе.
Простую программу с логотипом, которая рисует “ПРИВЕТ, МИР” на экране, можно найти здесь . Результат для этого будет выглядеть так:
Исходный Код
Исходный код проверен в github .
Можно сделать довольно много улучшений, как на стороне пользовательского интерфейса, так и на уровне языка:
- Реализовать управление потоком логотипов (т.е. циклы)
- Сделайте черепаху настоящим изображением черепахи, также показав направление ее движения
- и т.д…
Не стесняйтесь раскошеливаться или отправлять PR для любого дополнения:)
Оригинал: “https://dev.to/nikos_katsanos/building-a-logo-turtle-app-with-antlr-and-javafx-ben”