bambooflow Note

サンプル3

最終更新:

Bot(ページ名リンク)

- view
メンバー限定 登録/ログイン

サンプル3


ここでは、字句解析をクラスとして生成するようにしてみました。
簡単なC記述をターゲットとしています。

  • サンプル「Hello2」


記述


  • hello2.re
#include <cstdio>
#include <cstdlib>
//#include <cstring>
#include <cassert>
#include <fstream>
#include <iostream>

//--------------------------
 
enum {
    TOKEN_ERR = -1,
    TOKEN_EOF = 0,
    TOKEN_SEMI = 10,
    TOKEN_INT,
    TOKEN_VOID,
    TOKEN_RETURN,
    TOKEN_LPALEN,
    TOKEN_RPALEN,
    TOKEN_LCURLY,
    TOKEN_RCURLY,
    TOKEN_ASSIGN,
    TOKEN_ADD,
    TOKEN_IDENT,
    TOKEN_NUM,
};
 
class Scanner {
private:
    std::ifstream ifs;
 
    char* m_buffer;
    char* m_cursor;
    char* m_limit;
    char* m_token;
    char* m_marker;
    int m_buffer_size;
    int m_lineno;
 
public:
 
    Scanner( const std::string path, int init_size=1024 )
        : m_buffer(0)
        , m_cursor(0)
        , m_limit(0)
        , m_token(0)
        , m_marker(0)
        , m_buffer_size(init_size)
        , m_lineno(1)
    {
        m_buffer = new char[m_buffer_size];
 
        ifs.open( path.c_str(), std::ios::in );
        if (!ifs) {
            std::cerr << "can't not open file" << std::endl;
            exit( 1 );
        }
        m_cursor = m_limit = m_token = m_marker = m_buffer;
    }
 
    ~Scanner() {
        ifs.close();
        delete [] m_buffer;
    }
 
    bool fill(int n) {
 
        // EOF判定
        if (ifs.eof()) {
            if ((m_limit-m_cursor) <= 0)
                return false;
        }
 
        int restSize = m_limit-m_token;
        if (restSize+n >= m_buffer_size) {
            // バッファサイズが足りないため拡張する
            m_buffer_size *= 2;
            char* newBuffer = new char[m_buffer_size];
            for (int i=0; i<restSize; ++i) { // memcpy
                *(newBuffer+i) = *(m_token+i);
            }
            m_cursor = newBuffer + (m_cursor-m_token);
            m_token = newBuffer;
            m_limit = newBuffer + restSize;
 
            delete [] m_buffer;
            m_buffer = newBuffer;
        } else {
            // バッファに残っているデータを先頭に移動
            for (int i=0; i<restSize; ++i) { //memmove( m_buffer, m_token, (restSize)*sizeof(char) );
                *(m_buffer+i) = *(m_token+i);
            }
            m_cursor = m_buffer + (m_cursor-m_token);
            m_token = m_buffer;
            m_limit = m_buffer+restSize;
        }
 
        // バッファに充填
        int read_size = m_buffer_size - restSize;
        ifs.read( m_limit, read_size );
        m_limit += ifs.gcount();
 
        return true;
    }
 
 
    std::string text() {
        return std::string( m_token, m_token+length() );
    }
    int length() {
        return (m_cursor-m_token);
    }
    int lineno() {
        return m_lineno;
    }
 
    int scan(void) {
start:
        m_token = m_cursor;
 
    /*!re2c
        re2c:define:YYCTYPE = "char";
        re2c:define:YYCURSOR = m_cursor;
        re2c:define:YYMARKER = m_marker;
        re2c:define:YYLIMIT = m_limit;
        re2c:define:YYFILL:naked = 1;
        re2c:define:YYFILL@len = #;
        re2c:define:YYFILL = "if (!fill(#)) { return TOKEN_eof; }";
        re2c:yyfill:enable = 1;
        re2c:indent:top = 2;
        re2c:indent:string="    ";
 
        IDENT = [a-zA-Z_][a-zA-Z_0-9]*;
 
        "/*"         { goto comment;  }
        [ \t\v\f]+   { goto start; }
        [\r]?|[\n]   { m_lineno++; goto start;  }
 
        "int"        { return TOKEN_INT; }
        "void"       { return TOKEN_VOID; }
        "return"     { return TOKEN_RETURN; }
        ";"          { return TOKEN_SEMI; }
 
        "("          { return TOKEN_LPALEN; }
        ")"          { return TOKEN_RPALEN; }
        "{"          { return TOKEN_LCURLY; }
        "}"          { return TOKEN_RCURLY; }
        "="          { return TOKEN_ASSIGN; }
        "+"          { return TOKEN_ADD; }
 
        [0-9]+       { return TOKEN_NUM; }
        IDENT        { return TOKEN_IDENT; }
 
        [\000]       { return TOKEN_EOF; }
        .            { return TOKEN_ERR; }
    */
comment:
    /*!re2c
        "*/"     { goto start;  }
        "\n"     { m_lineno++; goto comment;  }
        .        { goto comment;  }
    */
    }
};
 
int main( int argc, char* argv[] )
{
    Scanner s( "func.c", 1024 );
    int v;
 
    while ((v=s.scan()) > 0) {
        printf( "scan = %3d, line=%3d, %s\n", v, s.lineno(), s.text().c_str() );
    }
    printf( "scan = %d\n", v );
 
    return 0;
}
 

結果


  • func.c
int a_0 = 100;
 
/* function */
int func(int x) {
    x = x + a_0;
 
    return x;
}
 
 

  • 実行結果
scan =  11, line=  3, int
scan =  20, line=  3, a_0
scan =  18, line=  3, =
scan =  21, line=  3, 100
scan =  10, line=  3, ;
scan =  11, line=  6, int
scan =  20, line=  6, func
scan =  14, line=  6, (
scan =  11, line=  6, int
scan =  20, line=  6, x
scan =  15, line=  6, )
scan =  16, line=  6, {
scan =  20, line=  7, x
scan =  18, line=  7, =
scan =  20, line=  7, x
scan =  19, line=  7, +
scan =  20, line=  7, a_0
scan =  10, line=  7, ;
scan =  13, line=  9, return
scan =  20, line=  9, x
scan =  10, line=  9, ;
scan =  17, line= 10, }
scan = 0
 
 

説明


[[re2c]]:define:YYCTYPE = "char";
文字の型を指定します。大抵の場合"char"または"unsigned char"です。

re2c:define:YYCURSOR = m_cursor;
バッファの先頭を指します。トークンを読むと、ポインタが進みます。
ここでは、トークンを読む前に一旦m_tokenにm_cursorのポインタを保存しておき、トークン読み込み後、m_tokenとm_cursorの差から1つのトークンの長さを求めています。

re2c:define:YYLIMIT = m_limit;
バッファの文字列が詰まっている一番後ろを指します。

re2c:define:YYMARKER = m_marker;
得に使いませんが、宣言しないといけないようです。

re2c:define:YYFILL:naked = 1;
re2c:define:YYFILL@len = #;
re2c:define:YYFILL = "if (!fill(#)) { return TOKEN_eof; }";
re2c:yyfill:enable = 1;
バッファにある文字列が少なくなったとき(m_limit-m_cursor<任意の長さn)、バッファに文字列を充填する必要があります。ここでは充填する記述を指定します。
デフォルトでは、YYFILLに関数を指定するのですが、YYFILL:naked=1とすることで、YYFILLに文字列を直接していできるようになります。
ここでは、バッファに文字列を充填するfill関数にEOF判定を返す機能を持たせるため、少し複雑になっています。


re2c:indent:top = 2;
re2c:indent:string="    ";
インデントを指定します。主に見栄えがよくなります。
stringのデフォルトは"\t"のようです。

タグ:

re2c scanner lexer
記事メニュー
ウィキ募集バナー