Точно так же, как JFlex генерирует лексеры, Yacc генерирует синтаксические анализаторы, но в чем разница? Лексер может распознавать слова, а синтаксический анализатор может распознавать целые предложения или, более формально, использовать лексеры для работы с обычными грамматиками и синтаксическими анализаторами для работы с контекстно-свободными грамматиками .
Именно по этой причине они часто используются вместе — сначала вы используете лексер для распознавания слов и передаете эти слова анализатору, который способен определить, образуют ли слова правильное предложение.
Теперь, когда Java является Java, существует множество генераторов синтаксических анализаторов: Antlr распространен повсеместно, но есть также COCO/R , JavaCC, СаблеЦК , Чашка , Byacc/J и, вероятно, многие другие. Даже почтенный Зубр способен генерировать Java-парсеры. Некоторым анализаторам, таким как Antlr, COCO/R и JavaCC, даже не нужен отдельный лексер для подачи слов — они могут генерировать свои собственные!
Так почему же Джек? Почему нет?
Итак, как JFlex и Джек работают вместе:
JFlex считывает входные данные в виде потока символов и выдает токен для Джека, когда Джек запрашивает его. Токен – это строка со значением . Например, +, true и 3.14 — это все токены – некоторым из них на самом деле не нужно значение, кроме их типа: true – логический литерал, но некоторые из них нужны: 3.14 – целочисленный литерал со значением 3.14.
Как и в случае с JFlex, файл Jacc состоит из трех отдельных разделов:
directives section %% rules section %% additional code section
Jacc создает список всех ожидаемых токенов в отдельном файле. Вы указываете файл и список маркеров следующим образом:
%interface ParserTokens %token X NL
Давайте посмотрим на сгенерированный файл:
// Output created by jacc on Mon Mar 11 09:54:05 CET 2019
interface ParserTokens {
int ENDINPUT = 0;
int NL = 1;
int X = 2;
int error = 3;
}
Это интерфейс, который одновременно выполняет функцию перечисления. Помимо двух запрошенных нами типов токенов, NL и X, создаются еще два: один для завершения ввода и один для ошибок. Вернувшись в файл JFlex, вы “реализуете” этот “интерфейс”.:
%class Lexer %implements ParserTokens
просто для того, чтобы наш лексер мог видеть константы ENDINPUT, NL, X И error. Есть еще несколько вещей, которых ожидает Жак:
- Функция, которая возвращает целочисленные значения, представляющие типы токенов (0, 1 или 3 в нашем примере). Называть эту функцию yylex – традиция, так что давайте сделаем это:
%function yylex %int
- Еще три функции: getToken для получения текущего кода токена, nextToken для чтения следующего кода токена и getSemantic для получения текущего значения токена:
%{
private int token;
private String semantic;
public int getToken()
{
return token;
}
public String getSemantic()
{
return semantic;
}
public int nextToken()
{
try
{
token = yylex();
}
catch (java.io.IOException e)
{
System.out.println(
"IO exception occured:\n" + e);
}
return token;
}
%}
Вы можете заметить, что мы решили сделать семантическое значение токена строкой, поэтому нам также нужно указать это в анализаторе:
%semantic String
Для нашего примера мы просто попросим лексер распознать следующие слова: слово, Слово, слово, слово, … , слово и СЛОВО и новые строки. Мы будем игнорировать пробелы.
x = [wW][oO][rR][dD] nl = \n | \r | \r\n space = [\t] %%
Когда лексер найдет слово, он вернет токен X (т.Е. 2 из интерфейса ParserTokens) со значением word, Word,… — это то, что делает();. Когда он встретит новую строку, он вернет КОНЕЧНЫЙ ВВОД (т.Е. 0):
{x} { semantic = yytext(); return X; }
{space} { /\* Ignore space \*/ }
{nl} { return ENDINPUT; }
[^] { System.out.println("Error?"); }
Это оно. Сейчас синтаксический анализатор “грамматика” во всей своей красе:
sentence : X { System.out.println("X found: " + $1); }
| sentence X { System.out.println("X found: " + $2); }
;
Грамматика леворекурсивная , что позволяет нам иметь одно или несколько X слов в предложении. $1 и $ 2 будут содержать семантическое значение X, переданное туда лексером, и мы просто распечатаем его.
В основном методе мы создаем экземпляры лексера и синтаксического анализатора и начинаем синтаксический анализ. Здесь следует отметить одну вещь: мы должны “загрунтовать” лексер с помощью
parser.lexer.nextToken();
до того, как анализатор сможет его использовать. Для справки, вот полный источник лексера
import java.io.\*;
%%
%class Lexer
%implements ParserTokens
%function yylex
%int
%{
private int token;
private String semantic;
public int getToken()
{
return token;
}
public String getSemantic()
{
return semantic;
}
public int nextToken()
{
try
{
token = yylex();
}
catch (java.io.IOException e)
{
System.out.println(
"IO exception occured:\n" + e);
}
return token;
}
%}
x = [wW][oO][rR][dD]
nl = \n | \r | \r\n
space = [\t]
%%
{x} { semantic = yytext(); return X; }
{space} { /\* Ignore space \*/ }
{nl} { return ENDINPUT; }
[^] { System.out.println("Error?"); }
и что из синтаксического анализатора:
%{
import java.io.\*;
%}
%class Parser
%interface ParserTokens
%semantic String
%token X NL
%%
sentence : X { System.out.println("X found: " + $1); }
| sentence X { System.out.println("X found: " + $2); }
;
%%
private Lexer lexer;
public Parser(Reader reader)
{
lexer = new Lexer(reader);
}
public void yyerror(String error)
{
System.err.println("Error: " + error);
}
public static void main(String args[]) throws IOException
{
System.out.println("Interactive evaluation:");
Parser parser = new Parser(
new InputStreamReader(System.in));
parser.lexer.nextToken();
parser.parse();
}
Вам нужно скомпилировать лексер.flex
jflex lexer.flex
и и
jacc parser.jacc
и три сгенерированных Java-файла ( Lexer.java , Parser.java и ПарсерТокены.java ):
javac \*.java
чтобы, наконец, иметь возможность запустить анализатор:
java Parser
Вот пример терминального сеанса:
Interactive evaluation: word Word wOrD WORD X found: word X found: Word X found: wOrD X found: WORD
Вы можете найти полный исходный код на Github .
На самом деле это было довольно скучно, но теперь мы можем свободно играть с контекстно-свободными грамматиками!
Оригинал: “https://dev.to/vicentemaldonado/use-jflex-and-jacc-together-3nnd”