Я изучаю Parser Combinators в scala и вижу различные способы анализа. В основном я вижу три разных типа парсеров: ieRegexpParsers, StandardTokenParsers и JavaTokenParsers. Я новичок в синтаксическом анализе и не понимаю, как мы будем выбирать подходящий Parser в соответствии с нашим требованием. Можете ли вы объяснить, как работают эти различные парсеры и когда их использовать.
Разница между RegexpParsers, StandardTokenParsers и JavaTokenParsers в scala
Ответ 1
RegexpParsers
позволяют использовать значения RE (обычно в форме "re pattern".r
, но равно как и любой другой экземпляр Regex). Нет предопределенных лексических произведений (токенов).
JavaTokenParsers
определяет лексические произведения для токенов Java: decimalNumber
, floatingPointNumber
, stringLiteral
, wholeNumber
, ident
(идентификатор).
StandardTokenParsers
определяет лексические произведения "... для простого, Scala -подобного языка. Он анализирует ключевые слова и идентификаторы, числовые литералы (целые числа), строки и разделители." Его составляющие фактически определены в StdLexical
.
Ответ 2
Существует несколько различных признаков парсера и базовых классов для разных целей.
Основной признак scala.util.parsing.combinator.Parsers
. У этого есть большинство главных комбинаторов, таких как opt
, rep
, elem
, accept
и т.д. Определенно просматривайте документацию для этого, так как это большая часть того, что вам нужно знать. Фактический класс Parser
определяется здесь как внутренний класс, и это также важно знать.
Другим важным признаком является scala.util.parsing.combinator.lexical.Scanners
. Это базовая черта для парсеров, которые читают поток символов и создают поток токенов (также известных как лексеры). Чтобы реализовать этот признак, вам нужно реализовать парсер whitespace
, который читает пробельные символы, комментарии и т.д. Вам также необходимо реализовать метод token
, который читает следующий токен. Токены могут быть любыми, что вы хотите, но они должны быть подклассом Scanners.Token
. Lexical
extends Scanners
и StdLexical
extends Lexical
. Первый содержит некоторые полезные базовые операции (например, digit
, letter
), в то время как последний фактически определяет и лексет общие токены (например, числовые литералы, идентификаторы, строки, зарезервированные слова). Вам просто нужно определить delimiters
и reserved
, и вы получите что-то полезное для большинства языков. Определения токенов находятся в scala.util.parsing.combinator.token.StdTokens
.
Как только у вас есть лексер, вы можете определить синтаксический анализатор, который читает поток токенов (созданных лексером) и генерирует абстрактное синтаксическое дерево. Разделение lexer и parser - хорошая идея, так как вам не нужно беспокоиться о пробелах или комментариях или других осложнениях в вашем синтаксисе. Если вы используете StdLexical
, вы можете использовать scala.util.parsing.combinator.syntax.StdTokenPasers
, в котором встроены парсеры для перевода токенов в значения (например, StringLit
в String
). Я не уверен, в чем разница с StandardTokenParsers
. Если вы определяете свои собственные классы токенов, вы просто должны использовать Parsers
для простоты.
Вы специально задали вопрос о RegexParsers
и JavaTokenParsers
. RegexParsers
- это признак, который расширяет Parsers
одним дополнительным комбинатором: regex
, что делает именно то, что вы ожидаете. Смешайте в RegexParsers
с вашим лексером, если вы хотите использовать регулярные выражения для соответствия токенам. JavaTokenParsers
предоставляет некоторые синтаксические анализаторы, которые лекс-маркеры из синтаксиса Java (например, идентификаторы, целые числа), но без токенового багажа Lexical
или StdLexical
.
Подводя итог, вы, вероятно, захотите два синтаксических анализатора: один, который читает символы и выдает токены, а другой - принимает токены и выдает АСТ. Используйте что-то, основанное на Lexical
или StdLexical
для первого. Используйте что-то на основе Parsers
или StdTokenParsers
для второго в зависимости от того, используете ли вы StdLexical
.