WikiDer > GNU Bison
Оригинальный автор (ы) | Роберт Корбетт |
---|---|
Разработчики) | В Проект GNU |
изначальный выпуск | Июнь 1985 г.[1] |
Стабильный выпуск | 3.7.2 / 5 сентября 2020 г.[2] |
Репозиторий | |
Написано в | C и м4 |
Операционная система | Unix-подобный |
Тип | Генератор парсеров |
Лицензия | GPL |
Интернет сайт | https://savannah.gnu.org/projects/bison/ www |
GNU Bison, широко известный как Бизон, генератор парсеров это часть Проект GNU. Бизон читает спецификацию контекстно-свободный язык, предупреждает о любых разбор неоднозначности, и генерирует синтаксический анализатор (либо в C, C ++, или же Ява), который читает последовательности жетоны и решает, соответствует ли последовательность синтаксису, заданному грамматикой. Сгенерированные парсеры переносимы: они не требуют каких-либо конкретных компиляторов. Bison по умолчанию генерирует LALR (1) парсеры но он также может генерировать канонический LR, IELR (1) и GLR парсеры.[3]
В POSIX режим, Bison совместим с Yacc, но также имеет несколько расширений по сравнению с предыдущей программой, в том числе:
- создание контрпримеров для конфликтов,
- отслеживание местоположения (например, файл, строка, столбец),
- богатые и интернационализированные сообщения об ошибках синтаксиса в сгенерированных парсерах,
- настраиваемая генерация синтаксических ошибок,
- реентерабельные парсеры,
- push-парсеры с автозаполнением,
- поддержка именованных ссылок,
- несколько типов отчетов (графический, XML) по сформированному парсеру,
- поддержка нескольких языков программирования,
- и Т. Д.
Flex, автоматический лексический анализатор, часто используется с Bison для токенизации входных данных и предоставления Bison токенов.[4]
Бизон был первоначально написан Робертом Корбеттом в 1985 году.[1] Позже, в 1989 году, Роберт Корбетт выпустил еще один генератор парсеров под названием Беркли Якк. Bison был сделан Yacc-совместимым Ричард Столмен.[5]
Бизон это бесплатно программное обеспечение и доступен под Стандартная общественная лицензия GNU, за исключением (обсуждается ниже), позволяющего использовать его сгенерированный код без запуска авторское лево требования лицензии.
Некоторые особенности Bison
Генерация контрпримера
Одна из деликатных проблем с генераторами парсеров LR - разрешение конфликтов (конфликты сдвига / уменьшения и уменьшения / уменьшения). Разрешение конфликтов обычно требует анализа автомата парсера, как описано в отчетах, и некоторого опыта со стороны пользователя. Контрпримеры помогают быстро понять некоторые конфликты и даже могут фактически доказать, что проблема в том, что грамматика на самом деле неоднозначна.
Например, по грамматике, страдающей от печально известного болтается еще проблема, Bison сообщает
doc / if-then-else.y: предупреждение: сдвинуть / уменьшить конфликт на токене "else" [-Контрпримеры] Пример: "если" выражение "то" "если" выражение "то" стмт " • "еще" stmt Вывод сдвига if_stmt ↳ "если" выражение "то" stmt ↳ if_stmt ↳ "если" выражение "то" стмт " • "еще" stmt Пример: "если" выражение "то" "если" выражение "то" стмт " • "еще" stmt Уменьшить вывод if_stmt ↳ "если" выражение "то" stmt "еще" stmt ↳ if_stmt ↳ "если" выражение "то" стмт " •
Реентерабельность
Повторный вход - это функция, которая была добавлена в Bison и отсутствует в Yacc.
Обычно Bison генерирует парсер, который не повторно въезжающий. Чтобы добиться повторного входа, декларация % определить api.pure
должны быть использованы. Более подробную информацию о повторном вхождении Bison можно найти в руководстве по Bison.[6]
Несколько языков вывода
Bison может генерировать код для C, C ++ и Ява.[7] Экспериментальный бэкэнд для D также доступен.[8]
Для использования синтаксического анализатора Bison с других языков языковая привязка инструмент, такой как SWIG может быть использован.
Лицензия и распространение сгенерированного кода
Поскольку Bison генерирует исходный код, который, в свою очередь, добавляется к исходному коду других программных проектов, возникает ряд простых, но интересных вопросов об авторских правах.
Лицензия, совместимая с GPL, не требуется
Код, созданный Bison, включает значительное количество кода из самого проекта Bison. Пакет Bison распространяется на условиях Стандартная общественная лицензия GNU (GPL), но было добавлено исключение, так что GPL не применяется к выходным данным.[9][10]
В более ранних выпусках Bison оговаривалось, что часть его вывода также была лицензирована под GPL из-за включения в вывод функции yyparse () из исходного исходного кода.
Распространение пакетов с помощью Bison
Проекты свободного программного обеспечения, использующие Bison, могут выбирать, распространять ли исходный код, который их проект передает в Bison, или полученный код C, выводимый Bison. И того, и другого достаточно, чтобы получатель смог скомпилировать исходный код проекта. Однако распространение только входных данных несет незначительные неудобства, поскольку у получателей должна быть установлена совместимая копия Bison, чтобы они могли сгенерировать необходимый код C при компиляции проекта. Распространение только кода C на выходе создает проблему, затрудняющую получателям изменение парсера, поскольку этот код не был написан ни к человек или за люди - его цель - загрузить непосредственно в компилятор C.
Этих проблем можно избежать, распространив как входные файлы, так и сгенерированный код. Большинство людей будут компилировать с использованием сгенерированного кода, ничем не отличающегося от любого другого программного пакета, но любой, кто хочет изменить компонент парсера, может сначала изменить входные файлы и повторно сгенерировать сгенерированные файлы перед компиляцией. Проекты, распространяющие оба, обычно не имеют сгенерированных файлов в своих контроль версий системы. Файлы создаются только при выпуске релиза.
Некоторые лицензии, такие как GPL, требуют, чтобы исходный код был в "предпочтительная форма работы для внесения в нее изменений". GPL-проекты, использующие Bison, должны, таким образом, распространять файлы, входящие в Bison. Конечно, они также могут включать сгенерированные файлы.
Использовать
Поскольку Bison был написан как замена Yacc и в значительной степени совместим, код из множества проектов, использующих Bison, может быть также загружен в Yacc. Это затрудняет определение того, «использует» ли проект исходный код, специфичный для Bison, или нет. Во многих случаях "использование" Bison может быть тривиально заменено эквивалентным использованием Yacc или одной из других его производных.
Bison действительно имеет функции, которых нет в Yacc, поэтому можно справедливо сказать, что некоторые проекты «используют» Bison, поскольку Yacc будет недостаточно.
В следующем списке перечислены проекты, которые, как известно, «используют» Bison в более свободном смысле, что они используют бесплатные инструменты разработки программного обеспечения и распространяют код, который предназначен для загрузки в Bison или в пакет, совместимый с Bison.
- Собственный синтаксический анализатор грамматики Bison генерируется Bison[11]
- В Рубин язык программирования (YARV)
- В PHP язык программирования (Zend Parser)
- GCC начал использовать Bison, но перешел на рукописный синтаксический анализатор с рекурсивным спуском для C ++ 2004 г. (версия 3.4),[12] и для C и Objective-C в 2006 г. (версия 4.1)[13]
- В Идти язык программирования (GC) использовал Bison, но в версии 1.5 перешел на рукописный сканер и парсер.[14]
- Баш оболочка использует грамматику yacc для анализа вводимой команды.
- Лилипруд
- PostgreSQL[15]
- MySQL[16]
- GNU Octave использует синтаксический анализатор, созданный Bison.[17]
- Perl 5 использует синтаксический анализатор, созданный Bison, начиная с версии 5.10.[18]
Пример полного реентерабельного парсера
Эта секция написано как руководство или путеводитель. (Сентябрь 2016) (Узнайте, как и когда удалить этот шаблон сообщения) |
В следующем примере показано, как использовать Bison и flex для написания простой программы-калькулятора (только сложение и умножение) и программы для создания абстрактное синтаксическое дерево. Следующие два файла предоставляют определение и реализацию функций синтаксического дерева.
/* * Expression.h * Определение структуры, используемой для построения синтаксического дерева. */#ifndef __EXPRESSION_H__#define __EXPRESSION_H__/** * @brief Тип операции */typedef перечислить tagEOperationType{ eVALUE, eMULTIPLY, eADD} EOperationType;/** * @brief Структура выражения */typedef структура tagSExpression{ EOperationType тип; / * / <тип операции * / int ценить; / * / <допустимо, только если тип равен eVALUE * / структура tagSExpression *оставили; / * / <левая часть дерева * / структура tagSExpression *верно; / * / <правая часть дерева * /} SE выражение;/** * @brief Создает идентификатор * @param value Числовое значение * @return Выражение или NULL в случае отсутствия памяти */SE выражение *createNumber(int ценить);/** * @brief Создает операцию * @param type Тип операции * @param left Левый операнд * @param right Правый операнд * @return Выражение или NULL в случае отсутствия памяти */SE выражение *createOperation(EOperationType тип, SE выражение *оставили, SE выражение *верно);/** * @brief Удаляет выражение * @param b Выражение */пустота deleteExpression(SE выражение *б);#endif / * __EXPRESSION_H__ * /
/* * Expression.c * Реализация функций, используемых для построения синтаксического дерева. */#включают "Expression.h"#включают <stdlib.h>/** * @brief - выделяет место для выражения * @return Выражение или NULL, если недостаточно памяти */статический SE выражение *allocateExpression(){ SE выражение *б = (SE выражение *)маллок(размер(SE экспрессия)); если (б == НОЛЬ) возвращаться НОЛЬ; б->тип = eVALUE; б->ценить = 0; б->оставили = НОЛЬ; б->верно = НОЛЬ; возвращаться б;}SE выражение *createNumber(int ценить){ SE выражение *б = allocateExpression(); если (б == НОЛЬ) возвращаться НОЛЬ; б->тип = eVALUE; б->ценить = ценить; возвращаться б;}SE выражение *createOperation(EOperationType тип, SE выражение *оставили, SE выражение *верно){ SE выражение *б = allocateExpression(); если (б == НОЛЬ) возвращаться НОЛЬ; б->тип = тип; б->оставили = оставили; б->верно = верно; возвращаться б;}пустота deleteExpression(SE выражение *б){ если (б == НОЛЬ) возвращаться; deleteExpression(б->оставили); deleteExpression(б->верно); свободный(б);}
Токены, необходимые парсеру Bison, будут сгенерированы с помощью flex.
%{/* * Файл Lexer.l * Для генерации лексического анализатора запустите: "flex Lexer.l" */#включают "Expression.h"#включают "Parser.h"#включают <stdio.h>%}%вариант Outfile="Lexer.c" заголовок-файл="Lexer.h"%вариант предупреждать nodefault%вариант повторно въезжающий noyywrap никогда-интерактивный nounistd%вариант зубр-мост%%[ \р\п\т]* { Продолжить; / * Пропускаем пробелы. * / }[0-9]+ { sscanf(yytext, "% d", &Yylval->ценить); возвращаться TOKEN_NUMBER; }"*" { возвращаться TOKEN_STAR; }"+" { возвращаться TOKEN_PLUS; }"(" { возвращаться TOKEN_LPAREN; }")" { возвращаться TOKEN_RPAREN; }. { Продолжить; / * Игнорировать неожиданные символы. * /}%%int йеррор(const char *сообщение) { fprintf(stderr, "Ошибка:% s п", сообщение); возвращаться 0;}
Имена токенов обычно нейтральны: «TOKEN_PLUS» и «TOKEN_STAR», а не «TOKEN_ADD» и «TOKEN_MULTIPLY». Например, если бы мы поддерживали унарный «+» (как в «+1»), было бы неправильно называть этот «+» «TOKEN_ADD». На таком языке, как C, «int * ptr» обозначает определение указателя, а не продукта: было бы неправильно называть этот «*» «TOKEN_MULTIPLY».
Поскольку токены предоставляются Flex, мы должны предоставить средства для связи между парсер и лексер.[19] Тип данных, используемый для связи, YYSTYPE, устанавливается с помощью Bison % union декларация.
Поскольку в этом примере мы используем реентерабельную версию как flex, так и yacc, мы вынуждены предоставлять параметры для yylex функция, когда вызывается из yyparse.[19] Это делается через Bison % lex-param и % parse-param декларации.[20]
%{/* * Файл Parser.y * Для генерации парсера запустите: "bison Parser.y" */#включают "Expression.h"#включают "Parser.h"#включают "Lexer.h"int йеррор(SE выражение **выражение, yyscan_t сканер, const char *сообщение) { / * При необходимости добавить процедуру обработки ошибок * /}%}%код требует { typedef пустота* yyscan_t;}%выход "Parser.c"%определяет "Parser.h"%определять api.чистый%lex-парам { yyscan_t сканер }%разбирать-парам { SE выражение **выражение }%разбирать-парам { yyscan_t сканер }%союз { int ценить; SE выражение *выражение;}%жетон TOKEN_LPAREN "("%жетон TOKEN_RPAREN ")"%жетон TOKEN_PLUS "+"%жетон TOKEN_STAR "*"%жетон <ценить> TOKEN_NUMBER "номер"%тип <выражение> expr/ * Приоритет (возрастание) и ассоциативность: a + b + c равно (a + b) + c: левая ассоциативность a + b * c равно a + (b * c): приоритет «*» выше, чем «+». * /%оставили "+"%оставили "*"%%Вход : expr { *выражение = $1; } ;expr : expr[L] "+" expr[р] { $$ = createOperation( eADD, $ L, $ R ); } | expr[L] "*" expr[р] { $$ = createOperation( eMULTIPLY, $ L, $ R ); } | "(" expr[E] ")" { $$ = $ E; } | "номер" { $$ = createNumber($1); } ;%%
Код, необходимый для получения синтаксического дерева с помощью синтаксического анализатора, созданного Bison, и сканера, созданного с помощью flex, следующий.
/* * файл main.c */#включают "Expression.h"#включают "Parser.h"#включают "Lexer.h"#включают <stdio.h>int yyparse(SE экспрессия **выражение, yyscan_t сканер);SE выражение *getAST(const char *expr){ SE выражение *выражение; yyscan_t сканер; YY_BUFFER_STATE государственный; если (yylex_init(&сканер)) { / * не удалось инициализировать * / возвращаться НОЛЬ; } государственный = yy_scan_string(expr, сканер); если (yyparse(&выражение, сканер)) { / * анализ ошибок * / возвращаться НОЛЬ; } yy_delete_buffer(государственный, сканер); yylex_destroy(сканер); возвращаться выражение;}int оценивать(SE экспрессия *е){ выключатель (е->тип) { дело eVALUE: возвращаться е->ценить; дело eMULTIPLY: возвращаться оценивать(е->оставили) * оценивать(е->верно); дело eADD: возвращаться оценивать(е->оставили) + оценивать(е->верно); дефолт: / * здесь быть не должно * / возвращаться 0; }}int главный(пустота){ char тест[] = " 4 + 2*10 + 3*( 5 + 1 )"; SE выражение *е = getAST(тест); int результат = оценивать(е); printf("Результат"% s ":% d п", тест, результат); deleteExpression(е); возвращаться 0;}
Вот простой make-файл для сборки проекта.
# MakefileФАЙЛЫ = Lexer.c Parser.c Expression.c main.cCC = g ++CFLAGS = -g -ansiтест: $(ФАЙЛЫ) $(CC) $(CFLAGS) $(ФАЙЛЫ) -о тестLexer.c: Лексер.лflex Lexer.lParser.c: Парсер.у Лексер.cbison Parser.yчистый:rm -f * .o * ~ Lexer.c Lexer.h Parser.c Parser.h тест
Смотрите также
- Беркли Якк (byacc) - еще одна бесплатная замена Yacc, написанная тем же автором, что и GNU Bison
Рекомендации
- ^ а б Корбетт, Роберт Пол (июнь 1985 г.). Статическая семантика и восстановление ошибок компилятора (Кандидат наук.). Калифорнийский университет в Беркли. DTIC ADA611756.
- ^ «Выпущен Bison 3.7.2 [стабильный]».
- ^ Руководство Bison: Введение.
- ^ Левин, Джон (Август 2009 г.). Flex & Bison. O'Reilly Media. ISBN 978-0-596-15597-1.
- ^ Руководство Bison: Чистый (реентерабельный) парсер
- ^ Руководство Bison: Резюме заявления Bison
- ^ https://savannah.gnu.org/forum/forum.php?forum_id=9639 Вышел Bison 3.5
- ^ Руководство по Bison: Условия использования Bison
- ^ Файл исходного кода parse-gram.c, содержащий исключение.
- ^ "parse-gram.y". bison.git. GNU Savannah. Получено 2020-07-29.
- ^ GCC 3.4 Release Series Изменения, новые функции и исправления
- ^ GCC 4.1 Release Series Изменения, новые функции и исправления
- ^ Определение грамматики Голанга
- ^ http://www.postgresql.org/docs/9.0/static/parser-stage.html
- ^ https://www.safaribooksonline.com/library/view/flex-bison/9780596805418/ch04.html
- ^ http://octave.org/doxygen/4.0/d5/d60/oct-parse_8cc_source.html
- ^ http://perldoc.perl.org/perl5100delta.html
- ^ а б Руководство Flex: Сканеры C с анализаторами Bison В архиве 2010-12-17 на Wayback Machine
- ^ Руководство Bison: Соглашения о вызовах для чистых парсеров
дальнейшее чтение
- Левин, Джон (Август 2009 г.). Flex & Bison. O'Reilly Media. ISBN 978-0-596-15597-1.