GNU bölény - GNU Bison

GNU Bölény
Heckert GNU white.svg
Eredeti szerző (k) Robert Corbett
Fejlesztő (k) A GNU projekt
Első kiadás 1985. június ; 36 évvel ezelőtt ( 1985-06 )
Stabil kiadás
3.8.1 / 2021. szeptember 11 .; 36 nappal ezelőtt ( 2021-09-11 )
Adattár
Beírva C és m4
Operációs rendszer Unix-szerű
típus Parser generátor
Engedély GPL
Weboldal www .gnu .org /software /bison / Szerkessze ezt a Wikidatában

A GNU Bison , közismert nevén Bison , egy elemző generátor , amely a GNU projekt része . Bölény elolvassa a kontextusmentes nyelv specifikációját , figyelmeztet az értelmezési kétértelműségekre, és létrehoz egy elemzőt, amely elolvassa a tokenek sorozatát, és eldönti, hogy a sorozat megfelel-e a nyelvtan által meghatározott szintaxisnak. A generált elemzők hordozhatók: nem igényelnek külön fordítókat. A Bison alapértelmezés szerint LALR (1) elemzőket generál, de létrehozhat kanonikus LR , IELR (1) és GLR elemzőket is.

A POSIX módban, Bison kompatibilis Yacc , hanem több kiterjesztése át ezt a korábbi program, beleértve a

  • Ellenpéldák generálása konfliktusokra
  • Helykövetés (pl. Fájl, sor, oszlop)
  • Gazdag és nemzetközivé tehető szintaktikai hibaüzenetek a generált elemzőkben
  • Testreszabható szintaktikai hiba generálás,
  • Visszatérő elemzők
  • Push elemzők, automatikus kiegészítéssel
  • A megnevezett hivatkozások támogatása
  • Többféle jelentés (grafikus, XML) a generált elemzőn
  • Több programozási nyelv támogatása ( C , C ++ , D vagy Java )

A Flex -et , az automatikus lexikai elemzőt gyakran használják a Bison -szal a bemeneti adatok tokenizálásához és a Bison tokenek biztosításához.

A Bison -t eredetileg Robert Corbett írta 1985 -ben. Később, 1989 -ben Robert Corbett kiadott egy másik elemző generátort, Berkeley Yacc néven . A bölényt Richard Stallman tette Yacc-kompatibilisvé .

A Bison ingyenes szoftver, és a GNU General Public License alatt érhető el , egy (az alábbiakban tárgyalt) kivétellel, amely lehetővé teszi, hogy generált kódját a licenc copyleft követelményeinek kiváltása nélkül használják .

Jellemzők

Ellenpélda generálása

Az LR elemző generátorok egyik kényes kérdése a konfliktusok megoldása (váltás/csökkentés és konfliktusok csökkentése/csökkentése). A konfliktusok megoldása általában a jelentésekben leírt elemző automatikus elemzést és a felhasználó bizonyos szakértelmét igényli. Az ellenpéldák segítenek néhány konfliktus gyors megértésében, sőt azt is bizonyítani tudják, hogy a probléma az, hogy a nyelvtan valójában kétértelmű.

Például a nyelvtantól, amely a hírhedt lelógó más problémától szenved - írja Bison

doc/ha-akkor-else.y: figyelmeztetés : shift/konfliktus csökkentése az "else" jelzőn [ -találkozópéldák ]
  Példa: "if" expr "then"  "if" expr "then" stmt   "else" stmt
  Váltási levezetés
    if_stmt 
    ↳ "ha" expr "majd"  stmt 
                         if_stmt 
                           ↳ "ha" expr "majd" stmt   "más" STMT 
  Példa: "ha" expr "majd"  "ha" expr "majd" stmt   "más" stmt
  Csökkentse a levezetést
    if_stmt 
    ↳ "if" expr ", akkor"  stmt                         "else" 
                        stmt  
                           if_stmt ↳ "if" expr "then" stmt  

Visszatérés

A Reentrancy egy olyan szolgáltatás, amelyet hozzáadtak a Bison -hoz, és nem létezik a Yacc -ban.

Általában a Bison olyan elemzőt állít elő, amely nem reentrant . Az ismétlődés eléréséhez a nyilatkozatot %define api.purekell használni. A Bölény visszacsatolásról további részletek a Bölény kézikönyvben találhatók.

Kimeneti nyelvek

A bölény képes kódot generálni a C , C ++ , D és Java számára .

Mert a Bison generált értelmező más nyelvek a nyelv kötelező eszköz, mint például korty lehet használni.

Licenc és a generált kód terjesztése

Mivel a Bison forráskódot generál, amelyet más szoftverprojektek forráskódjához adnak hozzá, néhány egyszerű, de érdekes szerzői jogi kérdést vet fel.

GPL-kompatibilis licenc nem szükséges

A Bison által generált kód jelentős mennyiségű kódot tartalmaz magából a Bison projektből. A Bison csomagot a GNU General Public License (GPL) feltételei szerint terjesztik, de hozzáadtak egy kivételt, így a GPL nem vonatkozik a kimenetre.

A Bison korábbi kiadásai kikötötték, hogy a kimenet egyes részeit a GPL alapján is engedélyezték, mivel az eredeti forráskódból származó yyparse () függvény a kimenetben szerepelt.

Csomagok forgalmazása Bison segítségével

A Bison -t használó szabad szoftverprojektek választhatnak, hogy terjesztik -e a projektjük által a Bison -ba táplált forráskódot, vagy a Bison által kiadott C kódot. Mindkettő elegendő ahhoz, hogy a címzett össze tudja állítani a projekt forráskódját. Azonban csak a bemenet terjesztésével jár az a kisebb kényelmetlenség, hogy a címzetteknek telepíteniük kell a Bison kompatibilis példányát, hogy a projekt összeállításakor elő tudják állítani a szükséges C kódot. És terjesztése csak a C kódot kimenet, megteremti a probléma, hogy nagyon nehéz a címzettek nevét, módosíthatja az elemző, mivel ez a kód volt írva sem az emberi, sem az emberekben - az a célja, hogy kell etetni közvetlenül a C fordító.

Ezek a problémák elkerülhetők a bemeneti fájlok és a generált kód terjesztésével. A legtöbb ember a generált kód használatával fordít, nem különbözik más szoftvercsomagoktól, de bárki, aki módosítani szeretné az elemző összetevőt, először módosíthatja a bemeneti fájlokat, és a generálás előtt újra generálhatja a létrehozott fájlokat. A mindkettőt terjesztő projektek rendszerint nem rendelkeznek a generált fájlokkal a felülvizsgálati vezérlőrendszereikben . A fájlok csak kiadáskor jönnek létre.

Egyes licencek, mint például a GPL , megkövetelik, hogy a forráskód " a munka előnyben részesített formája legyen a módosításhoz ". A GPL Bison projektjeinek tehát el kell osztaniuk azokat a fájlokat, amelyek a Bison számára szolgálnak. Természetesen a generált fájlokat is tartalmazhatják.

Használat

Mivel a Bison -t a Yacc helyettesítésére írták, és nagyrészt kompatibilis, a Bison -t használó projektek kódja egyformán betölthető a Yacc -ba. Ez megnehezíti annak megállapítását, hogy egy projekt "használ-e" Bison-specifikus forráskódot vagy sem. Sok esetben a Bölény "használatát" triviálisan helyettesítheti a Yacc vagy más származékai egyenértékű használata.

A Bison olyan tulajdonságokkal rendelkezik, amelyek nem találhatók meg a Yacc -ban, ezért néhány projektről valóban azt mondhatjuk, hogy "használja" a Bison -t, mivel a Yacc nem lenne elegendő.

A következő lista azokról a projektekről szól, amelyekről ismert, hogy "használnak" Bison-t lazább értelemben, szabad szoftverfejlesztő eszközöket használnak, és olyan kódot terjesztenek, amelyet Bison-ba vagy Bison-kompatibilis csomagba kívánnak betölteni.

  • A Bash shell yacc nyelvtant használ a parancsbevitel elemzésére.
  • Bölény saját nyelvtani elemzőjét a Bölény generálja.
  • A CMake több Bison nyelvtant használ.
  • A GCC a Bison használatával indult, de 2004-ben (3.4-es verzió) és C-ben és Objective-C-ben (4.1-es verzió) váltott kézzel írott rekurzív-leszármazási elemzőre
  • A Go programozási nyelv (GC) a Bison-t használta, de az 1.5-ös verzióban kézzel írott szkennerre és elemzőre váltott.
  • A LilyPond megköveteli, hogy a Bison készítse el az elemzőt.
  • MySQL
  • A GNU Octave egy Bison által generált elemzőt használ.
  • A Perl 5 Bison által generált elemzőt használ 5.10-től kezdődően.
  • A PHP programozási nyelv (Zend Parser).
  • PostgreSQL
  • A Ruby MRI , a Ruby programozási nyelv referencia megvalósítása, Bison grammatikán alapul.
  • A syslog-ng több Bison grammatikát használ össze.

Egy teljes újrafeldolgozó példa

A következő példa bemutatja, hogyan lehet a Bison és a flex segítségével írni egy egyszerű számolóprogramot (csak összeadás és szorzás) és egy programot absztrakt szintaxisfa létrehozásához . A következő két fájl a szintaxisfa függvények meghatározását és megvalósítását tartalmazza.

/*
 * Expression.h
 * Definition of the structure used to build the syntax tree.
 */
#ifndef __EXPRESSION_H__
#define __EXPRESSION_H__

/**
 * @brief The operation type
 */
typedef enum tagEOperationType
{
    eVALUE,
    eMULTIPLY,
    eADD
} EOperationType;

/**
 * @brief The expression structure
 */
typedef struct tagSExpression
{
    EOperationType type; /* /< type of operation */

    int value; /* /< valid only when type is eVALUE */
    struct tagSExpression *left; /* /<  left side of the tree */
    struct tagSExpression *right; /* /< right side of the tree */
} SExpression;

/**
 * @brief It creates an identifier
 * @param value The number value
 * @return The expression or NULL in case of no memory
 */
SExpression *createNumber(int value);

/**
 * @brief It creates an operation
 * @param type The operation type
 * @param left The left operand
 * @param right The right operand
 * @return The expression or NULL in case of no memory
 */
SExpression *createOperation(EOperationType type, SExpression *left, SExpression *right);

/**
 * @brief Deletes a expression
 * @param b The expression
 */
void deleteExpression(SExpression *b);

#endif /* __EXPRESSION_H__ */
/*
 * Expression.c
 * Implementation of functions used to build the syntax tree.
 */

#include "Expression.h"

#include <stdlib.h>

/**
 * @brief Allocates space for expression
 * @return The expression or NULL if not enough memory
 */
static SExpression *allocateExpression()
{
    SExpression *b = (SExpression *)malloc(sizeof(SExpression));

    if (b == NULL)
        return NULL;

    b->type = eVALUE;
    b->value = 0;

    b->left = NULL;
    b->right = NULL;

    return b;
}

SExpression *createNumber(int value)
{
    SExpression *b = allocateExpression();

    if (b == NULL)
        return NULL;

    b->type = eVALUE;
    b->value = value;

    return b;
}

SExpression *createOperation(EOperationType type, SExpression *left, SExpression *right)
{
    SExpression *b = allocateExpression();

    if (b == NULL)
        return NULL;

    b->type = type;
    b->left = left;
    b->right = right;

    return b;
}

void deleteExpression(SExpression *b)
{
    if (b == NULL)
        return;

    deleteExpression(b->left);
    deleteExpression(b->right);

    free(b);
}

A Bison Bars elemzőhöz szükséges tokenek a flex használatával jönnek létre.

%{

/*
 * Lexer.l file
 * To generate the lexical analyzer run: "flex Lexer.l"
 */

#include "Expression.h"
#include "Parser.h"

#include <stdio.h>

%}

%option outfile="Lexer.c" header-file="Lexer.h"
%option warn nodefault

%option reentrant noyywrap never-interactive nounistd
%option bison-bridge

%%

[ \r\n\t]*   { continue; /* Skip blanks. */ }
[0-9]+       { sscanf(yytext, "%d", &yylval->value); return TOKEN_NUMBER; }

"*"          { return TOKEN_STAR; }
"+"          { return TOKEN_PLUS; }
"("          { return TOKEN_LPAREN; }
")"          { return TOKEN_RPAREN; }

.            { continue; /* Ignore unexpected characters. */}

%%

int yyerror(const char *msg) {
    fprintf(stderr, "Error: %s\n", msg);
    return 0;
}

A tokenek neve általában semleges: "TOKEN_PLUS" és "TOKEN_STAR", nem "TOKEN_ADD" és "TOKEN_MULTIPLY". Például, ha támogatnánk az egységes "+" -t (mint a "+1" -ben), helytelen lenne ezt a "+" "TOKEN_ADD" nevet adni. Egy olyan nyelven, mint a C, az "int *ptr" a mutató, nem pedig a termék definícióját jelenti: helytelen lenne ezt a " *" "TOKEN_MULTIPLY" nevet adni.

Mivel a tokeneket a flex biztosítja, biztosítanunk kell az elemző és a lexer közötti kommunikáció eszközét . A kommunikációhoz használt adattípust, YYSTYPE , Bison %union deklarációval állítjuk be .

Mivel ebben a mintában a flex és a yacc reentrant verzióját használjuk, kénytelenek vagyunk megadni a yylex függvény paramétereit , amikor az yyparse meghívja . Ez a Bison %lex-param és %parse-param deklarációkon keresztül történik.

%{

/*
 * Parser.y file
 * To generate the parser run: "bison Parser.y"
 */

#include "Expression.h"
#include "Parser.h"
#include "Lexer.h"

int yyerror(SExpression **expression, yyscan_t scanner, const char *msg) {
    /* Add error handling routine as needed */
}

%}

%code requires {
  typedef void* yyscan_t;
}

%output  "Parser.c"
%defines "Parser.h"

%define api.pure
%lex-param   { yyscan_t scanner }
%parse-param { SExpression **expression }
%parse-param { yyscan_t scanner }

%union {
    int value;
    SExpression *expression;
}

%token TOKEN_LPAREN   "("
%token TOKEN_RPAREN   ")"
%token TOKEN_PLUS     "+"
%token TOKEN_STAR     "*"
%token <value> TOKEN_NUMBER "number"

%type <expression> expr

/* Precedence (increasing) and associativity:
   a+b+c is (a+b)+c: left associativity
   a+b*c is a+(b*c): the precedence of "*" is higher than that of "+". */
%left "+"
%left "*"

%%

input
    : expr { *expression = $1; }
    ;

expr
    : expr[L] "+" expr[R] { $$ = createOperation( eADD, $L, $R ); }
    | expr[L] "*" expr[R] { $$ = createOperation( eMULTIPLY, $L, $R ); }
    | "(" expr[E] ")"     { $$ = $E; }
    | "number"            { $$ = createNumber($1); }
    ;

%%

A Bison által generált elemző és a flex által létrehozott szkenner segítségével a szintaxisfa megszerzéséhez szükséges kód a következő.

/*
 * main.c file
 */

#include "Expression.h"
#include "Parser.h"
#include "Lexer.h"

#include <stdio.h>

int yyparse(SExpression **expression, yyscan_t scanner);

SExpression *getAST(const char *expr)
{
    SExpression *expression;
    yyscan_t scanner;
    YY_BUFFER_STATE state;

    if (yylex_init(&scanner)) {
        /* could not initialize */
        return NULL;
    }

    state = yy_scan_string(expr, scanner);

    if (yyparse(&expression, scanner)) {
        /* error parsing */
        return NULL;
    }

    yy_delete_buffer(state, scanner);

    yylex_destroy(scanner);

    return expression;
}

int evaluate(SExpression *e)
{
    switch (e->type) {
        case eVALUE:
            return e->value;
        case eMULTIPLY:
            return evaluate(e->left) * evaluate(e->right);
        case eADD:
            return evaluate(e->left) + evaluate(e->right);
        default:
            /* should not be here */
            return 0;
    }
}

int main(void)
{
    char test[] = " 4 + 2*10 + 3*( 5 + 1 )";
    SExpression *e = getAST(test);
    int result = evaluate(e);
    printf("Result of '%s' is %d\n", test, result);
    deleteExpression(e);
    return 0;
}

Egy egyszerű makefile a projekt felépítéséhez a következő.

# Makefile

FILES = Lexer.c Parser.c Expression.c main.c
CC = g++
CFLAGS = -g -ansi

test: $(FILES)
	$(CC) $(CFLAGS) $(FILES) -o test

Lexer.c: Lexer.l
	flex Lexer.l

Parser.c: Parser.y Lexer.c
	bison Parser.y

clean:
	rm -f *.o *~ Lexer.c Lexer.h Parser.c Parser.h test

Lásd még

  • Berkeley Yacc (byacc) - egy másik ingyenes szoftver Yacc helyettesítő, ugyanazzal a szerzővel, mint a GNU Bison
  • ANTLR Másik eszköz a nyelvfelismeréshez, egy másik nyílt forráskódú elemzőgenerátor

Hivatkozások

További irodalom

Külső linkek