字句解析器の基本設計
字句解析器はソースコードをトークンに分割するコンパイラの初期段階です。主な機能は以下の通りです:
- ソースコードの入力処理
- トークン分類と値の記録
- コメントや空白の除去
- 字句エラーの検出と報告
- 識別子表と定数表の管理
状態遷移図に基づく実装例
typedef struct {
int type;
char name[20];
int line;
} Token;
bool is_alpha(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
bool is_digit(char c) {
return c >= '0' && c <= '9';
}
Token* tokenize(const char* src) {
Token* tokens = malloc(MAX_TOKENS * sizeof(Token));
int pos = 0;
int line = 1;
while(*src) {
if(is_alpha(*src)) {
// 識別子またはキーワードの処理
char ident[20] = {0};
int i = 0;
while(is_alpha(*src) || is_digit(*src)) {
ident[i++] = *src++;
}
ident[i] = '\0';
// キーワードチェック
if(is_keyword(ident)) {
tokens[pos].type = KEYWORD;
} else {
tokens[pos].type = IDENTIFIER;
}
strcpy(tokens[pos].name, ident);
tokens[pos++].line = line;
}
// 他のトークンタイプの処理...
}
return tokens;
}
DFAを用いた効率的な実装
決定性有限オートマトン(DFA)を使用すると、より効率的な字句解析が可能です。
typedef enum {
STATE_START,
STATE_IDENT,
STATE_NUMBER,
STATE_OPERATOR
} LexerState;
void dfa_lexer(FILE* input) {
LexerState state = STATE_START;
char buffer[100];
int buf_pos = 0;
char c;
while((c = fgetc(input)) != EOF) {
switch(state) {
case STATE_START:
if(is_alpha(c)) {
state = STATE_IDENT;
buffer[buf_pos++] = c;
}
// 他の状態遷移...
break;
case STATE_IDENT:
if(is_alpha(c) || is_digit(c)) {
buffer[buf_pos++] = c;
} else {
buffer[buf_pos] = '\0';
process_token(buffer, TOKEN_IDENT);
buf_pos = 0;
state = STATE_START;
ungetc(c, input);
}
break;
// 他の状態処理...
}
}
}
エラー処理機構
字句解析段階で検出可能な主なエラー:
- 不正な文字(言語仕様にない文字)
- 終了しないコメント
void handle_error(int error_code, int line) {
const char* messages[] = {
"不正な文字",
"コメントが閉じられていません"
};
printf("行 %d: エラー%d - %s\n", line, error_code, messages[error_code-1]);
}
最適化技法
実用的な字句解析器の性能向上策:
- バッファリングによるI/O最適化
- キーワード検索のハッシュ化
- 正規表現マッチングの活用
- 並列処理による高速化
高度な実装例
class Lexer {
std::unordered_map<std::string, TokenType> keywords;
std::string buffer;
size_t position = 0;
public:
Lexer(const std::string& src) : buffer(src) {
// キーワード初期化
keywords = {{"int", TOKEN_INT}, {"if", TOKEN_IF} /* ... */};
}
Token next_token() {
skip_whitespace();
if(position >= buffer.size()) return {TOKEN_EOF};
char current = peek();
if(is_alpha(current)) return read_identifier();
if(is_digit(current)) return read_number();
// 他のトークンタイプ...
}
private:
char peek() const { return buffer[position]; }
Token read_identifier() {
size_t start = position;
while(is_alpha(peek()) || is_digit(peek())) {
advance();
}
std::string id = buffer.substr(start, position-start);
if(keywords.count(id)) {
return {keywords[id], id};
}
return {TOKEN_IDENTIFIER, id};
}
// 他の読み取りメソッド...
};