Иерархия классов токенов и проверка их типа в анализаторе

Я пытаюсь написать многоразовую библиотеку синтаксического анализа (для развлечения).

Я написал лексер ], который генерирует последовательность токенов . Токен - это базовый класс для иерархии подклассов, каждый из которых представляет разные типы токенов со своими собственными специфическими свойствами. Например, есть подкласс LiteralNumber (полученный из Literal и через него из Token ), который имеет свои собственные специальные методы для работы с числовым значением его лексемы. Методы работы с лексемами в целом (получение их представления в виде строки символов, положения в источнике и т. Д.) Находятся в базовом классе Token , потому что они являются общими для всех типов токенов.Пользователи этой иерархии классов могут создавать свои собственные классы для определенных типов токенов, которые я не предсказал.

Теперь у меня есть класс Parser , который читает поток токенов и пытается сопоставить их с определением своего синтаксиса. Например, у него есть метод matchExpression , который, в свою очередь, вызывает matchTerm , а этот вызывает matchFactor , который должен проверить, является ли текущий токен Литерал или Имя (оба являются производными от базового класса Token ).

Проблема:
Теперь мне нужно проверить, каков тип текущего токена в потоке и соответствует ли он синтаксису или нет. Если нет, выбросить исключение EParseError . Если да, действуйте соответствующим образом, чтобы получить его значение в выражении, сгенерировать машинный код или сделать все, что нужно синтаксическому анализатору при совпадении синтаксиса.

Но я много читал о том, что проверка типа во время выполнения и принятие решения на его основе - это плохой дизайн ™ , и его следует реорганизовать как полиморфные виртуальные методы. Конечно, я согласен с этим.

Итак, моей первой попыткой было поместить некий виртуальный метод типа в базовый класс Token , который будет переопределен производными классами и вернет некоторое перечисление с идентификатором типа.

Но я уже вижу недостатки этого подхода: пользователи, производные от Token своих собственных классов токенов, не смогут добавлять дополнительные идентификаторы в перечисление , которое находится в исходник библиотеки! : - / И цель состояла в том, чтобы позволить им расширить иерархию для новых типов токенов, когда они им понадобятся.

Я также мог бы вернуть некоторую строку из метода type , что позволило бы легко определять новые типы.

Но все же в обоих этих случаях информация о базовых типах теряется (из метода type возвращается только листовой тип), и класс Parser не сможет обнаруживать производный тип Literal , когда кто-то будет производным от него, и переопределить тип , чтобы вернуть что-то иное, кроме "Literal" .

И, конечно же, класс Parser , который также предназначен для расширения пользователями (то есть для написания собственных анализаторов, распознавания собственных токенов и синтаксиса), не знает, какие потомки Класс Token будет в будущем.

Многие часто задаваемые вопросы и книги по дизайну рекомендуют в этом сценарии взять поведение из кода, который должен определять тип, и поместить его в виртуальный метод, переопределенный в производных классах. Но я не могу себе представить, как я мог передать такое поведение потомкам Token , потому что это не их дела, например, для генерации машинного кода или оценки выражений.Более того, есть части синтаксиса, которые должны соответствовать более чем одному токену, поэтому нет ни одного конкретного токена, в который я мог бы поместить такое поведение. Это, скорее, ответственность конкретных синтаксических правил , которые могут соответствовать более чем одному токену в качестве их терминальных символов.

Есть идеи, как улучшить эту конструкцию?

10
задан Diego Sevilla 9 September 2011 в 21:27
поделиться