次: Handling Numbers, 前: Handling Comments, 上: Useful Code
文字列は、
それが入力として与えられた時に破棄されないという点で、
コメントとは若干異なります。
しかし、
基本的なアプローチは同じです。
第1の方法としては、
input()を使って文字列を処理することができます。
コードは以下のようになります。
/*
* string1.lex: input()を使って文字列を処理する
*/
%{
#include <stdio.h>
#include <malloc.h>
#include <ctype.h>
#define ALLOC_SIZE 32 /* バッファの(再)割り当て用 */
#define isodigit(x) ((x) >= '0' && (x) <= '7')
#define hextoint(x) (isdigit((x)) ? (x) - '0'\
: ((x) - 'A') + 10)
void yyerror(char *message)
{
printf("\nError: %s\n",message);
}
%}
%%
\" {
int inch,count,max_size;
char *buffer;
int temp;
buffer = malloc(ALLOC_SIZE);
max_size = ALLOC_SIZE;
inch = input();
count = 0;
while(inch != EOF && inch != '"' && inch != '\n'){
if(inch == '\\'){
inch = input();
switch(inch){
case '\n': inch = input(); break;
case 'b' : inch = '\b'; break;
case 't' : inch = '\t'; break;
case 'n' : inch = '\n'; break;
case 'v' : inch = '\v'; break;
case 'f' : inch = '\f'; break;
case 'r' : inch = '\r'; break;
case 'X' :
case 'x' : inch = input();
if(isxdigit(inch)){
temp = hextoint(toupper(inch));
inch = input();
if(isxdigit(inch)){
temp = (temp << 4) +
hextoint(toupper(inch));
} else {
unput(inch);
}
inch = temp;
} else {
unput(inch);
inch = 'x';
}
break;
default:
if(isodigit(inch)){
temp = inch - '0';
inch = input();
if(isodigit(inch)){
temp = (temp << 3) + (inch - '0');
} else {
unput(inch);
goto done;
}
inch = input();
if(isodigit(inch)){
temp = (temp << 3) + (inch - '0');
} else {
unput(inch);
}
done:
inch = temp;
}
}
}
buffer[count++] = inch;
if(count >= max_size){
buffer = realloc(buffer,max_size + ALLOC_SIZE);
max_size += ALLOC_SIZE;
}
inch = input();
}
if(inch == EOF || inch == '\n'){
yyerror("Unterminated string.");
}
buffer[count] = '\0';
printf("String = \"%s\"\n",buffer);
free(buffer);
}
.
\n
%%
このスキャナは、 複数行にわたる文字列や、 様々なエスケープ・シーケンスを処理します。 また、 文字列がどのような長さでも構わないように、 動的バッファを使っています。 これと同じことをスタート状態を使って行うコードは、 以下のようになります。
/*
* string2.lex: スタート状態を使って文字列をスキャンする例
*/
%{
#include <ctype.h>
#define isodigit(x) ((x) >= '0' && (x) <= '7')
#define hextoint(x) (isdigit((x)) ? (x) - '0' \
: ((x) - 'A') + 10)
char *buffer = NULL;
int buffer_size = 0;
void yyerror(char *message)
{
printf("\nError: %s\n",message);
}
%}
%x STRING
hex (x|X)[0-9a-fA-F]{1,2}
oct [0-7]{1,3}
%%
\" {
buffer = malloc(1);
buffer_size = 1; strcpy(buffer,"");
BEGIN(STRING);
}
<STRING>\n {
yyerror("Unterminated string");
free(buffer);
BEGIN(INITIAL);
}
<STRING><<EOF>> {
yyerror("EOF in string");
free(buffer);
BEGIN(INITIAL);
}
<STRING>[^\\\n"] {
buffer_size += yyleng;
buffer = realloc(buffer,buffer_size+1);
strcat(buffer,yytext);
}
<STRING>\\\n /* エスケープされた改行を無視する */
<STRING>\\{hex} {
int temp =0,loop = 0, foo;
for(loop=yyleng-2; loop>0; loop--){
temp <<= 4;
foo = toupper(yytext[yyleng-loop]);
temp += hextoint(foo);
}
buffer = realloc(buffer,buffer_size+1);
buffer[buffer_size-1] = temp;
buffer[buffer_size] = '\0';
buffer_size += 1;
}
<STRING>\\{oct} {
int temp =0,loop = 0;
for(loop=yyleng-1; loop>0; loop--){
temp <<= 3;
temp += (yytext[yyleng-loop] - '0');
}
buffer = realloc(buffer,buffer_size+1);
buffer[buffer_size-1] = temp;
buffer[buffer_size] = '\0';
buffer_size += 1;
}
<STRING>\\[^\n] {
buffer = realloc(buffer,buffer_size+1);
switch(yytext[yyleng-1]){
case 'b' : buffer[buffer_size-1] = '\b';
break;
case 't' : buffer[buffer_size-1] = '\t';
break;
case 'n' : buffer[buffer_size-1] = '\n';
break;
case 'v' : buffer[buffer_size-1] = '\v';
break;
case 'f' : buffer[buffer_size-1] = '\f';
break;
case 'r' : buffer[buffer_size-1] = '\r';
break;
default : buffer[buffer_size-1] =
yytext[yyleng-1];
}
buffer[buffer_size] = '\0';
buffer_size += 1;
}
<STRING>\" {
printf("string = \"%s\"",buffer);
free(buffer);
BEGIN(INITIAL);
}
%%
このスキャナは、
string1.lexよりもモジュール化されていて、
おそらくはより分かりやすいでしょう。
エラーのルールは、
INITIAL状態に戻るようになっていることに注意してください。
こうしないと、
スキャナは不当な文字列と正当な文字列とを結合してしまいます。
ここでも、
Flexのバッファ(YY_BUF_SIZE)が十分に大きいということをあてにせず、
動的バッファを使いました。
内部バッファが十分に大きいという確信が持てるのであれば、
yytextだけを使うことも可能です。
この場合には、
yytextの右端が確実に最初の位置に留まるようにすることが重要です。
より詳しい情報については、
Flex and Cのyymoreの項を参照してください。