Написать собственный интерпретатор синтаксиса в java?

Я собираюсь начать программировать демонстрационную программу для лекции, которую собираюсь прочесть. Я хочу, чтобы каждый ученик в классе загрузил это приложение, а затем смог создавать экземпляры объектов (и их графические представления) в интерактивном режиме через командную строку. Я решил писать на java не потому, что это язык, с которым я лучше всего знаком, а потому, что в нем есть простые графические классы, и я могу быть уверен, что jar будет работать на их компьютерах.

Интро окончено. Теперь вопрос:

Каков хороший способ реализовать некоторый пользовательский синтаксис командной строки для этой программы? Я хочу использовать простой произвольный синтаксис, например:

CREATE Monster Bob;    
Bob.jump();   
LS Bob //to list Bob's methods or something.   
LS CREATE //to list all the classes    

Сначала я расскажу о том, что первое пришло мне в голову, когда я подумал об этой проблеме.

Я могу представить, что у меня мог бы быть набор карт в виде дерева. Я мог разобрать каждое ключевое слово как ключ к следующей карте. Таким образом, «СОЗДАЙТЕ монстра Боба» можно оценить как

1) Найдите карту ключевых слов для ключа «СОЗДАТЬ». Возвращает значение, являющееся ссылкой на карту классов. 2) Поиск карты классов по ключу "Монстр". Возвращает значение, которое является фабричным классом, реализующим некоторый интерфейс Leaf, который позволяет мне узнать, что это значение листа (я проверю, используя instanceof).
3) Возможно, интерфейс Leaf будет содержать метод с именем execute(), который будет делать все, что захочет. В этом случае он создал бы объект Monster, добавив этот объект на карту под названием Objects с именем Bob. (Эта история с Листом звучит уродливо, но ее можно исправить.)

Прохладный. Но вот это утверждение для меня немного сложнее: Bob.jump();

1) Найдите на карте объектов «Боба». Возвратите некоторый объект, реализующий интерфейс, с помощью метода вроде "evaluate(String s)" и передайте ему строку "jump()"
2) Боб ищет некоторую внутреннюю карту методов для "jump()", затем... ? В С++ ключ был бы указателем на функцию-член Monster.jump(), которая будет выполняться. Но я не верю, что в java нет такой вещи, как указатель на функцию. Я читал, что для этого можно использовать анонимный класс, хотя я не пробовал. Похоже, это сработает.

Итак, это сработает, но есть ли более элегантный способ сделать это? Я никогда раньше не писал никаких интерпретаторов. Я хотел бы сделать это красиво и научиться чему-то в процессе, если у кого-то есть несколько советов. Это кажется потенциально подверженным ошибкам способом делать что-то, если я не очень структурирован, особенно когда Боб и любой другой объект начинают анализировать свои собственные инструкции и использовать анонимные функции. Кроме того, похоже, что каждому классу помимо обычного кода потребуется готовый к выполнению интерфейс.

Я также не очень хорошо знаю Java, поэтому, если есть места, где я могу столкнуться с кирпичной стеной, то я тоже хотел бы знать.

Спасибо за помощь заранее.


person user487100    schedule 24.09.2011    source источник
comment
@pst: я считаю, что ANTLR здесь излишество.   -  person Andrey Agibalov    schedule 24.09.2011


Ответы (3)


На самом деле я бы предложил использовать Python — если нет действительно веских причин не делать этого.

Это потому что:

  1. В Python есть очень хорошая IDE/REPL под названием IDLE. Я не могу сказать достаточно об использовании хорошего Read-Eval-Print-Loop : короткий цикл обратной связи очень удобен для обучения/игры. Предприимчивые студенты могут даже прыгнуть прямо сейчас!
  2. Поддержка графики является кросс-платформенной и хорошо поддерживается через TkInter.
  3. Я считаю, что это лучший язык для начинающих и/или непрограммистов, чем Java. (На самом деле Python не мой любимый язык, но он очень удобен для начинающих и, опять же, имеет очень хорошую IDE/REPL.)
  4. Это гораздо меньше работы для вас ;-)

Вот как может выглядеть код Python для демонстрации:

Bob = BigMonster()
Bob.jump()
dir(Bob)
dir(Monters)

Поскольку все это всего лишь обычный синтаксис Python, парсинга не требуется — просто создайте несколько классов, возможно, реализуйте протокол __dir__, и все готово к работе. Если требуется интеграция с Java, есть также Jython, хотя я никогда не пробовал это с IDLE (и не знаю, поддерживается ли он как таковой).

Удачного кодирования.

SmallTalk на основе изображений, например Sqeak гораздо более интерактивен, чем Python, поскольку код является частью постоянной рабочей среды. Однако требуется некоторое время, чтобы найти хороший образ — Squeak едва ли является лучшей реализацией, но он бесплатный — и изучить конкретную среду SmallTalk. Таким образом, хотя интеграция может в конечном итоге принести большие выплаты, она требует большей акклиматизации :)


Но, увы, чтобы заняться простым парсером на Java, будут интересны вот эти:

  1. лексер, который превращает вводимый текст в набор токенов, и;
  2. And a recursive descent parser (this is a really easy parsing approach) which either
    1. Builds an AST (Abstract Syntax Tree) which can be walked (read: "run") later, or;
    2. Или "делает что-то" прямо сейчас (немедленная оценка)

Простой синтаксический анализатор рекурсивного спуска – это ускоренный курс Java, посвященный описанным выше концепциям. Вот некоторые код для анализатора рекурсивного спуска для "синтаксиса нейтрино", что бы это ни было - посмотрите комментарии и узнайте, насколько хорошо анализатор рекурсивного спуска может соответствовать грамматике EBNF.

Теперь это «просто» вопрос определения семантических правил этого псевдо/мини-языка и его реализации ;-)


Изучение подхода семантики/Java немного больше (части - это просто упрощение/переформулировка исходного поста):

CREATE Monster Bob

Создаст новый MonsterObject. Некоторые подходы могут быть:

  1. Создайте объект с отражением или;
  2. карта классов Factory (из String -> FactoryObject), о которой говорилось, или;
  3. простая статическая ветвь if-else.

Результат будет сохранен в «переменной хэш», которая отображает Name -> MonsterObject.

Bob.jump()

Разберите это на [object Bob] [method jump] [p1], [p2], ..., [pn], найдите объект в «переменной хеше», а затем:

  1. Использовать отражение для вызова метода или;
  2. иметь карту (полученную с помощью метода MonsterObject) Name -> MethodEvaluatorObject (например, имеет метод eval(Object ... params)) или;
  3. вызовите метод формы eval(String action, String[] ... parameters) и заставьте его использовать ветвь if-else для «дела» (обратите внимание, что параметры, если они есть, уже разделены во время синтаксического анализа).

LS Bob и LS Monster во многом зависят от того, как реализованы два предыдущих.

Хотя в Java нет «указателей на функции», их можно эмулировать с помощью объектов с заданным интерфейсом (то есть сами объекты функционируют как указатели). Функциональная Java имеет F/F2/.../F8, чтобы попытаться справиться с этим единообразно с дженериками. Однако в Java обычно существует отдельный одноразовый интерфейс (или класс), созданный наподобие Runnable с одним методом "действия", который изменен, чтобы принимать соответствующие параметры и возвращать соответствующий результат (например, MethodEvaluatorObjects или FactoryObjects).

Если есть какие-либо конкретные вопросы по одной из тем (рефлексия, рекурсивный спуск, анонимные типы, [эмулированные] замыкания и т. д.), не стесняйтесь задавать другой SO вопрос с конкретным акцентом. (И, как всегда, должное усердие в исследованиях окупается ;-)

person Community    schedule 24.09.2011
comment
Это был взрыв ответа. Спасибо за все усилия. Теперь щелкайте ссылки и читайте. - person user487100; 24.09.2011
comment
Вау отражение, это как раз то, что мне нужно. Не имел представления. - person user487100; 24.09.2011

Если вы действительно не собираетесь создавать новый язык программирования, вы можете просто разделить команды на части (используя пробел в качестве разделителя), а затем выполнить поиск первой части: CREATE Monster Bob; => create, monster, bob:

String operation = parts[0];
if(operation.equals(`create`)) {
  String type = parts[1];
  String name = parts[2];
  // your logic here
} else if(operation.equals(`...`)) {
  ...
}
person Andrey Agibalov    schedule 24.09.2011
comment
Мне это нравится на самом деле. Назовем ли мы это подходом MUD? Ха. Я думаю, именно так MUD обычно реализуют свои команды, верно? - person Anonymous; 24.09.2011

Рассматривали ли вы возможность использования генератора парсеров, такого как ANTLR? Он может создавать синтаксические анализаторы для многих языков и выводить синтаксический анализатор на различных языках, включая Java. Это может значительно ускорить вашу задачу, а программное обеспечение бесплатное (хотя книги продаются, но ведь ваше время чего-то стоит, верно?).

http://en.wikipedia.org/wiki/ANTLR

С другой стороны, вы могли бы, вероятно, создать свой собственный парсер для простого языка, о котором говорит PST, но я бы не стал слишком усложнять его. Просто сделайте себе функцию, разбивающую файл на строковые токены (лексер), и еще одну, которая запрашивает токен за раз и определяет, что с ним делать. Если ваш язык прост, этого может быть достаточно.

person Anonymous    schedule 24.09.2011