文字列は、
それが入力として与えられた時に破棄されないという点で、
コメントとは若干異なります。
しかし、
基本的なアプローチは同じです。
第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
の項を参照してください。