A bisonc++ használata


Elérhetöségeim

A feladatgyüjtemény 3/1-es feladat b) részfeladatát oldjuk meg. C stílusú függvénydeklarációkhoz kell szintaktikus elemzöt csinálni.

Elsö lépés. Hozzuk létre a lexikális elemzöt. A következö lexikális elemekre lesz szükség: azonosító, nyitó zárójel, csukó zárójel, pontosvesszö, vesszö. Az fv.lex fájlba írjuk be ezeknek a definícióját:

%option noyywrap c++

%{
#include "Parser.h"
%}

LETTER          [a-zA-Z]
NUMBER          [0-9]
WHITESPACE      [ \t\n]

%%
("_"|{LETTER})("_"|{LETTER}|{NUMBER})*          return Parser::IDENTIFIER;
","                                             return Parser::COMMA;
"("                                             return Parser::OPENING_PAREN;
")"                                             return Parser::CLOSING_PAREN;
";"                                             return Parser::SEMICOLON;

{WHITESPACE}+                                   // Skip.

%%

Az egyes lexikális elemekhez egy "return Parser::XXX" utasítást kell írni. Így kommunikál a lexikális elemzö a szintaktikus elemzövel. Figyeljük meg, hogy be kellett include-olni egy Parser.h nevü fájlt. Ezt a bison fogja generálni.

Második lépés. Hozzuk létre a szintaktikus elemzöt. Az fv.y fájlba írjuk az alábbiakat:

%baseclass-preinclude <iostream>

%token IDENTIFIER COMMA OPENING_PAREN CLOSING_PAREN SEMICOLON

%%

start:
        declarationList         { std::cout << "Processing start symbol." << std::endl; }
;

declarationList:

|
        declaration declarationList
;

declaration:
        IDENTIFIER IDENTIFIER OPENING_PAREN parameterList CLOSING_PAREN SEMICOLON
|
        IDENTIFIER IDENTIFIER OPENING_PAREN error CLOSING_PAREN SEMICOLON
        {
                std::cout << "Error in parameter list." << std::endl;
        }
;

parameterList:

|
        parameter parameterTail
;

parameterTail:

|
        COMMA parameter parameterTail
;

parameter:
        IDENTIFIER IDENTIFIER
;

A %token utasítás után sorolhatjuk fel a feldolgozható lexikális elemeket, amelyek a nyelvtan terminálisai lesznek. Ezeknek legyen ugyanaz a neve, mint amit a lexikális elemzönél megadtunk. A %% jel után következnek a nyelvtan szabályai. A bisonban a szabályokat általában leíró névvel szoktuk ellátni, nem egy-egy betüvel jelöljük öket, mint általában a formális nyelveknél. Ha a terminálisokat csupa nagybetüvel írjuk, akkor jól elkülönülnek a nemterminálisoktól. Alapértelmezésben az elsö szabály bal oldala lesz a nyelvtan kezdöszimbóluma (ebben a példában a "start"). Ez felülírható a %start direktívával. Az azonos nemterminálishoz tartozó szabályokat a | (pipe) karakter választja el egymástól, a szabályokon belül pedig fehér szóközök szeparálják a mondatforma elemeit.

Az egyes szabályokhoz akciókat is rendelhetünk, amelyeket kapcsos zárójelek között adhatunk meg. A fenti példában a kezdö szabályra való illeszkedéskor egy üzenetet jelenítünk meg a standard kimeneten. Ha egy mondatformában az "error" nemterminális fordul elö, akkor a szabály illeszkedni fog azokra a mondatokra, amelyekben a hiba az errorral jelzett részmondatban van. Így egyrészt az elemzés nem szakad meg, másrészt specifikus hibaüzenetek kiadására van lehetöség.

Harmadik lépés. A program belépési pontjának létrehozása. Hozzuk létrea a main.cc fájlt az alábbi tartalommal.

#include <iostream>
#include <fstream>
#include <sstream>
#include "Parser.h"
#include <FlexLexer.h>

using namespace std;

yyFlexLexer *fl;

int Parser::lex()
{
        return fl->yylex();
}

int main( int argc, char* argv[] )
{
        if( argc != 2 )
        {
                cerr << "Egy parancssori argumentum kell!" << endl;
                return 1;
        }
        ifstream in( argv[1] );
        if( !in )
        {
                cerr << "Nem tudom megnyitni: " << argv[1] << endl;
                return 1;
        }

        fl = new yyFlexLexer(&in, &cout);
        Parser pars;
        pars.parse();
        return 0;
}

A fenti belépési pont parancssori argumentumként várja annak a fájlnak a nevét, amelyben az elemzendö szöveg található. Szükség szerint ez a müködés értelemszerüen megváltoztatható.

Negyedik lépés. Fordítsuk egybe az eddigieket:

$ flex fv.lex
$ bisonc++ fv.y
$ g++ -o fv *.cc

Az elsö sor a lexikális elemzöt, a második sor a szintaktikus elemzöt hozza létre. A harmadik egybefordítja öket egy az "fv" nevü futtatható fájlba.

Ötödik lépés. A program futtatása. Hozzunk létre egy inputfájlt mondjuk input.txt néven, és futtassuk le rá az elemzöt.

$ ./fv input.txt

Részletes leírást a bison kézikönyvében találunk. A 3/1-es feladat mintamegoldásait is érdemes átnézni.