サンプル3
ここでは、字句解析をクラスとして生成するようにしてみました。
簡単なC記述をターゲットとしています。
簡単な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つのトークンの長さを求めています。
ここでは、トークンを読む前に一旦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判定を返す機能を持たせるため、少し複雑になっています。
デフォルトでは、YYFILLに関数を指定するのですが、YYFILL:naked=1とすることで、YYFILLに文字列を直接していできるようになります。
ここでは、バッファに文字列を充填するfill関数にEOF判定を返す機能を持たせるため、少し複雑になっています。
re2c:indent:top = 2; re2c:indent:string=" ";
インデントを指定します。主に見栄えがよくなります。
stringのデフォルトは"\t"のようです。
stringのデフォルトは"\t"のようです。